b2523171dd6ac5c48bb7833ad52914044b9b9434
[ccsdk/features.git] /
1 package org.onap.ccsdk.features.sdnr.wt.oauthprovider.filters;
2
3 import static com.google.common.base.Preconditions.checkArgument;
4 import static java.util.Objects.requireNonNull;
5
6 import com.google.common.collect.Iterables;
7 import com.google.common.util.concurrent.Futures;
8 import com.google.common.util.concurrent.ListenableFuture;
9
10 import java.io.IOException;
11 import java.util.*;
12 import java.util.concurrent.ExecutionException;
13 import javax.servlet.Filter;
14 import javax.servlet.ServletRequest;
15 import javax.servlet.ServletResponse;
16 import javax.servlet.http.HttpServletRequest;
17 import javax.servlet.http.HttpServletResponse;
18
19 import org.apache.shiro.subject.Subject;
20 import org.apache.shiro.web.filter.authz.AuthorizationFilter;
21 import org.opendaylight.aaa.shiro.web.env.ThreadLocals;
22 import org.opendaylight.mdsal.binding.api.ClusteredDataTreeChangeListener;
23 import org.opendaylight.mdsal.binding.api.DataBroker;
24 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
25 import org.opendaylight.mdsal.binding.api.DataTreeModification;
26 import org.opendaylight.mdsal.binding.api.ReadTransaction;
27 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.HttpAuthorization;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.policies.Policies;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.permission.Permissions;
31 import org.opendaylight.yangtools.concepts.ListenerRegistration;
32 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 @SuppressWarnings("checkstyle:AbbreviationAsWordInName")
37 public class CustomizedMDSALDynamicAuthorizationFilter extends AuthorizationFilter
38         implements ClusteredDataTreeChangeListener<HttpAuthorization> {
39
40     private static final Logger LOG = LoggerFactory.getLogger(CustomizedMDSALDynamicAuthorizationFilter.class);
41
42     private static final DataTreeIdentifier<HttpAuthorization> AUTHZ_CONTAINER = DataTreeIdentifier.create(
43             LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(HttpAuthorization.class));
44
45     private final DataBroker dataBroker;
46
47     private ListenerRegistration<?> reg;
48     private volatile ListenableFuture<Optional<HttpAuthorization>> authContainer;
49
50     public CustomizedMDSALDynamicAuthorizationFilter() {
51         dataBroker = requireNonNull(ThreadLocals.DATABROKER_TL.get());
52     }
53
54     @Override
55     public Filter processPathConfig(final String path, final String config) {
56         try (ReadTransaction tx = dataBroker.newReadOnlyTransaction()) {
57             authContainer = tx.read(AUTHZ_CONTAINER.getDatastoreType(), AUTHZ_CONTAINER.getRootIdentifier());
58         }
59         this.reg = dataBroker.registerDataTreeChangeListener(AUTHZ_CONTAINER, this);
60         return super.processPathConfig(path, config);
61     }
62
63     @Override
64     public void destroy() {
65         if (reg != null) {
66             reg.close();
67             reg = null;
68         }
69         super.destroy();
70     }
71
72     @Override
73     public void onDataTreeChanged(final Collection<DataTreeModification<HttpAuthorization>> changes) {
74         final HttpAuthorization newVal = Iterables.getLast(changes).getRootNode().getDataAfter();
75         LOG.debug("Updating authorization information to {}", newVal);
76         authContainer = Futures.immediateFuture(Optional.ofNullable(newVal));
77     }
78
79     @Override
80     public boolean isAccessAllowed(final ServletRequest request, final ServletResponse response,
81                                    final Object mappedValue) {
82         checkArgument(request instanceof HttpServletRequest, "Expected HttpServletRequest, received {}", request);
83
84
85         final boolean defaultReturnValue=false;
86         final Subject subject = getSubject(request, response);
87         final HttpServletRequest httpServletRequest = (HttpServletRequest)request;
88         final String requestURI = httpServletRequest.getRequestURI();
89         LOG.debug("isAccessAllowed for user={} to requestURI={}", subject, requestURI);
90
91         final Optional<HttpAuthorization> authorizationOptional;
92         try {
93             authorizationOptional = authContainer.get();
94         } catch (ExecutionException | InterruptedException e) {
95             // Something went completely wrong trying to read the authz container.  Deny access.
96             LOG.warn("MDSAL attempt to read Http Authz Container failed, disallowing access", e);
97             return false;
98         }
99
100         if (!authorizationOptional.isPresent()) {
101             // The authorization container does not exist-- hence no authz rules are present
102             // Allow access.
103             LOG.debug("Authorization Container does not exist");
104             return defaultReturnValue;
105         }
106
107         final HttpAuthorization httpAuthorization = authorizationOptional.get();
108         final var policies = httpAuthorization.getPolicies();
109         List<Policies> policiesList = policies != null ? policies.getPolicies() : null;
110         if (policiesList == null || policiesList.isEmpty()) {
111             // The authorization container exists, but no rules are present.  Allow access.
112             LOG.debug("Exiting early since no authorization rules exist");
113             sendError(response, 403, "");
114             return defaultReturnValue;
115         }
116
117         // Sort the Policies list based on index
118         policiesList = new ArrayList<>(policiesList);
119         policiesList.sort(Comparator.comparing(Policies::getIndex));
120
121         for (Policies policy : policiesList) {
122             final String resource = policy.getResource();
123             final boolean pathsMatch = pathsMatch(resource, requestURI);
124             if (pathsMatch) {
125                 LOG.debug("paths match for policy {} pattern={} and requestURI={}", policy.getIndex(), resource, requestURI);
126                 final String method = httpServletRequest.getMethod();
127                 LOG.trace("method={}", method);
128                 List<Permissions> permissions = policy.getPermissions();
129                 LOG.trace("perm={}", permissions);
130                 if(permissions !=null) {
131                     for (Permissions permission : permissions) {
132                         final String role = permission.getRole();
133                         LOG.trace("role={}", role);
134                         Set<Permissions.Actions> actions = permission.getActions();
135                         if (actions != null) {
136                             for (Permissions.Actions action : actions) {
137                                 LOG.trace("action={}", action.getName());
138                                 if (action.getName().equalsIgnoreCase(method)) {
139                                     final boolean hasRole = subject.hasRole(role);
140                                     LOG.trace("hasRole({})={}", role, hasRole);
141                                     if (hasRole) {
142                                         return true;
143                                     }
144                                 }
145                             }
146                         }
147                         else{
148                             LOG.trace("no actions found");
149                         }
150                     }
151                 }
152                 else {
153                     LOG.trace("no permissions found");
154                 }
155                 LOG.debug("couldn't authorize the user for access");
156                 sendError(response, 403, "");
157                 return false;
158             }
159         }
160         LOG.debug("no path found that matches {}", requestURI);
161         sendError(response, 403, "");
162         return defaultReturnValue;
163     }
164
165     private void sendError(ServletResponse response, int code, String message)  {
166         if(response instanceof HttpServletResponse){
167             try {
168                 ((HttpServletResponse)response).sendError(code, message);
169             } catch (IOException e) {
170                 LOG.warn("unable to send {} {} response: ", code, message, e);
171             }
172         }
173         else{
174             LOG.warn("unable to send {} {} response", code, message);
175         }
176     }
177 }