2 * ============LICENSE_START==========================================
4 * ===================================================================
5 * Copyright (c) 2017 AT&T Intellectual Property
6 * ===================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 * ============LICENSE_END=============================================
20 * ====================================================================
23 package org.onap.music.authentication;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.regex.Pattern;
31 import javax.servlet.ServletRequest;
32 import javax.servlet.http.HttpServletRequest;
34 import org.apache.commons.codec.DecoderException;
35 import org.apache.commons.codec.binary.Hex;
36 import org.onap.aaf.cadi.CadiWrap;
37 import org.onap.aaf.cadi.Permission;
38 import org.onap.aaf.cadi.aaf.AAFPermission;
39 import org.onap.music.eelf.logging.EELFLoggerDelegate;
41 public class AuthUtil {
43 private static final String decodeValueOfForwardSlash = "2f";
44 private static final String decodeValueOfHyphen = "2d";
45 private static final String decodeValueOfAsterisk = "2a";
46 private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(AuthUtil.class);
49 * Get the list of permissions from the Request object.
52 * @param request servlet request object
53 * @return returns list of AAFPermission of the requested MechId for all the
56 public static List<AAFPermission> getAAFPermissions(ServletRequest request) {
57 CadiWrap wrapReq = (CadiWrap) request;
59 List<Permission> perms = wrapReq.getPermissions(wrapReq.getUserPrincipal());
60 List<AAFPermission> aafPermsList = new ArrayList<>();
61 for (Permission perm : perms) {
62 AAFPermission aafPerm = (AAFPermission) perm;
63 aafPermsList.add(aafPerm);
69 * Here is a sample of a permission object in AAI. The key attribute will have
70 * Type|Instance|Action.
73 * Type: org.onap.music.cadi.keyspace ( Permission Type )
74 * Instance: tomtest ( Cassandra Keyspace )
75 * Action: *|GET|ALL ( Access Level [*|ALL] for full access and [GET] for Read only)
76 * Key: org.onap.music.cadi.keyspace|tomtest|*
78 * This method will filter all permissions whose key starts with the requested namespace.
79 * The nsamespace here is the music namespace which is defined in music.property file.
80 * i;e is the type contains in key is org.onap.music.cadi.keyspace and the namespace
81 * value is org.onap.music.cadi.keyspace, it will add to list
85 * @param allPermissionsList
88 private static List<AAFPermission> filterNameSpacesAAFPermissions(String nameSpace,
89 List<AAFPermission> allPermissionsList) {
90 List<AAFPermission> list = new ArrayList<>();
91 for (Iterator<AAFPermission> iterator = allPermissionsList.iterator(); iterator.hasNext();) {
92 AAFPermission aafPermission = (AAFPermission) iterator.next();
93 if(aafPermission.getType().indexOf(nameSpace) == 0) {
94 list.add(aafPermission);
101 * Decode certian characters from url encoded to normal.
103 * @param str - String being decoded.
104 * @return returns the decoded string.
105 * @throws Exception throws excpetion
107 public static String decodeFunctionCode(String str) throws Exception {
108 String decodedString = str;
109 List<Pattern> decodingList = new ArrayList<>();
110 decodingList.add(Pattern.compile(decodeValueOfForwardSlash));
111 decodingList.add(Pattern.compile(decodeValueOfHyphen));
112 decodingList.add(Pattern.compile(decodeValueOfAsterisk));
113 for (Pattern xssInputPattern : decodingList) {
115 decodedString = decodedString.replaceAll("%" + xssInputPattern,
116 new String(Hex.decodeHex(xssInputPattern.toString().toCharArray())));
117 } catch (DecoderException e) {
118 logger.error(EELFLoggerDelegate.applicationLogger,
119 "AuthUtil Decode Failed! for instance: " + str);
120 throw new Exception("decode failed", e);
124 return decodedString;
130 * @param request servlet request object
131 * @param nameSpace application namespace
132 * @return boolean value if the access is allowed
133 * @throws Exception throws exception
135 public static boolean isAccessAllowed(ServletRequest request, String nameSpace) throws Exception {
138 throw new Exception("Request cannot be null");
141 if (nameSpace==null || nameSpace.isEmpty()) {
142 throw new Exception("NameSpace not Declared!");
145 boolean isauthorized = false;
146 List<AAFPermission> aafPermsList = getAAFPermissions(request);
147 //logger.info(EELFLoggerDelegate.applicationLogger,
148 // "AAFPermission of the requested MechId for all the namespaces: " + aafPermsList);
150 logger.debug(EELFLoggerDelegate.applicationLogger, "Requested nameSpace: " + nameSpace);
153 List<AAFPermission> aafPermsFinalList = filterNameSpacesAAFPermissions(nameSpace, aafPermsList);
155 logger.debug(EELFLoggerDelegate.securityLogger,
156 "AuthUtil list of AAFPermission for the specific namespace :::"
157 + aafPermsFinalList);
159 HttpServletRequest httpRequest = (HttpServletRequest) request;
160 String requestUri = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length() + 1);
162 logger.debug(EELFLoggerDelegate.securityLogger,
163 "AuthUtil requestUri :::" + requestUri);
165 for (Iterator iterator = aafPermsFinalList.iterator(); iterator.hasNext();) {
166 AAFPermission aafPermission = (AAFPermission) iterator.next();
168 isauthorized = isMatchPatternWithInstanceAndAction(aafPermission, requestUri, httpRequest.getMethod());
172 logger.debug(EELFLoggerDelegate.securityLogger,
173 "isAccessAllowed for the request uri: " + requestUri + "is :" + isauthorized);
179 * This method will check, if the requested URI matches any of the instance
180 * found with the AAF permission list.
181 * i;e if the request URI is; /v2/keyspaces/tomtest/tables/emp15 and in the
182 * AAF permission table, we have an instance
183 * defined as "tomtest" mapped the logged in user, it will allow else error.
185 * User trying to create or aquire a lock
186 * Here is the requested URI /v2/locks/create/tomtest.MyTable.Field1
187 * Here the keyspace name i;e tomtest will be test throught out the URL if it
188 * matches, it will allow the user to create a lock.
189 * "tomtest" here is the key, which is mapped as an instance in permission object.
190 * Instance can be delimited with ":" i;e ":music-cassandra-1908-dev:admin". In this case,
192 * token will be matched with that of request URI.
194 * Example Permission:
195 * org.onap.music.api.user.access|tomtest|* or ALL
196 * org.onap.music.api.user.access|tomtest|GET
197 * In case of the action field is ALL and *, user will be allowed else it will
198 * be matched with the requested http method type.
202 * @param aafPermission - AAfpermission obtained by cadi.
203 * @param requestUri - Rest URL client is calling.
204 * @param method - REST Method being used (GET,POST,PUT,DELETE)
205 * @return returns a boolean
206 * @throws Exception - throws an exception
208 private static boolean isMatchPatternWithInstanceAndAction(
209 AAFPermission aafPermission,
211 String method) throws Exception {
212 if (null == aafPermission || null == requestUri || null == method) {
216 String permKey = aafPermission.getKey();
218 logger.debug(EELFLoggerDelegate.auditLogger, "isMatchPattern permKey: "
219 + permKey + ", requestUri " + requestUri + " ," + method);
221 String[] keyArray = permKey.split("\\|");
222 String[] subPath = null;
223 //String type = null;
224 //type = keyArray[0];
225 String instance = keyArray[1];
226 String action = keyArray[2];
228 //if the instance & action both are * , then allow
229 if ("*".equalsIgnoreCase(instance) && "*".equalsIgnoreCase(action)) {
232 //Decode string like %2f, %2d and %2a
233 if (!"*".equals(instance)) {
234 instance = decodeFunctionCode(instance);
236 if (!"*".equals(action)) {
237 action = decodeFunctionCode(action);
239 //Instance: :music-cassandra-1908-dev:admin
240 List<String> instanceList = Arrays.asList(instance.split(":"));
242 String[] path = requestUri.split("/");
244 for (int i = 0; i < path.length; i++) {
245 // Sometimes the value will begin with "$", so we need to remove it
246 if (path[i].startsWith("$")) {
247 path[i] = path[i].replace("$","");
249 // Each path element can again delemited by ".";i;e
250 // tomtest.tables.emp. We have scenarios like lock aquire URL
251 subPath = path[i].split("\\.");
252 for (int j = 0; j < subPath.length; j++) {
253 if (instanceList.contains(subPath[j])) {
254 if ("*".equals(action) || "ALL".equalsIgnoreCase(action)) {
256 } else if (method.equalsIgnoreCase(action)) {