This is an automated email from the ASF dual-hosted git repository.
shuber pushed a commit to branch SPIKE-simple-url-forward
in repository https://gitbox.apache.org/repos/asf/unomi.git
The following commit(s) were added to refs/heads/SPIKE-simple-url-forward by
this push:
new a413cf4 DMF-4225 Make it possible to have public and private CXS
endpoints
a413cf4 is described below
commit a413cf4c090b90313019aa6ff1019d3202375dd3
Author: Serge Huber <[email protected]>
AuthorDate: Wed Mar 24 16:43:12 2021 +0100
DMF-4225 Make it possible to have public and private CXS endpoints
---
package/src/main/resources/etc/users.properties | 2 +-
rest/pom.xml | 6 +
.../apache/unomi/rest/AuthenticationFilter.java | 136 +++++++++++++++++++++
.../apache/unomi/rest/ClusterServiceEndPoint.java | 2 +
.../org/apache/unomi/rest/ContextJsonEndpoint.java | 2 +-
.../java/org/apache/unomi/rest/RestServer.java | 24 +++-
6 files changed, 165 insertions(+), 7 deletions(-)
diff --git a/package/src/main/resources/etc/users.properties
b/package/src/main/resources/etc/users.properties
index 1c9cf58..3848b12 100644
--- a/package/src/main/resources/etc/users.properties
+++ b/package/src/main/resources/etc/users.properties
@@ -30,4 +30,4 @@
# with the name "karaf".
#
karaf = ${org.apache.unomi.security.root.password:-karaf},_g_:admingroup
-_g_\:admingroup = group,admin,manager,viewer,systembundles,ssh
+_g_\:admingroup = group,admin,manager,viewer,systembundles,ssh,ROLE_UNOMI_ADMIN
diff --git a/rest/pom.xml b/rest/pom.xml
index 8ed6e07..aef5354 100644
--- a/rest/pom.xml
+++ b/rest/pom.xml
@@ -120,6 +120,12 @@
<version>${cxf.version}</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.karaf.jaas</groupId>
+ <artifactId>org.apache.karaf.jaas.boot</artifactId>
+ <version>${version.karaf}</version>
+ <scope>provided</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/rest/src/main/java/org/apache/unomi/rest/AuthenticationFilter.java
b/rest/src/main/java/org/apache/unomi/rest/AuthenticationFilter.java
new file mode 100644
index 0000000..b76621c
--- /dev/null
+++ b/rest/src/main/java/org/apache/unomi/rest/AuthenticationFilter.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.unomi.rest;
+
+import org.apache.cxf.interceptor.security.DefaultSecurityContext;
+import org.apache.cxf.interceptor.security.JAASLoginInterceptor;
+import org.apache.cxf.interceptor.security.RolePrefixSecurityContextImpl;
+import org.apache.cxf.jaxrs.security.JAASAuthenticationFilter;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.security.SecurityContext;
+import org.apache.karaf.jaas.boot.principal.RolePrincipal;
+import org.apache.karaf.jaas.boot.principal.UserPrincipal;
+
+import javax.annotation.Priority;
+import javax.security.auth.Subject;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * A wrapper filter around JAASAuthenticationFilter so that we can deactivate
JAAS login around some resources and make
+ * them publicly accessible.
+ */
+@PreMatching
+@Priority(Priorities.AUTHENTICATION)
+public class AuthenticationFilter implements ContainerRequestFilter {
+
+ public static final String GUEST_USERNAME = "guest";
+ public static final String GUEST_DEFAULT_ROLE = "ROLE_UNOMI_PUBLIC";
+
+ private JAASAuthenticationFilter jaasAuthenticationFilter;
+ private String roleClassifier;
+ private String roleClassifierType =
JAASLoginInterceptor.ROLE_CLASSIFIER_PREFIX;
+
+ private String guestUsername = GUEST_USERNAME;
+ private List<String> guestRoles = Arrays.asList(GUEST_DEFAULT_ROLE);
+
+ private Set<String> publicPaths = new LinkedHashSet<>();
+
+ public AuthenticationFilter() {
+ jaasAuthenticationFilter = new JAASAuthenticationFilter();
+ }
+
+ public void setContextName(String contextName) {
+ jaasAuthenticationFilter.setContextName(contextName);
+ }
+
+ public void setRoleClassifier(String roleClassifier) {
+ this.roleClassifier = roleClassifier;
+ jaasAuthenticationFilter.setRoleClassifier(roleClassifier);
+ }
+
+ public void setRoleClassifierType(String roleClassifierType) {
+ this.roleClassifierType = roleClassifierType;
+ jaasAuthenticationFilter.setRoleClassifierType(roleClassifierType);
+ }
+
+ public void setRealmName(String realmName) {
+ jaasAuthenticationFilter.setRealmName(realmName);
+ }
+
+ public void setGuestUsername(String guestUsername) {
+ this.guestUsername = guestUsername;
+ }
+
+ public void setGuestRoles(List<String> guestRoles) {
+ this.guestRoles = guestRoles;
+ }
+
+ public void setPublicPaths(Set<String> publicPaths) {
+ this.publicPaths = publicPaths;
+ }
+
+ @Override
+ public void filter(ContainerRequestContext requestContext) throws
IOException {
+ if (isPublicPath(requestContext)) {
+ Message message = JAXRSUtils.getCurrentMessage();
+ Subject guestSubject = new Subject();
+ guestSubject.getPrincipals().add(new UserPrincipal(guestUsername));
+ for (String roleName : guestRoles) {
+ guestSubject.getPrincipals().add(new RolePrincipal(roleName));
+ }
+ message.put(SecurityContext.class,
createSecurityContext(guestUsername, guestSubject));
+ } else{
+ jaasAuthenticationFilter.filter(requestContext);
+ }
+ }
+
+ private boolean isPublicPath(ContainerRequestContext requestContext) {
+ // First we do some quick checks to protect against malformed requests
+ if (requestContext.getMethod() == null) {
+ return false;
+ }
+ if (requestContext.getMethod().length() > 10) {
+ // this is a fishy request, we reject it
+ return false;
+ }
+ if (requestContext.getUriInfo().getPath() == null) {
+ return false;
+ }
+ if (requestContext.getUriInfo().getPath().length() > 1000) {
+ return false;
+ }
+ if (publicPaths.contains(requestContext.getMethod() + " " +
requestContext.getUriInfo().getPath())) {
+ return true;
+ }
+ return false;
+ }
+
+ protected SecurityContext createSecurityContext(String name, Subject
subject) {
+ if (roleClassifier != null) {
+ return new RolePrefixSecurityContextImpl(subject, roleClassifier,
+ roleClassifierType);
+ }
+ return new DefaultSecurityContext(name, subject);
+ }
+
+}
diff --git
a/rest/src/main/java/org/apache/unomi/rest/ClusterServiceEndPoint.java
b/rest/src/main/java/org/apache/unomi/rest/ClusterServiceEndPoint.java
index 9cc869e..cbeb9d8 100644
--- a/rest/src/main/java/org/apache/unomi/rest/ClusterServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/ClusterServiceEndPoint.java
@@ -26,6 +26,7 @@ import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.annotation.security.RolesAllowed;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.ws.rs.*;
@@ -75,6 +76,7 @@ public class ClusterServiceEndPoint {
* @return a list of {@link ClusterNode}
*/
@GET
+ @RolesAllowed("admin")
@Path("/")
public List<ClusterNode> getClusterNodes() {
return clusterService.getClusterNodes();
diff --git a/rest/src/main/java/org/apache/unomi/rest/ContextJsonEndpoint.java
b/rest/src/main/java/org/apache/unomi/rest/ContextJsonEndpoint.java
index 9b53820..5376f62 100644
--- a/rest/src/main/java/org/apache/unomi/rest/ContextJsonEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/ContextJsonEndpoint.java
@@ -70,7 +70,7 @@ public class ContextJsonEndpoint {
@POST
@Path("/context.json")
- public ContextResponse getContext(String contextRequestAsString,
@CookieParam("context-profile-id") String cookieProfileId) {
+ public ContextResponse getContextJSON(String contextRequestAsString,
@CookieParam("context-profile-id") String cookieProfileId) {
try {
ObjectMapper mapper = CustomObjectMapper.getObjectMapper();
JsonFactory factory = mapper.getFactory();
diff --git a/rest/src/main/java/org/apache/unomi/rest/RestServer.java
b/rest/src/main/java/org/apache/unomi/rest/RestServer.java
index 9004aae..15d56f0 100644
--- a/rest/src/main/java/org/apache/unomi/rest/RestServer.java
+++ b/rest/src/main/java/org/apache/unomi/rest/RestServer.java
@@ -21,10 +21,13 @@ import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.feature.LoggingFeature;
+import org.apache.cxf.interceptor.security.SecureAnnotationsInterceptor;
+import org.apache.cxf.interceptor.security.SimpleAuthorizingInterceptor;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.cxf.jaxrs.openapi.OpenApiCustomizer;
import org.apache.cxf.jaxrs.openapi.OpenApiFeature;
import org.apache.cxf.jaxrs.security.JAASAuthenticationFilter;
+import org.apache.cxf.jaxrs.security.SimpleAuthorizingFilter;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
import org.osgi.framework.ServiceReference;
@@ -161,16 +164,27 @@ public class RestServer {
new
org.apache.unomi.persistence.spi.CustomObjectMapper(),
JacksonJaxbJsonProvider.DEFAULT_ANNOTATIONS));
jaxrsServerFactoryBean.setProvider(new
org.apache.cxf.rs.security.cors.CrossOriginResourceSharingFilter());
- JAASAuthenticationFilter jaasFilter = new
org.apache.cxf.jaxrs.security.JAASAuthenticationFilter();
- jaasFilter.setContextName("karaf");
- jaasFilter.setRoleClassifier("ROLE_");
- jaasFilter.setRealmName("cxs");
- jaxrsServerFactoryBean.setProvider(jaasFilter);
+ AuthenticationFilter authenticationFilter = new AuthenticationFilter();
+ authenticationFilter.setContextName("karaf");
+ authenticationFilter.setRoleClassifier("ROLE_UNOMI_");
+ authenticationFilter.setRealmName("cxs");
+ Set<String> publicPaths = new HashSet<>();
+ publicPaths.add("GET context.json");
+ authenticationFilter.setPublicPaths(publicPaths);
+ jaxrsServerFactoryBean.setProvider(authenticationFilter);
for (ExceptionMapper exceptionMapper : exceptionMappers) {
jaxrsServerFactoryBean.setProvider(exceptionMapper);
}
jaxrsServerFactoryBean.setServiceBeans(serviceBeans);
jaxrsServerFactoryBean.getFeatures().add(openApiFeature);
+ SimpleAuthorizingInterceptor simpleAuthorizingInterceptor = new
SimpleAuthorizingInterceptor();
+ Map<String,String> rolesMap = new HashMap<>();
+ rolesMap.put("getContextJSON", "ROLE_UNOMI_PUBLIC");
+ simpleAuthorizingInterceptor.setMethodRolesMap(rolesMap);
+ simpleAuthorizingInterceptor.setGlobalRoles("ROLE_UNOMI_ADMIN");
+ SimpleAuthorizingFilter simpleAuthorizingFilter = new
SimpleAuthorizingFilter();
+ simpleAuthorizingFilter.setInterceptor(simpleAuthorizingInterceptor);
+ jaxrsServerFactoryBean.setProvider(simpleAuthorizingFilter);
if (serviceBeans.size() > 0) {
logger.info("Starting JAX RS Endpoint...");
server = jaxrsServerFactoryBean.create();