aboutsummaryrefslogtreecommitdiffstats
path: root/music-rest/src/main/java/org/onap/music/authentication
diff options
context:
space:
mode:
Diffstat (limited to 'music-rest/src/main/java/org/onap/music/authentication')
-rw-r--r--music-rest/src/main/java/org/onap/music/authentication/AuthUtil.java276
-rw-r--r--music-rest/src/main/java/org/onap/music/authentication/AuthorizationError.java55
-rw-r--r--music-rest/src/main/java/org/onap/music/authentication/CadiAuthFilter.java65
-rw-r--r--music-rest/src/main/java/org/onap/music/authentication/MusicAuthorizationFilter.java122
4 files changed, 518 insertions, 0 deletions
diff --git a/music-rest/src/main/java/org/onap/music/authentication/AuthUtil.java b/music-rest/src/main/java/org/onap/music/authentication/AuthUtil.java
new file mode 100644
index 00000000..ee3b77a4
--- /dev/null
+++ b/music-rest/src/main/java/org/onap/music/authentication/AuthUtil.java
@@ -0,0 +1,276 @@
+/*
+ * ============LICENSE_START==========================================
+ * org.onap.music
+ * ===================================================================
+ * Copyright (c) 2017 AT&T Intellectual Property
+ *
+ * Modifications Copyright (C) 2019 IBM.
+ * ===================================================================
+ * 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.music.authentication;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Hex;
+import org.onap.aaf.cadi.CadiWrap;
+import org.onap.aaf.cadi.Permission;
+import org.onap.aaf.cadi.aaf.AAFPermission;
+import org.onap.music.eelf.logging.EELFLoggerDelegate;
+import org.onap.music.exceptions.MusicAuthenticationException;
+
+public class AuthUtil {
+
+ private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(AuthUtil.class);
+
+ private AuthUtil() {
+ throw new IllegalStateException("Utility class");
+ }
+
+ /**
+ * Get the list of permissions from the Request object.
+ *
+ *
+ * @param request servlet request object
+ * @return returns list of AAFPermission of the requested MechId for all the
+ * namespaces
+ */
+ public static List<AAFPermission> getAAFPermissions(ServletRequest request) {
+ CadiWrap wrapReq = (CadiWrap) request;
+
+ List<Permission> perms = wrapReq.getPermissions(wrapReq.getUserPrincipal());
+ List<AAFPermission> aafPermsList = new ArrayList<>();
+ for (Permission perm : perms) {
+ AAFPermission aafPerm = (AAFPermission) perm;
+ aafPermsList.add(aafPerm);
+ }
+ return aafPermsList;
+ }
+
+ /**
+ * Here is a sample of a permission object in AAI. The key attribute will have
+ * Type|Instance|Action.
+ * AAFPermission:
+ * NS: null
+ * Type: org.onap.music.cadi.keyspace ( Permission Type )
+ * Instance: tomtest ( Cassandra Keyspace )
+ * Action: *|GET|ALL ( Access Level [*|ALL] for full access and [GET] for Read only)
+ * Key: org.onap.music.cadi.keyspace|tomtest|*
+ *
+ * This method will filter all permissions whose key starts with the requested namespace.
+ * The nsamespace here is the music namespace which is defined in music.property file.
+ * i;e is the type contains in key is org.onap.music.cadi.keyspace and the namespace
+ * value is org.onap.music.cadi.keyspace, it will add to list
+ * otherwise reject.
+ *
+ * @param nameSpace
+ * @param allPermissionsList
+ * @return
+ */
+ private static List<AAFPermission> filterNameSpacesAAFPermissions(String nameSpace,
+ List<AAFPermission> allPermissionsList) {
+ List<AAFPermission> list = new ArrayList<>();
+ for (Iterator<AAFPermission> iterator = allPermissionsList.iterator(); iterator.hasNext();) {
+ AAFPermission aafPermission = iterator.next();
+ if(aafPermission.getType().indexOf(nameSpace) == 0) {
+ list.add(aafPermission);
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Decode certian characters from url encoded to normal.
+ *
+ * @param str - String being decoded.
+ * @return returns the decoded string.
+ * @throws Exception throws excpetion
+ */
+ public static String decodeFunctionCode(String str) throws MusicAuthenticationException {
+ final String DECODEVALUE_FORWARDSLASH = "2f";
+ final String DECODEVALUE_HYPHEN = "2d";
+ final String DECODEVALUE_ASTERISK = "2a";
+ String decodedString = str;
+ List<Pattern> decodingList = new ArrayList<>();
+ decodingList.add(Pattern.compile(DECODEVALUE_FORWARDSLASH));
+ decodingList.add(Pattern.compile(DECODEVALUE_HYPHEN));
+ decodingList.add(Pattern.compile(DECODEVALUE_ASTERISK));
+ for (Pattern xssInputPattern : decodingList) {
+ try {
+ decodedString = decodedString.replaceAll("%" + xssInputPattern,
+ new String(Hex.decodeHex(xssInputPattern.toString().toCharArray())));
+ } catch (DecoderException e) {
+ logger.error(EELFLoggerDelegate.securityLogger,
+ "AuthUtil Decode Failed! for instance: " + str);
+ throw new MusicAuthenticationException("Decode failed", e);
+ }
+ }
+
+ return decodedString;
+ }
+
+ /**
+ *
+ *
+ * @param request servlet request object
+ * @param nameSpace application namespace
+ * @return boolean value if the access is allowed
+ * @throws Exception throws exception
+ */
+ public static boolean isAccessAllowed(ServletRequest request, String nameSpace) throws MusicAuthenticationException {
+
+ if (request==null) {
+ throw new MusicAuthenticationException("Request cannot be null");
+ }
+
+ if (nameSpace==null || nameSpace.isEmpty()) {
+ throw new MusicAuthenticationException("NameSpace not Declared!");
+ }
+
+ boolean isauthorized = false;
+ List<AAFPermission> aafPermsList = getAAFPermissions(request);
+ logger.info(EELFLoggerDelegate.securityLogger,
+ "AAFPermission of the requested MechId for all the namespaces: " + aafPermsList);
+ logger.debug(EELFLoggerDelegate.securityLogger, "Requested nameSpace: " + nameSpace);
+
+ List<AAFPermission> aafPermsFinalList = filterNameSpacesAAFPermissions(nameSpace, aafPermsList);
+
+ logger.debug(EELFLoggerDelegate.securityLogger,
+ "AuthUtil list of AAFPermission for the specific namespace :::"
+ + aafPermsFinalList);
+
+ HttpServletRequest httpRequest = (HttpServletRequest) request;
+ String requestUri = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length() + 1);
+
+ logger.debug(EELFLoggerDelegate.securityLogger,
+ "AuthUtil requestUri :::" + requestUri);
+
+ for (Iterator<AAFPermission> iterator = aafPermsFinalList.iterator(); iterator.hasNext();) {
+ AAFPermission aafPermission = iterator.next();
+ if(!isauthorized) {
+ isauthorized = isMatchPattern(aafPermission, requestUri, httpRequest.getMethod());
+ }
+ }
+
+ logger.debug(EELFLoggerDelegate.securityLogger,
+ "isAccessAllowed for the request uri: " + requestUri + "is :" + isauthorized);
+ return isauthorized;
+ }
+
+ /**
+ *
+ * This method will check, if the requested URI matches any of the instance
+ * found with the AAF permission list.
+ * i;e if the request URI is; /v2/keyspaces/tomtest/tables/emp15 and in the
+ * AAF permission table, we have an instance
+ * defined as "tomtest" mapped the logged in user, it will allow else error.
+ *
+ * User trying to create or aquire a lock
+ * Here is the requested URI /v2/locks/create/tomtest.MyTable.Field1
+ * Here the keyspace name i;e tomtest will be test throught out the URL if it
+ * matches, it will allow the user to create a lock.
+ * "tomtest" here is the key, which is mapped as an instance in permission object.
+ * Instance can be delimited with ":" i;e ":music-cassandra-1908-dev:admin". In this case,
+ * each delimited
+ * token will be matched with that of request URI.
+ *
+ * Example Permission:
+ * org.onap.music.api.user.access|tomtest|* or ALL
+ * org.onap.music.api.user.access|tomtest|GET
+ * In case of the action field is ALL and *, user will be allowed else it will
+ * be matched with the requested http method type.
+ *
+ *
+ *
+ * @param aafPermission - AAfpermission obtained by cadi.
+ * @param requestUri - Rest URL client is calling.
+ * @param method - REST Method being used (GET,POST,PUT,DELETE)
+ * @return returns a boolean
+ * @throws Exception - throws an exception
+ */
+ private static boolean isMatchPattern(
+ AAFPermission aafPermission,
+ String requestUri,
+ String method) throws MusicAuthenticationException {
+ if (null == aafPermission || null == requestUri || null == method) {
+ return false;
+ }
+
+ String permKey = aafPermission.getKey();
+
+ logger.debug(EELFLoggerDelegate.securityLogger, "isMatchPattern permKey: "
+ + permKey + ", requestUri " + requestUri + " ," + method);
+
+ String[] keyArray = permKey.split("\\|");
+ String[] subPath = null;
+ String instance = keyArray[1];
+ String action = keyArray[2];
+
+ //if the instance & action both are * , then allow
+ if ("*".equalsIgnoreCase(instance) && "*".equalsIgnoreCase(action)) {
+ return true;
+ }
+ //Decode string like %2f, %2d and %2a
+ if (!"*".equals(instance)) {
+ instance = decodeFunctionCode(instance);
+ }
+ if (!"*".equals(action)) {
+ action = decodeFunctionCode(action);
+ }
+ //Instance: :music-cassandra-1908-dev:admin
+ List<String> instanceList = Arrays.asList(instance.split(":"));
+
+ String[] path = requestUri.split("/");
+
+ for (int i = 0; i < path.length; i++) {
+ // Sometimes the value will begin with "$", so we need to remove it
+ if (path[i].startsWith("$")) {
+ path[i] = path[i].replace("$","");
+ }
+ // Each path element can again delemited by ".";i;e
+ // tomtest.tables.emp. We have scenarios like lock aquire URL
+ subPath = path[i].split("\\.");
+ for (int j = 0; j < subPath.length; j++) {
+ if (instanceList.contains(subPath[j])) {
+ return checkAction(method,action);
+ } else {
+ continue;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static boolean checkAction(String method, String action) {
+ if ("*".equals(action) || "ALL".equalsIgnoreCase(action)) {
+ return true;
+ } else {
+ return (method.equalsIgnoreCase(action));
+ }
+ }
+
+
+
+} \ No newline at end of file
diff --git a/music-rest/src/main/java/org/onap/music/authentication/AuthorizationError.java b/music-rest/src/main/java/org/onap/music/authentication/AuthorizationError.java
new file mode 100644
index 00000000..7015b550
--- /dev/null
+++ b/music-rest/src/main/java/org/onap/music/authentication/AuthorizationError.java
@@ -0,0 +1,55 @@
+/*
+ * ============LICENSE_START==========================================
+ * org.onap.music
+ * ===================================================================
+ * Copyright (c) 2017 AT&T Intellectual Property
+ * ===================================================================
+ * 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.music.authentication;
+
+
+/**
+ * Authorization error class used while setting error code and description back to client.
+ *
+ *
+ * @author sp931a
+ *
+ */
+public class AuthorizationError {
+
+ private int responseCode;
+
+ private String responseMessage;
+
+ public int getResponseCode() {
+ return responseCode;
+ }
+
+ public void setResponseCode(int responseCode) {
+ this.responseCode = responseCode;
+ }
+
+ public String getResponseMessage() {
+ return responseMessage;
+ }
+
+ public void setResponseMessage(String responseMessage) {
+ this.responseMessage = responseMessage;
+ }
+
+} \ No newline at end of file
diff --git a/music-rest/src/main/java/org/onap/music/authentication/CadiAuthFilter.java b/music-rest/src/main/java/org/onap/music/authentication/CadiAuthFilter.java
new file mode 100644
index 00000000..d043e6d6
--- /dev/null
+++ b/music-rest/src/main/java/org/onap/music/authentication/CadiAuthFilter.java
@@ -0,0 +1,65 @@
+/*
+ * ============LICENSE_START==========================================
+ * org.onap.music
+ * ===================================================================
+ * Copyright (c) 2017 AT&T Intellectual Property
+ * ===================================================================
+ * 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.music.authentication;
+
+
+import java.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.annotation.WebFilter;
+
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.filter.CadiFilter;
+import org.onap.music.eelf.logging.EELFLoggerDelegate;
+
+@WebFilter(urlPatterns = { "/*" })
+public class CadiAuthFilter extends CadiFilter {
+
+ private static final EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(CadiAuthFilter.class);
+
+ public CadiAuthFilter(PropAccess access) throws ServletException {
+ super(true, access);
+ }
+
+ public CadiAuthFilter() throws ServletException {
+ super();
+ }
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ super.init(filterConfig);
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ logger.info(EELFLoggerDelegate.securityLogger, "Request is entering cadifilter");
+ long startTime = System.currentTimeMillis();
+ request.setAttribute("startTime", startTime);
+ super.doFilter(request, response, chain);
+ }
+} \ No newline at end of file
diff --git a/music-rest/src/main/java/org/onap/music/authentication/MusicAuthorizationFilter.java b/music-rest/src/main/java/org/onap/music/authentication/MusicAuthorizationFilter.java
new file mode 100644
index 00000000..bde3e205
--- /dev/null
+++ b/music-rest/src/main/java/org/onap/music/authentication/MusicAuthorizationFilter.java
@@ -0,0 +1,122 @@
+/*
+ * ============LICENSE_START==========================================
+ * org.onap.music
+ * ===================================================================
+ * Copyright (c) 2017 AT&T Intellectual Property
+ * ===================================================================
+ * Modifications Copyright (c) 2019 Samsung
+ * ===================================================================
+ * 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.music.authentication;
+
+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.onap.music.eelf.logging.EELFLoggerDelegate;
+import org.onap.music.exceptions.MusicAuthenticationException;
+import org.onap.music.main.MusicUtil;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * This filter class does authorization from AAF
+ *
+ * @author sp931a
+ *
+ */
+//@PropertySource(value = {"file:/opt/app/music/etc/music.properties"})
+public class MusicAuthorizationFilter implements Filter {
+
+ private String musicNS = MusicUtil.getMusicAafNs();
+
+ private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MusicAuthorizationFilter.class);
+
+ public MusicAuthorizationFilter() throws ServletException {
+ super();
+ }
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ // Do Nothing
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
+ throws IOException, ServletException {
+ HttpServletResponse httpResponse = null;
+
+ boolean isAuthAllowed = false;
+
+ if (null != servletRequest && null != servletResponse) {
+ httpResponse = (HttpServletResponse) servletResponse;
+ long startTime = 0;
+ if( null != servletRequest.getAttribute("startTime")) {
+ startTime = ((Long)servletRequest.getAttribute("startTime")).longValue();
+ } else {
+ startTime = System.currentTimeMillis(); // this will set only incase the request attribute not found
+ }
+
+ try {
+ isAuthAllowed = AuthUtil.isAccessAllowed(servletRequest, musicNS);
+ } catch (MusicAuthenticationException e) {
+ logger.error(EELFLoggerDelegate.securityLogger,
+ "Error while checking authorization Music Namespace: " + musicNS + " : " + e.getMessage(),e);
+ } catch ( Exception e) {
+ logger.error(EELFLoggerDelegate.securityLogger,
+ "Error while checking authorization Music Namespace: " + musicNS + " : " + e.getMessage(),e);
+ }
+
+ long endTime = System.currentTimeMillis();
+
+ //startTime set in <code>CadiAuthFilter</code> doFilter
+ logger.debug(EELFLoggerDelegate.securityLogger,
+ "Time took for authentication & authorization : "
+ + (endTime - startTime) + " milliseconds");
+
+ if (!isAuthAllowed) {
+ logger.info(EELFLoggerDelegate.securityLogger,
+ "Unauthorized Access");
+ AuthorizationError authError = new AuthorizationError();
+ authError.setResponseCode(HttpServletResponse.SC_UNAUTHORIZED);
+ authError.setResponseMessage("Unauthorized Access - Please make sure you are "
+ + "onboarded and have proper access to MUSIC. ");
+
+ byte[] responseToSend = restResponseBytes(authError);
+ httpResponse.setHeader("Content-Type", "application/json");
+
+ httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ servletResponse.getOutputStream().write(responseToSend);
+ return;
+ } else {
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+ }
+ }
+
+ private byte[] restResponseBytes(AuthorizationError eErrorResponse) throws IOException {
+ String serialized = new ObjectMapper().writeValueAsString(eErrorResponse);
+ return serialized.getBytes();
+ }
+}
+