package org.dcache.gplazma.strategies; import com.google.common.collect.Sets; import org.junit.Before; import org.junit.Test; import javax.security.auth.kerberos.KerberosPrincipal; import java.security.Principal; import java.util.Arrays; import java.util.Set; import org.dcache.gplazma.AuthenticationException; import org.dcache.gplazma.configuration.ConfigurationItemControl; import org.dcache.gplazma.configuration.parser.FactoryConfigurationException; import org.dcache.gplazma.monitor.IgnoringLoginMonitor; import org.dcache.gplazma.monitor.LoginMonitor; import org.dcache.gplazma.plugins.GPlazmaAccountPlugin; import static org.dcache.gplazma.util.Preconditions.checkAuthentication; import static org.junit.Assert.fail; /** * Test the default accounting strategy */ @SuppressWarnings("unchecked") public class AccountStrategyTests { private static final String DEFAULT_STRATEGY_FACTORY = "org.dcache.gplazma.strategies.DefaultStrategyFactory"; private static final LoginMonitor IGNORING_LOGIN_MONITOR = new IgnoringLoginMonitor(); private static final Principal PAUL_KERBEROS_PRINCIPAL = new KerberosPrincipal("paul@DESY.DE"); private static final Principal TIGRAN_KERBEROS_PRINCIPAL = new KerberosPrincipal("tigran@DESY.DE"); private AccountStrategy _strategy; private Set<Principal> _principals; @Before public void setup() throws FactoryConfigurationException { StrategyFactory factory = StrategyFactory.getInstance(DEFAULT_STRATEGY_FACTORY); _strategy = factory.newAccountStrategy(); _principals = Sets.newHashSet(); } @Test public void shouldSucceedForNoConfiguration() throws AuthenticationException { // given an empty configuration givenStrategyWithPlugins(noPlugins()); // given an empty set of principals givenAuthorizedPrincipals(noPrincipals()); runAccountPhase(); } @Test public void shouldSucceedForSuccessfulRequiredPlugin() throws AuthenticationException { // given configuration of a REQUIRED plugin (which succeeds) givenStrategyWithPlugins(required(Succeeds.class)); // given use an empty set of principals givenAuthorizedPrincipals(noPrincipals()); runAccountPhase(); } @Test public void shouldSucceedForSuccessfulRequisitePlugin() throws AuthenticationException { // given configuration of a REQUISITE plugin (which succeeds) givenStrategyWithPlugins(requisite(Succeeds.class)); // given an empty set of principals givenAuthorizedPrincipals(noPrincipals()); runAccountPhase(); } @Test public void shouldSucceedForSuccessfulSufficientPlugin() throws AuthenticationException { // given configuration of a SUFFICIENT plugin (which succeeds) // followed by plugin that will fail the test, if run givenStrategyWithPlugins(sufficient(Succeeds.class), required(FailsTest.class)); // given an empty set of principals givenAuthorizedPrincipals(noPrincipals()); runAccountPhase(); } @Test public void shouldSucceedForSuccessfulOptionalPlugin() throws AuthenticationException { // given configuration of an OPTIONAL plugin (which succeeds) givenStrategyWithPlugins(optional(Succeeds.class)); // given an empty set of principals givenAuthorizedPrincipals(noPrincipals()); runAccountPhase(); } @Test(expected=AuthenticationException.class) public void shouldFailForFailingRequiredPlugin() throws AuthenticationException { // given configuration of a REQUIRED plugin (which fails) givenStrategyWithPlugins(required(Fails.class)); // given an empty set of principals givenAuthorizedPrincipals(noPrincipals()); runAccountPhase(); } @Test(expected=AuthenticationException.class) public void shouldFailForFailingRequisitePlugin() throws AuthenticationException { // given configuration of a REQUISITE plugin (which fails) followed by // a plugin that will fail the test, if run givenStrategyWithPlugins(requisite(Fails.class), required(FailsTest.class)); // given an empty set of principals givenAuthorizedPrincipals(noPrincipals()); runAccountPhase(); } @Test public void shouldSucceedForFailingOptionalPlugin() throws AuthenticationException { // given configuration of an OPTIONAL plugin (which fails) givenStrategyWithPlugins(optional(Fails.class)); // given an empty set of principals givenAuthorizedPrincipals(noPrincipals()); runAccountPhase(); } @Test public void shouldSucceedForFailingSufficientPlugin() throws AuthenticationException { // given configuration of a SUFFICIENT plugin (which fails) givenStrategyWithPlugins(sufficient(Fails.class)); // given an empty set of principals givenAuthorizedPrincipals(noPrincipals()); runAccountPhase(); } @Test public void shouldSucceedForTwoSuccessfulReqiredPlugins() throws AuthenticationException { // given configuration of a REQUIRED plugin (which succeeds) followed // by another REQUIRED plugin (which also succeeds) givenStrategyWithPlugins( required(Succeeds.class), required(Succeeds.class)); // given an empty set of principals givenAuthorizedPrincipals(noPrincipals()); runAccountPhase(); } @Test public void shouldSucceedForTwoSuccessfulRequisitePlugins() throws AuthenticationException { // given configuration of a REQUISITE plugin (which succeeds) followed // by another REQUISITE plugin (which succeeds) givenStrategyWithPlugins( requisite(Succeeds.class), requisite(Succeeds.class)); // given an empty set of principals givenAuthorizedPrincipals(noPrincipals()); runAccountPhase(); } @Test public void shouldSucceedForTwoSucceedingOptionalPlugins() throws AuthenticationException { // given configuration of an OPTIONAL plugin (which succeeds) followed // by another OPTIONAL plugin (which succeeds) givenStrategyWithPlugins( optional(Succeeds.class), optional(Succeeds.class)); // given an empty set of principals givenAuthorizedPrincipals(noPrincipals()); runAccountPhase(); } @Test(expected=AuthenticationException.class) public void shouldFailForSuccessfulRequiredAndFailingRequiredPlugins() throws AuthenticationException { // given configuration of a REQUIRED plugin (which succeeds) followed // by another REQUIRED plugin (which fails). givenStrategyWithPlugins( required(Succeeds.class), required(Fails.class)); // given an empty set of principals givenAuthorizedPrincipals(noPrincipals()); runAccountPhase(); } @Test public void shouldSucceedForSuccessfulRequiredAndFailingOptionalPlugins() throws AuthenticationException { // given configuration of a REQUIRED plugin (which succeeds) followed // by an OPTIONAL plugin (which fails). givenStrategyWithPlugins( required(Succeeds.class), optional(Fails.class)); // given an empty set of principals givenAuthorizedPrincipals(noPrincipals()); runAccountPhase(); } @Test(expected=AuthenticationException.class) public void shouldFailForFailingRequiredAndFailingRequisitePlugins() throws AuthenticationException { // given configuration of a REQUIRED plugin (which fails) followed by // a REQUISITE plugin (which fails) followed by a plugin that will // fail the unit-test, if run. givenStrategyWithPlugins( required(Fails.class), requisite(Fails.class), required(FailsTest.class)); // given an empty set of principals givenAuthorizedPrincipals(noPrincipals()); runAccountPhase(); } @Test(expected=AuthenticationException.class) public void shouldFailForRequisitePluginWithWrongPrincipal() throws AuthenticationException { // given configuration of a REQUISITE plugin (which fails) followed by // a plugin that will fail the unit-test, if run. givenStrategyWithPlugins( requisite(SucceedIfPaul.class), required(FailsTest.class)); // given Tigran's Kerberos principal givenAuthorizedPrincipals(TIGRAN_KERBEROS_PRINCIPAL); runAccountPhase(); } @Test public void shouldSucceedForSufficientPluginWithCorrectPrincipal() throws AuthenticationException { // given configuration of a SUFFICIENT plugin (which succeeds) followed // by a plugin that will fail the unit-test, if run. givenStrategyWithPlugins( sufficient(SucceedIfPaul.class), required(FailsTest.class)); // given Paul's Kerberos principal givenAuthorizedPrincipals(PAUL_KERBEROS_PRINCIPAL); runAccountPhase(); } @Test(expected=AuthenticationException.class) public void shouldFailForRequisiteBuggyPlugin() throws AuthenticationException { // given configuration of a REQUISITE plugin (which is buggy) followed // by a plugin that will fail the unit-test, if run. givenStrategyWithPlugins( requisite(Buggy.class), required(FailsTest.class)); // given an empty set of principals givenAuthorizedPrincipals(noPrincipals()); runAccountPhase(); } @Test public void shouldSucceedForOptionalBuggyPlugin() throws AuthenticationException { // given configuration of an OPTIONAL plugin (which is buggy) givenStrategyWithPlugins(optional(Buggy.class)); // given an empty set of principals givenAuthorizedPrincipals(noPrincipals()); runAccountPhase(); } private void runAccountPhase() throws AuthenticationException { _strategy.account(IGNORING_LOGIN_MONITOR, _principals); } private void givenAuthorizedPrincipals(Principal... principals) { _principals.clear(); _principals.addAll(Arrays.asList(principals)); } private static Principal[] noPrincipals() { return new Principal[0]; } private void givenStrategyWithPlugins(GPlazmaPluginService<GPlazmaAccountPlugin>... plugins) { _strategy.setPlugins(Arrays.asList(plugins)); } private static GPlazmaPluginService<GPlazmaAccountPlugin>[] noPlugins() { return new GPlazmaPluginService[0]; } private static GPlazmaPluginService<GPlazmaAccountPlugin> sufficient( Class<? extends GPlazmaAccountPlugin> type) { return configuredPlugin(type, ConfigurationItemControl.SUFFICIENT); } private static GPlazmaPluginService<GPlazmaAccountPlugin> required( Class<? extends GPlazmaAccountPlugin> type) { return configuredPlugin(type, ConfigurationItemControl.REQUIRED); } private static GPlazmaPluginService<GPlazmaAccountPlugin> requisite( Class<? extends GPlazmaAccountPlugin> type) { return configuredPlugin(type, ConfigurationItemControl.REQUISITE); } private static GPlazmaPluginService<GPlazmaAccountPlugin> optional( Class<? extends GPlazmaAccountPlugin> type) { return configuredPlugin(type, ConfigurationItemControl.OPTIONAL); } private static GPlazmaPluginService<GPlazmaAccountPlugin> configuredPlugin( Class<? extends GPlazmaAccountPlugin> type, ConfigurationItemControl control) { GPlazmaAccountPlugin plugin; try { plugin = type.newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } return new GPlazmaPluginService<>(plugin, type.getSimpleName(), control); } /** * AccountPlugin that always succeeds */ public static final class Succeeds implements GPlazmaAccountPlugin { @Override public void account(Set<Principal> authorizedPrincipals) throws AuthenticationException { // do nothing here } } /** * AccountPlugin that always fails */ public static final class Fails implements GPlazmaAccountPlugin { @Override public void account(Set<Principal> authorizedPrincipals) throws AuthenticationException { throw new AuthenticationException("I always fail"); } } /** * AccountPlugin that fails the unit-test if called. This is used to * ensure that the list of plugins does not extend beyond a particular * point */ public static final class FailsTest implements GPlazmaAccountPlugin { @Override public void account(Set<Principal> authorizedPrincipals) throws AuthenticationException { fail("mistaken attempt to query plugin"); } } /** * AccountPlugin that requires a principal. If the set of principals * contains the KerberosPrincipal paul@DESY.DE then account will succeed; * if not, then the account will fail. */ public static final class SucceedIfPaul implements GPlazmaAccountPlugin { @Override public void account(Set<Principal> authorizedPrincipals) throws AuthenticationException { checkAuthentication( authorizedPrincipals.contains(PAUL_KERBEROS_PRINCIPAL), "you are not Paul"); } } /** * An AccountPlugin that contains a bug; any attempt to use the * account method will trigger this bug. */ public static final class Buggy implements GPlazmaAccountPlugin { @Override public void account(Set<Principal> authorizedPrincipals) throws AuthenticationException { throw new RuntimeException("this is a bug"); } } }