/*
* JBoss, Home of Professional Open Source.
* Copyright 2010, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.security.plugins;
import org.jboss.as.security.logging.SecurityLogger;
import org.jboss.modules.ModuleLoader;
import org.jboss.security.AuthenticationManager;
import org.jboss.security.AuthorizationManager;
import org.jboss.security.CacheableManager;
import org.jboss.security.ISecurityManagement;
import org.jboss.security.JSSESecurityDomain;
import org.jboss.security.SecurityConstants;
import org.jboss.security.audit.AuditManager;
import org.jboss.security.identitytrust.IdentityTrustManager;
import org.jboss.security.mapping.MappingManager;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.security.auth.callback.CallbackHandler;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.security.Principal;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* JNDI based implementation of {@code ISecurityManagement}
*
* @author Anil.Saldhana@redhat.com
* @author <a href="mailto:mmoyses@redhat.com">Marcus Moyses</a>
*/
public class JNDIBasedSecurityManagement implements ISecurityManagement {
private static final long serialVersionUID = 1924631329555621041L;
private transient ConcurrentHashMap<String, SecurityDomainContext> securityMgrMap = new ConcurrentHashMap<String, SecurityDomainContext>();
private transient ConcurrentHashMap<String, AuthenticationManager> authMgrMap = new ConcurrentHashMap<String, AuthenticationManager>();
private transient ConcurrentHashMap<String, AuthorizationManager> authzMgrMap = new ConcurrentHashMap<String, AuthorizationManager>();
private transient ConcurrentHashMap<String, AuditManager> auditMgrMap = new ConcurrentHashMap<String, AuditManager>();
private transient ConcurrentHashMap<String, IdentityTrustManager> idmMgrMap = new ConcurrentHashMap<String, IdentityTrustManager>();
private transient ConcurrentHashMap<String, MappingManager> mappingMgrMap = new ConcurrentHashMap<String, MappingManager>();
private transient ConcurrentHashMap<String, JSSESecurityDomain> jsseMap = new ConcurrentHashMap<String, JSSESecurityDomain>();
private String authenticationManagerClassName;
private boolean deepCopySubjectMode;
private String callbackHandlerClassName;
private String authorizationManagerClassName;
private String auditManagerClassName;
private String identityTrustManagerClassName;
private String mappingManagerClassName;
private ModuleLoader loader;
// creating a singleton
public JNDIBasedSecurityManagement(ModuleLoader loader) {
this.loader = loader;
}
public ConcurrentHashMap<String, SecurityDomainContext> getSecurityManagerMap() {
return securityMgrMap;
}
/** {@inheritDoc} */
public AuditManager getAuditManager(String securityDomain) {
AuditManager am = null;
try {
am = auditMgrMap.get(securityDomain);
if (am == null) {
am = (AuditManager) lookUpJNDI(securityDomain + "/auditMgr");
auditMgrMap.put(securityDomain, am);
}
} catch (Exception e) {
SecurityLogger.ROOT_LOGGER.tracef(e, "Exception getting AuditManager for domain=%s", securityDomain);
}
return am;
}
/** {@inheritDoc} */
public AuthenticationManager getAuthenticationManager(String securityDomain) {
AuthenticationManager am = null;
try {
am = authMgrMap.get(securityDomain);
if (am == null) {
am = (AuthenticationManager) lookUpJNDI(securityDomain + "/authenticationMgr");
authMgrMap.put(securityDomain, am);
}
} catch (Exception e) {
SecurityLogger.ROOT_LOGGER.tracef(e, "Exception getting AuthenticationManager for domain=%s", securityDomain);
}
return am;
}
/** {@inheritDoc} */
public AuthorizationManager getAuthorizationManager(String securityDomain) {
AuthorizationManager am = null;
try {
am = authzMgrMap.get(securityDomain);
if (am == null) {
am = (AuthorizationManager) lookUpJNDI(securityDomain + "/authorizationMgr");
authzMgrMap.put(securityDomain, am);
}
} catch (Exception e) {
SecurityLogger.ROOT_LOGGER.tracef(e, "Exception getting AuthorizationManager for domain=%s", securityDomain);
}
return am;
}
/** {@inheritDoc} */
public IdentityTrustManager getIdentityTrustManager(String securityDomain) {
IdentityTrustManager itm = null;
try {
itm = idmMgrMap.get(securityDomain);
if (itm == null) {
itm = (IdentityTrustManager) lookUpJNDI(securityDomain + "/identityTrustMgr");
idmMgrMap.put(securityDomain, itm);
}
} catch (Exception e) {
SecurityLogger.ROOT_LOGGER.tracef(e, "Exception getting IdentityTrustManager for domain=%s" + securityDomain);
}
return itm;
}
/** {@inheritDoc} */
public MappingManager getMappingManager(String securityDomain) {
MappingManager mm = null;
try {
mm = mappingMgrMap.get(securityDomain);
if (mm == null) {
mm = (MappingManager) lookUpJNDI(securityDomain + "/mappingMgr");
mappingMgrMap.put(securityDomain, mm);
}
} catch (Exception e) {
SecurityLogger.ROOT_LOGGER.tracef(e, "Exception getting MappingManager for domain=%s", securityDomain);
}
return mm;
}
/** {@inheritDoc} */
public JSSESecurityDomain getJSSE(String securityDomain) {
JSSESecurityDomain jsse = null;
try {
jsse = jsseMap.get(securityDomain);
if (jsse == null) {
jsse = (JSSESecurityDomain) lookUpJNDI(securityDomain + "/jsse");
jsseMap.put(securityDomain, jsse);
}
} catch (Exception e) {
SecurityLogger.ROOT_LOGGER.tracef(e, "Exception getting JSSESecurityDomain for domain=%s", securityDomain);
}
return jsse;
}
public String getAuthenticationManagerClassName() {
return authenticationManagerClassName;
}
public void setAuthenticationManagerClassName(String authenticationManagerClassName) {
this.authenticationManagerClassName = authenticationManagerClassName;
}
public boolean isDeepCopySubjectMode() {
return deepCopySubjectMode;
}
public void setDeepCopySubjectMode(boolean deepCopySubjectMode) {
this.deepCopySubjectMode = deepCopySubjectMode;
}
public String getCallbackHandlerClassName() {
return callbackHandlerClassName;
}
public void setCallbackHandlerClassName(String callbackHandlerClassName) {
this.callbackHandlerClassName = callbackHandlerClassName;
}
public String getAuthorizationManagerClassName() {
return authorizationManagerClassName;
}
public void setAuthorizationManagerClassName(String authorizationManagerClassName) {
this.authorizationManagerClassName = authorizationManagerClassName;
}
public String getAuditManagerClassName() {
return auditManagerClassName;
}
public void setAuditManagerClassName(String auditManagerClassName) {
this.auditManagerClassName = auditManagerClassName;
}
public String getIdentityTrustManagerClassName() {
return identityTrustManagerClassName;
}
public void setIdentityTrustManagerClassName(String identityTrustManagerClassName) {
this.identityTrustManagerClassName = identityTrustManagerClassName;
}
public String getMappingManagerClassName() {
return mappingManagerClassName;
}
public void setMappingManagerClassName(String mappingManagerClassName) {
this.mappingManagerClassName = mappingManagerClassName;
}
/**
* Removes one security domain from the maps
*
* @param securityDomain name of the security domain
*/
public void removeSecurityDomain(String securityDomain) {
securityMgrMap.remove(securityDomain);
auditMgrMap.remove(securityDomain);
authMgrMap.remove(securityDomain);
authzMgrMap.remove(securityDomain);
idmMgrMap.remove(securityDomain);
mappingMgrMap.remove(securityDomain);
jsseMap.remove(securityDomain);
}
/**
* Lookup a context in JNDI
*
* @param contextName the context
* @return the Object found at the context or null if there is nothing bound
*/
private Object lookUpJNDI(String contextName) {
Object result = null;
try {
Context ctx = new InitialContext();
if (contextName.startsWith(SecurityConstants.JAAS_CONTEXT_ROOT))
result = ctx.lookup(contextName);
else
result = ctx.lookup(SecurityConstants.JAAS_CONTEXT_ROOT + contextName);
} catch (Exception e) {
SecurityLogger.ROOT_LOGGER.tracef("Look up of JNDI for %s failed with %s", contextName, e.getLocalizedMessage());
return null;
}
return result;
}
/**
* Creates a {@code SecurityDomainContext}
*
* @param securityDomain name of the security domain
* @param cacheFactory creates a cache implementation
* @return an instance of {@code SecurityDomainContext}
* @throws Exception if an error occurs during creation
*/
public SecurityDomainContext createSecurityDomainContext(String securityDomain, AuthenticationCacheFactory cacheFactory) throws Exception {
return createSecurityDomainContext(securityDomain, cacheFactory, null);
}
/**
* Creates a {@code SecurityDomainContext} optionally including a {@link JSSESecurityDomain}
*
* @param securityDomain name of the security domain. Cannot be {@code null}
* @param cacheFactory creates a cache implementation. Cannot be {@code null}
* @param jsseSecurityDomain a JSSE security domain. May be {@code null}
* @return an instance of {@code SecurityDomainContext}
* @throws Exception if an error occurs during creation
*/
public SecurityDomainContext createSecurityDomainContext(String securityDomain,
AuthenticationCacheFactory cacheFactory,
JSSESecurityDomain jsseSecurityDomain) throws Exception {
SecurityLogger.ROOT_LOGGER.debugf("Creating SDC for domain = %s", securityDomain);
AuthenticationManager am = createAuthenticationManager(securityDomain);
if (cacheFactory != null && am instanceof CacheableManager) {
// create authentication cache
final Map<Principal, ?> cache = cacheFactory.getCache();
if (cache != null) {
@SuppressWarnings({ "unchecked", "rawtypes" })
CacheableManager<Map, Principal> cm = (CacheableManager<Map, Principal>) am;
cm.setCache(cache);
}
}
// set DeepCopySubject option if supported
if (deepCopySubjectMode) {
setDeepCopySubjectMode(am);
}
return new SecurityDomainContext(am,
createAuthorizationManager(securityDomain),
createAuditManager(securityDomain),
createIdentityTrustManager(securityDomain), createMappingManager(securityDomain),
jsseSecurityDomain);
}
/**
* Creates an {@code AuthenticationManager}
*
* @param securityDomain name of the security domain
* @return an instance of {@code AuthenticationManager}
* @throws Exception if creation fails
*/
private AuthenticationManager createAuthenticationManager(String securityDomain) throws Exception {
int i = callbackHandlerClassName.lastIndexOf(":");
if (i == -1)
throw SecurityLogger.ROOT_LOGGER.missingModuleName("default-callback-handler-class-name attribute");
String moduleSpec = callbackHandlerClassName.substring(0, i);
String className = callbackHandlerClassName.substring(i + 1);
Class<?> callbackHandlerClazz = SecurityActions.getModuleClassLoader(loader, moduleSpec).loadClass(className);
CallbackHandler ch = (CallbackHandler) callbackHandlerClazz.newInstance();
i = authenticationManagerClassName.lastIndexOf(":");
if (i == -1)
throw SecurityLogger.ROOT_LOGGER.missingModuleName("authentication-manager-class-name attribute");
moduleSpec = authenticationManagerClassName.substring(0, i);
className = authenticationManagerClassName.substring(i + 1);
Class<?> clazz = SecurityActions.getModuleClassLoader(loader, moduleSpec).loadClass(className);
Constructor<?> ctr = clazz.getConstructor(new Class[] { String.class, CallbackHandler.class });
return (AuthenticationManager) ctr.newInstance(new Object[] { securityDomain, ch });
}
/**
* Creates an {@code AuthorizationManager}
*
* @param securityDomain name of the security domain
* @return an instance of {@code AuthorizationManager}
* @throws Exception if creation fails
*/
private AuthorizationManager createAuthorizationManager(String securityDomain) throws Exception {
int i = authorizationManagerClassName.lastIndexOf(":");
if (i == -1)
throw SecurityLogger.ROOT_LOGGER.missingModuleName("authorization manager class");
String moduleSpec = authorizationManagerClassName.substring(0, i);
String className = authorizationManagerClassName.substring(i + 1);
Class<?> clazz = SecurityActions.getModuleClassLoader(loader, moduleSpec).loadClass(className);
Constructor<?> ctr = clazz.getConstructor(new Class[] { String.class });
return (AuthorizationManager) ctr.newInstance(new Object[] { securityDomain });
}
/**
* Creates an {@code AuditManager}
*
* @param securityDomain name of the security domain
* @return an instance of {@code AuditManager}
* @throws Exception if creation fails
*/
private AuditManager createAuditManager(String securityDomain) throws Exception {
int i = auditManagerClassName.lastIndexOf(":");
if (i == -1)
throw SecurityLogger.ROOT_LOGGER.missingModuleName("audit manager class");
String moduleSpec = auditManagerClassName.substring(0, i);
String className = auditManagerClassName.substring(i + 1);
Class<?> clazz = SecurityActions.getModuleClassLoader(loader, moduleSpec).loadClass(className);
Constructor<?> ctr = clazz.getConstructor(new Class[] { String.class });
return (AuditManager) ctr.newInstance(new Object[] { securityDomain });
}
/**
* Creates an {@code IdentityTrustManager}
*
* @param securityDomain name of the security domain
* @return an instance of {@code IdentityTrustManager}
* @throws Exception if creation fails
*/
private IdentityTrustManager createIdentityTrustManager(String securityDomain) throws Exception {
int i = identityTrustManagerClassName.lastIndexOf(":");
if (i == -1)
throw SecurityLogger.ROOT_LOGGER.missingModuleName("identity trust manager class");
String moduleSpec = identityTrustManagerClassName.substring(0, i);
String className = identityTrustManagerClassName.substring(i + 1);
Class<?> clazz = SecurityActions.getModuleClassLoader(loader, moduleSpec).loadClass(className);
Constructor<?> ctr = clazz.getConstructor(new Class[] { String.class });
return (IdentityTrustManager) ctr.newInstance(new Object[] { securityDomain });
}
/**
* Creates an {@code MappingManager}
*
* @param securityDomain name of the security domain
* @return an instance of {@code MappingManager}
* @throws Exception if creation fails
*/
private MappingManager createMappingManager(String securityDomain) throws Exception {
int i = mappingManagerClassName.lastIndexOf(":");
if (i == -1)
throw SecurityLogger.ROOT_LOGGER.missingModuleName("mapping manager class");
String moduleSpec = mappingManagerClassName.substring(0, i);
String className = mappingManagerClassName.substring(i + 1);
Class<?> clazz = SecurityActions.getModuleClassLoader(loader, moduleSpec).loadClass(className);
Constructor<?> ctr = clazz.getConstructor(new Class[] { String.class });
return (MappingManager) ctr.newInstance(new Object[] { securityDomain });
}
/**
* Use reflection to attempt to set the deep copy subject mode on the {@code AuthenticationManager}
*
* @param authenticationManager the {@code AuthenticationManager}
*/
private static void setDeepCopySubjectMode(AuthenticationManager authenticationManager) {
try {
Class<?>[] argsType = { Boolean.class };
Method m = authenticationManager.getClass().getMethod("setDeepCopySubjectOption", argsType);
Object[] deepCopyArgs = { Boolean.TRUE };
m.invoke(authenticationManager, deepCopyArgs);
} catch (Exception e) {
SecurityLogger.ROOT_LOGGER.tracef("Optional setDeepCopySubjectMode failed: %s", e.getLocalizedMessage());
}
}
}