/* * JBoss, Home of Professional Open Source. * Copyright 2016 Red Hat, Inc., and individual contributors * as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.wildfly.security.auth.server; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.fail; import static org.junit.Assert.assertTrue; import java.security.Provider; import java.security.Security; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import javax.security.auth.callback.CallbackHandler; import org.junit.BeforeClass; import org.junit.Test; import org.wildfly.security.WildFlyElytronProvider; import org.wildfly.security.auth.permission.LoginPermission; import org.wildfly.security.auth.realm.SimpleMapBackedSecurityRealm; import org.wildfly.security.auth.realm.SimpleRealmEntry; import org.wildfly.security.authz.MapAttributes; import org.wildfly.security.authz.RoleDecoder; import org.wildfly.security.authz.Roles; import org.wildfly.security.credential.Credential; import org.wildfly.security.credential.PasswordCredential; import org.wildfly.security.password.PasswordFactory; import org.wildfly.security.password.interfaces.ClearPassword; import org.wildfly.security.password.spec.ClearPasswordSpec; import org.wildfly.security.permission.PermissionVerifier; /** * Simple tests for propagating an identity from one domain to another. * * @author <a href="mailto:fjuma@redhat.com">Farah Juma</a> */ public class IdentityPropagationTest { private static final Provider provider = new WildFlyElytronProvider(); private static volatile SecurityDomain domain1; private static volatile SecurityDomain domain2; private static volatile SecurityDomain domain3; @BeforeClass public static void setupSecurityDomains() throws Exception { Security.addProvider(provider); // Create some realms SimpleMapBackedSecurityRealm realm1 = new SimpleMapBackedSecurityRealm(); Map<String, SimpleRealmEntry> users = new HashMap<>(); addUser(users, "joe", "User"); addUser(users, "bob", "User"); realm1.setPasswordMap(users); SimpleMapBackedSecurityRealm realm2 = new SimpleMapBackedSecurityRealm(); users = new HashMap<>(); addUser(users, "sam", "Manager"); addUser(users, "bob", "Manager"); realm2.setPasswordMap(users); // domain1 contains both realms SecurityDomain.Builder builder = SecurityDomain.builder(); builder.addRealm("users", realm1).build(); builder.addRealm("managers", realm2).build(); builder.setDefaultRealmName("users"); builder.setPermissionMapper((permissionMappable, roles) -> PermissionVerifier.from(new LoginPermission())); domain1 = builder.build(); // domain2 contains one of the realms builder = SecurityDomain.builder(); builder.addRealm("usersRealm", realm1).setRoleMapper(rolesToMap -> Roles.of("UserRole")).build(); builder.setDefaultRealmName("usersRealm"); builder.setPermissionMapper((permissionMappable, roles) -> { if (permissionMappable.getPrincipal().getName().equals("joe")) { return PermissionVerifier.from(new LoginPermission()); } return PermissionVerifier.NONE; }); domain2 = builder.build(); // domain3 contains one of the realms and it trusts domain2 builder = SecurityDomain.builder(); builder.addRealm("managersRealm", realm2).setRoleMapper(rolesToMap -> Roles.of("ManagerRole")).build(); builder.setDefaultRealmName("managersRealm"); builder.setPermissionMapper((permissionMappable, roles) -> PermissionVerifier.from(new LoginPermission())); HashSet<SecurityDomain> trustedSecurityDomains = new HashSet<>(); trustedSecurityDomains.add(domain2); builder.setTrustedSecurityDomainPredicate(trustedSecurityDomains::contains); domain3 = builder.build(); } @Test public void testInflowFromTrustedIdentityWithCommonRealm() throws Exception { ServerAuthenticationContext context = domain2.createNewAuthenticationContext(); SecurityIdentity establishedIdentity = getIdentityFromDomain(domain1, "joe"); assertTrue(context.importIdentity(establishedIdentity)); SecurityIdentity inflowedIdentity = context.getAuthorizedIdentity(); assertEquals("joe", inflowedIdentity.getPrincipal().getName()); assertEquals(domain2, inflowedIdentity.getSecurityDomain()); assertTrue(inflowedIdentity.getRoles().contains("UserRole")); } @Test public void testInflowFromTrustedIdentityWithoutCommonRealm() throws Exception { ServerAuthenticationContext context = domain3.createNewAuthenticationContext(); SecurityIdentity establishedIdentity = getIdentityFromDomain(domain2, "bob"); assertTrue(context.importIdentity(establishedIdentity)); SecurityIdentity inflowedIdentity = context.getAuthorizedIdentity(); assertEquals("bob", inflowedIdentity.getPrincipal().getName()); assertEquals(domain3, inflowedIdentity.getSecurityDomain()); assertTrue(inflowedIdentity.getRoles().contains("ManagerRole")); } @Test public void testInflowFromUntrustedIdentity() throws Exception { final ServerAuthenticationContext context = domain2.createNewAuthenticationContext(); SecurityIdentity establishedIdentity = getIdentityFromDomain(domain3, "bob"); assertFalse(context.importIdentity(establishedIdentity)); try { context.getAuthorizedIdentity(); fail("Expected IllegalStateException not thrown"); } catch (IllegalStateException expected) { } } @Test public void testInflowFromAnonymousIdentity() throws Exception { final ServerAuthenticationContext context = domain2.createNewAuthenticationContext(); final SecurityIdentity establishedIdentity = domain1.getCurrentSecurityIdentity(); assertTrue(context.importIdentity(establishedIdentity)); SecurityIdentity inflowedIdentity = context.getAuthorizedIdentity(); assertEquals(domain2.getAnonymousSecurityIdentity(), inflowedIdentity); } @Test public void testInflowFromSameDomain() throws Exception { final ServerAuthenticationContext context = domain2.createNewAuthenticationContext(); SecurityIdentity establishedIdentity = getIdentityFromDomain(domain2, "joe"); assertTrue(context.importIdentity(establishedIdentity)); SecurityIdentity inflowedIdentity = context.getAuthorizedIdentity(); assertEquals(establishedIdentity.getSecurityDomain(), inflowedIdentity.getSecurityDomain()); assertEquals(establishedIdentity.getPrincipal().getName(), inflowedIdentity.getPrincipal().getName()); assertEquals(establishedIdentity.getRealmInfo(), inflowedIdentity.getRealmInfo()); assertTrue(inflowedIdentity.getAttributes().get("roles").containsAll(establishedIdentity.getAttributes().get("roles"))); } private static void addUser(Map<String, SimpleRealmEntry> securityRealm, String userName, String roles) { List<Credential> credentials; try { credentials = Collections.singletonList( new PasswordCredential( PasswordFactory.getInstance(ClearPassword.ALGORITHM_CLEAR).generatePassword( new ClearPasswordSpec("password".toCharArray())))); } catch (Exception e) { throw new RuntimeException(e); } MapAttributes attributes = new MapAttributes(); attributes.addAll(RoleDecoder.KEY_ROLES, Collections.singletonList(roles)); securityRealm.put(userName, new SimpleRealmEntry(credentials, attributes)); } private SecurityIdentity getIdentityFromDomain(final SecurityDomain securityDomain, final String userName) throws Exception { return securityDomain.getAnonymousSecurityIdentity().createRunAsIdentity(userName, false); } private CallbackHandler createCallbackHandler(final SecurityDomain securityDomain) throws Exception { return securityDomain.createNewAuthenticationContext().createCallbackHandler(); } }