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
9 package org.opendaylight.aaa.impl.shiro.realm;
11 import static junit.framework.TestCase.assertEquals;
12 import static junit.framework.TestCase.assertFalse;
13 import static junit.framework.TestCase.assertNotNull;
14 import static junit.framework.TestCase.assertTrue;
15 import static org.mockito.Matchers.any;
16 import static org.mockito.Mockito.mock;
17 import static org.mockito.Mockito.when;
19 import com.google.common.base.Optional;
20 import com.google.common.collect.Lists;
21 import com.google.common.util.concurrent.CheckedFuture;
22 import com.google.common.util.concurrent.Futures;
23 import java.util.List;
24 import javax.servlet.ServletRequest;
25 import javax.servlet.ServletResponse;
26 import javax.servlet.http.HttpServletRequest;
27 import org.apache.shiro.subject.Subject;
28 import org.junit.Test;
29 import org.opendaylight.aaa.shiro.realm.MDSALDynamicAuthorizationFilter;
30 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
31 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
32 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.HttpAuthorization;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.Policies;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.permission.Permissions;
36 import org.opendaylight.yangtools.yang.binding.DataObject;
39 * Tests the Dyanmic AuthZ Filter.
41 * @author Ryan Goulding (ryandgoulding@gmail.com)
43 public class MDSALDynamicAuthorizationFilterTest {
45 // test helper method to generate some cool mdsal data
46 private DataBroker getTestData() throws Exception {
47 return getTestData("/**", "admin", "Default Test AuthZ Rule", Permissions.Actions.Put);
50 // test helper method to generate some cool mdsal data
51 private DataBroker getTestData(final String resource, final String role,
52 final String description, final Permissions.Actions actions) throws Exception {
54 final List<Permissions.Actions> actionsList = Lists.newArrayList(actions);
55 final Permissions permissions = mock(Permissions.class);
56 when(permissions.getRole()).thenReturn(role);
57 when(permissions.getActions()).thenReturn(actionsList);
58 final List<Permissions> permissionsList = Lists.newArrayList(permissions);
59 final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.policies.Policies innerPolicies =
60 mock(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.policies.Policies.class);
61 when(innerPolicies.getResource()).thenReturn(resource);
62 when(innerPolicies.getDescription()).thenReturn(description);
63 when(innerPolicies.getPermissions()).thenReturn(permissionsList);
64 final List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.policies.Policies> policiesList =
65 Lists.newArrayList(innerPolicies);
66 final Policies policies = mock(Policies.class);
67 when(policies.getPolicies()).thenReturn(policiesList);
68 final HttpAuthorization httpAuthorization = mock(HttpAuthorization.class);
69 when(httpAuthorization.getPolicies()).thenReturn(policies);
70 final Optional<DataObject> dataObjectOptional = mock(Optional.class);
71 when(dataObjectOptional.get()).thenReturn(httpAuthorization);
72 when(dataObjectOptional.isPresent()).thenReturn(true);
73 final CheckedFuture<Optional<DataObject>, ReadFailedException> cf = mock(CheckedFuture.class);
74 when(cf.get()).thenReturn(dataObjectOptional);
75 final ReadOnlyTransaction rot = mock(ReadOnlyTransaction.class);
76 when(rot.read(any(), any())).thenReturn(cf);
77 final DataBroker dataBroker = mock(DataBroker.class);
78 when(dataBroker.newReadOnlyTransaction()).thenReturn(rot);
84 public void testIsAccessAllowed() throws Exception {
88 // Ensure that the base isAccessAllowed(...) method calls the static helper method.
89 final MDSALDynamicAuthorizationFilter filter = mock(MDSALDynamicAuthorizationFilter.class);
90 when(filter.isAccessAllowed(any(), any(), any(), any())).thenReturn(true);
91 when(filter.isAccessAllowed(any(), any(), any())).thenCallRealMethod();
92 assertTrue(filter.isAccessAllowed(null, null, null));
93 when(filter.isAccessAllowed(any(), any(), any(), any())).thenReturn(false);
94 assertFalse(filter.isAccessAllowed(null, null, null));
98 public void testGetHttpAuthzContainer() throws Exception {
102 // Ensure that data can be extracted appropriately.
103 final DataBroker dataBroker = getTestData();
104 final Optional<HttpAuthorization> httpAuthorizationOptional =
105 MDSALDynamicAuthorizationFilter.getHttpAuthzContainer(dataBroker);
107 assertNotNull(httpAuthorizationOptional);
108 final HttpAuthorization authz = httpAuthorizationOptional.get();
109 assertNotNull(authz);
110 assertEquals(1, authz.getPolicies().getPolicies().size());
114 public void testBasicAccessWithNoRules() throws Exception {
116 // Test Setup: No rules are added to the HttpAuthorization container. Open access should be allowed.
117 final Subject subject = mock(Subject.class);
118 final MDSALDynamicAuthorizationFilter filter = new MDSALDynamicAuthorizationFilter() {
120 protected Subject getSubject(final ServletRequest request, final ServletResponse servletResponse) {
125 final HttpServletRequest request = mock(HttpServletRequest.class);
126 when(request.getRequestURI()).thenReturn("abc");
127 when(request.getMethod()).thenReturn("Put");
128 final Optional<DataObject> dataObjectOptional = mock(Optional.class);
129 when(dataObjectOptional.isPresent()).thenReturn(false);
130 final CheckedFuture<Optional<DataObject>, ReadFailedException> cf = mock(CheckedFuture.class);
131 when(cf.get()).thenReturn(dataObjectOptional);
132 final ReadOnlyTransaction rot = mock(ReadOnlyTransaction.class);
133 when(rot.read(any(), any())).thenReturn(cf);
134 final DataBroker dataBroker = mock(DataBroker.class);
135 when(dataBroker.newReadOnlyTransaction()).thenReturn(rot);
138 // Ensure that access is allowed since no data is returned from the MDSAL read.
139 // This is through making sure the Optional is not present.
140 assertTrue(filter.isAccessAllowed(request, null, null, dataBroker));
143 // Same as above, but with an empty policy list returned.
144 final Policies policies = mock(Policies.class);
145 when(policies.getPolicies()).thenReturn(Lists.newArrayList());
146 final HttpAuthorization httpAuthorization = mock(HttpAuthorization.class);
147 when(httpAuthorization.getPolicies()).thenReturn(policies);
148 when(dataObjectOptional.isPresent()).thenReturn(true);
149 when(dataObjectOptional.get()).thenReturn(httpAuthorization);
150 assertTrue(filter.isAccessAllowed(request, null, null, dataBroker));
154 public void testMDSALExceptionDuringRead() throws Exception {
156 // Test Setup: No rules are added to the HttpAuthorization container. The MDSAL read
157 // is instructed to return an immediateFailedCheckedFuture, to emulate an error in reading
159 final Subject subject = mock(Subject.class);
160 final MDSALDynamicAuthorizationFilter filter = new MDSALDynamicAuthorizationFilter() {
162 protected Subject getSubject(final ServletRequest request, final ServletResponse servletResponse) {
167 final HttpServletRequest request = mock(HttpServletRequest.class);
168 when(request.getRequestURI()).thenReturn("abc");
169 when(request.getMethod()).thenReturn("Put");
171 final Optional<DataObject> dataObjectOptional = mock(Optional.class);
172 when(dataObjectOptional.isPresent()).thenReturn(false);
173 final CheckedFuture<Optional<DataObject>, ReadFailedException> cf =
174 Futures.immediateFailedCheckedFuture(new ReadFailedException("Test Fail"));
175 final ReadOnlyTransaction rot = mock(ReadOnlyTransaction.class);
176 when(rot.read(any(), any())).thenReturn(cf);
177 final DataBroker dataBroker = mock(DataBroker.class);
178 when(dataBroker.newReadOnlyTransaction()).thenReturn(rot);
181 // Ensure that if an error occurs while reading MD-SAL that access is denied.
182 assertFalse(filter.isAccessAllowed(request, null, null, dataBroker));
186 public void testBasicAccessWithOneRule() throws Exception {
191 // A Rule is added to match /** allowing HTTP PUT for the admin role.
192 // All other Methods are considered unauthorized.
193 final Subject subject = mock(Subject.class);
194 final DataBroker dataBroker = getTestData();
195 final MDSALDynamicAuthorizationFilter filter = new MDSALDynamicAuthorizationFilter() {
197 protected Subject getSubject(final ServletRequest request, final ServletResponse servletResponse) {
202 final HttpServletRequest request = mock(HttpServletRequest.class);
203 when(request.getRequestURI()).thenReturn("abc");
204 when(request.getMethod()).thenReturn("Put");
205 when(subject.hasRole("admin")).thenReturn(true);
210 // Make a PUT HTTP request from a Subject with the admin role. The request URL does not match,
211 // since "abc" does not start with a "/" character. Since no rule exists for this particular request,
212 // then access should be allowed.
213 assertTrue(filter.isAccessAllowed(request, null, null, dataBroker));
218 // Repeat of the above against a matching endpoint. Access should be allowed.
219 when(request.getRequestURI()).thenReturn("/anotherexamplethatshouldwork");
220 assertTrue(filter.isAccessAllowed(request, null, null, dataBroker));
225 // Repeat of the above request against a more complex endpoint. Access should be allowed.
226 when(request.getRequestURI()).thenReturn("/auth/v1/users");
227 assertTrue(filter.isAccessAllowed(request, null, null, dataBroker));
232 // Negative test case-- ensure that when an unallowed method (POST) is tried with an otherwise
233 // allowable request, that access is denied.
234 when(request.getMethod()).thenReturn("Post");
235 assertFalse(filter.isAccessAllowed(request, null, null, dataBroker));
240 // Negative test case-- ensure that when an unallowed role is tried with an otherwise allowable
241 // request, that acess is denied.
242 when(request.getMethod()).thenReturn("Put");
243 when(subject.hasRole("admin")).thenReturn(false);
244 assertFalse(filter.isAccessAllowed(request, null, null, dataBroker));
248 public void testSeveralMatchingRules() throws Exception {
252 // Create some mock data which has a couple of rules which may/may not match. This
253 // test ensures the correct application of said rules.
254 final List<Permissions.Actions> actionsList = Lists.newArrayList(Permissions.Actions.Get,
255 Permissions.Actions.Delete, Permissions.Actions.Patch, Permissions.Actions.Put,
256 Permissions.Actions.Post);
257 final String role = "admin";
258 final String resource = "/**";
259 final String resource2 = "/specialendpoint/**";
260 final String description = "All encompassing rule";
261 final Permissions permissions = mock(Permissions.class);
262 when(permissions.getRole()).thenReturn(role);
263 when(permissions.getActions()).thenReturn(actionsList);
264 final List<Permissions> permissionsList = Lists.newArrayList(permissions);
265 final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.policies.Policies innerPolicies =
266 mock(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.policies.Policies.class);
267 when(innerPolicies.getResource()).thenReturn(resource);
268 when(innerPolicies.getDescription()).thenReturn(description);
269 when(innerPolicies.getPermissions()).thenReturn(permissionsList);
270 final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.policies.Policies innerPolicies2 =
271 mock(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.policies.Policies.class);
272 when(innerPolicies2.getResource()).thenReturn(resource2);
273 final Permissions permissions2 = mock(Permissions.class);
274 when(permissions2.getRole()).thenReturn("dog");
275 when(permissions2.getActions()).thenReturn(actionsList);
276 when(innerPolicies2.getPermissions()).thenReturn(Lists.newArrayList(permissions2));
277 when(innerPolicies2.getDescription()).thenReturn("Specialized Rule");
278 List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.policies.Policies> policiesList =
279 Lists.newArrayList(innerPolicies, innerPolicies2);
280 final Policies policies = mock(Policies.class);
281 when(policies.getPolicies()).thenReturn(policiesList);
282 final HttpAuthorization httpAuthorization = mock(HttpAuthorization.class);
283 when(httpAuthorization.getPolicies()).thenReturn(policies);
284 final Optional<DataObject> dataObjectOptional = mock(Optional.class);
285 when(dataObjectOptional.get()).thenReturn(httpAuthorization);
286 when(dataObjectOptional.isPresent()).thenReturn(true);
287 final CheckedFuture<Optional<DataObject>, ReadFailedException> cf = mock(CheckedFuture.class);
288 when(cf.get()).thenReturn(dataObjectOptional);
289 final ReadOnlyTransaction rot = mock(ReadOnlyTransaction.class);
290 when(rot.read(any(), any())).thenReturn(cf);
291 final DataBroker dataBroker = mock(DataBroker.class);
292 when(dataBroker.newReadOnlyTransaction()).thenReturn(rot);
294 final Subject subject = mock(Subject.class);
295 final MDSALDynamicAuthorizationFilter filter = new MDSALDynamicAuthorizationFilter() {
297 protected Subject getSubject(final ServletRequest request, final ServletResponse servletResponse) {
302 final HttpServletRequest request = mock(HttpServletRequest.class);
303 when(request.getRequestURI()).thenReturn("/abc");
304 when(request.getMethod()).thenReturn("Put");
305 when(subject.hasRole("admin")).thenReturn(true);
310 // In the setup, two rules were added. First, make sure that the first rule is working.
311 assertTrue(filter.isAccessAllowed(request, null, null, dataBroker));
316 // Both rules would technically match the input request URI. We want to make sure that
317 // order is respected. We do this by ensuring access is granted (i.e., the first rule is matched).
318 when(request.getRequestURI()).thenReturn("/specialendpoint");
319 assertTrue(filter.isAccessAllowed(request, null, null, dataBroker));
320 when(request.getRequestURI()).thenReturn("/specialendpoint/");
321 assertTrue(filter.isAccessAllowed(request, null, null, dataBroker));
322 when(request.getRequestURI()).thenReturn("/specialendpoint/somewhatextended");
323 assertTrue(filter.isAccessAllowed(request, null, null, dataBroker));
328 // Now reverse the ordering of the rules, and ensure that access is denied (except for
329 // the first non-applicable rule, which should still be allowed). This is
330 // because the Subject making the request is not granted the "dog" role.
331 policiesList = Lists.newArrayList(innerPolicies2, innerPolicies);
332 when(policies.getPolicies()).thenReturn(policiesList);
333 when(request.getRequestURI()).thenReturn("/abc");
334 assertTrue(filter.isAccessAllowed(request, null, null, dataBroker));
335 when(request.getRequestURI()).thenReturn("/specialendpoint");
336 assertFalse(filter.isAccessAllowed(request, null, null, dataBroker));
337 when(request.getRequestURI()).thenReturn("/specialendpoint/");
338 assertFalse(filter.isAccessAllowed(request, null, null, dataBroker));
339 when(request.getRequestURI()).thenReturn("/specialendpoint/somewhatextended");
340 assertFalse(filter.isAccessAllowed(request, null, null, dataBroker));
344 public void testMultiplePolicies() throws Exception {
345 // admin can do anything
346 final String role = "admin";
347 final String resource = "/**";
348 final String description = "Test description";
349 final List<Permissions.Actions> actionsList = Lists.newArrayList(
350 Permissions.Actions.Get, Permissions.Actions.Put, Permissions.Actions.Delete,
351 Permissions.Actions.Patch, Permissions.Actions.Post
353 final Permissions permissions = mock(Permissions.class);
354 when(permissions.getRole()).thenReturn(role);
355 when(permissions.getActions()).thenReturn(actionsList);
356 final Permissions permissions2 = mock(Permissions.class);
357 when(permissions2.getRole()).thenReturn("user");
358 when(permissions2.getActions()).thenReturn(Lists.newArrayList(Permissions.Actions.Get));
359 final List<Permissions> permissionsList = Lists.newArrayList(permissions, permissions2);
360 final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.policies.Policies innerPolicies =
361 mock(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.policies.Policies.class);
362 when(innerPolicies.getResource()).thenReturn(resource);
363 when(innerPolicies.getDescription()).thenReturn(description);
364 when(innerPolicies.getPermissions()).thenReturn(permissionsList);
365 final List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.policies.Policies> policiesList =
366 Lists.newArrayList(innerPolicies);
367 final Policies policies = mock(Policies.class);
368 when(policies.getPolicies()).thenReturn(policiesList);
369 final HttpAuthorization httpAuthorization = mock(HttpAuthorization.class);
370 when(httpAuthorization.getPolicies()).thenReturn(policies);
371 final Optional<DataObject> dataObjectOptional = mock(Optional.class);
372 when(dataObjectOptional.get()).thenReturn(httpAuthorization);
373 when(dataObjectOptional.isPresent()).thenReturn(true);
374 final CheckedFuture<Optional<DataObject>, ReadFailedException> cf = mock(CheckedFuture.class);
375 when(cf.get()).thenReturn(dataObjectOptional);
376 final ReadOnlyTransaction rot = mock(ReadOnlyTransaction.class);
377 when(rot.read(any(), any())).thenReturn(cf);
378 final DataBroker dataBroker = mock(DataBroker.class);
379 when(dataBroker.newReadOnlyTransaction()).thenReturn(rot);
381 final Subject subject = mock(Subject.class);
382 final MDSALDynamicAuthorizationFilter filter = new MDSALDynamicAuthorizationFilter() {
384 protected Subject getSubject(final ServletRequest request, final ServletResponse servletResponse) {
389 final HttpServletRequest request = mock(HttpServletRequest.class);
390 when(request.getRequestURI()).thenReturn("/abc");
391 when(request.getMethod()).thenReturn("Put");
392 when(subject.hasRole("admin")).thenReturn(false);
393 when(subject.hasRole("user")).thenReturn(true);
395 assertFalse(filter.isAccessAllowed(request, null, null, dataBroker));
396 when(request.getMethod()).thenReturn("Get");
397 assertTrue(filter.isAccessAllowed(request, null, null, dataBroker));