/**
* 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 ddf.catalog.security.filter.plugin.test;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.support.DelegatingSubject;
import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.opengis.filter.FilterVisitor;
import org.opengis.filter.sort.SortBy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import junit.framework.Assert;
import ddf.catalog.data.AttributeDescriptor;
import ddf.catalog.data.Metacard;
import ddf.catalog.data.MetacardType;
import ddf.catalog.data.impl.MetacardImpl;
import ddf.catalog.data.impl.MetacardTypeImpl;
import ddf.catalog.data.impl.ResultImpl;
import ddf.catalog.operation.Query;
import ddf.catalog.operation.QueryResponse;
import ddf.catalog.operation.impl.QueryRequestImpl;
import ddf.catalog.operation.impl.QueryResponseImpl;
import ddf.catalog.plugin.PluginExecutionException;
import ddf.catalog.plugin.StopProcessingException;
import ddf.catalog.security.filter.plugin.FilterPlugin;
import ddf.security.SecurityConstants;
import ddf.security.Subject;
import ddf.security.permission.KeyValueCollectionPermission;
/**
*/
public class FilterPluginTest {
private static final Logger LOGGER = LoggerFactory.getLogger(FilterPluginTest.class);
FilterPlugin plugin;
QueryResponseImpl incomingResponse;
@Before
public void setup() {
plugin = new FilterPlugin();
QueryRequestImpl request = getSampleRequest();
Map<String, Serializable> properties = new HashMap<String, Serializable>();
AuthorizingRealm realm = mock(AuthorizingRealm.class);
when(realm.getName()).thenReturn("mockRealm");
when(realm.isPermitted(any(PrincipalCollection.class), any(Permission.class)))
.then(makeDecision());
Collection<org.apache.shiro.realm.Realm> realms = new ArrayList<org.apache.shiro.realm.Realm>();
realms.add(realm);
DefaultSecurityManager manager = new DefaultSecurityManager();
manager.setRealms(realms);
SimplePrincipalCollection principalCollection = new SimplePrincipalCollection(
new Principal() {
@Override
public String getName() {
return "testuser";
}
}, realm.getName());
Subject subject = new MockSubject(manager, principalCollection);
properties.put(SecurityConstants.SECURITY_SUBJECT, subject);
request.setProperties(properties);
incomingResponse = new QueryResponseImpl(request);
ResultImpl result1 = new ResultImpl(getMoreRolesMetacard());
ResultImpl result2 = new ResultImpl(getMissingRolesMetacard());
ResultImpl result3 = new ResultImpl(getExactRolesMetacard());
ResultImpl result4 = new ResultImpl(getNoRolesMetacard());
ResultImpl result5 = new ResultImpl(getNoSecurityAttributeMetacard());
incomingResponse.addResult(result1, false);
incomingResponse.addResult(result2, false);
incomingResponse.addResult(result3, false);
incomingResponse.addResult(result4, false);
incomingResponse.addResult(result5, true);
}
public Answer<Boolean> makeDecision() {
Map<String, List<String>> testRoleMap = new HashMap<String, List<String>>();
List<String> testRoles = new ArrayList<String>();
testRoles.add("A");
testRoles.add("B");
testRoleMap.put("Roles", testRoles);
final KeyValueCollectionPermission testUserPermission = new KeyValueCollectionPermission(
testRoleMap);
return new Answer<Boolean>() {
@Override
public Boolean answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
Permission incomingPermission = (Permission) args[1];
return testUserPermission.implies(incomingPermission);
}
};
}
@Test
public void testPluginFilter() {
try {
QueryResponse response = plugin.process(incomingResponse);
verifyFilterResponse(response);
} catch (PluginExecutionException e) {
LOGGER.error("Error while processing the redaction plugin", e);
} catch (StopProcessingException e) {
LOGGER.error("Stopped processing the redaction plugin", e);
}
}
@Test(expected = StopProcessingException.class)
public void testNoSubject() throws Exception {
QueryResponseImpl response = new QueryResponseImpl(getSampleRequest());
plugin.process(response);
fail("Plugin should have thrown exception when no subject was sent in.");
}
@Test(expected = StopProcessingException.class)
public void testNoRequestSubject() throws Exception {
QueryResponseImpl response = new QueryResponseImpl(null);
plugin.process(response);
fail("Plugin should have thrown exception when no subject was sent in.");
}
public void verifyFilterResponse(QueryResponse response) {
LOGGER.info("Filtered with " + response.getResults().size() + " out of 5 original.");
LOGGER.info("Checking Results");
Assert.assertEquals(4, response.getResults().size());
LOGGER.info("Filtering succeeded.");
}
public Metacard getMoreRolesMetacard() {
MetacardImpl metacard = new MetacardImpl();
metacard.setResourceSize("100");
try {
metacard.setResourceURI(new URI("http://some.fancy.uri/goes/here"));
} catch (URISyntaxException e) {
LOGGER.error("", e);
}
metacard.setContentTypeName("Resource");
metacard.setTitle("Metacard 1");
metacard.setContentTypeVersion("1");
metacard.setType(new MetacardTypeImpl(MetacardType.DEFAULT_METACARD_TYPE_NAME,
new HashSet<AttributeDescriptor>()));
HashMap<String, List<String>> security = new HashMap<String, List<String>>();
security.put("Roles", Arrays.asList("A", "B", "CR", "WS"));
metacard.setSecurity(security);
return metacard;
}
public Metacard getMissingRolesMetacard() {
MetacardImpl metacard = new MetacardImpl();
metacard.setResourceSize("100");
try {
metacard.setResourceURI(new URI("http://some.fancy.uri/goes/here"));
} catch (URISyntaxException e) {
LOGGER.error("", e);
}
metacard.setContentTypeName("Resource");
metacard.setTitle("Metacard 1");
metacard.setContentTypeVersion("1");
metacard.setType(new MetacardTypeImpl(MetacardType.DEFAULT_METACARD_TYPE_NAME,
new HashSet<AttributeDescriptor>()));
HashMap<String, List<String>> security = new HashMap<String, List<String>>();
security.put("Roles", Arrays.asList("A"));
metacard.setSecurity(security);
return metacard;
}
public Metacard getExactRolesMetacard() {
MetacardImpl metacard = new MetacardImpl();
metacard.setResourceSize("100");
try {
metacard.setResourceURI(new URI("http://some.fancy.uri/goes/here"));
} catch (URISyntaxException e) {
LOGGER.error("", e);
}
metacard.setContentTypeName("Resource");
metacard.setTitle("Metacard 1");
metacard.setContentTypeVersion("1");
metacard.setType(new MetacardTypeImpl(MetacardType.DEFAULT_METACARD_TYPE_NAME,
new HashSet<AttributeDescriptor>()));
HashMap<String, List<String>> security = new HashMap<String, List<String>>();
security.put("Roles", Arrays.asList("A", "B"));
metacard.setSecurity(security);
return metacard;
}
public Metacard getNoRolesMetacard() {
MetacardImpl metacard = new MetacardImpl();
metacard.setResourceSize("100");
try {
metacard.setResourceURI(new URI("http://some.fancy.uri/goes/here"));
} catch (URISyntaxException e) {
LOGGER.error("", e);
}
metacard.setContentTypeName("Resource");
metacard.setTitle("Metacard 1");
metacard.setContentTypeVersion("1");
metacard.setType(new MetacardTypeImpl(MetacardType.DEFAULT_METACARD_TYPE_NAME,
new HashSet<AttributeDescriptor>()));
HashMap<String, List<String>> security = new HashMap<String, List<String>>();
metacard.setSecurity(security);
return metacard;
}
public Metacard getNoSecurityAttributeMetacard() {
MetacardImpl metacard = new MetacardImpl();
metacard.setResourceSize("100");
try {
metacard.setResourceURI(new URI("http://some.fancy.uri/goes/here"));
} catch (URISyntaxException e) {
LOGGER.error("", e);
}
//Intentionally do not set the Metacard.SECURITY attribute
metacard.setContentTypeName("Resource");
metacard.setTitle("Metacard 1");
metacard.setContentTypeVersion("1");
metacard.setType(new MetacardTypeImpl(MetacardType.DEFAULT_METACARD_TYPE_NAME,
new HashSet<AttributeDescriptor>()));
return metacard;
}
private QueryRequestImpl getSampleRequest() {
return new QueryRequestImpl(new Query() {
@Override
public int getStartIndex() {
return 0;
}
@Override
public int getPageSize() {
return 10;
}
@Override
public SortBy getSortBy() {
return null;
}
@Override
public boolean requestsTotalResultsCount() {
return false;
}
@Override
public long getTimeoutMillis() {
return 0;
}
@Override
public boolean evaluate(Object o) {
return true;
}
@Override
public Object accept(FilterVisitor filterVisitor, Object o) {
return null;
}
});
}
private class MockSubject extends DelegatingSubject implements Subject {
public MockSubject(SecurityManager manager, PrincipalCollection principals) {
super(principals, true, null, new SimpleSession(UUID.randomUUID().toString()), manager);
}
@Override
public boolean isAnonymous() {
return false;
}
}
}