2 * Copyright (c) 2017 Brocade Communications Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.aaa.shiro.realm;
10 import com.google.common.base.Optional;
11 import com.google.common.util.concurrent.CheckedFuture;
12 import java.util.List;
13 import java.util.Objects;
14 import java.util.concurrent.ExecutionException;
15 import javax.servlet.ServletRequest;
16 import javax.servlet.ServletResponse;
17 import javax.servlet.http.HttpServletRequest;
18 import org.apache.shiro.subject.Subject;
19 import org.apache.shiro.web.filter.authz.AuthorizationFilter;
20 import org.opendaylight.aaa.shiro.web.env.ThreadLocals;
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
23 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
24 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.HttpAuthorization;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.Policies;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.permission.Permissions;
28 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
33 * Provides a dynamic authorization mechanism for restful web services with permission grain
34 * scope. <code>aaa.yang</code> defines the model for this filtering mechanism.
35 * This model exposes the ability to manipulate policy information for specific paths
36 * based on a tuple of (role, http_permission_list).
38 * <p>This mechanism will only work when put behind <code>authcBasic</code>.
40 @SuppressWarnings("checkstyle:AbbreviationAsWordInName")
41 public class MDSALDynamicAuthorizationFilter extends AuthorizationFilter {
43 private static final Logger LOG = LoggerFactory.getLogger(MDSALDynamicAuthorizationFilter.class);
45 private static final InstanceIdentifier<HttpAuthorization> AUTHZ_CONTAINER_IID =
46 InstanceIdentifier.builder(HttpAuthorization.class).build();
48 public static Optional<HttpAuthorization> getHttpAuthzContainer(final DataBroker dataBroker)
49 throws ExecutionException, InterruptedException, ReadFailedException {
51 try (ReadOnlyTransaction ro = dataBroker.newReadOnlyTransaction()) {
52 final CheckedFuture<Optional<HttpAuthorization>, ReadFailedException> result =
53 ro.read(LogicalDatastoreType.CONFIGURATION, AUTHZ_CONTAINER_IID);
58 private final DataBroker dataBroker;
60 public MDSALDynamicAuthorizationFilter() {
61 this.dataBroker = Objects.requireNonNull(ThreadLocals.DATABROKER_TL.get());
65 public boolean isAccessAllowed(final ServletRequest request, final ServletResponse response,
66 final Object mappedValue) {
67 final Subject subject = getSubject(request, response);
68 final HttpServletRequest httpServletRequest = (HttpServletRequest)request;
69 final String requestURI = httpServletRequest.getRequestURI();
70 LOG.debug("isAccessAllowed for user={} to requestURI={}", subject, requestURI);
72 final Optional<HttpAuthorization> authorizationOptional;
74 authorizationOptional = getHttpAuthzContainer(dataBroker);
75 } catch(ExecutionException | InterruptedException e) {
76 // Something went completely wrong trying to read the authz container. Deny access.
77 LOG.debug("Error accessing the Http Authz Container", e);
79 } catch(final ReadFailedException e) {
80 // The MDSAL read attempt failed. fail-closed to prevent unauthorized access
81 LOG.warn("MDSAL attempt to read Http Authz Container failed, disallowing access", e);
85 if (!authorizationOptional.isPresent()) {
86 // The authorization container does not exist-- hence no authz rules are present
88 LOG.debug("Authorization Container does not exist");
93 final HttpAuthorization httpAuthorization = authorizationOptional.get();
94 final Policies policies = httpAuthorization.getPolicies();
95 final List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.policies.Policies> policiesList =
96 policies.getPolicies();
97 if(policiesList.isEmpty()) {
98 // The authorization container exists, but no rules are present. Allow access.
99 LOG.debug("Exiting successfully early since no authorization rules exist");
103 for (org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.policies.Policies policy :
105 final String resource = policy.getResource();
106 final boolean pathsMatch = pathsMatch(resource, requestURI);
108 LOG.debug("paths match for pattern={} and requestURI={}", resource, requestURI);
109 final String method = httpServletRequest.getMethod();
110 LOG.trace("method={}", method);
111 final List<Permissions> permissions = policy.getPermissions();
112 for (Permissions permission : permissions) {
113 final String role = permission.getRole();
114 LOG.trace("role={}", role);
115 final List<Permissions.Actions> actions = permission.getActions();
116 for(Permissions.Actions action : actions) {
117 LOG.trace("action={}", action.getName());
118 if(action.getName().equalsIgnoreCase(method)) {
119 final boolean hasRole = subject.hasRole(role);
120 LOG.trace("hasRole({})={}", role, hasRole);
127 LOG.debug("couldn't authorize the user for access");
131 LOG.debug("successfully authorized the user for access");