summaryrefslogtreecommitdiffstats
path: root/aai-aaf-auth/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'aai-aaf-auth/src/main/java')
-rw-r--r--aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/AAIAuthCore.java377
-rw-r--r--aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/AAIUser.java81
-rw-r--r--aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/AafRequestFilter.java114
-rw-r--r--aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/AafRequestWrapper.java78
-rw-r--r--aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/CertUtil.java157
-rw-r--r--aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/FileWatcher.java60
-rw-r--r--aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/ResponseFormatter.java57
-rw-r--r--aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/exceptions/AAIUnrecognizedFunctionException.java46
-rw-r--r--aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/AafAuthorizationFilter.java119
-rw-r--r--aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/AafCertAuthorizationFilter.java107
-rw-r--r--aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/AafCertFilter.java111
-rw-r--r--aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/AafFilter.java108
-rw-r--r--aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/AafProfiles.java33
-rw-r--r--aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/CadiProps.java81
-rw-r--r--aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/FilterPriority.java39
-rw-r--r--aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/GremlinFilter.java99
-rw-r--r--aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/PayloadBufferingRequestWrapper.java50
-rw-r--r--aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/TwoWaySslAuthorization.java186
18 files changed, 1903 insertions, 0 deletions
diff --git a/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/AAIAuthCore.java b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/AAIAuthCore.java
new file mode 100644
index 00000000..c64251ad
--- /dev/null
+++ b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/AAIAuthCore.java
@@ -0,0 +1,377 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T 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.aai.aaf.auth;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import org.eclipse.jetty.util.security.Password;
+import org.onap.aai.aaf.auth.exceptions.AAIUnrecognizedFunctionException;
+import org.onap.aai.logging.ErrorLogHelper;
+import org.onap.aai.util.AAIConfig;
+import org.onap.aai.util.AAIConstants;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.UnsupportedEncodingException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * The Class AAIAuthCore.
+ */
+public final class AAIAuthCore {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(AAIAuthCore.class);
+
+ private static final String ERROR_CODE_AAI_4001 = "AAI_4001";
+
+ private String globalAuthFileName = AAIConstants.AAI_AUTH_CONFIG_FILENAME;
+
+ private final Pattern AUTH_POLICY_PATTERN;
+ private final Set<String> validFunctions = new HashSet<>();
+ private Map<String, AAIUser> users;
+ private boolean timerSet = false;
+ private Timer timer = null;
+
+ private String basePath;
+
+ /**
+ * Instantiates a new AAI auth core.
+ */
+ public AAIAuthCore(String basePath) {
+ this(basePath, AAIConstants.AAI_AUTH_CONFIG_FILENAME);
+ }
+
+ public AAIAuthCore(String basePath, String filename){
+ this.basePath = basePath;
+ this.globalAuthFileName = filename;
+ AUTH_POLICY_PATTERN = Pattern.compile("^" + this.basePath + "/v\\d+/([\\w\\-]*)");
+ init();
+ }
+
+ public AAIAuthCore(String basePath, String filename, String pattern){
+ this.basePath = basePath;
+ this.globalAuthFileName = filename;
+ AUTH_POLICY_PATTERN = Pattern.compile(pattern);
+ init();
+ }
+
+ /**
+ * Inits the.
+ */
+ private synchronized void init() {
+
+ LOGGER.debug("Initializing Auth Policy Config");
+
+ reloadUsers();
+
+ /*
+ * this timer code is setting up a recurring task that checks if the
+ * auth config file has been updated and reloads the users if so to get
+ * the most up to date info (that update check logic is within
+ * FileWatcher)
+ *
+ * the timing this method uses is coarser than the frequency of requests
+ * AI&I gets so we're looking at better ways of doing this (TODO)
+ */
+ TimerTask task = new FileWatcher(new File(globalAuthFileName)) {
+ @Override
+ protected void onChange(File file) {
+ reloadUsers();
+ }
+ };
+
+ if (!timerSet) {
+ timerSet = true;
+ timer = new Timer();
+
+ // repeat the check every second
+ timer.schedule(task, new Date(), 10000);
+ }
+ LOGGER.debug("Static Initializiation complete");
+ }
+
+ /**
+ * Cleanup.
+ */
+ // just ends the auth config file update checking timer
+ public void cleanup() {
+ timer.cancel();
+ }
+
+ /**
+ * Reload users.
+ */
+ /*
+ * this essentially takes the data file, which is organized role-first with
+ * users under each role and converts it to data organized user-first with
+ * each user containing their role with its associated allowed functions
+ * this data stored in the class field users
+ */
+ private synchronized void reloadUsers() {
+
+ Map<String, AAIUser> tempUsers = new HashMap<>();
+
+ try {
+ LOGGER.debug("Reading from " + globalAuthFileName);
+ String authFile = new String(Files.readAllBytes(Paths.get(globalAuthFileName)));
+
+ JsonParser parser = new JsonParser();
+ JsonObject authObject = parser.parse(authFile).getAsJsonObject();
+ if (authObject.has("roles")) {
+ JsonArray roles = authObject.getAsJsonArray("roles");
+ for (JsonElement role : roles) {
+ if (role.isJsonObject()) {
+ JsonObject roleObject = role.getAsJsonObject();
+ String roleName = roleObject.get("name").getAsString();
+ Map<String, Boolean> usrs = this.getUsernamesFromRole(roleObject);
+ List<String> aaiFunctions = this.getAAIFunctions(roleObject);
+
+ usrs.forEach((key, value) -> {
+ final AAIUser au = tempUsers.getOrDefault(key, new AAIUser(key, value));
+ au.addRole(roleName);
+ aaiFunctions.forEach(f -> {
+ List<String> httpMethods = this.getRoleHttpMethods(f, roleObject);
+ httpMethods.forEach(hm -> au.setUserAccess(f, hm));
+ this.validFunctions.add(f);
+ });
+
+ tempUsers.put(key, au);
+
+ });
+ }
+ }
+ if (!tempUsers.isEmpty()) {
+ users = tempUsers;
+ }
+ }
+ } catch (FileNotFoundException e) {
+ ErrorLogHelper.logError(ERROR_CODE_AAI_4001, globalAuthFileName + ". Exception: " + e);
+ } catch (JsonProcessingException e) {
+ ErrorLogHelper.logError(ERROR_CODE_AAI_4001, globalAuthFileName + ". Not valid JSON: " + e);
+ } catch (Exception e) {
+ ErrorLogHelper.logError(ERROR_CODE_AAI_4001, globalAuthFileName + ". Exception caught: " + e);
+ }
+ }
+
+ private List<String> getRoleHttpMethods(String aaiFunctionName, JsonObject roleObject) {
+ List<String> httpMethods = new ArrayList<>();
+
+ JsonArray ja = roleObject.getAsJsonArray("functions");
+ for (JsonElement je : ja) {
+ if (je.isJsonObject() && je.getAsJsonObject().has("name")
+ && je.getAsJsonObject().get("name").getAsString().equals(aaiFunctionName)) {
+ JsonArray jaMeth = je.getAsJsonObject().getAsJsonArray("methods");
+ for (JsonElement jeMeth : jaMeth) {
+ if (jeMeth.isJsonObject() && jeMeth.getAsJsonObject().has("name")) {
+ httpMethods.add(jeMeth.getAsJsonObject().get("name").getAsString());
+ }
+ }
+ }
+ }
+
+ return httpMethods;
+ }
+
+ private List<String> getAAIFunctions(JsonObject roleObject) {
+ List<String> aaiFunctions = new ArrayList<>();
+
+ JsonArray ja = roleObject.getAsJsonArray("functions");
+ for (JsonElement je : ja) {
+ if (je.isJsonObject() && je.getAsJsonObject().has("name")) {
+ aaiFunctions.add(je.getAsJsonObject().get("name").getAsString());
+ }
+ }
+
+ return aaiFunctions;
+ }
+
+ private Map<String, Boolean> getUsernamesFromRole(JsonObject roleObject) throws UnsupportedEncodingException {
+ Map<String, Boolean> usernames = new HashMap<>();
+
+ JsonArray uja = roleObject.getAsJsonArray("users");
+ for (JsonElement je : uja) {
+ if (je.isJsonObject()) {
+ if (je.getAsJsonObject().has("username")) {
+ if (je.getAsJsonObject().has("is-wildcard-id")) {
+ usernames.put(je.getAsJsonObject().get("username").getAsString().toLowerCase(),
+ je.getAsJsonObject().get("is-wildcard-id").getAsBoolean());
+ } else {
+ usernames.put(je.getAsJsonObject().get("username").getAsString().toLowerCase(), false);
+ }
+ } else if (je.getAsJsonObject().has("user")) {
+ String auth = je.getAsJsonObject().get("user").getAsString() + ":"
+ + Password.deobfuscate(je.getAsJsonObject().get("pass").getAsString());
+ String authorizationCode = new String(Base64.getEncoder().encode(auth.getBytes("utf-8")));
+ usernames.put(authorizationCode, false);
+ }
+ }
+ }
+
+ return usernames;
+ }
+
+ public String getAuthPolicyFunctName(String uri) {
+ String authPolicyFunctionName = "";
+ if (uri.startsWith(basePath + "/search")) {
+ authPolicyFunctionName = "search";
+ } else if (uri.startsWith(basePath + "/recents")) {
+ authPolicyFunctionName = "recents";
+ } else if (uri.startsWith(basePath + "/cq2gremlin")) {
+ authPolicyFunctionName = "cq2gremlin";
+ } else if (uri.startsWith(basePath + "/cq2gremlintest")) {
+ authPolicyFunctionName = "cq2gremlintest";
+ } else if (uri.startsWith(basePath + "/util/echo")) {
+ authPolicyFunctionName = "util";
+ } else if (uri.startsWith(basePath + "/tools")) {
+ authPolicyFunctionName = "tools";
+ } else {
+ Matcher match = AUTH_POLICY_PATTERN.matcher(uri);
+ if (match.find()) {
+ authPolicyFunctionName = match.group(1);
+ }
+ }
+ return authPolicyFunctionName;
+ }
+
+ /**
+ * for backwards compatibility
+ *
+ * @param username
+ * @param uri
+ * @param httpMethod
+ * @param haProxyUser
+ * @return
+ * @throws AAIUnrecognizedFunctionException
+ */
+ public boolean authorize(String username, String uri, String httpMethod, String haProxyUser)
+ throws AAIUnrecognizedFunctionException {
+ return authorize(username, uri, httpMethod, haProxyUser, null);
+ }
+
+ /**
+ *
+ * @param username
+ * @param uri
+ * @param httpMethod
+ * @param haProxyUser
+ * @param issuer issuer of the cert
+ * @return
+ * @throws AAIUnrecognizedFunctionException
+ */
+ public boolean authorize(String username, String uri, String httpMethod, String haProxyUser, String issuer)
+ throws AAIUnrecognizedFunctionException {
+ String aaiMethod = this.getAuthPolicyFunctName(uri);
+ if (!this.validFunctions.contains(aaiMethod) && !("info".equalsIgnoreCase(aaiMethod))) {
+ throw new AAIUnrecognizedFunctionException(aaiMethod);
+ }
+ boolean wildcardCheck = isWildcardIssuer(issuer);
+ boolean authorized;
+ LOGGER.debug(
+ "Authorizing the user for the request cert {}, haproxy header {}, aai method {}, httpMethod {}, cert issuer {}",
+ username, haProxyUser, aaiMethod, httpMethod, issuer);
+ Optional<AAIUser> oau = this.getUser(username, wildcardCheck);
+ if (oau.isPresent()) {
+ AAIUser au = oau.get();
+ if (au.hasRole("HAProxy")) {
+ LOGGER.debug("User has HAProxy role");
+ if ("GET".equalsIgnoreCase(httpMethod) && "util".equalsIgnoreCase(aaiMethod) && haProxyUser.isEmpty()) {
+ LOGGER.debug("Authorized user has HAProxy role with echo request");
+ authorized = this.authorize(au, aaiMethod, httpMethod);
+ } else {
+ authorized = this.authorize(haProxyUser, uri, httpMethod, "", issuer);
+ }
+ } else {
+ LOGGER.debug("User doesn't have HAProxy role so assuming its a regular client");
+ authorized = this.authorize(au, aaiMethod, httpMethod);
+ }
+ } else {
+ LOGGER.debug("User not found: " + username + " on function " + aaiMethod + " request type " + httpMethod);
+ authorized = false;
+ }
+
+ return authorized;
+ }
+
+ private boolean isWildcardIssuer(String issuer) {
+ if (issuer != null && !issuer.isEmpty()) {
+ List<String> validIssuers = Arrays
+ .asList(AAIConfig.get("aaf.valid.issuer.wildcard", UUID.randomUUID().toString()).split("\\|"));
+ for (String validIssuer : validIssuers) {
+ if (issuer.contains(validIssuer)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * returns aai user either matching the username or containing the wildcard.
+ *
+ * @param username
+ * @return
+ */
+ public Optional<AAIUser> getUser(String username, boolean wildcardCheck) {
+ if (users.containsKey(username)) {
+ return Optional.of(users.get(username));
+ } else if (wildcardCheck) {
+ List<AAIUser> laus =
+ users.entrySet().stream().filter(e -> e.getValue().isWildcard() && username.contains(e.getKey()))
+ .map(Map.Entry::getValue).collect(Collectors.toList());
+ if (!laus.isEmpty()) {
+ return Optional.of(laus.get(0));
+ }
+ }
+ return Optional.empty();
+ }
+
+ /**
+ *
+ * @param aaiUser
+ * aai user with the username
+ * @param aaiMethod
+ * aai function the authorization is required on
+ * @param httpMethod
+ * http action user is attempting
+ * @return true, if successful
+ */
+ private boolean authorize(AAIUser aaiUser, String aaiMethod, String httpMethod) {
+ if ("info".equalsIgnoreCase(aaiMethod)|| aaiUser.hasAccess(aaiMethod, httpMethod)) {
+ LOGGER.debug("AUTH ACCEPTED: " + aaiUser.getUsername() + " on function " + aaiMethod + " request type "
+ + httpMethod);
+ return true;
+ } else {
+ LOGGER.debug("AUTH FAILED: " + aaiUser.getUsername() + " on function " + aaiMethod + " request type "
+ + httpMethod);
+ return false;
+ }
+ }
+}
diff --git a/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/AAIUser.java b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/AAIUser.java
new file mode 100644
index 00000000..4512adb0
--- /dev/null
+++ b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/AAIUser.java
@@ -0,0 +1,81 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Modifications Copyright © 2018 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.aai.aaf.auth;
+
+import java.util.*;
+
+public class AAIUser {
+
+ private String username;
+
+ private boolean isWildcard = false;
+ private Set<String> roles;
+ private Map<String, Set<String>> aaiFunctionToHttpMethod;
+
+ public AAIUser(String username) {
+ this(username, false);
+ }
+
+ public AAIUser(String username, boolean isWildcard) {
+ this.username = username;
+ this.roles = new HashSet<>();
+ this.aaiFunctionToHttpMethod = new HashMap<>();
+ this.isWildcard = isWildcard;
+ }
+
+ public boolean isWildcard() {
+ return isWildcard;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void addRole(String role) {
+ this.roles.add(role);
+ }
+
+ public boolean hasRole(String role) {
+ return this.roles.contains(role);
+ }
+
+ public void setUserAccess(String aaiMethod, String... httpMethods) {
+ for (String httpMethod : httpMethods) {
+ this.addUserAccess(aaiMethod, httpMethod);
+ }
+ }
+
+ private void addUserAccess(String aaiMethod, String httpMethod) {
+ Set<String> httpMethods = new HashSet<>();
+ if (this.aaiFunctionToHttpMethod.containsKey(aaiMethod)) {
+ httpMethods = this.aaiFunctionToHttpMethod.get(aaiMethod);
+ }
+ httpMethods.add(httpMethod);
+ this.aaiFunctionToHttpMethod.put(aaiMethod, httpMethods);
+ }
+
+ public boolean hasAccess(String aaiMethod, String httpMethod) {
+ return this.aaiFunctionToHttpMethod.getOrDefault(aaiMethod, Collections.emptySet()).contains(httpMethod);
+ }
+
+}
diff --git a/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/AafRequestFilter.java b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/AafRequestFilter.java
new file mode 100644
index 00000000..9a02fe2c
--- /dev/null
+++ b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/AafRequestFilter.java
@@ -0,0 +1,114 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T 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.aai.aaf.auth;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.onap.aaf.cadi.filter.CadiFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+
+import static org.onap.aai.aaf.auth.ResponseFormatter.errorResponse;
+
+/**
+ * The Class AafRequestFilter provides common auth filter methods
+ */
+public class AafRequestFilter {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(AafRequestFilter.class);
+
+ public static void authenticationFilter(HttpServletRequest request, HttpServletResponse response,
+ FilterChain filterChain, CadiFilter cadiFilter, Properties props, String userChainPattern)
+ throws IOException, ServletException {
+ if (!request.getRequestURI().matches("^.*/util/echo$")) {
+
+ List<String> cadiConfiguredIssuers = CertUtil.getCadiCertIssuers(props);
+ String issuer = CertUtil.getCertIssuer(request);
+ if (issuer == null || issuer.isEmpty()) {
+ errorResponse(request, response);
+ return;
+ }
+ issuer = issuer.replaceAll("\\s+", "").toUpperCase();
+
+ if (cadiConfiguredIssuers.contains(issuer)) {
+ LOGGER.debug("authenticationFilter CADI issuer " + issuer);
+ if (CertUtil.isHaProxy(request)) {
+ // get the end user/client mechid and use it in the user chain header value
+ String user = CertUtil.getMechId(request);
+ LOGGER.debug("authenticationFilter haProxy sent end user/mechid " + user);
+ if (user == null || user.isEmpty()) {
+ errorResponse(request, response);
+ return;
+ }
+ AafRequestWrapper reqWrapper = new AafRequestWrapper(request);
+ String userChainHdr = CertUtil.buildUserChainHeader(user, userChainPattern);
+ LOGGER.debug("User chain header value: " + userChainHdr);
+ reqWrapper.putHeader(CertUtil.AAF_USER_CHAIN_HDR, userChainHdr);
+ cadiFilter.doFilter(reqWrapper, response, filterChain);
+ } else {
+ cadiFilter.doFilter(request, response, filterChain);
+ }
+ if (response.getStatus() == 401 || response.getStatus() == 403) {
+ LOGGER.debug("authenticationFilter failed CADI authentication");
+ errorResponse(request, response);
+ return;
+ }
+ } else {
+ filterChain.doFilter(request, response);
+ }
+ } else {
+ filterChain.doFilter(request, response);
+ }
+ }
+
+ public static void authorizationFilter(HttpServletRequest request, HttpServletResponse response,
+ FilterChain filterChain, String permission, Properties props) throws IOException, ServletException {
+ if (request.getRequestURI().matches("^.*/util/echo$")) {
+ filterChain.doFilter(request, response);
+ }
+ List<String> cadiConfiguredIssuers = CertUtil.getCadiCertIssuers(props);
+ String issuer = CertUtil.getCertIssuer(request);
+ if (issuer == null || issuer.isEmpty()) {
+ errorResponse(request, response);
+ return;
+ }
+ issuer = issuer.replaceAll("\\s+", "").toUpperCase();
+ Enumeration hdrs = request.getHeaders(CertUtil.AAF_USER_CHAIN_HDR);
+ while (hdrs.hasMoreElements()) {
+ String headerValue = (String) hdrs.nextElement();
+ LOGGER.debug("authorizationFilter user chain headerValue=" + headerValue);
+ }
+ if ((cadiConfiguredIssuers.contains(issuer)) && (!request.isUserInRole(permission))) {
+ LOGGER.debug(
+ "authorizationFilter failed CADI authorization issuer=" + issuer + " permission=" + permission);
+ errorResponse(request, response);
+ } else {
+ filterChain.doFilter(request, response);
+ }
+ }
+}
diff --git a/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/AafRequestWrapper.java b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/AafRequestWrapper.java
new file mode 100644
index 00000000..0ecca679
--- /dev/null
+++ b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/AafRequestWrapper.java
@@ -0,0 +1,78 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T 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.aai.aaf.auth;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.util.*;
+
+/**
+ * The AafRequestWrapper sets the user in the principal name
+ */
+public class AafRequestWrapper extends HttpServletRequestWrapper {
+
+ private final Map<String, String> customHeaders;
+
+ public AafRequestWrapper(HttpServletRequest request) {
+ super(request);
+ this.customHeaders = new HashMap<String, String>();
+ }
+
+ public void putHeader(String name, String value) {
+ this.customHeaders.put(name, value);
+ }
+
+ @Override
+ public String getHeader(String name) {
+ String headerValue = customHeaders.get(name);
+ if (headerValue != null) {
+ return headerValue;
+ }
+ return (((HttpServletRequest) getRequest()).getHeader(name));
+ }
+
+ @Override
+ public Enumeration<String> getHeaderNames() {
+ Set<String> nameSet = new HashSet<String>(customHeaders.keySet());
+
+ Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
+ while (e.hasMoreElements()) {
+ String headerName = e.nextElement();
+ nameSet.add(headerName);
+ }
+ return Collections.enumeration(nameSet);
+ }
+
+ @Override
+ public Enumeration<String> getHeaders(String name) {
+ String myHeaderValue = customHeaders.get(name);
+ Set<String> headerValueSet = new HashSet<String>();
+ if (myHeaderValue != null) {
+ headerValueSet.add(myHeaderValue);
+ }
+ Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaders(name);
+ while (e.hasMoreElements()) {
+ String headerValue = e.nextElement();
+ headerValueSet.add(headerValue);
+ }
+ return Collections.enumeration(headerValueSet);
+ }
+}
diff --git a/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/CertUtil.java b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/CertUtil.java
new file mode 100644
index 00000000..334a3060
--- /dev/null
+++ b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/CertUtil.java
@@ -0,0 +1,157 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T 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.aai.aaf.auth;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * The Class CertUtil provides cert related utility methods.
+ */
+public class CertUtil {
+ public static final String DEFAULT_CADI_ISSUERS =
+ "CN=ATT AAF CADI Test Issuing CA 01, OU=CSO, O=ATT, C=US:CN=ATT AAF CADI Test Issuing CA 02, OU=CSO, O=ATT, C=US";
+ public static final String CADI_PROP_FILES = "cadi_prop_files";
+ public static final String CADI_ISSUERS_PROP_NAME = "cadi_x509_issuers";
+ public static final String CADI_ISSUERS_SEPARATOR = ":";
+ public static final String AAI_SSL_CLIENT_OU_HDR = "X-AAI-SSL-Client-OU";
+ public static final String AAI_SSL_ISSUER_HDR = "X-AAI-SSL-Issuer";
+ public static final String AAI_SSL_CLIENT_CN_HDR = "X-AAI-SSL-Client-CN";
+ public static final String AAI_SSL_CLIENT_O_HDR = "X-AAI-SSL-Client-O";
+ public static final String AAI_SSL_CLIENT_L_HDR = "X-AAI-SSL-Client-L";
+ public static final String AAI_SSL_CLIENT_ST_HDR = "X-AAI-SSL-Client-ST";
+ public static final String AAI_SSL_CLIENT_C_HDR = "X-AAI-SSL-Client-C";
+ public static final String AAF_USER_CHAIN_HDR = "USER_CHAIN";
+ public static final String AAF_ID = "<AAF-ID>";
+ private static final Logger LOGGER = LoggerFactory.getLogger(CertUtil.class);
+
+ public static String getAaiSslClientOuHeader(HttpServletRequest hsr) {
+ return (hsr.getHeader(AAI_SSL_CLIENT_OU_HDR));
+ }
+
+ public static boolean isHaProxy(HttpServletRequest hsr) {
+
+ String haProxyUser = "";
+ if (Objects.isNull(hsr.getHeader(AAI_SSL_CLIENT_CN_HDR)) || Objects.isNull(hsr.getHeader(AAI_SSL_CLIENT_OU_HDR))
+ || Objects.isNull(hsr.getHeader(AAI_SSL_CLIENT_O_HDR))
+ || Objects.isNull(hsr.getHeader(AAI_SSL_CLIENT_L_HDR))
+ || Objects.isNull(hsr.getHeader(AAI_SSL_CLIENT_ST_HDR))
+ || Objects.isNull(hsr.getHeader(AAI_SSL_CLIENT_C_HDR))) {
+ haProxyUser = "";
+ } else {
+ haProxyUser = String.format("CN=%s, OU=%s, O=\"%s\", L=%s, ST=%s, C=%s",
+ Objects.toString(hsr.getHeader(AAI_SSL_CLIENT_CN_HDR), ""),
+ Objects.toString(hsr.getHeader(AAI_SSL_CLIENT_OU_HDR), ""),
+ Objects.toString(hsr.getHeader(AAI_SSL_CLIENT_O_HDR), ""),
+ Objects.toString(hsr.getHeader(AAI_SSL_CLIENT_L_HDR), ""),
+ Objects.toString(hsr.getHeader(AAI_SSL_CLIENT_ST_HDR), ""),
+ Objects.toString(hsr.getHeader(AAI_SSL_CLIENT_C_HDR), "")).toLowerCase();
+ }
+ if (!haProxyUser.isEmpty()) {
+ LOGGER.debug("isHaProxy haProxyUser=" + haProxyUser);
+ return true;
+ }
+ LOGGER.debug("isHaProxy haProxyUser not found");
+ return false;
+ }
+
+ public static String getMechId(HttpServletRequest hsr) {
+ String mechId = null;
+ String ou = getAaiSslClientOuHeader(hsr);
+ if ((ou != null) && (!ou.isEmpty())) {
+ String[] parts = ou.split(CADI_ISSUERS_SEPARATOR);
+ if (parts != null && parts.length >= 1) {
+ mechId = parts[0];
+ }
+ }
+ LOGGER.debug("getMechId mechId=" + mechId);
+ return (mechId);
+ }
+
+ public static String getCertIssuer(HttpServletRequest hsr) {
+ String issuer = hsr.getHeader(AAI_SSL_ISSUER_HDR);
+ if (issuer != null && !issuer.isEmpty()) {
+ LOGGER.debug("getCertIssuer issuer from header " + AAI_SSL_ISSUER_HDR + " " + issuer);
+ // the haproxy header replaces the ', ' with '/' and reverses on the '/' need to undo that.
+ List<String> broken = Arrays.asList(issuer.split("/"));
+ broken = broken.stream().filter(s -> !s.isEmpty()).collect(Collectors.toList());
+ Collections.reverse(broken);
+ issuer = String.join(", ", broken);
+ } else {
+ if (hsr.getAttribute("javax.servlet.request.cipher_suite") != null) {
+ X509Certificate[] certChain =
+ (X509Certificate[]) hsr.getAttribute("javax.servlet.request.X509Certificate");
+ if (certChain != null && certChain.length > 0) {
+ X509Certificate clientCert = certChain[0];
+ issuer = clientCert.getIssuerX500Principal().getName();
+ LOGGER.debug("getCertIssuer issuer from client cert " + issuer);
+ }
+ }
+ }
+ return issuer;
+ }
+
+ public static List<String> getCadiCertIssuers(Properties cadiProperties) {
+
+ List<String> defaultList = new ArrayList<String>();
+ List<String> resultList = new ArrayList<String>();
+
+ String[] cIssuers = DEFAULT_CADI_ISSUERS.split(CADI_ISSUERS_SEPARATOR);
+ for (String issuer : cIssuers) {
+ defaultList.add(issuer.replaceAll("\\s+", "").toUpperCase());
+ }
+ try {
+ String certPropFileName = cadiProperties.getProperty(CADI_PROP_FILES);
+ String configuredIssuers = DEFAULT_CADI_ISSUERS;
+ Properties certProperties = new Properties();
+ if (certPropFileName != null) {
+ certProperties.load(new FileInputStream(new File(certPropFileName)));
+ configuredIssuers = certProperties.getProperty(CADI_ISSUERS_PROP_NAME);
+ }
+ if ((configuredIssuers != null) && (!configuredIssuers.isEmpty())) {
+ cIssuers = configuredIssuers.split(CADI_ISSUERS_SEPARATOR);
+ for (String issuer : cIssuers) {
+ resultList.add(issuer.replaceAll("\\s+", "").toUpperCase());
+ }
+ }
+ } catch (IOException ioe) {
+ return (defaultList);
+ }
+ if (resultList.isEmpty()) {
+ return defaultList;
+ }
+ LOGGER.debug("getCadiCertIssuers " + resultList.toString());
+ return resultList;
+ }
+
+ public static String buildUserChainHeader(String user, String userChainPattern) {
+ // aaf.userchain.pattern=<AAF-ID>:${aaf.userchain.service.reference}:${aaf.userchain.auth.type}:AS
+ return (userChainPattern.replaceAll(AAF_ID, user));
+ }
+}
diff --git a/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/FileWatcher.java b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/FileWatcher.java
new file mode 100644
index 00000000..45331ed9
--- /dev/null
+++ b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/FileWatcher.java
@@ -0,0 +1,60 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T 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.aai.aaf.auth;
+
+import java.io.File;
+import java.util.TimerTask;
+
+public abstract class FileWatcher extends TimerTask {
+ private long timeStamp;
+ private File file;
+
+ /**
+ * Instantiates a new file watcher.
+ *
+ * @param file the file
+ */
+ public FileWatcher(File file) {
+ this.file = file;
+ this.timeStamp = file.lastModified();
+ }
+
+ /**
+ * runs a timer task
+ *
+ * @see TimerTask#run
+ */
+ public final void run() {
+ long timeStamp = file.lastModified();
+
+ if ((timeStamp - this.timeStamp) > 500) {
+ this.timeStamp = timeStamp;
+ onChange(file);
+ }
+ }
+
+ /**
+ * On change.
+ *
+ * @param file the file
+ */
+ protected abstract void onChange(File file);
+}
diff --git a/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/ResponseFormatter.java b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/ResponseFormatter.java
new file mode 100644
index 00000000..d7c88e81
--- /dev/null
+++ b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/ResponseFormatter.java
@@ -0,0 +1,57 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T 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.aai.aaf.auth;
+
+import org.onap.aai.exceptions.AAIException;
+import org.onap.aai.logging.ErrorLogHelper;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.MediaType;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class ResponseFormatter {
+
+ private static final String ACCEPT_HEADER = "accept";
+
+ public static void errorResponse(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ errorResponse(new AAIException("AAI_3300"), request, response);
+ }
+
+ public static void errorResponse(AAIException exception, HttpServletRequest request, HttpServletResponse response) throws IOException {
+
+ if(response.isCommitted()){
+ return;
+ }
+
+ String accept = request.getHeader(ACCEPT_HEADER) == null ? MediaType.APPLICATION_XML : request.getHeader(ACCEPT_HEADER);
+
+ response.setStatus(exception.getErrorObject().getHTTPResponseCode().getStatusCode());
+ response.setHeader("Content-Type", accept);
+ response.resetBuffer();
+
+ String resp = ErrorLogHelper.getRESTAPIErrorResponse(Collections.singletonList(MediaType.valueOf(accept)), exception, new ArrayList<>());
+ response.getOutputStream().print(resp);
+ response.flushBuffer();
+ }
+}
diff --git a/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/exceptions/AAIUnrecognizedFunctionException.java b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/exceptions/AAIUnrecognizedFunctionException.java
new file mode 100644
index 00000000..c6a97ac5
--- /dev/null
+++ b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/auth/exceptions/AAIUnrecognizedFunctionException.java
@@ -0,0 +1,46 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Modifications Copyright © 2018 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.aai.aaf.auth.exceptions;
+
+import org.onap.aai.exceptions.AAIException;
+
+public class AAIUnrecognizedFunctionException extends AAIException {
+
+ private static final String AAI_3012 = "AAI_3012";
+ private static final long serialVersionUID = 3297064867724071290L;
+
+ public AAIUnrecognizedFunctionException() {
+ }
+
+ public AAIUnrecognizedFunctionException(String message) {
+ super(AAI_3012, message);
+ }
+
+ public AAIUnrecognizedFunctionException(Throwable cause) {
+ super(AAI_3012, cause);
+ }
+
+ public AAIUnrecognizedFunctionException(String message, Throwable cause) {
+ super(AAI_3012, cause, message);
+ }
+}
diff --git a/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/AafAuthorizationFilter.java b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/AafAuthorizationFilter.java
new file mode 100644
index 00000000..9f2782a4
--- /dev/null
+++ b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/AafAuthorizationFilter.java
@@ -0,0 +1,119 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.aai.aaf.filters;
+
+import org.onap.aai.aaf.auth.ResponseFormatter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.web.filter.OrderedRequestContextFilter;
+import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * AAF authorization filter
+ */
+
+@Component
+@Profile(AafProfiles.AAF_AUTHENTICATION)
+@PropertySource(value = "file:${CONFIG_HOME}/aaf/permissions.properties", ignoreResourceNotFound = true)
+@PropertySource(value = "file:${server.local.startpath}/aaf/permissions.properties", ignoreResourceNotFound = true)
+public class AafAuthorizationFilter extends OrderedRequestContextFilter {
+
+ private static final String ADVANCED = "advanced";
+ private static final String BASIC = "basic";
+
+ private final String type;
+ private final String instance;
+
+ private GremlinFilter gremlinFilter;
+
+ private List<String> advancedKeywordsList;
+
+ @Autowired
+ public AafAuthorizationFilter(
+ GremlinFilter gremlinFilter,
+ @Value("${permission.type}") String type,
+ @Value("${permission.instance}") String instance,
+ @Value("${advanced.keywords.list:}") String advancedKeys
+ ) {
+ this.gremlinFilter = gremlinFilter;
+ this.type = type;
+ this.instance = instance;
+ if(advancedKeys == null || advancedKeys.isEmpty()){
+ this.advancedKeywordsList = new ArrayList<>();
+ } else {
+ this.advancedKeywordsList = Arrays.stream(advancedKeys.split(","))
+ .collect(Collectors.toList());
+ }
+ this.setOrder(FilterPriority.AAF_AUTHORIZATION.getPriority());
+ }
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
+ if(request.getRequestURI().endsWith("/query")){
+ gremlinFilter.doBasicAuthFilter(request, response, filterChain);
+ } else {
+
+ String permission = null;
+
+ if(advancedKeywordsList == null || advancedKeywordsList.size() == 0) {
+ permission = String.format("%s|%s|%s", type, instance, request.getMethod().toLowerCase());
+ } else {
+
+ boolean isAdvanced = this.containsAdvancedKeywords(request);
+
+ //if the URI contains advanced.keywords it's an advanced query
+ String queryType = isAdvanced ? ADVANCED : BASIC;
+ permission = String.format("%s|%s|%s", type, instance, queryType);
+ }
+
+ boolean isAuthorized = request.isUserInRole(permission);
+
+ if(!isAuthorized){
+ ResponseFormatter.errorResponse(request, response);
+ } else {
+ filterChain.doFilter(request,response);
+ }
+
+ }
+ }
+
+ private boolean containsAdvancedKeywords(HttpServletRequest request) {
+ String uri = request.getRequestURI();
+ for (String keyword: advancedKeywordsList) {
+ if (uri.contains(keyword)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/AafCertAuthorizationFilter.java b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/AafCertAuthorizationFilter.java
new file mode 100644
index 00000000..7ec6bb64
--- /dev/null
+++ b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/AafCertAuthorizationFilter.java
@@ -0,0 +1,107 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.aai.aaf.filters;
+
+import org.onap.aai.aaf.auth.AafRequestFilter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.web.filter.OrderedRequestContextFilter;
+import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Properties;
+import java.util.stream.Collectors;
+
+
+/**
+ * AAF with client cert authorization filter
+ */
+
+@Component
+@Profile(AafProfiles.AAF_CERT_AUTHENTICATION)
+@PropertySource(value = "file:${CONFIG_HOME}/aaf/permissions.properties", ignoreResourceNotFound = true)
+@PropertySource(value = "file:${server.local.startpath}/aaf/permissions.properties", ignoreResourceNotFound = true)
+public class AafCertAuthorizationFilter extends OrderedRequestContextFilter {
+
+ private static final String ADVANCED = "advanced";
+ private static final String BASIC = "basic";
+
+ String type;
+
+ String instance;
+
+ private CadiProps cadiProps;
+
+ private List<String> advancedKeywordsList;
+
+ @Autowired
+ public AafCertAuthorizationFilter(
+ @Value("${permission.type}") String type,
+ @Value("${permission.instance}") String instance,
+ @Value("${advanced.keywords.list:}") String advancedKeys,
+ CadiProps cadiProps
+ ) {
+ this.type = type;
+ this.instance = instance;
+ this.cadiProps = cadiProps;
+ if(advancedKeys == null || advancedKeys.isEmpty()){
+ this.advancedKeywordsList = new ArrayList<>();
+ } else {
+ this.advancedKeywordsList = Arrays.stream(advancedKeys.split(","))
+ .collect(Collectors.toList());
+ }
+ this.setOrder(FilterPriority.AAF_CERT_AUTHORIZATION.getPriority());
+ }
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
+ if(advancedKeywordsList == null || advancedKeywordsList.size() == 0){
+ String permission = String.format("%s|%s|%s", type, instance, request.getMethod().toLowerCase());
+ AafRequestFilter.authorizationFilter(request, response, filterChain, permission, cadiProps.getCadiProperties());
+ } else {
+ boolean isAdvanced = this.containsAdvancedKeywords(request);
+
+ //if the URI contains advanced.keywords it's an advanced query
+ String queryType = isAdvanced ? ADVANCED : BASIC;
+ String permission = String.format("%s|%s|%s", type, instance, queryType);
+ AafRequestFilter.authorizationFilter(request, response, filterChain, permission, cadiProps.getCadiProperties());
+ }
+ }
+
+ private boolean containsAdvancedKeywords(HttpServletRequest request) {
+ String uri = request.getRequestURI();
+ for (String keyword: advancedKeywordsList) {
+ if (uri.contains(keyword)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/AafCertFilter.java b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/AafCertFilter.java
new file mode 100644
index 00000000..71238cfc
--- /dev/null
+++ b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/AafCertFilter.java
@@ -0,0 +1,111 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.aai.aaf.filters;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.filter.CadiFilter;
+import org.onap.aai.aaf.auth.AafRequestFilter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.web.filter.OrderedRequestContextFilter;
+import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * AAF with client cert authentication filter
+ */
+
+@Component
+@Profile(AafProfiles.AAF_CERT_AUTHENTICATION)
+@PropertySource(value = "file:${CONFIG_HOME}/aaf/permissions.properties", ignoreResourceNotFound = true)
+@PropertySource(value = "file:${server.local.startpath}/aaf/permissions.properties", ignoreResourceNotFound = true)
+public class AafCertFilter extends OrderedRequestContextFilter {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(AafCertFilter.class);
+
+ String aafUserChainPattern;
+
+ private final CadiFilter cadiFilter;
+
+ private final CadiProps cadiProps;
+
+ @Autowired
+ public AafCertFilter( @Value("${aaf.userchain.pattern}") String aafUserChainPattern,
+ CadiProps cadiProps) throws IOException, ServletException {
+
+ this.aafUserChainPattern = aafUserChainPattern;
+ this.cadiProps = cadiProps;
+ cadiFilter = new CadiFilter(new PropAccess((level,element)->{
+ switch (level) {
+ case DEBUG:
+ LOGGER.debug(buildMsg(element));
+ break;
+ case INFO:
+ case AUDIT:
+ LOGGER.info(buildMsg(element));
+ break;
+ case WARN:
+ LOGGER.warn(buildMsg(element));
+ break;
+ case ERROR:
+ LOGGER.error(buildMsg(element));
+ break;
+ case INIT:
+ LOGGER.info(buildMsg(element));
+ break;
+ case TRACE:
+ LOGGER.trace(buildMsg(element));
+ break;
+ case NONE:
+ break;
+ }
+ }, new String[]{"cadi_prop_files=" + cadiProps.getCadiFileName()} ));
+ this.setOrder(FilterPriority.AAF_CERT_AUTHENTICATION.getPriority());
+ }
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
+ AafRequestFilter.authenticationFilter(request, response, filterChain, cadiFilter, cadiProps.getCadiProperties(), aafUserChainPattern);
+ }
+ private String buildMsg(Object[] objects) {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for ( Object o: objects ) {
+ if (first) {
+ first = false;
+ }
+ else {
+ sb.append(' ');
+ }
+ sb.append(o.toString());
+ }
+ return (sb.toString());
+ }
+}
diff --git a/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/AafFilter.java b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/AafFilter.java
new file mode 100644
index 00000000..16d9afd2
--- /dev/null
+++ b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/AafFilter.java
@@ -0,0 +1,108 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.aai.aaf.filters;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.filter.CadiFilter;
+import org.onap.aai.aaf.auth.ResponseFormatter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.web.filter.OrderedRequestContextFilter;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+
+/**
+ * AAF authentication filter
+ */
+
+@Component
+@Profile(AafProfiles.AAF_AUTHENTICATION)
+public class AafFilter extends OrderedRequestContextFilter {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(AafCertFilter.class);
+
+ private final CadiFilter cadiFilter;
+
+ @Autowired
+ public AafFilter(CadiProps cadiProps) throws IOException, ServletException {
+ cadiFilter = new CadiFilter(new PropAccess((level,element)->{
+ switch (level) {
+ case DEBUG:
+ LOGGER.debug(buildMsg(element));
+ break;
+ case INFO:
+ case AUDIT:
+ LOGGER.info(buildMsg(element));
+ break;
+ case WARN:
+ LOGGER.warn(buildMsg(element));
+ break;
+ case ERROR:
+ LOGGER.error(buildMsg(element));
+ break;
+ case INIT:
+ LOGGER.info(buildMsg(element));
+ break;
+ case TRACE:
+ LOGGER.trace(buildMsg(element));
+ break;
+ case NONE:
+ break;
+ }
+ }, new String[]{"cadi_prop_files=" + cadiProps.getCadiFileName()} ));
+ this.setOrder(FilterPriority.AAF_AUTHENTICATION.getPriority());
+ }
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
+ if (!request.getRequestURI().matches("^.*/util/echo$")) {
+ cadiFilter.doFilter(request, response, filterChain);
+ if (response.getStatus() == 401 || response.getStatus() == 403) {
+ ResponseFormatter.errorResponse(request, response);
+ }
+ } else {
+ filterChain.doFilter(request, response);
+ }
+ }
+
+ private String buildMsg(Object[] objects) {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for ( Object o: objects ) {
+ if (first) {
+ first = false;
+ }
+ else {
+ sb.append(' ');
+ }
+ sb.append(o.toString());
+ }
+ return (sb.toString());
+ }
+}
diff --git a/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/AafProfiles.java b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/AafProfiles.java
new file mode 100644
index 00000000..b587716e
--- /dev/null
+++ b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/AafProfiles.java
@@ -0,0 +1,33 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2019 AT&T 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.aai.aaf.filters;
+
+public class AafProfiles {
+
+ // AAF Basic Auth
+ public static final String AAF_AUTHENTICATION = "aaf-auth";
+
+ // AAF Auth with Client Certs
+ public static final String AAF_CERT_AUTHENTICATION = "aaf-cert-auth";
+
+ public static final String TWO_WAY_SSL = "two-way-ssl";
+
+ private AafProfiles(){}
+}
diff --git a/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/CadiProps.java b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/CadiProps.java
new file mode 100644
index 00000000..35e88f5f
--- /dev/null
+++ b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/CadiProps.java
@@ -0,0 +1,81 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2019 AT&T 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.aai.aaf.filters;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+// This component will be created if and only if any of the following profiles are active
+@Component
+@Profile({
+ AafProfiles.AAF_CERT_AUTHENTICATION,
+ AafProfiles.AAF_AUTHENTICATION,
+ AafProfiles.TWO_WAY_SSL
+})
+public class CadiProps {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CadiProps.class);
+
+ private String cadiFileName;
+
+ private Properties cadiProperties;
+
+ @Autowired
+ public CadiProps(@Value("${aaf.cadi.file:./resources/cadi.properties}") String filename){
+ cadiFileName = filename;
+ cadiProperties = new Properties();
+ }
+
+ @PostConstruct
+ public void init() throws IOException {
+
+ File cadiFile = new File(cadiFileName);
+
+ if(!cadiFile.exists()){
+ LOGGER.warn("Unable to find the cadi file in the given path {} so loading cadi.properties from classloader", cadiFileName);
+ InputStream is = this.getClass().getClassLoader().getResourceAsStream("cadi.properties");
+ cadiProperties.load(is);
+ } else {
+ LOGGER.info("Successfully found the file {} and started loading the properties from it", cadiFileName);
+ cadiFileName = cadiFile.getAbsolutePath();
+ try (InputStream inputStream = new FileInputStream(cadiFile)) {
+ cadiProperties.load(inputStream);
+ }
+
+ }
+ }
+ public String getCadiFileName() {
+ return cadiFileName;
+ }
+ public Properties getCadiProperties(){
+ return cadiProperties;
+ }
+}
diff --git a/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/FilterPriority.java b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/FilterPriority.java
new file mode 100644
index 00000000..17b9f0e4
--- /dev/null
+++ b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/FilterPriority.java
@@ -0,0 +1,39 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T 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.aai.aaf.filters;
+
+import org.springframework.core.Ordered;
+
+public enum FilterPriority {
+
+ AAF_AUTHENTICATION(Ordered.HIGHEST_PRECEDENCE),
+ AAF_AUTHORIZATION(Ordered.HIGHEST_PRECEDENCE + 1), //higher number = lower priority
+ AAF_CERT_AUTHENTICATION(Ordered.HIGHEST_PRECEDENCE + 2 ),
+ AAF_CERT_AUTHORIZATION(Ordered.HIGHEST_PRECEDENCE + 3),
+ TWO_WAY_SSL_AUTH(Ordered.HIGHEST_PRECEDENCE + 4);
+
+ private final int priority;
+
+ FilterPriority(final int p) {
+ priority = p;
+ }
+
+ public int getPriority() { return priority; }
+}
diff --git a/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/GremlinFilter.java b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/GremlinFilter.java
new file mode 100644
index 00000000..23ff0c6d
--- /dev/null
+++ b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/GremlinFilter.java
@@ -0,0 +1,99 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2019 AT&T 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.aai.aaf.filters;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.commons.io.IOUtils;
+import org.onap.aai.aaf.auth.ResponseFormatter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.regex.Pattern;
+
+@Component
+@Profile({
+ AafProfiles.AAF_CERT_AUTHENTICATION,
+ AafProfiles.AAF_AUTHENTICATION
+})
+public class GremlinFilter {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(GremlinFilter.class);
+
+ private static final String ADVANCED = "advanced";
+ private static final String BASIC = "basic";
+ private static final Pattern ECHO_ENDPOINT = Pattern.compile("^.*/util/echo$");
+
+ String type;
+
+ String instance;
+
+ private CadiProps cadiProps;
+
+ @Autowired
+ public GremlinFilter(
+ @Value("${permission.type}") String type,
+ @Value("${permission.instance}") String instance,
+ CadiProps cadiProps
+ ) {
+ this.type = type;
+ this.instance = instance;
+ this.cadiProps = cadiProps;
+ }
+
+ public void doBasicAuthFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
+ PayloadBufferingRequestWrapper requestBufferWrapper = new PayloadBufferingRequestWrapper(request);
+
+ if(ECHO_ENDPOINT.matcher(request.getRequestURI()).matches()){
+ filterChain.doFilter(requestBufferWrapper, response);
+ }
+
+ String payload = IOUtils.toString(requestBufferWrapper.getInputStream(), StandardCharsets.UTF_8.name());
+ boolean containsWordGremlin = payload.contains("\"gremlin\"");
+
+ //if the requestBufferWrapper contains the word "gremlin" it's an "advanced" query needing an "advanced" role
+ String permissionBasic = String.format("%s|%s|%s", type, instance, BASIC);
+ String permissionAdvanced = String.format("%s|%s|%s", type, instance, ADVANCED);
+
+ boolean isAuthorized;
+
+ if(containsWordGremlin){
+ isAuthorized = requestBufferWrapper.isUserInRole(permissionAdvanced);
+ }else{
+ isAuthorized = requestBufferWrapper.isUserInRole(permissionAdvanced) || requestBufferWrapper.isUserInRole(permissionBasic);
+ }
+
+ if(!isAuthorized){
+ String name = requestBufferWrapper.getUserPrincipal() != null ? requestBufferWrapper.getUserPrincipal().getName() : "unknown";
+ LOGGER.info("User " + name + " does not have a role for " + (containsWordGremlin ? "gremlin" : "non-gremlin") + " query" );
+ ResponseFormatter.errorResponse(request, response);
+ } else {
+ filterChain.doFilter(requestBufferWrapper,response);
+ }
+ }
+}
diff --git a/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/PayloadBufferingRequestWrapper.java b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/PayloadBufferingRequestWrapper.java
new file mode 100644
index 00000000..eb8a61dd
--- /dev/null
+++ b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/PayloadBufferingRequestWrapper.java
@@ -0,0 +1,50 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T 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.aai.aaf.filters;
+
+import org.apache.commons.io.IOUtils;
+import org.onap.aaf.cadi.BufferedServletInputStream;
+
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+/**
+ * This class buffers the payload of the servlet request. The reason is that we access the payload multiple times,
+ * which is not supported by the request per se.
+ */
+
+class PayloadBufferingRequestWrapper extends HttpServletRequestWrapper {
+
+ private byte[] buffer;
+
+ PayloadBufferingRequestWrapper(HttpServletRequest req) throws IOException {
+ super(req);
+ this.buffer = IOUtils.toByteArray(req.getInputStream());
+ }
+
+ @Override
+ public ServletInputStream getInputStream() {
+ ByteArrayInputStream bais = new ByteArrayInputStream(this.buffer);
+ return new BufferedServletInputStream(bais);
+ }
+}
diff --git a/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/TwoWaySslAuthorization.java b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/TwoWaySslAuthorization.java
new file mode 100644
index 00000000..0e206c50
--- /dev/null
+++ b/aai-aaf-auth/src/main/java/org/onap/aai/aaf/filters/TwoWaySslAuthorization.java
@@ -0,0 +1,186 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2019 AT&T 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.aai.aaf.filters;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.onap.aai.aaf.auth.AAIAuthCore;
+import org.onap.aai.aaf.auth.CertUtil;
+import org.onap.aai.aaf.auth.ResponseFormatter;
+import org.onap.aai.exceptions.AAIException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.web.filter.OrderedRequestContextFilter;
+import org.springframework.context.annotation.Profile;
+import org.springframework.core.env.Environment;
+import org.springframework.stereotype.Component;
+
+import javax.security.auth.x500.X500Principal;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+import java.util.*;
+
+@Component
+@Profile("two-way-ssl")
+public class TwoWaySslAuthorization extends OrderedRequestContextFilter {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TwoWaySslAuthorization.class);
+
+ public static final String HTTP_METHOD_OVERRIDE = "X-HTTP-Method-Override";
+
+ public static final String MERGE_PATCH = "MERGE_PATCH";
+
+ @Autowired
+ private Environment environment;
+
+ @Autowired
+ private AAIAuthCore aaiAuthCore;
+
+ @Autowired
+ private CadiProps cadiProps;
+
+ public TwoWaySslAuthorization(){
+ this.setOrder(FilterPriority.TWO_WAY_SSL_AUTH.getPriority());
+ }
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
+
+ String uri = request.getRequestURI();
+ String httpMethod = getHttpMethod(request);
+
+ Optional<String> authUser = getUser(request);
+
+ if (authUser.isPresent()) {
+ Properties cadiProperties = cadiProps.getCadiProperties();
+
+ String issuer = CertUtil.getCertIssuer(request);
+ if (issuer == null || issuer.isEmpty()) {
+ AAIException aaie = new AAIException("AAI_9107");
+ ResponseFormatter.errorResponse(aaie, request, response);
+ return;
+ }
+ issuer = issuer.replaceAll("\\s+","").toUpperCase();
+
+ List<String> cadiConfiguredIssuers = CertUtil.getCadiCertIssuers(cadiProperties);
+ boolean isAafAuthProfileActive = this.isAafAuthProfileActive();
+ if ((!isAafAuthProfileActive) || (!cadiConfiguredIssuers.contains(issuer)) ) {
+ try {
+ this.authorize(uri, httpMethod, authUser.get(), this.getHaProxyUser(request), issuer);
+ } catch (AAIException e) {
+ ResponseFormatter.errorResponse(e, request, response);
+ return;
+ }
+ }
+ } else {
+ AAIException aaie = new AAIException("AAI_9107");
+ ResponseFormatter.errorResponse(aaie, request, response);
+ return;
+ }
+ filterChain.doFilter(request, response);
+ }
+
+
+ private String getHttpMethod(HttpServletRequest request) {
+ String httpMethod = request.getMethod();
+ if ("POST".equalsIgnoreCase(httpMethod)
+ && "PATCH".equals(request.getHeader(HTTP_METHOD_OVERRIDE))) {
+ httpMethod = MERGE_PATCH;
+ }
+ if (httpMethod.equalsIgnoreCase(MERGE_PATCH) || "patch".equalsIgnoreCase(httpMethod)) {
+ httpMethod = "PUT";
+ }
+ return httpMethod;
+ }
+
+ private Optional<String> getUser(HttpServletRequest hsr) {
+ String authUser = null;
+ if (hsr.getAttribute("javax.servlet.request.cipher_suite") != null) {
+ X509Certificate[] certChain = (X509Certificate[]) hsr.getAttribute("javax.servlet.request.X509Certificate");
+
+ /*
+ * If the certificate is null or the certificate chain length is zero Then
+ * retrieve the authorization in the request header Authorization Check that it
+ * is not null and that it starts with Basic and then strip the basic portion to
+ * get the base64 credentials Check if this is contained in the AAIBasicAuth
+ * Singleton class If it is, retrieve the username associated with that
+ * credentials and set to authUser Otherwise, get the principal from certificate
+ * and use that authUser
+ */
+
+ if (certChain == null || certChain.length == 0) {
+
+ String authorization = hsr.getHeader("Authorization");
+
+ if (authorization != null && authorization.startsWith("Basic ")) {
+ authUser = authorization.replace("Basic ", "");
+ }
+
+ } else {
+ X509Certificate clientCert = certChain[0];
+ X500Principal subjectDN = clientCert.getSubjectX500Principal();
+ authUser = subjectDN.toString().toLowerCase();
+ }
+ }
+
+ return Optional.ofNullable(authUser);
+ }
+
+ private String getHaProxyUser(HttpServletRequest hsr) {
+ String haProxyUser;
+ if (Objects.isNull(hsr.getHeader("X-AAI-SSL-Client-CN"))
+ || Objects.isNull(hsr.getHeader("X-AAI-SSL-Client-OU"))
+ || Objects.isNull(hsr.getHeader("X-AAI-SSL-Client-O"))
+ || Objects.isNull(hsr.getHeader("X-AAI-SSL-Client-L"))
+ || Objects.isNull(hsr.getHeader("X-AAI-SSL-Client-ST"))
+ || Objects.isNull(hsr.getHeader("X-AAI-SSL-Client-C"))) {
+ haProxyUser = "";
+ } else {
+ haProxyUser = String.format("CN=%s, OU=%s, O=\"%s\", L=%s, ST=%s, C=%s",
+ Objects.toString(hsr.getHeader("X-AAI-SSL-Client-CN"), ""),
+ Objects.toString(hsr.getHeader("X-AAI-SSL-Client-OU"), ""),
+ Objects.toString(hsr.getHeader("X-AAI-SSL-Client-O"), ""),
+ Objects.toString(hsr.getHeader("X-AAI-SSL-Client-L"), ""),
+ Objects.toString(hsr.getHeader("X-AAI-SSL-Client-ST"), ""),
+ Objects.toString(hsr.getHeader("X-AAI-SSL-Client-C"), "")).toLowerCase();
+ }
+ return haProxyUser;
+ }
+
+ private void authorize(String uri, String httpMethod, String authUser, String haProxyUser, String issuer) throws AAIException {
+ if (!aaiAuthCore.authorize(authUser, uri, httpMethod, haProxyUser, issuer)) {
+ throw new AAIException("AAI_9101", "Request on " + httpMethod + " " + uri + " status is not OK");
+ }
+ }
+
+ private boolean isAafAuthProfileActive() {
+ String[] profiles = environment.getActiveProfiles();
+ if (profiles != null) {
+ if (Arrays.stream(profiles).anyMatch(
+ env -> (env.equalsIgnoreCase(AafProfiles.AAF_CERT_AUTHENTICATION)))) {
+ return true;
+ }
+ }
+ return false;
+ }
+}