/**
* Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.seedstack.seed.security.internal;
import org.apache.shiro.util.CollectionUtils;
import org.seedstack.seed.security.PrincipalCustomizer;
import org.seedstack.seed.security.Realm;
import org.seedstack.seed.security.RoleMapping;
import org.seedstack.seed.security.RolePermissionResolver;
import org.seedstack.seed.security.SecurityConfig;
import org.seedstack.seed.security.internal.authorization.ConfigurationRoleMapping;
import org.seedstack.seed.security.internal.authorization.ConfigurationRolePermissionResolver;
import org.seedstack.seed.security.internal.authorization.EmptyRolePermissionResolver;
import org.seedstack.seed.security.internal.authorization.SameRoleMapping;
import org.seedstack.seed.security.internal.realms.ConfigurationRealm;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
class SecurityConfigurer {
private static final String REALMS_KEY = "realms";
private static final String ROLE_MAPPING_KEY = ".role-mapping";
private static final String ROLE_PERMISSION_RESOLVER_KEY = ".role-permission-resolver";
private static final Class<? extends Realm> DEFAULT_REALM = ConfigurationRealm.class;
private static final Class<? extends RoleMapping> DEFAULT_ROLE_MAPPING = SameRoleMapping.class;
private static final Class<? extends RoleMapping> CONFIGURATION_ROLE_MAPPING = ConfigurationRoleMapping.class;
private static final Class<? extends RolePermissionResolver> DEFAULT_ROLE_PERMISSION_RESOLVER = EmptyRolePermissionResolver.class;
private static final Class<? extends RolePermissionResolver> CONFIGURATION_ROLE_PERMISSION_RESOLVER = ConfigurationRolePermissionResolver.class;
private final SecurityConfig securityConfig;
private final Map<Class<?>, Collection<Class<?>>> securityClasses;
private final Collection<Class<? extends PrincipalCustomizer<?>>> principalCustomizerClasses;
private final Collection<RealmConfiguration> configurationRealms = new ArrayList<>();
SecurityConfigurer(SecurityConfig securityConfig, Map<Class<?>, Collection<Class<?>>> securityClasses,
Collection<Class<? extends PrincipalCustomizer<?>>> principalCustomizerClasses) {
this.securityConfig = securityConfig;
this.securityClasses = securityClasses;
this.principalCustomizerClasses = principalCustomizerClasses;
if (CollectionUtils.isEmpty(securityClasses.get(Realm.class))) {
throw new IllegalArgumentException("No realm class provided !");
}
buildRealms();
}
Collection<Class<? extends PrincipalCustomizer<?>>> getPrincipalCustomizers() {
if (principalCustomizerClasses != null) {
return principalCustomizerClasses;
}
return Collections.emptyList();
}
Collection<RealmConfiguration> getConfigurationRealms() {
return Collections.unmodifiableCollection(configurationRealms);
}
SecurityConfig getSecurityConfiguration() {
return this.securityConfig;
}
@SuppressWarnings("unchecked")
private void buildRealms() {
if (securityConfig.getRealms().isEmpty()) {
RealmConfiguration confRealm = new RealmConfiguration(DEFAULT_REALM.getSimpleName(), DEFAULT_REALM);
confRealm.setRolePermissionResolverClass(findRolePermissionResolver(null, confRealm));
confRealm.setRoleMappingClass(findRoleMapping(null, confRealm));
configurationRealms.add(confRealm);
} else {
for (SecurityConfig.RealmConfig realmConfig : securityConfig.getRealms()) {
Class<? extends Realm> realmClass = (Class<? extends Realm>) findClass(realmConfig.getName(), securityClasses.get(Realm.class));
if (realmClass == null) {
throw new IllegalArgumentException("Unknown realm defined in property " + REALMS_KEY + " : " + realmConfig.getName());
}
RealmConfiguration confRealm = new RealmConfiguration(realmConfig.getName(), realmClass);
confRealm.setRolePermissionResolverClass(findRolePermissionResolver(realmConfig, confRealm));
confRealm.setRoleMappingClass(findRoleMapping(realmConfig, confRealm));
configurationRealms.add(confRealm);
}
}
}
private Class<? extends RolePermissionResolver> findRolePermissionResolver(SecurityConfig.RealmConfig realmConfig, RealmConfiguration realm) {
if (realmConfig == null || realmConfig.getPermissionResolver() == null) {
if (securityConfig.getPermissions().isEmpty()) {
return DEFAULT_ROLE_PERMISSION_RESOLVER;
}
return CONFIGURATION_ROLE_PERMISSION_RESOLVER;
}
return findRealmComponent(realm.getName(), realmConfig.getPermissionResolver(), RolePermissionResolver.class);
}
private Class<? extends RoleMapping> findRoleMapping(SecurityConfig.RealmConfig realmConfig, RealmConfiguration realm) {
if (realmConfig == null || realmConfig.getRoleMapper() == null) {
if (securityConfig.getRoles().isEmpty()) {
return DEFAULT_ROLE_MAPPING;
}
return CONFIGURATION_ROLE_MAPPING;
}
return findRealmComponent(realm.getName(), realmConfig.getRoleMapper(), RoleMapping.class);
}
@SuppressWarnings("unchecked")
private <T> Class<? extends T> findRealmComponent(String realmName, String componentName, Class<? extends T> clazz) {
Class<? extends T> componentClass;
if (CollectionUtils.isEmpty(securityClasses.get(clazz))) {
throw new IllegalArgumentException("No class of type " + componentName + " were found");
}
componentClass = (Class<? extends T>) findClass(componentName, securityClasses.get(clazz));
if (componentClass == null) {
throw new IllegalArgumentException("Unknown property value " + componentName + " for realm " + realmName);
}
return componentClass;
}
private Class<?> findClass(String name, Collection<Class<?>> classes) {
for (Class<?> clazz : classes) {
if (clazz.getSimpleName().equals(name)) {
return clazz;
}
}
return null;
}
}