CADI authentication and authorization filters 54/84054/4
authorTomek Kaminski <tomasz.kaminski@nokia.com>
Wed, 3 Apr 2019 07:45:09 +0000 (09:45 +0200)
committerTomek Kaminski <tomasz.kaminski@nokia.com>
Fri, 5 Apr 2019 13:13:08 +0000 (15:13 +0200)
Implemented AAF CADI usage for authN/authZ
Old solution left for backward compatibility
UseAAF flag used to turn on/of CADI and AAF permissions
AAfConnection fixed to accept AAF server certificate when adding perm

Change-Id: I8e946bda14c53e57c3236f2a7dfe806bcd45e519
Issue-ID: DMAAP-1112
Signed-off-by: Tomek Kaminski <tomasz.kaminski@nokia.com>
src/main/java/org/onap/dmaap/dbcapi/aaf/AafConnection.java
src/main/java/org/onap/dmaap/dbcapi/resources/AAFAuthenticationFilter.java [new file with mode: 0644]
src/main/java/org/onap/dmaap/dbcapi/resources/AAFAuthorizationFilter.java [new file with mode: 0644]
src/main/java/org/onap/dmaap/dbcapi/resources/AuthorizationFilter.java
src/main/java/org/onap/dmaap/dbcapi/server/JettyServer.java
src/main/java/org/onap/dmaap/dbcapi/util/PermissionBuilder.java [new file with mode: 0644]
src/test/java/org/onap/dmaap/dbcapi/resources/AAFAuthenticationFilterTest.java [new file with mode: 0644]
src/test/java/org/onap/dmaap/dbcapi/resources/AAFAuthorizationFilterTest.java [new file with mode: 0644]
src/test/java/org/onap/dmaap/dbcapi/util/PermissionBuilderTest.java [new file with mode: 0644]

index e22290a..934e541 100644 (file)
@@ -37,8 +37,11 @@ import java.net.UnknownHostException;
 import java.net.ConnectException;
 
 import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLHandshakeException;
 
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
 import org.apache.commons.codec.binary.Base64;
 import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
 import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum;
@@ -129,6 +132,10 @@ public class AafConnection extends BaseLoggingClass {
                        uc.setRequestProperty( "Content-Length", Integer.toString( postData.length ));
                        uc.setUseCaches(false);
                        uc.setDoOutput(true);
+
+                       SSLContext sc = SSLContext.getInstance("SSL");
+                       sc.init(null, trustAllCerts, new java.security.SecureRandom());
+                       uc.setSSLSocketFactory(sc.getSocketFactory());
                        OutputStream os = null;
 
                        
@@ -296,6 +303,27 @@ public class AafConnection extends BaseLoggingClass {
                return rc;
                
        }
+
+       private TrustManager[] trustAllCerts = new TrustManager[]{
+               new X509TrustManager() {
+
+                       @Override
+                       public java.security.cert.X509Certificate[] getAcceptedIssuers()
+                       {
+                               return null;
+                       }
+                       @Override
+                       public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
+                       {
+                               //No need to implement.
+                       }
+                       @Override
+                       public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
+                       {
+                               //No need to implement.
+                       }
+               }
+       };
        
 
 }
diff --git a/src/main/java/org/onap/dmaap/dbcapi/resources/AAFAuthenticationFilter.java b/src/main/java/org/onap/dmaap/dbcapi/resources/AAFAuthenticationFilter.java
new file mode 100644 (file)
index 0000000..8739511
--- /dev/null
@@ -0,0 +1,128 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.resources;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.log4j.Logger;
+import org.eclipse.jetty.http.HttpStatus;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.filter.CadiFilter;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+public class AAFAuthenticationFilter implements Filter {
+
+    private static final Logger LOGGER = Logger.getLogger(AAFAuthenticationFilter.class.getName());
+    static final String CADI_PROPERTIES = "cadi.properties";
+    static final String AAF_AUTHN_FLAG = "UseAAF";
+
+    private boolean isAafEnabled;
+    private CadiFilter cadiFilter;
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+        DmaapConfig dmaapConfig = getConfig();
+        String flag = dmaapConfig.getProperty(AAF_AUTHN_FLAG, "false");
+        isAafEnabled = "true".equalsIgnoreCase(flag);
+        initCadi(dmaapConfig);
+    }
+
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
+        throws IOException, ServletException {
+
+        if(isAafEnabled) {
+            cadiFilter.doFilter(servletRequest, servletResponse, filterChain);
+            updateResponseBody((HttpServletResponse)servletResponse);
+        } else {
+            filterChain.doFilter(servletRequest, servletResponse);
+        }
+    }
+
+    private void updateResponseBody(HttpServletResponse httpResponse)
+        throws IOException {
+        if(httpResponse.getStatus() == 401) {
+            String errorMsg = "invalid or no credentials provided";
+            LOGGER.error(errorMsg);
+            httpResponse.setContentType("application/json");
+            httpResponse.setCharacterEncoding("UTF-8");
+            httpResponse.getWriter().print(buildErrorResponse(errorMsg));
+            httpResponse.getWriter().flush();
+        }
+    }
+
+    private String buildErrorResponse(String msg) {
+        try {
+            return new ObjectMapper().writeValueAsString(new ApiError(HttpStatus.UNAUTHORIZED_401, msg, "Authentication"));
+        } catch (JsonProcessingException e) {
+            LOGGER.warn("Could not serialize response entity: " + e.getMessage());
+            return "";
+        }
+    }
+
+
+    @Override
+    public void destroy() {
+        //nothing to cleanup
+    }
+
+    private void initCadi(DmaapConfig dmaapConfig) throws ServletException {
+        if(isAafEnabled) {
+            try {
+                String cadiPropertiesFile = dmaapConfig.getProperty(CADI_PROPERTIES);
+                if(cadiPropertiesFile != null && !cadiPropertiesFile.isEmpty()) {
+                    cadiFilter = new CadiFilter(new PropAccess(cadiPropertiesFile));
+                } else {
+                    throw new ServletException("Cannot initialize CADI filter.CADI properties not available.");
+                }
+            } catch (ServletException e) {
+                LOGGER.error("CADI init error :" + e.getMessage());
+                throw e;
+            }
+        }
+    }
+
+    DmaapConfig getConfig() {
+        return (DmaapConfig) DmaapConfig.getConfig();
+    }
+
+    //tests only
+    CadiFilter getCadiFilter() {
+        return cadiFilter;
+    }
+
+    void setCadiFilter(CadiFilter cadiFilter) {
+        this.cadiFilter = cadiFilter;
+    }
+
+    boolean isAafEnabled() {
+        return isAafEnabled;
+    }
+}
diff --git a/src/main/java/org/onap/dmaap/dbcapi/resources/AAFAuthorizationFilter.java b/src/main/java/org/onap/dmaap/dbcapi/resources/AAFAuthorizationFilter.java
new file mode 100644 (file)
index 0000000..5bc3dec
--- /dev/null
@@ -0,0 +1,116 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.resources;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.log4j.Logger;
+import org.eclipse.jetty.http.HttpStatus;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.service.DmaapService;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+import org.onap.dmaap.dbcapi.util.PermissionBuilder;
+
+public class AAFAuthorizationFilter implements Filter{
+
+    private static final Logger LOGGER = Logger.getLogger(AAFAuthenticationFilter.class.getName());
+    static final String AAF_AUTHZ_FLAG = "UseAAF";
+    private boolean isAafEnabled = false;
+
+    private PermissionBuilder permissionBuilder;
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+        DmaapConfig dmaapConfig = getConfig();
+        isAafEnabled = "true".equalsIgnoreCase(dmaapConfig.getProperty(AAF_AUTHZ_FLAG, "false"));
+        if(isAafEnabled) {
+            permissionBuilder = new PermissionBuilder(dmaapConfig, getDmaapService());
+        }
+    }
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
+        throws IOException, ServletException {
+
+        if(isAafEnabled) {
+            HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
+            permissionBuilder.updateDmaapInstance();
+            String permission = permissionBuilder.buildPermission(httpRequest);
+
+            if (httpRequest.isUserInRole(permission)) {
+                LOGGER.info("User " + httpRequest.getUserPrincipal().getName() + " has permission " + permission);
+                filterChain.doFilter(servletRequest, servletResponse);
+            } else {
+                String msg = "User " + httpRequest.getUserPrincipal().getName() + " does not have permission " + permission;
+                LOGGER.error(msg);
+                ((HttpServletResponse) servletResponse).setStatus(HttpStatus.FORBIDDEN_403);
+                servletResponse.setContentType("application/json");
+                servletResponse.setCharacterEncoding("UTF-8");
+                servletResponse.getWriter().print(buildErrorResponse(msg));
+                servletResponse.getWriter().flush();
+            }
+        } else {
+            filterChain.doFilter(servletRequest, servletResponse);
+        }
+    }
+
+    @Override
+    public void destroy() {
+        //nothing to cleanup
+    }
+
+    DmaapConfig getConfig() {
+        return (DmaapConfig) DmaapConfig.getConfig();
+    }
+
+    DmaapService getDmaapService() {
+        return new DmaapService();
+    }
+
+    private String buildErrorResponse(String msg) {
+        try {
+            return new ObjectMapper().writeValueAsString(new ApiError(HttpStatus.FORBIDDEN_403, msg, "Authorization"));
+        } catch (JsonProcessingException e) {
+            LOGGER.warn("Could not serialize response entity: " + e.getMessage());
+            return "";
+        }
+    }
+
+    PermissionBuilder getPermissionBuilder() {
+        return permissionBuilder;
+    }
+
+    void setPermissionBuilder(PermissionBuilder permissionBuilder) {
+        this.permissionBuilder = permissionBuilder;
+    }
+
+    void setAafEnabled(boolean aafEnabled) {
+        isAafEnabled = aafEnabled;
+    }
+}
index fd5b4aa..3ed5717 100644 (file)
@@ -26,33 +26,44 @@ import javax.ws.rs.container.ContainerRequestFilter;
 import org.apache.log4j.Logger;
 import org.onap.dmaap.dbcapi.authentication.AuthenticationErrorException;
 import org.onap.dmaap.dbcapi.service.ApiService;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
 
 
 @Authorization
 public class AuthorizationFilter implements ContainerRequestFilter   {
-       
-       private Logger logger = Logger.getLogger(AuthorizationFilter.class.getName());
-       private ResponseBuilder responseBuilder = new ResponseBuilder();
-       
+
+       private static final String AAF_FLAG = "UseAAF";
+       private final Logger logger = Logger.getLogger(AuthorizationFilter.class.getName());
+       private final ResponseBuilder responseBuilder = new ResponseBuilder();
+       private final boolean isAafEnabled;
+
+
+       public AuthorizationFilter() {
+               DmaapConfig dmaapConfig = (DmaapConfig) DmaapConfig.getConfig();
+               String flag = dmaapConfig.getProperty(AAF_FLAG, "false");
+               isAafEnabled = "true".equalsIgnoreCase(flag);
+       }
+
        @Override
        public void filter(ContainerRequestContext requestContext) {
 
-               ApiService apiResp = new ApiService()
-                       .setAuth( requestContext.getHeaderString("Authorization") )
-                       .setUriPath(requestContext.getUriInfo().getPath())
-                       .setHttpMethod( requestContext.getMethod() )
-                       .setRequestId( requestContext.getHeaderString("X-ECOMP-RequestID") );
-
-               try {
-                       apiResp.checkAuthorization();
-               } catch ( AuthenticationErrorException ae ) {
-                       logger.error("Error", ae);
-                       requestContext.abortWith( responseBuilder.unauthorized( apiResp.getErr().getMessage() ) );
-               } catch ( Exception e ) {
-                       logger.error("Error", e);
-                       requestContext.abortWith( responseBuilder.unavailable() );
-               }
+               if(!isAafEnabled) {
+                       ApiService apiResp = new ApiService()
+                               .setAuth(requestContext.getHeaderString("Authorization"))
+                               .setUriPath(requestContext.getUriInfo().getPath())
+                               .setHttpMethod(requestContext.getMethod())
+                               .setRequestId(requestContext.getHeaderString("X-ECOMP-RequestID"));
 
+                       try {
+                               apiResp.checkAuthorization();
+                       } catch (AuthenticationErrorException ae) {
+                               logger.error("Error", ae);
+                               requestContext.abortWith(responseBuilder.unauthorized(apiResp.getErr().getMessage()));
+                       } catch (Exception e) {
+                               logger.error("Error", e);
+                               requestContext.abortWith(responseBuilder.unavailable());
+                       }
+               }
        }
 
 }
index 7f34725..7457ce9 100644 (file)
@@ -22,7 +22,8 @@
 
 package org.onap.dmaap.dbcapi.server;
 
-
+import com.google.common.collect.Sets;
+import javax.servlet.DispatcherType;
 import org.eclipse.jetty.server.*;
 import org.eclipse.jetty.servlet.DefaultServlet;
 import org.eclipse.jetty.servlet.ServletContextHandler;
@@ -31,6 +32,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
 
 import java.util.Properties;
+
 /**
  * A  Jetty server which supports:
  *     - http and https (simultaneously for dev env)
@@ -38,48 +40,47 @@ import java.util.Properties;
  *  - static html pages (for documentation).
  */
 public class JettyServer extends BaseLoggingClass {
-       private Server server;
 
+    private Server server;
 
-       public Server getServer() {
-               return server;
-       }
 
-    public JettyServer( Properties params ) throws Exception {
+    public Server getServer() {
+        return server;
+    }
+
+    public JettyServer(Properties params) throws Exception {
 
         server = new Server();
-       int httpPort = Integer.valueOf(params.getProperty("IntHttpPort", "80" ));
-               int sslPort = Integer.valueOf(params.getProperty("IntHttpsPort", "443" ));
-               boolean allowHttp = Boolean.valueOf(params.getProperty("HttpAllowed", "false"));
-       serverLogger.info( "port params: http=" + httpPort + " https=" + sslPort );
-       serverLogger.info( "allowHttp=" + allowHttp );
-
-               // HTTP Server
-       HttpConfiguration http_config = new HttpConfiguration();
-       http_config.setSecureScheme("https");
-       http_config.setSecurePort(sslPort);
-       http_config.setOutputBufferSize(32768);
-
-       
-       
-        try(ServerConnector httpConnector = new ServerConnector(server, new HttpConnectionFactory(http_config))) {
-                       httpConnector.setPort(httpPort);
-                       httpConnector.setIdleTimeout(30000);
-
-
-                       // HTTPS Server
-
-                       HttpConfiguration https_config = new HttpConfiguration(http_config);
-                       https_config.addCustomizer(new SecureRequestCustomizer());
-                       SslContextFactory sslContextFactory = new SslContextFactory();
-
-                       setUpKeystore(params, sslContextFactory);
-                       setUpTrustStore(params, sslContextFactory);
-
-                       if (sslPort != 0) {
-                try(ServerConnector sslConnector = new ServerConnector(server,
-                                               new SslConnectionFactory(sslContextFactory, "http/1.1"),
-                                               new HttpConnectionFactory(https_config))) {
+        int httpPort = Integer.valueOf(params.getProperty("IntHttpPort", "80"));
+        int sslPort = Integer.valueOf(params.getProperty("IntHttpsPort", "443"));
+        boolean allowHttp = Boolean.valueOf(params.getProperty("HttpAllowed", "false"));
+        serverLogger.info("port params: http=" + httpPort + " https=" + sslPort);
+        serverLogger.info("allowHttp=" + allowHttp);
+
+        // HTTP Server
+        HttpConfiguration http_config = new HttpConfiguration();
+        http_config.setSecureScheme("https");
+        http_config.setSecurePort(sslPort);
+        http_config.setOutputBufferSize(32768);
+
+        try (ServerConnector httpConnector = new ServerConnector(server, new HttpConnectionFactory(http_config))) {
+            httpConnector.setPort(httpPort);
+            httpConnector.setIdleTimeout(30000);
+
+            // HTTPS Server
+
+            HttpConfiguration https_config = new HttpConfiguration(http_config);
+            https_config.addCustomizer(new SecureRequestCustomizer());
+            SslContextFactory sslContextFactory = new SslContextFactory();
+            sslContextFactory.setWantClientAuth(true);
+
+            setUpKeystore(params, sslContextFactory);
+            setUpTrustStore(params, sslContextFactory);
+
+            if (sslPort != 0) {
+                try (ServerConnector sslConnector = new ServerConnector(server,
+                    new SslConnectionFactory(sslContextFactory, "http/1.1"),
+                    new HttpConnectionFactory(https_config))) {
                     sslConnector.setPort(sslPort);
                     if (allowHttp) {
                         logger.info("Starting httpConnector on port " + httpPort);
@@ -91,62 +92,71 @@ public class JettyServer extends BaseLoggingClass {
                         server.setConnectors(new Connector[]{sslConnector});
                     }
                 }
-                       } else {
-                               serverLogger.info("NOT starting sslConnector on port " + sslPort + " for https");
-                               if (allowHttp) {
-                                       serverLogger.info("Starting httpConnector on port " + httpPort);
-                                       server.setConnectors(new Connector[]{httpConnector});
-                               }
-                       }
-               }
+            } else {
+                serverLogger.info("NOT starting sslConnector on port " + sslPort + " for https");
+                if (allowHttp) {
+                    serverLogger.info("Starting httpConnector on port " + httpPort);
+                    server.setConnectors(new Connector[]{httpConnector});
+                }
+            }
+        }
 
         // Set context for servlet.  This is shared for http and https
-               ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
-       context.setContextPath("/");
-        server.setHandler( context );
+        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
+        context.setContextPath("/");
+        server.setHandler(context);
 
-        ServletHolder jerseyServlet = context.addServlet( org.glassfish.jersey.servlet.ServletContainer.class, "/webapi/*");
+        ServletHolder jerseyServlet = context
+            .addServlet(org.glassfish.jersey.servlet.ServletContainer.class, "/webapi/*");
         jerseyServlet.setInitOrder(1);
-        jerseyServlet.setInitParameter("jersey.config.server.provider.packages", "org.onap.dmaap.dbcapi.resources" );   
-        jerseyServlet.setInitParameter("javax.ws.rs.Application", "org.onap.dmaap.dbcapi.server.ApplicationConfig" );
-        
+        jerseyServlet.setInitParameter("jersey.config.server.provider.packages", "org.onap.dmaap.dbcapi.resources");
+        jerseyServlet.setInitParameter("javax.ws.rs.Application", "org.onap.dmaap.dbcapi.server.ApplicationConfig");
+
         // also serve up some static pages...
-        ServletHolder staticServlet = context.addServlet(DefaultServlet.class,"/*");
-        staticServlet.setInitParameter("resourceBase","www");
-        staticServlet.setInitParameter("pathInfoOnly","true");
+        ServletHolder staticServlet = context.addServlet(DefaultServlet.class, "/*");
+        staticServlet.setInitParameter("resourceBase", "www");
+        staticServlet.setInitParameter("pathInfoOnly", "true");
+
+        registerAuthFilters(context);
 
         try {
 
             serverLogger.info("Starting jetty server");
-               String unit_test = params.getProperty("UnitTest", "No");
+            String unit_test = params.getProperty("UnitTest", "No");
             serverLogger.info("UnitTest=" + unit_test);
-                       if ( unit_test.equals( "No" ) ) {
-                       server.start();
-                       server.dumpStdErr();
-               server.join();
-                       }
-        } catch ( Exception e ) {
-               errorLogger.error( "Exception " + e );
+            if (unit_test.equals("No")) {
+                server.start();
+                server.dumpStdErr();
+                server.join();
+            }
+        } catch (Exception e) {
+            errorLogger.error("Exception " + e);
         } finally {
-               server.destroy();
+            server.destroy();
         }
-        
+
+    }
+
+    private void registerAuthFilters(ServletContextHandler context) {
+        context.addFilter("org.onap.dmaap.dbcapi.resources.AAFAuthenticationFilter", "/webapi/*",
+            Sets.newEnumSet(Sets.newHashSet(DispatcherType.FORWARD, DispatcherType.REQUEST), DispatcherType.class));
+        context.addFilter("org.onap.dmaap.dbcapi.resources.AAFAuthorizationFilter", "/webapi/*",
+            Sets.newEnumSet(Sets.newHashSet(DispatcherType.FORWARD, DispatcherType.REQUEST), DispatcherType.class));
     }
 
-       private void setUpKeystore(Properties params, SslContextFactory sslContextFactory) {
-               String keystore = params.getProperty("KeyStoreFile", "etc/keystore");
-               logger.info("https Server using keystore at " + keystore);
-               sslContextFactory.setKeyStorePath(keystore);
-               sslContextFactory.setKeyStoreType(params.getProperty("KeyStoreType", "jks"));
-               sslContextFactory.setKeyStorePassword(params.getProperty("KeyStorePassword", "changeit"));
-               sslContextFactory.setKeyManagerPassword(params.getProperty("KeyPassword", "changeit"));
-       }
-
-       private void setUpTrustStore(Properties params, SslContextFactory sslContextFactory) {
-               String truststore = params.getProperty("TrustStoreFile", "etc/org.onap.dmaap-bc.trust.jks");
-               logger.info("https Server using truststore at " + truststore);
-               sslContextFactory.setTrustStorePath(truststore);
-               sslContextFactory.setTrustStoreType(params.getProperty("TrustStoreType", "jks"));
-               sslContextFactory.setTrustStorePassword(params.getProperty("TrustStorePassword", "changeit"));
-       }
+    private void setUpKeystore(Properties params, SslContextFactory sslContextFactory) {
+        String keystore = params.getProperty("KeyStoreFile", "etc/keystore");
+        logger.info("https Server using keystore at " + keystore);
+        sslContextFactory.setKeyStorePath(keystore);
+        sslContextFactory.setKeyStorePassword(params.getProperty("KeyStorePassword", "changeit"));
+        sslContextFactory.setKeyManagerPassword(params.getProperty("KeyPassword", "changeit"));
+    }
+
+    private void setUpTrustStore(Properties params, SslContextFactory sslContextFactory) {
+        String truststore = params.getProperty("TrustStoreFile", "etc/org.onap.dmaap-bc.trust.jks");
+        logger.info("https Server using truststore at " + truststore);
+        sslContextFactory.setTrustStorePath(truststore);
+        sslContextFactory.setTrustStoreType(params.getProperty("TrustStoreType", "jks"));
+        sslContextFactory.setTrustStorePassword(params.getProperty("TrustStorePassword", "changeit"));
+    }
 }
diff --git a/src/main/java/org/onap/dmaap/dbcapi/util/PermissionBuilder.java b/src/main/java/org/onap/dmaap/dbcapi/util/PermissionBuilder.java
new file mode 100644 (file)
index 0000000..44c94af
--- /dev/null
@@ -0,0 +1,86 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.util;
+
+import javax.servlet.http.HttpServletRequest;
+import org.onap.dmaap.dbcapi.model.Dmaap;
+import org.onap.dmaap.dbcapi.service.DmaapService;
+
+public class PermissionBuilder {
+
+    static final String API_NS_PROP = "ApiNamespace";
+    static final String DEFAULT_API_NS = "org.onap.dmaap-bc.api";
+    static final String BOOT_INSTANCE = "boot";
+    private static final String PERM_SEPARATOR = "|";
+    private static final String NS_SEPARATOR = ".";
+    private DmaapConfig dmaapConfig;
+    private DmaapService dmaapService;
+    private String instance;
+    private String apiNamespace;
+
+    public PermissionBuilder(DmaapConfig dmaapConfig, DmaapService dmaapService) {
+        this.dmaapConfig = dmaapConfig;
+        this.dmaapService = dmaapService;
+        initFields();
+    }
+
+    public synchronized void updateDmaapInstance() {
+        if(instance == null || instance.isEmpty() || instance.equalsIgnoreCase(BOOT_INSTANCE)) {
+            String dmaapName = getDmaapName();
+            instance = (dmaapName == null || dmaapName.isEmpty()) ? BOOT_INSTANCE : dmaapName;
+        }
+    }
+
+    public String buildPermission(HttpServletRequest httpRequest) {
+
+        StringBuilder sb = new StringBuilder(apiNamespace);
+        sb.append(NS_SEPARATOR)
+            .append(getPermissionType(httpRequest.getPathInfo()))
+            .append(PERM_SEPARATOR)
+            .append(instance)
+            .append(PERM_SEPARATOR)
+            .append(httpRequest.getMethod());
+        return sb.toString();
+    }
+
+
+    private void initFields() {
+        apiNamespace = dmaapConfig.getProperty(API_NS_PROP, DEFAULT_API_NS);
+        updateDmaapInstance();
+    }
+
+    private String getDmaapName() {
+        Dmaap dmaap = dmaapService.getDmaap();
+        return ( dmaap != null ) ? dmaap.getDmaapName() : BOOT_INSTANCE;
+    }
+
+    private String getPermissionType(String pathInfo) {
+        char pathSeparator = '/';
+        String relativePath = (pathInfo.charAt(pathInfo.length()-1) == pathSeparator) ?
+            pathInfo.substring(0,pathInfo.length()-1) : pathInfo;
+
+        String[] pathSlices = relativePath.split(String.valueOf(pathSeparator));
+        return pathSlices[pathSlices.length-1];
+    }
+
+    String getInstance() {
+        return instance;
+    }
+}
diff --git a/src/test/java/org/onap/dmaap/dbcapi/resources/AAFAuthenticationFilterTest.java b/src/test/java/org/onap/dmaap/dbcapi/resources/AAFAuthenticationFilterTest.java
new file mode 100644 (file)
index 0000000..d5ae5fd
--- /dev/null
@@ -0,0 +1,178 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.resources;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.onap.aaf.cadi.filter.CadiFilter;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+@RunWith(MockitoJUnitRunner.class)
+public class AAFAuthenticationFilterTest {
+
+    @Spy
+    private AAFAuthenticationFilter filter;
+    @Mock
+    private FilterConfig filterConfig;
+    @Mock
+    private CadiFilter cadiFilterMock;
+    @Mock
+    private HttpServletRequest servletRequest;
+    @Mock
+    private HttpServletResponse servletResponse;
+    @Mock
+    private FilterChain filterChain;
+    @Mock
+    private DmaapConfig dmaapConfig;
+
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
+
+    @Before
+    public void setUp() throws Exception {
+        doReturn(dmaapConfig).when(filter).getConfig();
+    }
+
+    @Test
+    public void init_shouldNotInitializeCADI_whenAafIsNotUsed() throws Exception {
+        //given
+        doReturn("false").when(dmaapConfig).getProperty(eq(AAFAuthenticationFilter.AAF_AUTHN_FLAG), anyString());
+
+        //when
+        filter.init(filterConfig);
+
+        //then
+        assertFalse(filter.isAafEnabled());
+        assertNull(filter.getCadiFilter());
+    }
+
+    @Test
+    public void doFilter_shouldSkipCADI_whenAafIsNotUsed() throws Exception {
+        //given
+        doReturn("false").when(dmaapConfig).getProperty(eq(AAFAuthenticationFilter.AAF_AUTHN_FLAG), anyString());
+        filter.init(filterConfig);
+        filter.setCadiFilter(cadiFilterMock);
+
+        //when
+        filter.doFilter(servletRequest, servletResponse, filterChain);
+
+        //then
+        verify(filterChain).doFilter(servletRequest,servletResponse);
+        verifyZeroInteractions(cadiFilterMock,servletRequest,servletResponse);
+    }
+
+    @Test
+    public void init_shouldFail_whenAafIsUsed_andCadiPropertiesHasNotBeenSet() throws Exception {
+        //given
+        doReturn("true").when(dmaapConfig).getProperty(eq(AAFAuthenticationFilter.AAF_AUTHN_FLAG), anyString());
+        doReturn("").when(dmaapConfig).getProperty(AAFAuthenticationFilter.CADI_PROPERTIES);
+
+        //then
+        thrown.expect(ServletException.class);
+        thrown.expectMessage("Cannot initialize CADI filter.CADI properties not available.");
+
+        //when
+        filter.init(filterConfig);
+    }
+
+    @Test
+    public void init_shouldInitializeCADI_whenAafIsUsed_andCadiPropertiesSet() throws Exception {
+        //given
+        doReturn("true").when(dmaapConfig).getProperty(eq(AAFAuthenticationFilter.AAF_AUTHN_FLAG), anyString());
+        doReturn("cadi.properties").when(dmaapConfig).getProperty(AAFAuthenticationFilter.CADI_PROPERTIES);
+
+        //when
+        filter.init(filterConfig);
+
+        //then
+        assertTrue(filter.isAafEnabled());
+        assertNotNull(filter.getCadiFilter());
+    }
+
+    @Test
+    public void doFilter_shouldUseCADIfilter_andAuthenticateUser_whenAAFisUsed_andUserIsValid() throws Exception{
+        //given
+        initCADIFilter();
+        doReturn(200).when(servletResponse).getStatus();
+
+        //when
+        filter.doFilter(servletRequest,servletResponse,filterChain);
+
+        //then
+        verify(cadiFilterMock).doFilter(servletRequest,servletResponse,filterChain);
+        verify(servletResponse).getStatus();
+        verifyNoMoreInteractions(servletResponse);
+        verifyZeroInteractions(filterChain, servletRequest);
+    }
+
+    @Test
+    public void doFilter_shouldUseCADIfilter_andReturnAuthenticationError_whenAAFisUsed_andUserInvalid() throws Exception{
+        //given
+        String errorResponseJson = "{\"code\":401,\"message\":\"invalid or no credentials provided\",\"fields\":\"Authentication\",\"2xx\":false}";
+        initCADIFilter();
+        doReturn(401).when(servletResponse).getStatus();
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        doReturn(pw).when(servletResponse).getWriter();
+
+        //when
+        filter.doFilter(servletRequest,servletResponse,filterChain);
+
+        //then
+        verify(cadiFilterMock).doFilter(servletRequest,servletResponse,filterChain);
+        verify(servletResponse).getStatus();
+        verify(servletResponse).setContentType("application/json");
+        verifyZeroInteractions(filterChain, servletRequest);
+        assertEquals(errorResponseJson, sw.toString());
+    }
+
+    private void initCADIFilter() throws Exception{
+        doReturn("true").when(dmaapConfig).getProperty(eq(AAFAuthenticationFilter.AAF_AUTHN_FLAG), anyString());
+        doReturn("cadi.properties").when(dmaapConfig).getProperty(AAFAuthenticationFilter.CADI_PROPERTIES);
+        filter.init(filterConfig);
+        filter.setCadiFilter(cadiFilterMock);
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/onap/dmaap/dbcapi/resources/AAFAuthorizationFilterTest.java b/src/test/java/org/onap/dmaap/dbcapi/resources/AAFAuthorizationFilterTest.java
new file mode 100644 (file)
index 0000000..73794cd
--- /dev/null
@@ -0,0 +1,172 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.resources;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.onap.dmaap.dbcapi.model.Dmaap;
+import org.onap.dmaap.dbcapi.service.DmaapService;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+import org.onap.dmaap.dbcapi.util.PermissionBuilder;
+import sun.security.acl.PrincipalImpl;
+
+@RunWith(MockitoJUnitRunner.class)
+public class AAFAuthorizationFilterTest {
+
+    @Spy
+    private AAFAuthorizationFilter filter;
+    @Mock
+    private FilterConfig filterConfig;
+    @Mock
+    private HttpServletRequest servletRequest;
+    @Mock
+    private HttpServletResponse servletResponse;
+    @Mock
+    private FilterChain filterChain;
+    @Mock
+    private DmaapConfig dmaapConfig;
+    @Mock
+    private PermissionBuilder permissionBuilder;
+    @Mock
+    private DmaapService dmaapService;
+
+    @Before
+    public void setUp() throws Exception {
+        filter.setPermissionBuilder(permissionBuilder);
+        doReturn(dmaapConfig).when(filter).getConfig();
+        doReturn(dmaapService).when(filter).getDmaapService();
+    }
+
+    @Test
+    public void init_shouldNotInitializePermissionBuilder_whenAAFnotUsed() throws Exception {
+        //given
+        filter.setPermissionBuilder(null);
+        configureAAFUsage(false);
+
+        //when
+        filter.init(filterConfig);
+
+        //then
+        assertNull(filter.getPermissionBuilder());
+    }
+
+    @Test
+    public void init_shouldInitializePermissionBuilder_whenAAFisUsed() throws Exception {
+        //given
+        filter.setPermissionBuilder(null);
+        configureAAFUsage(true);
+        //doReturn(provideEmptyInstance()).when(dmaapService).getDmaap();
+        when(dmaapService.getDmaap()).thenReturn(mock(Dmaap.class));
+
+        //when
+        filter.init(filterConfig);
+
+        //then
+        assertNotNull(permissionBuilder);
+    }
+
+    @Test
+    public void doFilter_shouldSkipAuthorization_whenAAFnotUsed() throws Exception {
+        //given
+        filter.setAafEnabled(false);
+
+        //when
+        filter.doFilter(servletRequest,servletResponse,filterChain);
+
+        //then
+        verify(filterChain).doFilter(servletRequest,servletResponse);
+        verifyNoMoreInteractions(filterChain);
+        verifyZeroInteractions(permissionBuilder, servletRequest, servletResponse);
+    }
+
+    @Test
+    public void doFilter_shouldPass_whenUserHasPermissionToResourceEndpoint() throws Exception {
+        //given
+        String user = "johnny";
+        String permission = "org.onap.dmaap-bc.api.topics|mr|GET";
+        when(permissionBuilder.buildPermission(servletRequest)).thenReturn(permission);
+        configureServletRequest(permission, user, true);
+        filter.setAafEnabled(true);
+
+        //when
+        filter.doFilter(servletRequest,servletResponse,filterChain);
+
+        //then
+        verify(filterChain).doFilter(servletRequest,servletResponse);
+        verify(permissionBuilder).updateDmaapInstance();
+        verifyZeroInteractions(servletResponse);
+    }
+
+    @Test
+    public void doFilter_shouldReturnError_whenUserDontHavePermissionToResourceEndpoint() throws Exception {
+        //given
+        String user = "jack";
+        String permission = "org.onap.dmaap-bc.api.topics|mr|GET";
+        when(permissionBuilder.buildPermission(servletRequest)).thenReturn(permission);
+        configureServletRequest(permission, user, false);
+        filter.setAafEnabled(true);
+
+        String errorMsgJson = "{\"code\":403,\"message\":\"User "+user+" does not have permission "
+            + permission +"\",\"fields\":\"Authorization\",\"2xx\":false}";
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        when(servletResponse.getWriter()).thenReturn(pw);
+
+        //when
+        filter.doFilter(servletRequest,servletResponse,filterChain);
+
+        //then
+        verifyZeroInteractions(filterChain);
+        verify(permissionBuilder).updateDmaapInstance();
+        verify(servletResponse).setStatus(403);
+        assertEquals(errorMsgJson, sw.toString());
+    }
+
+    private void configureServletRequest(String permission, String user, boolean isUserInRole) {
+        when(servletRequest.getUserPrincipal()).thenReturn(new PrincipalImpl(user));
+        when(servletRequest.isUserInRole(permission)).thenReturn(isUserInRole);
+    }
+
+    private void configureAAFUsage(Boolean isUsed) {
+        doReturn(isUsed.toString()).when(dmaapConfig).getProperty(eq(AAFAuthorizationFilter.AAF_AUTHZ_FLAG), anyString());
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/onap/dmaap/dbcapi/util/PermissionBuilderTest.java b/src/test/java/org/onap/dmaap/dbcapi/util/PermissionBuilderTest.java
new file mode 100644 (file)
index 0000000..1858e47
--- /dev/null
@@ -0,0 +1,151 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.util;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.atMost;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import javax.servlet.http.HttpServletRequest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.onap.dmaap.dbcapi.model.Dmaap;
+import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status;
+import org.onap.dmaap.dbcapi.service.DmaapService;
+
+@RunWith(MockitoJUnitRunner.class)
+public class PermissionBuilderTest {
+
+    private static final String DMAAP_NAME = "mr";
+    private PermissionBuilder permissionBuilder;
+    @Mock
+    private DmaapConfig dmaapConfig;
+    @Mock
+    private DmaapService dmaapService;
+    @Mock
+    private HttpServletRequest request;
+
+
+    @Test
+    public void updateDmaapInstance_shouldSetBootInstance_whenDmaapIsNotInitialized() {
+        //given
+        doReturn(null).when(dmaapService).getDmaap();
+        permissionBuilder = new PermissionBuilder(dmaapConfig, dmaapService);
+
+        //when
+        permissionBuilder.updateDmaapInstance();
+
+        //then
+        assertEquals(PermissionBuilder.BOOT_INSTANCE, permissionBuilder.getInstance());
+    }
+
+    @Test
+    public void updateDmaapInstance_shouldSetBootInstance_whenDmaapIsInitializedWithDefaultInstance() {
+        //given
+        doReturn(provideDefaultInstance()).when(dmaapService).getDmaap();
+        permissionBuilder = new PermissionBuilder(dmaapConfig, dmaapService);
+
+        //when
+        permissionBuilder.updateDmaapInstance();
+
+        //then
+        assertEquals(PermissionBuilder.BOOT_INSTANCE, permissionBuilder.getInstance());
+    }
+
+    @Test
+    public void updateDmaapInstance_shouldSetRealInstance_whenDmaapServiceProvidesOne() {
+        //given
+        when(dmaapService.getDmaap()).thenReturn(provideDefaultInstance(), provideRealInstance(DMAAP_NAME));
+        permissionBuilder = new PermissionBuilder(dmaapConfig, dmaapService);
+
+        //when
+        permissionBuilder.updateDmaapInstance();
+
+        //then
+        assertEquals(DMAAP_NAME, permissionBuilder.getInstance());
+    }
+
+    @Test
+    public void updateDmaapInstance_shouldNotUpdateDmaapInstance_whenAlreadyInitializedWithRealInstance() {
+        //given
+        when(dmaapService.getDmaap()).thenReturn(provideRealInstance(DMAAP_NAME), provideRealInstance("newName"));
+        permissionBuilder = new PermissionBuilder(dmaapConfig, dmaapService);
+
+        //when
+        permissionBuilder.updateDmaapInstance();
+
+        //then
+        assertEquals(DMAAP_NAME, permissionBuilder.getInstance());
+        verify(dmaapService, atMost(1)).getDmaap();
+    }
+
+    @Test
+    public void buildPermission_shouldBuildPermissionWithBootInstance() {
+        //given
+        String path = "/dmaap";
+        String method = "GET";
+        initPermissionBuilder(path, method, provideDefaultInstance());
+
+        //when
+        String permission = permissionBuilder.buildPermission(request);
+
+        //then
+        assertEquals("org.onap.dmaap-bc.api.dmaap|boot|GET", permission);
+    }
+
+    @Test
+    public void buildPermission_shouldBuildPermissionWithRealInstance() {
+        //given
+        String path = "/subpath/topics/";
+        String method = "GET";
+        initPermissionBuilder(path, method, provideRealInstance(DMAAP_NAME));
+
+        //when
+        String permission = permissionBuilder.buildPermission(request);
+
+        //then
+        assertEquals("org.onap.dmaap-bc.api.topics|mr|GET", permission);
+    }
+
+    private void initPermissionBuilder(String path, String method, Dmaap dmaapInstance) {
+        when(dmaapConfig.getProperty(PermissionBuilder.API_NS_PROP, PermissionBuilder.DEFAULT_API_NS))
+            .thenReturn(PermissionBuilder.DEFAULT_API_NS);
+        when(dmaapService.getDmaap()).thenReturn(dmaapInstance);
+        permissionBuilder = new PermissionBuilder(dmaapConfig, dmaapService);
+
+        when(request.getPathInfo()).thenReturn(path);
+        when(request.getMethod()).thenReturn(method);
+    }
+
+    private Dmaap provideDefaultInstance() {
+        return new  Dmaap("0", "", "", "", "", "", "", "");
+    }
+
+    private Dmaap provideRealInstance(String dmaapName) {
+        Dmaap dmaap = new Dmaap("1", "org.onap.dmaap", dmaapName, "https://dmaap-dr-prov:8443", "", "DCAE_MM_AGENT", "", "");
+        dmaap.setStatus(DmaapObject_Status.VALID);
+        return dmaap;
+    }
+
+}
\ No newline at end of file