/*
* JBoss, Home of Professional Open Source
*
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
*
* 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.authz.jacc;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.wildfly.security.WildFlyElytronProvider;
import org.wildfly.security.auth.permission.RunAsPrincipalPermission;
import org.wildfly.security.auth.realm.SimpleMapBackedSecurityRealm;
import org.wildfly.security.auth.realm.SimpleRealmEntry;
import org.wildfly.security.auth.server.SecurityDomain;
import org.wildfly.security.authz.MapAttributes;
import org.wildfly.security.authz.RoleDecoder;
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.PermissionUtil;
import org.wildfly.security.permission.PermissionVerifier;
import javax.security.jacc.PolicyConfiguration;
import javax.security.jacc.PolicyContext;
import javax.security.jacc.PolicyContextException;
import javax.security.jacc.WebResourcePermission;
import java.security.PermissionCollection;
import java.security.Policy;
import java.security.Provider;
import java.security.Security;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* <p>This test case provides policy enforcement tests based on the JACC specification as well relying on Elytron's Permission
* Mapping API in order to define and enforce additional permissions.
*
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class ElytronPolicyEnforcementTest extends AbstractAuthorizationTestCase {
private static final Provider provider = new WildFlyElytronProvider();
@BeforeClass
public static void onBeforeClass() {
System.setProperty("javax.security.jacc.PolicyConfigurationFactory.provider", ElytronPolicyConfigurationFactory.class.getName());
Policy.setPolicy(new JaccDelegatingPolicy());
Security.addProvider(provider);
}
@AfterClass
public static void onAfter() throws Exception {
Security.removeProvider(provider.getName());
}
private static final String CONTEXT_ID = "third-party-app";
private HashMap<String, SecurityDomain> securityDomains;
@Override
protected SecurityDomain createSecurityDomain() {
SecurityDomain.Builder builder = SecurityDomain.builder();
SimpleMapBackedSecurityRealm securityRealm = new SimpleMapBackedSecurityRealm();
Map<String, SimpleRealmEntry> users = new HashMap<>();
addUser(users, "user-admin", "Administrator");
addUser(users, "user-manager", "Manager");
addUser(users, "user-user", "User");
securityRealm.setPasswordMap(users);
builder.addRealm("default", securityRealm).build();
builder.setDefaultRealmName("default");
builder.setPermissionMapper((permissionMappable, roles) -> {
PermissionCollection collection;
if (roles.contains("Administrator")) {
collection = PermissionUtil.readOnlyCollectionOf(
new WebResourcePermission("/webResource", "GET"),
new WebResourcePermission("/webResource", "PUT"),
new WebResourcePermission("/webResource", "POST"),
new RunAsPrincipalPermission("*")
);
} else if (roles.contains("Manager")) {
collection = PermissionUtil.readOnlyCollectionOf(
new WebResourcePermission("/webResource", "GET"),
new WebResourcePermission("/webResource", "POST"),
new RunAsPrincipalPermission("*")
);
} else if (roles.contains("User")) {
collection = PermissionUtil.readOnlyCollectionOf(
new WebResourcePermission("/webResource", "GET"),
new RunAsPrincipalPermission("*")
);
} else {
collection = PermissionUtil.readOnlyCollectionOf(new RunAsPrincipalPermission("*"));
}
return PermissionVerifier.from(collection);
});
SecurityDomain securityDomain = builder.build();
this.securityDomains = new HashMap<>();
this.securityDomains.computeIfAbsent(CONTEXT_ID, s -> securityDomain);
try {
PolicyContext.registerHandler(SecurityIdentityHandler.KEY, new SecurityIdentityHandler(this.securityDomains), true);
} catch (PolicyContextException e) {
e.printStackTrace();
fail("Could not register [" + SecurityIdentityHandler.class + "].");
}
return securityDomain;
}
@Test
@SecurityIdentityRule.RunAs("user-admin")
public void testAdministratorRoleBasedPolicy() throws Exception {
String contextID = "third-party-app";
PolicyConfiguration policyConfiguration = createPolicyConfiguration(contextID, toConfigure -> {
toConfigure.addToRole("Administrator", new WebResourcePermission("/webResource", "HEAD"));
});
policyConfiguration.commit();
PolicyContext.setContextID(contextID);
Policy policy = Policy.getPolicy();
// this permission was defined using a PermissionMapper and it should be granted for user-admin
assertTrue(policy.implies(createProtectionDomain(), new WebResourcePermission("/webResource", "POST")));
// however, this one was set using JACC API, via PolicyConfiguration. It should be valid as well.
assertTrue(policy.implies(createProtectionDomain(), new WebResourcePermission("/webResource", "HEAD")));
// this one was not granted for user-admin
assertFalse(policy.implies(createProtectionDomain(), new WebResourcePermission("/webResource", "OPTIONS")));
policyConfiguration.delete();
}
@Test
@SecurityIdentityRule.RunAs("user-manager")
public void testManagerRoleBasedPolicy() throws Exception {
String contextID = "third-party-app";
PolicyConfiguration policyConfiguration = createPolicyConfiguration(contextID, toConfigure -> {});
policyConfiguration.commit();
PolicyContext.setContextID(contextID);
Policy policy = Policy.getPolicy();
// these permissions were defined using a PermissionMapper and they should be granted for user-manager
assertTrue(policy.implies(createProtectionDomain(), new WebResourcePermission("/webResource", "POST")));
assertTrue(policy.implies(createProtectionDomain(), new WebResourcePermission("/webResource", "GET")));
// this one was not granted for user-manager
assertFalse(policy.implies(createProtectionDomain(), new WebResourcePermission("/webResource", "PUT")));
policyConfiguration.delete();
}
@Test
@SecurityIdentityRule.RunAs("user-user")
public void testUserRoleBasedPolicy() throws Exception {
String contextID = "third-party-app";
PolicyConfiguration policyConfiguration = createPolicyConfiguration(contextID, toConfigure -> {});
policyConfiguration.commit();
PolicyContext.setContextID(contextID);
Policy policy = Policy.getPolicy();
// this permissions was defined using a PermissionMapper and they should be granted for user-user
assertTrue(policy.implies(createProtectionDomain(), new WebResourcePermission("/webResource", "GET")));
// this one was not granted for user-manager
assertFalse(policy.implies(createProtectionDomain(), new WebResourcePermission("/webResource", "PUT")));
assertFalse(policy.implies(createProtectionDomain(), new WebResourcePermission("/webResource", "POST")));
policyConfiguration.delete();
}
private void addUser(Map<String, SimpleRealmEntry> securityRealm, String userName, String roles) {
List<Credential> defaultInsecurePasswords;
try {
defaultInsecurePasswords = 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(defaultInsecurePasswords, attributes));
}
}