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();

Reply via email to