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