/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.geronimo.security.deployment;
import java.security.Principal;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Collection;
import java.util.jar.JarFile;
import java.net.URL;
import javax.xml.namespace.QName;
import org.apache.geronimo.common.DeploymentException;
import org.apache.geronimo.deployment.DeploymentContext;
import org.apache.geronimo.deployment.NamespaceDrivenBuilder;
import org.apache.geronimo.deployment.ModuleIDBuilder;
import org.apache.geronimo.deployment.service.EnvironmentBuilder;
import org.apache.geronimo.deployment.service.SingleGBeanBuilder;
import org.apache.geronimo.deployment.xbeans.PatternType;
import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
import org.apache.geronimo.gbean.AbstractName;
import org.apache.geronimo.gbean.AbstractNameQuery;
import org.apache.geronimo.gbean.GBeanData;
import org.apache.geronimo.gbean.GBeanInfoBuilder;
import org.apache.geronimo.gbean.GBeanLifecycle;
import org.apache.geronimo.gbean.annotation.GBean;
import org.apache.geronimo.gbean.annotation.ParamAttribute;
import org.apache.geronimo.j2ee.deployment.EARContext;
import org.apache.geronimo.j2ee.deployment.ModuleBuilderExtension;
import org.apache.geronimo.j2ee.deployment.Module;
import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
import org.apache.geronimo.kernel.GBeanNotFoundException;
import org.apache.geronimo.kernel.Naming;
import org.apache.geronimo.kernel.config.ConfigurationStore;
import org.apache.geronimo.kernel.repository.Environment;
import org.apache.geronimo.schema.ElementConverter;
import org.apache.geronimo.schema.NamespaceElementConverter;
import org.apache.geronimo.schema.SchemaConversionUtils;
import org.apache.geronimo.schema.SecurityElementConverter;
import org.apache.geronimo.security.SecurityNames;
import org.apache.geronimo.security.credentialstore.CredentialStore;
import org.apache.geronimo.security.deploy.PrincipalInfo;
import org.apache.geronimo.security.deploy.SubjectInfo;
import org.apache.geronimo.security.jacc.ApplicationPolicyConfigurationManager;
import org.apache.geronimo.security.jacc.ComponentPermissions;
import org.apache.geronimo.security.jacc.mappingprovider.ApplicationPrincipalRoleConfigurationManager;
import org.apache.geronimo.security.jacc.PrincipalRoleMapper;
import org.apache.geronimo.security.util.ConfigurationUtil;
import org.apache.geronimo.xbeans.geronimo.security.GerLoginDomainPrincipalType;
import org.apache.geronimo.xbeans.geronimo.security.GerPrincipalType;
import org.apache.geronimo.xbeans.geronimo.security.GerRealmPrincipalType;
import org.apache.geronimo.xbeans.geronimo.security.GerRoleMappingsType;
import org.apache.geronimo.xbeans.geronimo.security.GerRoleType;
import org.apache.geronimo.xbeans.geronimo.security.GerSecurityDocument;
import org.apache.geronimo.xbeans.geronimo.security.GerSecurityRefDocument;
import org.apache.geronimo.xbeans.geronimo.security.GerSecurityRefType;
import org.apache.geronimo.xbeans.geronimo.security.GerSecurityType;
import org.apache.geronimo.xbeans.geronimo.security.GerSubjectInfoType;
import org.apache.xmlbeans.QNameSet;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
/**
* @version $Rev: 947943 $ $Date: 2010-05-25 14:59:55 +0800 (Tue, 25 May 2010) $
*/
@GBean(j2eeType = NameFactory.MODULE_BUILDER)
public class GeronimoSecurityBuilderImpl implements NamespaceDrivenBuilder, ModuleBuilderExtension, GBeanLifecycle {
private static final QName BASE_SECURITY_QNAME = org.apache.geronimo.xbeans.geronimo.j2ee.GerSecurityDocument.type.getDocumentElementName();
private static final QName SECURITY_QNAME = GerSecurityDocument.type.getDocumentElementName();
private static final QName SECURITY_REF_QNAME = GerSecurityRefDocument.type.getDocumentElementName();
private static final QNameSet SECURITY_QNAME_SET = QNameSet.forArray(new QName[]{SECURITY_QNAME, SECURITY_REF_QNAME});
public static final String GERONIMO_SECURITY_NAMESPACE = "http://geronimo.apache.org/xml/ns/security-2.0";
private static final Map<String, String> NAMESPACE_UPDATES = new HashMap<String, String>();
private static final String ROLE_MAPPER_DATA_NAME = "roleMapperDataName";
static {
NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/loginconfig", "http://geronimo.apache.org/xml/ns/loginconfig-2.0");
NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/loginconfig-1.1", "http://geronimo.apache.org/xml/ns/loginconfig-2.0");
NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/loginconfig-1.2", "http://geronimo.apache.org/xml/ns/loginconfig-2.0");
NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/security", "http://geronimo.apache.org/xml/ns/security-1.2");
NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/security-1.1", "http://geronimo.apache.org/xml/ns/security-2.0");
NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/security-1.2", "http://geronimo.apache.org/xml/ns/security-2.0");
}
private static final Map<String, ElementConverter> GERONIMO_SCHEMA_CONVERSIONS = new HashMap<String, ElementConverter>();
static {
GERONIMO_SCHEMA_CONVERSIONS.put("security", new SecurityElementConverter());
GERONIMO_SCHEMA_CONVERSIONS.put("security-ref", new NamespaceElementConverter(GERONIMO_SECURITY_NAMESPACE));
GERONIMO_SCHEMA_CONVERSIONS.put("default-subject", new NamespaceElementConverter(GERONIMO_SECURITY_NAMESPACE));
}
private final AbstractNameQuery defaultCredentialStoreName;
private final AbstractNameQuery defaultRoleMappingName;
private final Environment defaultEnvironment;
public GeronimoSecurityBuilderImpl(@ParamAttribute(name = "credentialStoreName")AbstractNameQuery credentialStoreName,
@ParamAttribute(name = "defaultRoleMappingName")AbstractNameQuery defaultRoleMappingName,
@ParamAttribute(name = "defaultEnvironment")Environment defaultEnvironment) {
this.defaultCredentialStoreName = credentialStoreName;
this.defaultRoleMappingName = defaultRoleMappingName;
this.defaultEnvironment = defaultEnvironment;
}
public void doStart() {
XmlBeansUtil.registerNamespaceUpdates(NAMESPACE_UPDATES);
SchemaConversionUtils.registerNamespaceConversions(GERONIMO_SCHEMA_CONVERSIONS);
}
public void doStop() {
XmlBeansUtil.unregisterNamespaceUpdates(NAMESPACE_UPDATES);
SchemaConversionUtils.unregisterNamespaceConversions(GERONIMO_SCHEMA_CONVERSIONS);
}
public void doFail() {
doStop();
}
//MBE methods
public void createModule(Module module, Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, Environment environment, Object moduleContextInfo, AbstractName earName, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException {
}
public void installModule(JarFile earFile, EARContext earContext, Module module, Collection configurationStores, ConfigurationStore targetConfigurationStore, Collection repository) throws DeploymentException {
}
public void initContext(EARContext earContext, Module module, ClassLoader cl) throws DeploymentException {
}
public void addGBeans(EARContext earContext, Module module, ClassLoader cl, Collection repository) throws DeploymentException {
buildJaccManager(earContext);
}
//NamespaceDrivenBuilder methods
public void buildEnvironment(XmlObject container, Environment environment) throws DeploymentException {
}
public void build(XmlObject container, DeploymentContext applicationContext, DeploymentContext moduleContext) throws DeploymentException {
XmlObject[] items = container.selectChildren(SECURITY_QNAME);
for (XmlObject item : items) {
GerSecurityType securityType;
try {
securityType = (GerSecurityType) XmlBeansUtil.typedCopy(item, GerSecurityType.type);
} catch (XmlException e) {
throw new DeploymentException("Could not validate security element", e);
}
ClassLoader classLoader = applicationContext.getClassLoader();
if (applicationContext instanceof EARContext) {
SecurityConfiguration securityConfiguration = buildSecurityConfig(securityType);
((EARContext)applicationContext).setSecurityConfiguration(securityConfiguration);
}
AbstractNameQuery roleMapperDataName = configureRoleMapper(applicationContext, securityType, classLoader);
if (applicationContext instanceof EARContext) {
setRoleMapperName(applicationContext, roleMapperDataName);
}
}
XmlObject[] refs = container.selectChildren(SECURITY_REF_QNAME);
if (refs.length > 1) {
throw new DeploymentException("Unexpected count of security-ref elements in geronimo plan " + refs.length + " qname: " + SECURITY_REF_QNAME);
}
if (refs.length == 1) {
GerSecurityRefType ref;
try {
ref = (GerSecurityRefType) XmlBeansUtil.typedCopy(refs[0], GerSecurityRefType.type);
if (ref.isSetName()) {
String name = ref.getName().trim();
AbstractNameQuery roleMapperDataName = new AbstractNameQuery(null, Collections.singletonMap("name", name), PrincipalRoleMapper.class.getName());
setRoleMapperName(applicationContext, roleMapperDataName);
} else {
PatternType SecurityRefType = ref.getRef();
AbstractNameQuery roleMapperDataName = SingleGBeanBuilder.buildAbstractNameQuery(SecurityRefType, GBeanInfoBuilder.DEFAULT_J2EE_TYPE, Collections.singleton(CredentialStore.class.getName()));
setRoleMapperName(applicationContext, roleMapperDataName);
}
} catch (XmlException e) {
throw new DeploymentException("Could not validate security element", e);
}
}
}
private void setRoleMapperName(DeploymentContext applicationContext, AbstractNameQuery roleMapperDataName) throws DeploymentException {
EARContext earContext = (EARContext) applicationContext;
if (earContext.getGeneralData().put(ROLE_MAPPER_DATA_NAME, roleMapperDataName) != null) {
throw new DeploymentException("Only one role mapping or role mapping reference can be present in an ear");
}
}
private void buildJaccManager(EARContext earContext) throws DeploymentException {
if (earContext.isHasSecurity()) {
//Be sure to only set once per app
earContext.setHasSecurity(false);
AbstractName applicationPolicyManagerName = earContext.getNaming().createChildName(earContext.getModuleName(), SecurityNames.JACC_MANAGER, SecurityNames.JACC_MANAGER);
//TODO A better way to avoid the multiple invocation on securityBuilder.addGBeans ?
try {
if (earContext.getGBeanInstance(applicationPolicyManagerName) != null) {
return;
}
} catch (GBeanNotFoundException e1) {
}
AbstractNameQuery roleMapperDataName = (AbstractNameQuery)earContext.getGeneralData().get(ROLE_MAPPER_DATA_NAME);
if (roleMapperDataName == null) {
roleMapperDataName = defaultRoleMappingName;
EnvironmentBuilder.mergeEnvironments(earContext.getConfiguration().getEnvironment(), defaultEnvironment);
}
GBeanData jaccBeanData = configureApplicationPolicyManager(applicationPolicyManagerName, earContext.getContextIDToPermissionsMap());
jaccBeanData.setReferencePattern("PrincipalRoleMapper", roleMapperDataName);
try {
earContext.addGBean(jaccBeanData);
} catch (GBeanAlreadyExistsException e) {
throw new DeploymentException("JACC manager gbean already present", e);
}
// earContext.setJaccManagerName(jaccBeanData.getAbstractName());
}
}
private SecurityConfiguration buildSecurityConfig(GerSecurityType securityType) {
if (securityType == null) {
return null;
}
boolean doAsCurrentCaller = securityType.getDoasCurrentCaller();
boolean useContextHandler = securityType.getUseContextHandler();
String defaultRole = securityType.isSetDefaultRole() ? securityType.getDefaultRole().trim() : null;
return new SecurityConfiguration(defaultRole, doAsCurrentCaller, useContextHandler);
}
private void add(String roleName, Principal principal, Map<Principal, Set<String>> principalRoleMap) {
Set<String> roles = principalRoleMap.get(principal);
if (roles == null) {
roles = new HashSet<String>();
principalRoleMap.put(principal, roles);
}
roles.add(roleName);
}
private SubjectInfo buildSubjectInfo(GerSubjectInfoType defaultSubject) {
if (defaultSubject == null) {
return null;
}
String realmName = defaultSubject.getRealm().trim();
String id = defaultSubject.getId().trim();
return new SubjectInfo(realmName, id);
}
private static Principal buildRealmPrincipal(GerRealmPrincipalType realmPrincipalType, ClassLoader classLoader) {
return ConfigurationUtil.generateRealmPrincipal(realmPrincipalType.getRealmName().trim(), realmPrincipalType.getDomainName().trim(), realmPrincipalType.getClass1().trim(), realmPrincipalType.getName().trim(), classLoader);
}
private static Principal buildDomainPrincipal(GerLoginDomainPrincipalType domainPrincipalType, ClassLoader classLoader) {
return ConfigurationUtil.generateDomainPrincipal(domainPrincipalType.getDomainName().trim(), domainPrincipalType.getClass1().trim(), domainPrincipalType.getName().trim(), classLoader);
}
private static Principal buildPrincipal(GerPrincipalType principalType, ClassLoader classLoader) {
return ConfigurationUtil.generatePrincipal(principalType.getClass1().trim(), principalType.getName().trim(), classLoader);
}
//used from TSSConfigEditor
public PrincipalInfo buildPrincipal(XmlObject xmlObject) {
GerPrincipalType principalType = (GerPrincipalType) xmlObject;
return new PrincipalInfo(principalType.getClass1().trim(), principalType.getName().trim());
}
protected AbstractNameQuery configureRoleMapper(DeploymentContext deploymentContext, GerSecurityType securityType, ClassLoader classLoader) throws DeploymentException {
Map<String, SubjectInfo> roleDesignates = new HashMap<String, SubjectInfo>();
Map<Principal, Set<String>> principalRoleMap = new HashMap<Principal, Set<String>>();
if (securityType.isSetRoleMappings()) {
GerRoleMappingsType roleMappingsType = securityType.getRoleMappings();
for (int i = 0; i < roleMappingsType.sizeOfRoleArray(); i++) {
GerRoleType roleType = roleMappingsType.getRoleArray(i);
String roleName = roleType.getRoleName().trim();
if (roleType.isSetRunAsSubject()) {
SubjectInfo subjectInfo = buildSubjectInfo(roleType.getRunAsSubject());
roleDesignates.put(roleName, subjectInfo);
}
for (int j = 0; j < roleType.sizeOfRealmPrincipalArray(); j++) {
Principal principal = buildRealmPrincipal(roleType.getRealmPrincipalArray(j), classLoader);
add(roleName, principal, principalRoleMap);
}
for (int j = 0; j < roleType.sizeOfLoginDomainPrincipalArray(); j++) {
Principal principal = buildDomainPrincipal(roleType.getLoginDomainPrincipalArray(j), classLoader);
add(roleName, principal, principalRoleMap);
}
for (int j = 0; j < roleType.sizeOfPrincipalArray(); j++) {
Principal principal = buildPrincipal(roleType.getPrincipalArray(j), classLoader);
add(roleName, principal, principalRoleMap);
}
}
}
SubjectInfo defaultSubjectInfo = buildSubjectInfo(securityType.getDefaultSubject());
AbstractNameQuery credentialStoreName;
if (securityType.isSetCredentialStoreRef()) {
PatternType credentialStoreType = securityType.getCredentialStoreRef();
credentialStoreName = SingleGBeanBuilder.buildAbstractNameQuery(credentialStoreType, GBeanInfoBuilder.DEFAULT_J2EE_TYPE, Collections.singleton(CredentialStore.class.getName()));
} else {
credentialStoreName = this.defaultCredentialStoreName;
}
Naming naming = deploymentContext.getNaming();
String name = securityType.isSetName() ? securityType.getName() : "RoleMapper";
AbstractName roleMapperName = naming.createChildName(deploymentContext.getModuleName(), "RoleMapper", name);
GBeanData roleMapperData = new GBeanData(roleMapperName, ApplicationPrincipalRoleConfigurationManager.GBEAN_INFO);
roleMapperData.setAttribute("principalRoleMap", principalRoleMap);
roleMapperData.setAttribute("roleDesignates", roleDesignates);
roleMapperData.setAttribute("defaultSubjectInfo", defaultSubjectInfo);
if ((roleDesignates != null && !roleDesignates.isEmpty()) || defaultSubjectInfo != null) {
roleMapperData.setReferencePattern("CredentialStore", credentialStoreName);
}
try {
deploymentContext.addGBean(roleMapperData);
} catch (GBeanAlreadyExistsException e) {
throw new DeploymentException("Role mapper gbean already present", e);
}
return new AbstractNameQuery(roleMapperData.getAbstractName());
}
protected GBeanData configureApplicationPolicyManager(AbstractName applicationPolicyManagerName, Map<String, ComponentPermissions> contextIDToPermissionsMap) {
GBeanData jaccBeanData = new GBeanData(applicationPolicyManagerName, ApplicationPolicyConfigurationManager.GBEAN_INFO);
jaccBeanData.setAttribute("contextIdToPermissionsMap", contextIDToPermissionsMap);
return jaccBeanData;
}
public QNameSet getSpecQNameSet() {
return QNameSet.EMPTY;
}
public QNameSet getPlanQNameSet() {
return SECURITY_QNAME_SET;
}
public QName getBaseQName() {
return BASE_SECURITY_QNAME;
}
}