/**
* Copyright (c) Codice Foundation
* <p>
* This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or any later version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. A copy of the GNU Lesser General Public License
* is distributed along with this program and can be found at
* <http://www.gnu.org/licenses/lgpl.html>.
*/
package org.codice.security.policy.context;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.shiro.authz.Permission;
import org.codice.ddf.security.policy.context.ContextPolicy;
import org.codice.ddf.security.policy.context.attributes.ContextAttributeMapping;
import org.codice.ddf.security.policy.context.attributes.DefaultContextAttributeMapping;
import org.codice.ddf.security.policy.context.impl.Policy;
import org.codice.ddf.security.policy.context.impl.PolicyManager;
import org.junit.Before;
import org.junit.Test;
import com.google.common.collect.ImmutableMap;
import ddf.security.permission.CollectionPermission;
/**
* Test for PolicyManager
*/
public class PolicyManagerTest {
private static final String REALMS = "realms";
private static final String DEFAULT_REALM_CONTEXT_VALUE = "karaf";
private static final String AUTH_TYPES = "authenticationTypes";
private static final String REQ_ATTRS = "requiredAttributes";
private static final List<String> DEFAULT_AUTH_TYPES = Arrays.asList("SAML", "GUEST");
private PolicyManager manager;
private PolicyManager rollBackTestManager;
private String[] rollBackRealmValues =
{"/=" + DEFAULT_REALM_CONTEXT_VALUE, "/A=a", "/A/B/C/testContext4=abcTestContext4",
"/testContext5=karaf", "/1/2/3/testContext6=" + DEFAULT_REALM_CONTEXT_VALUE,
"/A/B/C/testContext7=" + DEFAULT_REALM_CONTEXT_VALUE};
private String[] rollBackAuthTypesValues =
{"/=SAML|GUEST", "/A=a", "/A/B/C/testContext4=abcTestContext4"};
private String[] rollBackReqAttrValues =
{"/=", "/A={A=a}", "/testContext=", "/1/2/3/testContext2=", "/A/B/C/testContext3=",
"/A/B/C/testContext4=",
"/A/B/C/testContext8={AbcTestContext8=abcTestContext8}"};
private final Map<String, String> expectedRollBackRealms =
new ImmutableMap.Builder<String, String>().put("/testContext",
DEFAULT_REALM_CONTEXT_VALUE)
.put("/1/2/3/testContext2", DEFAULT_REALM_CONTEXT_VALUE)
.put("/A/B/C/testContext3", "a")
.put("/A/B/C/testContext4", "abcTestContext4")
.build();
private final Map<String, List<String>> expectedRollBackAuthTypes =
new ImmutableMap.Builder<String, List<String>>().put("/testContext", DEFAULT_AUTH_TYPES)
.put("/1/2/3/testContext2", Arrays.asList("SAML", "GUEST"))
.put("/A/B/C/testContext3", Collections.singletonList("a"))
.put("/A/B/C/testContext4", Collections.singletonList("abcTestContext4"))
.build();
private final Map<String, List<String>> expectedRollBackReqAttrs =
new ImmutableMap.Builder<String, List<String>>().put("/testContext5",
Arrays.asList(new String[] {}))
.put("/1/2/3/testContext6", Arrays.asList(new String[] {}))
.put("/A/B/C/testContext7", Collections.singletonList("A"))
.put("/A/B/C/testContext8", Collections.singletonList("AbcTestContext8"))
.build();
private final ContextAttributeMapping[] attributes = {new DefaultContextAttributeMapping(null,
"a",
"a"), new DefaultContextAttributeMapping(null, "b", "b"),
new DefaultContextAttributeMapping(null, "c", "c")};
private final Map<String, List<ContextAttributeMapping>> simpleAttributeMap =
new ImmutableMap.Builder<String, List<ContextAttributeMapping>>().put("/a",
Collections.singletonList(attributes[0]))
.put("/a/b", Collections.singletonList(attributes[1]))
.put("/a/b/c", Collections.singletonList(attributes[2]))
.build();
private final Map<String, List<ContextAttributeMapping>> complexAttributeMap =
new ImmutableMap.Builder<String, List<ContextAttributeMapping>>().put("/x",
Collections.singletonList(attributes[0]))
.put("/x/y/z", Collections.singletonList(attributes[1]))
.build();
@Before
public void setup() {
manager = new PolicyManager();
manager.setTraversalDepth(10);
manager.setContextPolicy("/", new Policy("/", null, new ArrayList<>(), null));
manager.setContextPolicy("/search", new Policy("/search", null, new ArrayList<>(), null));
manager.setContextPolicy("/admin", new Policy("/admin", null, new ArrayList<>(), null));
manager.setContextPolicy("/search/standard",
new Policy("/search/standard", null, new ArrayList<>(), null));
manager.setContextPolicy("/search/cometd",
new Policy("/search/cometd", null, new ArrayList<>(), null));
manager.setContextPolicy("/search/simple",
new Policy("/search/simple", null, new ArrayList<>(), null));
manager.setContextPolicy("/aaaaaa", new Policy("/aaaaaa", null, new ArrayList<>(), null));
manager.setContextPolicy("/aaa", new Policy("/aaa", null, new ArrayList<>(), null));
manager.setContextPolicy("/aaa/aaa", new Policy("/aaa/aaa", null, new ArrayList<>(), null));
manager.setContextPolicy("/foo/bar", new Policy("/foo/bar", null, new ArrayList<>(), null));
manager.setContextPolicy("/1/2", new Policy("/1/2", null, new ArrayList<>(), null));
manager.setContextPolicy("/1/2/3/4/5/6/7/8/9/10/11/12/13/14",
new Policy("/1/2/3/4/5/6/7/8/9/10/11/12/13/14", null, new ArrayList<>(), null));
for (Map.Entry<String, List<ContextAttributeMapping>> entry : simpleAttributeMap.entrySet()) {
manager.setContextPolicy(entry.getKey(),
new Policy(entry.getKey(), null, new ArrayList<>(), entry.getValue()));
}
for (Map.Entry<String, List<ContextAttributeMapping>> entry : complexAttributeMap.entrySet()) {
manager.setContextPolicy(entry.getKey(),
new Policy(entry.getKey(), null, new ArrayList<>(), entry.getValue()));
}
// Can't use Collections.singletonList because the context policy manager must be able to change the passed in list
manager.setWhiteListContexts(Arrays.asList("/foo"));
Map<String, Object> contextPolicies = new HashMap<>();
contextPolicies.put(REALMS, rollBackRealmValues);
contextPolicies.put(AUTH_TYPES, rollBackAuthTypesValues);
contextPolicies.put(REQ_ATTRS, rollBackReqAttrValues);
rollBackTestManager = new PolicyManager();
rollBackTestManager.setPolicies(contextPolicies);
}
@Test
public void testSimpleAttributeMappings() {
for (Map.Entry<String, List<ContextAttributeMapping>> entry : simpleAttributeMap.entrySet()) {
ContextPolicy policy = manager.getContextPolicy(entry.getKey());
CollectionPermission permission = policy.getAllowedAttributePermissions();
assertThat(permission.implies(entry.getValue()
.get(0)
.getAttributePermission()), is(true));
}
}
@Test
public void testComplexPaths() {
CollectionPermission rootPermissions = manager.getContextPolicy("/x")
.getAllowedAttributePermissions();
CollectionPermission noPermissions = manager.getContextPolicy("/x/y")
.getAllowedAttributePermissions();
CollectionPermission lastPermission = manager.getContextPolicy("/x/y/z")
.getAllowedAttributePermissions();
assertThat(noPermissions.implies(rootPermissions), is(true));
assertThat(rootPermissions.implies(lastPermission), is(false));
assertThat(lastPermission.implies(noPermissions), is(false));
}
@Test
public void testBadTraversal() {
//test that we can still resolve policies for paths larger than the limit
ContextPolicy contextPolicy = manager.getContextPolicy(
"/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15");
assertThat("/1/2/3/4/5/6/7/8/9/10/11/12/13/14", is(contextPolicy.getContextPath()));
//test that extra /s are removed from the end
ContextPolicy contextPolicy1 = manager.getContextPolicy(
"/1/2/3/4/5/6/7/8/9/10/11/12/13/14////////////////");
assertThat("/1/2/3/4/5/6/7/8/9/10/11/12/13/14", is(contextPolicy1.getContextPath()));
//test that all slashes resolves to just /
ContextPolicy contextPolicy2 = manager.getContextPolicy(
"///////////////////////////////////////////////////////////////////////////");
assertThat("/", is(contextPolicy2.getContextPath()));
//test that we can remove slashes within paths and still resolve a policy
ContextPolicy contextPolicy3 = manager.getContextPolicy(
"/1/2/3/////////////////////////////////////4/5//6/7////////////////");
assertThat("/1/2", is(contextPolicy3.getContextPath()));
//test same as above but with a path that is too long so it resolves to /
ContextPolicy contextPolicy4 = manager.getContextPolicy(
"/1/2/3////////4/5//////////6/7/8//////////9/10//////////11/12/13/14////////////////");
assertThat("/", is(contextPolicy4.getContextPath()));
//test two slashes
ContextPolicy contextPolicy5 = manager.getContextPolicy("//");
assertThat("/", is(contextPolicy5.getContextPath()));
//test one slash
ContextPolicy contextPolicy6 = manager.getContextPolicy("/");
assertThat("/", is(contextPolicy6.getContextPath()));
}
/**
* Tests context rollbacks to the specified realms
* / <- /testContext - single rollback to default
* / <- /1/2/3/testContext2 - several rollbacks to default
* /A <- /A/B/C/testContext3 - several rollback to parent
* /A/B/C/testContext4 <- /A/B/C/testContext4 - no rollback
*/
@Test
public void testContextRealmRollBack() {
for (String contextPath : expectedRollBackRealms.keySet()) {
assertThat(expectedRollBackRealms.get(contextPath),
is(rollBackTestManager.getContextPolicy(contextPath)
.getRealm()));
}
}
/**
* Tests context rollbacks to the specified authorization types
* / <- /testContext - single rollback to default
* / <- /1/2/3/testContext2 - several rollbacks to default
* /A <- /A/B/C/testContext3 - several rollback to parent
* /A/B/C/testContext4 <- /A/B/C/testContext4 - no rollback
*/
@Test
public void testAuthTypesRollBack() {
for (String contextPath : expectedRollBackAuthTypes.keySet()) {
for (String authType : rollBackTestManager.getContextPolicy(contextPath)
.getAuthenticationMethods()) {
assertThat(expectedRollBackAuthTypes.get(contextPath)
.contains(authType), is(true));
}
}
}
/**
* Tests context rollbacks to the specified required attributes
* / <- /testContext5 - single rollback to default
* / <- /1/2/3/testContext6 - several rollbacks to default
* /A <- /A/B/C/testContext7 - several rollback to parent
* /A/B/C/testContext8 <- /A/B/C/testContext8 - no rollback
*/
@Test
public void testReqAttrRollBack() {
for (String contextPath : expectedRollBackReqAttrs.keySet()) {
for (String authType : rollBackTestManager.getContextPolicy(contextPath)
.getAllowedAttributeNames()) {
assertThat(expectedRollBackReqAttrs.get(contextPath)
.contains(authType), is(true));
}
}
}
@Test
public void testInvalidEntry() {
assertThat(rollBackTestManager.getContextPolicy("invalidContextPathEntry")
.getRealm(), is(PolicyManager.DEFAULT_REALM_CONTEXT_VALUE));
assertThat(rollBackTestManager.getContextPolicy("invalidContextPathEntry")
.getAllowedAttributeNames(), is(Arrays.asList(new String[] {})));
assertThat(rollBackTestManager.getContextPolicy("invalidContextPathEntry")
.getAuthenticationMethods(), is(DEFAULT_AUTH_TYPES));
}
@Test
public void testFindContextPaths() {
ContextPolicy policy = manager.getContextPolicy("/search/standard/user");
assertThat("/search/standard", is(policy.getContextPath()));
policy = manager.getContextPolicy("/search/standard");
assertThat("/search/standard", is(policy.getContextPath()));
policy = manager.getContextPolicy("/search/endpoint");
assertThat("/search", is(policy.getContextPath()));
policy = manager.getContextPolicy("/random/other/endpoint");
assertThat("/", is(policy.getContextPath()));
policy = manager.getContextPolicy("/aaaaab");
assertThat("/", is(policy.getContextPath()));
policy = manager.getContextPolicy("/aaa/aab");
assertThat("/aaa", is(policy.getContextPath()));
policy = manager.getContextPolicy("/");
assertThat("/", is(policy.getContextPath()));
policy = manager.getContextPolicy("blah");
assertThat("/", is(policy.getContextPath()));
policy = manager.getContextPolicy("/foo/bar");
assertThat("/foo/bar", is(policy.getContextPath()));
policy = manager.getContextPolicy("/foo/bar/foobar");
assertThat("/foo/bar", is(policy.getContextPath()));
policy = manager.getContextPolicy("/foo");
assertThat(policy, is(nullValue()));
assertThat(manager.isWhiteListed("/foo"), is(true));
assertThat(manager.isWhiteListed("/foo/bar"), is(false));
}
@Test
public void testConfiguration() {
Map<String, Object> properties = new HashMap<>();
String[] authTypes =
new String[] {"/=SAML|BASIC", "/search=SAML|BASIC|GUEST", "/admin=SAML|BASIC",
"/foo=BASIC", "/blah=GUEST", "/bleh=GUEST", "/unprotected=",
"/unprotected2="};
String[] requiredAttributes =
new String[] {"/={}", "/blah=", "/search={role=user;control=foo;control=bar}",
"/admin={role=admin}"};
properties.put("authenticationTypes", authTypes);
properties.put("requiredAttributes", requiredAttributes);
manager.setPolicies(properties);
testAllPolicies();
}
@Test
public void testSetPropertiesIgnoresNullMap() {
String[] authTypes =
new String[] {"/=SAML|BASIC", "/search=SAML|BASIC|GUEST", "/admin=SAML|BASIC",
"/foo=BASIC", "/blah=GUEST", "/unprotected=", "/unprotected2=",
"/bleh=GUEST"};
String[] requiredAttributes =
new String[] {"/={}", "/search={role=user;control=foo;control=bar}",
"/admin={role=admin|supervisor}"};
manager.setAuthenticationTypes(Arrays.asList(authTypes));
manager.setRequiredAttributes(Arrays.asList(requiredAttributes));
manager.configure();
manager.setPolicies(null);
testAllPolicies();
}
@Test
public void testWhiteListWithProperties() {
System.setProperty("org.codice.security.policy.context.test.bar", "/baz");
manager.setWhiteListContexts(Arrays.asList("/foo",
"${org.codice.security.policy.context.test.bar}"));
assertThat(manager.getWhiteListContexts()
.contains("/baz"), is(true));
}
private void testAllPolicies() {
//check search policy
ContextPolicy policy = manager.getContextPolicy("/search");
assertThat("/search", is(policy.getContextPath()));
Iterator<String> authIter = policy.getAuthenticationMethods()
.iterator();
int i = 0;
while (authIter.hasNext()) {
if (i == 0) {
assertThat("SAML", is(authIter.next()));
} else if (i == 1) {
assertThat("BASIC", is(authIter.next()));
} else if (i == 2) {
assertThat("GUEST", is(authIter.next()));
}
i++;
}
List<Permission> permissionList = policy.getAllowedAttributePermissions()
.getPermissionList();
assertThat("role : user",
is(permissionList.get(0)
.toString()));
assertThat("control : foo",
is(permissionList.get(1)
.toString()));
assertThat("control : bar",
is(permissionList.get(2)
.toString()));
//check admin policy
policy = manager.getContextPolicy("/admin");
assertThat("/admin", is(policy.getContextPath()));
authIter = policy.getAuthenticationMethods()
.iterator();
i = 0;
while (authIter.hasNext()) {
if (i == 0) {
assertThat("SAML", is(authIter.next()));
} else if (i == 1) {
assertThat("BASIC", is(authIter.next()));
}
i++;
}
//check foo policy
policy = manager.getContextPolicy("/foo");
assertThat("/foo", is(policy.getContextPath()));
authIter = policy.getAuthenticationMethods()
.iterator();
i = 0;
while (authIter.hasNext()) {
if (i == 0) {
assertThat("BASIC", is(authIter.next()));
}
i++;
}
//make sure some random context points to /
policy = manager.getContextPolicy("/random");
assertThat("/", is(policy.getContextPath()));
authIter = policy.getAuthenticationMethods()
.iterator();
i = 0;
while (authIter.hasNext()) {
if (i == 0) {
assertThat("SAML", is(authIter.next()));
} else if (i == 1) {
assertThat("BASIC", is(authIter.next()));
}
i++;
}
//check unprotected contexts
policy = manager.getContextPolicy("/unprotected");
assertThat("/unprotected", is(policy.getContextPath()));
authIter = policy.getAuthenticationMethods()
.iterator();
assertThat(false, is(authIter.hasNext()));
policy = manager.getContextPolicy("/unprotected2");
assertThat("/unprotected2", is(policy.getContextPath()));
authIter = policy.getAuthenticationMethods()
.iterator();
assertThat(authIter.hasNext(), is(false));
}
}