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