Fix NexusIQ security vulnerabilities
[aaf/cadi.git] / sidecar / rproxy / src / main / java / org / onap / aaf / rproxy / ReverseProxyAuthorizationFilter.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aaf
4  * ================================================================================
5  * Copyright © 2018 European Software Marketing Ltd.
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
10  *
11  *       http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  * ============LICENSE_END=========================================================
19  */
20 package org.onap.aaf.rproxy;
21
22 import com.google.gson.Gson;
23 import com.google.gson.reflect.TypeToken;
24 import com.google.gson.stream.JsonReader;
25 import java.io.File;
26 import java.io.FileInputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.InputStreamReader;
30 import java.net.URI;
31 import java.net.URISyntaxException;
32 import java.security.Principal;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.List;
36 import javax.annotation.Resource;
37 import javax.servlet.Filter;
38 import javax.servlet.FilterChain;
39 import javax.servlet.FilterConfig;
40 import javax.servlet.ServletException;
41 import javax.servlet.ServletRequest;
42 import javax.servlet.ServletResponse;
43 import javax.servlet.http.HttpServletRequest;
44 import javax.servlet.http.HttpServletResponse;
45 import org.eclipse.jetty.http.HttpStatus;
46 import org.onap.aaf.cadi.CadiWrap;
47 import org.onap.aaf.cadi.Permission;
48 import org.onap.aaf.rproxy.config.ReverseProxyURIAuthorizationProperties;
49 import org.onap.aaf.rproxy.utils.ReverseProxyAuthorization;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52 import org.springframework.boot.context.properties.EnableConfigurationProperties;
53 import org.springframework.core.annotation.Order;
54 import org.springframework.stereotype.Component;
55
56 @Component
57 @Order(1)
58 @EnableConfigurationProperties(ReverseProxyURIAuthorizationProperties.class)
59 public class ReverseProxyAuthorizationFilter implements Filter {
60
61     private static final Logger LOGGER = LoggerFactory.getLogger(ReverseProxyAuthorizationFilter.class);
62
63     private List<ReverseProxyAuthorization> reverseProxyAuthorizations = new ArrayList<>();
64
65     @Resource
66     private ReverseProxyURIAuthorizationProperties reverseProxyURIAuthorizationProperties;
67
68     @Override
69     public void init(FilterConfig filterConfig) throws ServletException {
70
71         // Read in the URI Authorisation configuration file
72         String authFilePath = reverseProxyURIAuthorizationProperties.getConfigurationFile();
73         if (authFilePath != null) {
74             try (InputStream inputStream =
75                     new FileInputStream(new File(reverseProxyURIAuthorizationProperties.getConfigurationFile()));
76                     JsonReader jsonReader = new JsonReader(new InputStreamReader(inputStream))) {
77                 List<ReverseProxyAuthorization> untrimmedList = new Gson().fromJson(jsonReader,
78                         new TypeToken<ArrayList<ReverseProxyAuthorization>>() {}.getType());
79                 untrimmedList.removeAll(Collections.singleton(null));
80                 reverseProxyAuthorizations = untrimmedList;
81             } catch (IOException e) {
82                 throw new ServletException("Authorizations config file not found.", e);
83             }
84         }
85     }
86
87     @Override
88     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
89             throws IOException, ServletException {
90
91         CadiWrap cadiWrap = (CadiWrap) servletRequest;
92         Principal principal = cadiWrap.getUserPrincipal();
93         List<Permission> grantedPermissions = new ArrayList<>();
94         cadiWrap.getLur().fishAll(principal, grantedPermissions);
95
96         if (LOGGER.isDebugEnabled()) {
97             logNeededPermissions();
98         }
99
100         String requestPath;
101         try {
102             requestPath = new URI(((HttpServletRequest) servletRequest).getRequestURI()).getPath();
103         } catch (URISyntaxException e) {
104             throw new ServletException("Request URI not valid", e);
105         }
106
107         if (authorizeRequest(grantedPermissions, requestPath)) {
108             LOGGER.info("Authorized");
109             filterChain.doFilter(servletRequest, servletResponse);
110         } else {
111             LOGGER.info("Unauthorized");
112             ((HttpServletResponse) servletResponse).setStatus(HttpStatus.FORBIDDEN_403);
113             ((HttpServletResponse) servletResponse).setContentType("application/json");
114             ((HttpServletResponse) servletResponse).sendError(HttpStatus.FORBIDDEN_403,
115                     "Sorry, the request is not allowed");
116         }
117     }
118
119     /**
120      * Check if the granted permissions for the request path matches the configured needed permissions.
121      * 
122      * @param grantedPermissions The granted permissions for the request path
123      * @param requestPath The request path
124      * @return true if permissions match
125      */
126     private boolean authorizeRequest(List<Permission> grantedPermissions, String requestPath) {
127         boolean authorized = false;
128         for (ReverseProxyAuthorization reverseProxyAuthorization : reverseProxyAuthorizations) {
129             if (requestPath.matches(reverseProxyAuthorization.getUri())) {
130                 LOGGER.debug("The URI:{}  matches:{}", requestPath, reverseProxyAuthorization.getUri());
131                 if (checkPermissionsMatch(grantedPermissions, reverseProxyAuthorization)) {
132                     authorized = true;
133                     break;
134                 }
135             } else {
136                 LOGGER.debug("The URI:{} doesn't match any in the configuration:{}", requestPath,
137                         reverseProxyAuthorization.getUri());
138             }
139         }
140         return authorized;
141     }
142
143     /**
144      * Check all needed permissions match the granted permissions.
145      * 
146      * @param grantedPermissions the granted permissions
147      * @param reverseProxyAuthorization the bean that contains the needed permissions
148      * @return true if all needed permissions match
149      */
150     private boolean checkPermissionsMatch(List<Permission> grantedPermissions,
151             ReverseProxyAuthorization reverseProxyAuthorization) {
152
153         boolean matchedAllPermissions = true;
154         for (String neededPermission : reverseProxyAuthorization.getPermissions()) {
155
156             // Check needed permission is granted
157             boolean matchedNeededPermission = false;
158             for (Permission grantedPermission : grantedPermissions) {
159                 if (checkGrantedPermission(neededPermission, grantedPermission.getKey())) {
160                     LOGGER.debug("Permission match found - needed permission:{}, granted permission:{}",
161                             neededPermission, grantedPermission.getKey());
162                     matchedNeededPermission = true;
163                     break;
164                 }
165             }
166             if (!matchedNeededPermission) {
167                 matchedAllPermissions = false;
168                 break;
169             }
170         }
171         return matchedAllPermissions;
172     }
173
174     /**
175      * Check whether an AAF style permission matches a needed permission. Wildcards (*) are supported.
176      * 
177      * @param neededPermission, the needed permission
178      * @param grantedPermission, the granted permission
179      * 
180      * @return true if the needed permission matches a granted permission
181      */
182     private boolean checkGrantedPermission(String neededPermission, String grantedPermission) {
183         boolean permissionMatch = false;
184         if (grantedPermission.matches(neededPermission)) {
185             permissionMatch = true;
186         } else if (grantedPermission.contains("*")) {
187             String[] splitNeededPermission = neededPermission.split("\\\\\\|");
188             String[] splitGrantedPermission = grantedPermission.split("\\|");
189             if ((splitGrantedPermission[0].matches(splitNeededPermission[0]))
190                     && (splitGrantedPermission[1].equals("*")
191                             || splitGrantedPermission[1].matches(splitNeededPermission[1]))
192                     && (splitGrantedPermission[2].equals("*")
193                             || splitGrantedPermission[2].matches(splitNeededPermission[2]))) {
194                 permissionMatch = true;
195             }
196         }
197         return permissionMatch;
198     }
199
200     /**
201      * Log the needed permissions for each URL configured.
202      */
203     private void logNeededPermissions() {
204         for (ReverseProxyAuthorization reverseProxyAuthorization : reverseProxyAuthorizations) {
205             LOGGER.debug("URI For authorization: {}", reverseProxyAuthorization.getUri());
206             for (String permission : reverseProxyAuthorization.getPermissions()) {
207                 LOGGER.debug("\t Needed permission:{}", permission);
208             }
209         }
210     }
211
212     @Override
213     public void destroy() {
214         // No op
215     }
216 }