/*
* ModeShape (http://www.modeshape.org)
*
* Licensed 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.modeshape.jboss.security;
import java.security.AccessController;
import java.util.Map;
import javax.jcr.Credentials;
import javax.jcr.SimpleCredentials;
import javax.security.auth.Subject;
import org.jboss.security.AuthenticationManager;
import org.modeshape.jboss.service.RepositoryService;
import org.modeshape.jcr.ExecutionContext;
import org.modeshape.jcr.api.JaasCredentials;
import org.modeshape.jcr.security.EnvironmentAuthenticationProvider;
import org.modeshape.jcr.security.JaasSecurityContext;
import org.modeshape.jcr.security.JaccSubjectResolver;
import org.modeshape.jcr.security.SecurityContext;
import org.modeshape.jcr.security.SimplePrincipal;
/**
* {@link org.modeshape.jcr.security.EnvironmentAuthenticationProvider} used to interact with the security subsystem from a
* JBoss Application Server.
*
* @author Horia Chiorean (hchiorea@redhat.com)
* @see <a href="http://issues.jboss.org/browse/MODE-2411">MODE-2411</a>
*/
public class JBossDomainAuthenticationProvider extends EnvironmentAuthenticationProvider {
private static final org.jboss.logging.Logger LOGGER = org.jboss.logging.Logger.getLogger(
JBossDomainAuthenticationProvider.class.getPackage().getName());
private AuthenticationManager authenticationManager;
private JaccSubjectResolver jaccSubjectResolver;
@Override
public void initialize() {
String domainName = securityDomain();
this.authenticationManager = environment().getSecurityManagementServiceInjector().getValue().getAuthenticationManager(domainName);
assert this.authenticationManager != null;
// any JBoss container should be JACC compliant, so the necessary jars should be present in the classpath
this.jaccSubjectResolver = new JaccSubjectResolver();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Initialized JBoss authentication provider using the container's authentication manager....");
}
}
@Override
public ExecutionContext authenticate( Credentials credentials, String repositoryName, String workspaceName,
ExecutionContext repositoryContext, Map<String, Object> sessionAttributes ) {
if (credentials == null) {
return getPreauthenticatedSubject(repositoryContext);
}
if (credentials instanceof SimpleCredentials) {
return validateSimpleCredentials((SimpleCredentials)credentials, repositoryContext);
}
if (credentials instanceof JaasCredentials) {
return getSubjectFromJaas((JaasCredentials)credentials, repositoryContext);
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debugv("Unknown {0} implementation: {1}. Please user either {2} or {3}", Credentials.class.getName(),
credentials.getClass().getName(), SimpleCredentials.class.getName(), JaasCredentials.class.getName());
}
return null;
}
private ExecutionContext getSubjectFromJaas( JaasCredentials credentials, ExecutionContext repositoryContext ) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Looking for an active subject in the JaasCredentials instance...");
}
Subject subject = credentials.getLoginContext().getSubject();
if (subject == null) {
LOGGER.warn("Cannot authenticate because the JassCredentials instance has a login context with a null subject...");
return null;
}
return repositoryContext.with(new JBossSecurityContext(new JaasSecurityContext(subject)));
}
private ExecutionContext validateSimpleCredentials( SimpleCredentials credentials, ExecutionContext repositoryContext) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debugv("Authenticating {0} in the {1} security domain using the JBoss Server security manager", credentials.getUserID(),
securityDomain());
}
Subject subject = new Subject();
if (authenticationManager.isValid(SimplePrincipal.newInstance(credentials.getUserID()), credentials.getPassword(),
subject)) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Authentication successful....");
}
return repositoryContext.with(new JBossSecurityContext(new JaasSecurityContext(subject)));
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debugv("Credentials for {0} are not valid for the {1} security domain", credentials.getUserID(), securityDomain());
}
return null;
}
}
private ExecutionContext getPreauthenticatedSubject( ExecutionContext repositoryContext ) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Received null credentials, attempting to search for an active subject on the calling thread via JACC");
}
// There are no credentials, so see if there is an authenticated Subject ...
Subject subject = Subject.getSubject(AccessController.getContext());
if (subject != null) {
// There is, so use this subject ...
return repositoryContext.with(new JBossSecurityContext(new JaasSecurityContext(subject)));
}
subject = jaccSubjectResolver.resolveSubject();
// there are no credentials and we failed to find a pre-authenticated subject, so return null.
return subject != null ? repositoryContext.with(new JBossSecurityContext(new JaasSecurityContext(subject))) : null;
}
@Override
protected RepositoryService environment() {
return (RepositoryService)super.environment();
}
private final class JBossSecurityContext implements SecurityContext {
private final JaasSecurityContext jaasSecurityContext;
private JBossSecurityContext( JaasSecurityContext jaasSecurityContext ) {
assert jaasSecurityContext != null;
this.jaasSecurityContext = jaasSecurityContext;
}
@Override
public boolean isAnonymous() {
return jaasSecurityContext.isAnonymous();
}
@Override
public String getUserName() {
return jaasSecurityContext.getUserName();
}
@Override
public boolean hasRole( String roleName ) {
return jaasSecurityContext.hasRole(roleName);
}
@Override
public void logout() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Logging out security context....");
}
authenticationManager.logout(SimplePrincipal.newInstance(jaasSecurityContext.getUserName()), null);
jaasSecurityContext.logout();
}
}
}