/**
* 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 com.google.common.collect.Lists;
import io.nuun.kernel.api.plugin.InitState;
import io.nuun.kernel.api.plugin.context.InitContext;
import io.nuun.kernel.api.plugin.request.ClasspathScanRequest;
import org.seedstack.seed.SeedException;
import org.seedstack.seed.core.internal.AbstractSeedPlugin;
import org.seedstack.seed.core.internal.el.ELPlugin;
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.Scope;
import org.seedstack.seed.security.SecurityConfig;
import org.seedstack.seed.security.spi.SecurityScope;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* This plugin provides core security infrastructure, based on Apache Shiro
* implementation.
*/
public class SecurityPlugin extends AbstractSeedPlugin {
private static final Logger LOGGER = LoggerFactory.getLogger(SecurityPlugin.class);
private final Map<String, Class<? extends Scope>> scopeClasses = new HashMap<>();
private final Set<SecurityProvider> securityProviders = new HashSet<>();
private SecurityConfigurer securityConfigurer;
private boolean elAvailable;
@Override
public String name() {
return "security";
}
@Override
public Collection<Class<?>> dependencies() {
return Lists.newArrayList(ELPlugin.class, SecurityProvider.class);
}
@Override
public Collection<ClasspathScanRequest> classpathScanRequests() {
return classpathScanRequestBuilder()
.descendentTypeOf(Realm.class)
.descendentTypeOf(RoleMapping.class)
.descendentTypeOf(RolePermissionResolver.class)
.descendentTypeOf(Scope.class)
.descendentTypeOf(PrincipalCustomizer.class).build();
}
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public InitState initialize(InitContext initContext) {
SecurityConfig securityConfig = getConfiguration(SecurityConfig.class);
Map<Class<?>, Collection<Class<?>>> scannedClasses = initContext.scannedSubTypesByAncestorClass();
configureScopes(scannedClasses.get(Scope.class));
securityProviders.addAll(initContext.dependencies(SecurityProvider.class));
elAvailable = initContext.dependency(ELPlugin.class).isFunctionMappingAvailable();
Collection<Class<? extends PrincipalCustomizer<?>>> principalCustomizerClasses = (Collection) scannedClasses.get(PrincipalCustomizer.class);
securityConfigurer = new SecurityConfigurer(securityConfig, scannedClasses, principalCustomizerClasses);
return InitState.INITIALIZED;
}
@SuppressWarnings("unchecked")
private void configureScopes(Collection<Class<?>> scopeClasses) {
if (scopeClasses != null) {
for (Class<?> scopeCandidateClass : scopeClasses) {
if (Scope.class.isAssignableFrom(scopeCandidateClass)) {
SecurityScope securityScope = scopeCandidateClass.getAnnotation(SecurityScope.class);
String scopeName;
if (securityScope != null) {
scopeName = securityScope.value();
} else {
scopeName = scopeCandidateClass.getSimpleName();
}
try {
scopeCandidateClass.getConstructor(String.class);
} catch (NoSuchMethodException e) {
throw SeedException.wrap(e, SecurityErrorCode.MISSING_ADEQUATE_SCOPE_CONSTRUCTOR)
.put("scopeName", scopeName)
.put("class", scopeCandidateClass.getName());
}
if (this.scopeClasses.containsKey(scopeName)) {
throw SeedException.createNew(SecurityErrorCode.DUPLICATE_SCOPE_NAME)
.put("scopeName", scopeName)
.put("class1", this.scopeClasses.get(scopeName).getName())
.put("class2", scopeCandidateClass.getName());
}
this.scopeClasses.put(scopeName, (Class<? extends Scope>) scopeCandidateClass);
}
}
}
}
@Override
public Object nativeUnitModule() {
return new SecurityModule(
securityConfigurer,
scopeClasses,
elAvailable,
securityProviders
);
}
}