/*
* 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.context;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.NameClassPair;
import javax.naming.NameParser;
import javax.naming.NamingEnumeration;
import javax.naming.OperationNotSupportedException;
import org.jboss.as.naming.ManagedReference;
import org.jboss.as.naming.ManagedReferenceFactory;
import org.jboss.as.naming.ValueManagedReference;
import org.jboss.as.security.plugins.DefaultAuthenticationCacheFactory;
import org.jboss.as.security.plugins.JNDIBasedSecurityManagement;
import org.jboss.as.security.plugins.SecurityDomainContext;
import org.jboss.modules.ModuleLoadException;
import org.jboss.msc.inject.Injector;
import org.jboss.msc.value.ImmediateValue;
import org.jboss.msc.value.InjectedValue;
import org.jboss.security.ISecurityManagement;
import org.jboss.security.SecurityConstants;
/**
* Implementation of {@code ManagedReferenceFactory} that returns {@code AuthenticationManager}s
*
* @author <a href="mailto:mmoyses@redhat.com">Marcus Moyses</a>
*/
public class SecurityDomainJndiInjectable implements InvocationHandler, ManagedReferenceFactory {
private final InjectedValue<ISecurityManagement> securityManagementValue = new InjectedValue<ISecurityManagement>();
/**
* {@inheritDoc}
*
* This method returns a Context proxy that is only able to handle a lookup operation for an atomic name of a security
* domain.
*/
public ManagedReference getReference() {
final ClassLoader loader;
try {
loader = SecurityActions.getModuleClassLoader();
} catch (ModuleLoadException e) {
throw new IllegalStateException("Unable to get module classloader", e);
}
Class<?>[] interfaces = { Context.class };
return new ValueManagedReference(new ImmediateValue<Object>(Proxy.newProxyInstance(loader, interfaces, this)));
}
/**
* This is the InvocationHandler callback for the Context interface that was created by our getObjectInstance() method. We
* handle the java:jboss/jaas/domain level operations here.
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Context ctx = new InitialContext();
NameParser parser = ctx.getNameParser("");
String securityDomain = null;
Name name = null;
final JNDIBasedSecurityManagement securityManagement = JNDIBasedSecurityManagement.class.cast(securityManagementValue
.getValue());
final ConcurrentHashMap<String, SecurityDomainContext> securityManagerMap = securityManagement.getSecurityManagerMap();
String methodName = method.getName();
if (methodName.equals("toString"))
return SecurityConstants.JAAS_CONTEXT_ROOT + " Context proxy";
if (methodName.equals("list"))
return new DomainEnumeration(securityManagerMap.keys(), securityManagerMap);
if (methodName.equals("bind") || methodName.equals("rebind")) {
if (args[0] instanceof String)
name = parser.parse((String) args[0]);
else
name = (Name) args[0];
securityDomain = name.get(0);
SecurityDomainContext val = (SecurityDomainContext) args[1];
securityManagerMap.put(securityDomain, val);
return proxy;
}
if (!methodName.equals("lookup"))
throw new OperationNotSupportedException("Operation not supported: " + method);
if (args[0] instanceof String)
name = parser.parse((String) args[0]);
else
name = (Name) args[0];
securityDomain = name.get(0);
SecurityDomainContext securityDomainCtx = lookupSecurityDomain(securityManagement, securityManagerMap, securityDomain);
Object binding = securityDomainCtx.getAuthenticationManager();
// Look for requests against the security domain context
if (name.size() == 2) {
String request = name.get(1);
binding = securityDomainCtx.lookup(request);
}
return binding;
}
/**
* Creates a {@code SecurityDomainContext} if one cannot be found in JNDI for a given security domain
*
* @param securityManagement security management
* @param securityManagerMap security manager map
* @param securityDomain the name of the security domain
* @return an instance of {@code SecurityDomainContext}
* @throws Exception if an error occurs
*/
private SecurityDomainContext lookupSecurityDomain(final JNDIBasedSecurityManagement securityManagement,
final ConcurrentHashMap<String, SecurityDomainContext> securityManagerMap, final String securityDomain)
throws Exception {
SecurityDomainContext sdc = securityManagerMap.get(securityDomain);
if (sdc == null) {
sdc = securityManagement.createSecurityDomainContext(securityDomain, new DefaultAuthenticationCacheFactory());
securityManagerMap.put(securityDomain, sdc);
}
return sdc;
}
public Injector<ISecurityManagement> getSecurityManagementInjector() {
return securityManagementValue;
}
/**
* Enumeration for the bound security domains in JNDI
*
* @author <a href="mailto:mmoyses@redhat.com">Marcus Moyses</a>
*/
class DomainEnumeration implements NamingEnumeration<NameClassPair> {
Enumeration<String> domains;
Map<String, SecurityDomainContext> ctxMap;
DomainEnumeration(Enumeration<String> domains, Map<String, SecurityDomainContext> ctxMap) {
this.domains = domains;
this.ctxMap = ctxMap;
}
public void close() {
}
public boolean hasMoreElements() {
return domains.hasMoreElements();
}
public boolean hasMore() {
return domains.hasMoreElements();
}
public NameClassPair next() {
String name = (String) domains.nextElement();
Object value = ctxMap.get(name);
String className = value.getClass().getName();
NameClassPair pair = new NameClassPair(name, className);
return pair;
}
public NameClassPair nextElement() {
return next();
}
}
}