2 * ============LICENSE_START==========================================
4 * ===================================================================
5 * Copyright (c) 2017 AT&T Intellectual Property
7 * Modifications Copyright (C) 2019 IBM.
8 * ===================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
21 * ============LICENSE_END=============================================
22 * ====================================================================
25 package org.onap.music.authentication;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.regex.Pattern;
33 import javax.servlet.ServletRequest;
34 import javax.servlet.http.HttpServletRequest;
36 import org.apache.commons.codec.DecoderException;
37 import org.apache.commons.codec.binary.Hex;
38 import org.onap.aaf.cadi.CadiWrap;
39 import org.onap.aaf.cadi.Permission;
40 import org.onap.aaf.cadi.aaf.AAFPermission;
41 import org.onap.music.eelf.logging.EELFLoggerDelegate;
42 import org.onap.music.exceptions.MusicAuthenticationException;
44 public class AuthUtil {
46 private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(AuthUtil.class);
49 throw new IllegalStateException("Utility class");
53 * Get the list of permissions from the Request object.
56 * @param request servlet request object
57 * @return returns list of AAFPermission of the requested MechId for all the
60 public static List<AAFPermission> getAAFPermissions(ServletRequest request) {
61 CadiWrap wrapReq = (CadiWrap) request;
63 List<Permission> perms = wrapReq.getPermissions(wrapReq.getUserPrincipal());
64 List<AAFPermission> aafPermsList = new ArrayList<>();
65 for (Permission perm : perms) {
66 AAFPermission aafPerm = (AAFPermission) perm;
67 aafPermsList.add(aafPerm);
73 * Here is a sample of a permission object in AAI. The key attribute will have
74 * Type|Instance|Action.
77 * Type: org.onap.music.cadi.keyspace ( Permission Type )
78 * Instance: tomtest ( Cassandra Keyspace )
79 * Action: *|GET|ALL ( Access Level [*|ALL] for full access and [GET] for Read only)
80 * Key: org.onap.music.cadi.keyspace|tomtest|*
82 * This method will filter all permissions whose key starts with the requested namespace.
83 * The nsamespace here is the music namespace which is defined in music.property file.
84 * i;e is the type contains in key is org.onap.music.cadi.keyspace and the namespace
85 * value is org.onap.music.cadi.keyspace, it will add to list
89 * @param allPermissionsList
92 private static List<AAFPermission> filterNameSpacesAAFPermissions(String nameSpace,
93 List<AAFPermission> allPermissionsList) {
94 List<AAFPermission> list = new ArrayList<>();
95 for (Iterator<AAFPermission> iterator = allPermissionsList.iterator(); iterator.hasNext();) {
96 AAFPermission aafPermission = iterator.next();
97 if(aafPermission.getType().indexOf(nameSpace) == 0) {
98 list.add(aafPermission);
105 * Decode certian characters from url encoded to normal.
107 * @param str - String being decoded.
108 * @return returns the decoded string.
109 * @throws Exception throws excpetion
111 public static String decodeFunctionCode(String str) throws MusicAuthenticationException {
112 final String DECODEVALUE_FORWARDSLASH = "2f";
113 final String DECODEVALUE_HYPHEN = "2d";
114 final String DECODEVALUE_ASTERISK = "2a";
115 String decodedString = str;
116 List<Pattern> decodingList = new ArrayList<>();
117 decodingList.add(Pattern.compile(DECODEVALUE_FORWARDSLASH));
118 decodingList.add(Pattern.compile(DECODEVALUE_HYPHEN));
119 decodingList.add(Pattern.compile(DECODEVALUE_ASTERISK));
120 for (Pattern xssInputPattern : decodingList) {
122 decodedString = decodedString.replaceAll("%" + xssInputPattern,
123 new String(Hex.decodeHex(xssInputPattern.toString().toCharArray())));
124 } catch (DecoderException e) {
125 logger.error(EELFLoggerDelegate.securityLogger,
126 "AuthUtil Decode Failed! for instance: " + str);
127 throw new MusicAuthenticationException("Decode failed", e);
131 return decodedString;
137 * @param request servlet request object
138 * @param nameSpace application namespace
139 * @return boolean value if the access is allowed
140 * @throws Exception throws exception
142 public static boolean isAccessAllowed(ServletRequest request, String nameSpace) throws MusicAuthenticationException {
145 throw new MusicAuthenticationException("Request cannot be null");
148 if (nameSpace==null || nameSpace.isEmpty()) {
149 throw new MusicAuthenticationException("NameSpace not Declared!");
152 boolean isauthorized = false;
153 List<AAFPermission> aafPermsList = getAAFPermissions(request);
154 logger.info(EELFLoggerDelegate.securityLogger,
155 "AAFPermission of the requested MechId for all the namespaces: " + aafPermsList);
156 logger.debug(EELFLoggerDelegate.securityLogger, "Requested nameSpace: " + nameSpace);
158 List<AAFPermission> aafPermsFinalList = filterNameSpacesAAFPermissions(nameSpace, aafPermsList);
160 logger.debug(EELFLoggerDelegate.securityLogger,
161 "AuthUtil list of AAFPermission for the specific namespace :::"
162 + aafPermsFinalList);
164 HttpServletRequest httpRequest = (HttpServletRequest) request;
165 String requestUri = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length() + 1);
167 logger.debug(EELFLoggerDelegate.securityLogger,
168 "AuthUtil requestUri :::" + requestUri);
170 for (Iterator<AAFPermission> iterator = aafPermsFinalList.iterator(); iterator.hasNext();) {
171 AAFPermission aafPermission = iterator.next();
173 isauthorized = isMatchPattern(aafPermission, requestUri, httpRequest.getMethod());
177 logger.debug(EELFLoggerDelegate.securityLogger,
178 "isAccessAllowed for the request uri: " + requestUri + "is :" + isauthorized);
184 * This method will check, if the requested URI matches any of the instance
185 * found with the AAF permission list.
186 * i;e if the request URI is; /v2/keyspaces/tomtest/tables/emp15 and in the
187 * AAF permission table, we have an instance
188 * defined as "tomtest" mapped the logged in user, it will allow else error.
190 * User trying to create or aquire a lock
191 * Here is the requested URI /v2/locks/create/tomtest.MyTable.Field1
192 * Here the keyspace name i;e tomtest will be test throught out the URL if it
193 * matches, it will allow the user to create a lock.
194 * "tomtest" here is the key, which is mapped as an instance in permission object.
195 * Instance can be delimited with ":" i;e ":music-cassandra-1908-dev:admin". In this case,
197 * token will be matched with that of request URI.
199 * Example Permission:
200 * org.onap.music.api.user.access|tomtest|* or ALL
201 * org.onap.music.api.user.access|tomtest|GET
202 * In case of the action field is ALL and *, user will be allowed else it will
203 * be matched with the requested http method type.
207 * @param aafPermission - AAfpermission obtained by cadi.
208 * @param requestUri - Rest URL client is calling.
209 * @param method - REST Method being used (GET,POST,PUT,DELETE)
210 * @return returns a boolean
211 * @throws Exception - throws an exception
213 private static boolean isMatchPattern(
214 AAFPermission aafPermission,
216 String method) throws MusicAuthenticationException {
217 if (null == aafPermission || null == requestUri || null == method) {
221 String permKey = aafPermission.getKey();
223 logger.debug(EELFLoggerDelegate.securityLogger, "isMatchPattern permKey: "
224 + permKey + ", requestUri " + requestUri + " ," + method);
226 String[] keyArray = permKey.split("\\|");
227 String[] subPath = null;
228 String instance = keyArray[1];
229 String action = keyArray[2];
231 //if the instance & action both are * , then allow
232 if ("*".equalsIgnoreCase(instance) && "*".equalsIgnoreCase(action)) {
235 //Decode string like %2f, %2d and %2a
236 if (!"*".equals(instance)) {
237 instance = decodeFunctionCode(instance);
239 if (!"*".equals(action)) {
240 action = decodeFunctionCode(action);
242 //Instance: :music-cassandra-1908-dev:admin
243 List<String> instanceList = Arrays.asList(instance.split(":"));
245 String[] path = requestUri.split("/");
247 for (int i = 0; i < path.length; i++) {
248 // Sometimes the value will begin with "$", so we need to remove it
249 if (path[i].startsWith("$")) {
250 path[i] = path[i].replace("$","");
252 // Each path element can again delemited by ".";i;e
253 // tomtest.tables.emp. We have scenarios like lock aquire URL
254 subPath = path[i].split("\\.");
255 for (int j = 0; j < subPath.length; j++) {
256 if (instanceList.contains(subPath[j])) {
257 return checkAction(method,action);
266 private static boolean checkAction(String method, String action) {
267 if ("*".equals(action) || "ALL".equalsIgnoreCase(action)) {
270 return (method.equalsIgnoreCase(action));