/*
* 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.example.security;
import java.security.Principal;
import java.security.acl.Group;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.jcr.Credentials;
import javax.jcr.SimpleCredentials;
import javax.security.auth.Subject;
import org.jboss.security.AuthenticationManager;
import org.jboss.security.AuthorizationManager;
import org.jboss.security.authorization.AuthorizationException;
import org.modeshape.jcr.ExecutionContext;
import org.modeshape.jcr.security.AuthenticationProvider;
import org.modeshape.jcr.security.AuthorizationProvider;
import org.modeshape.jcr.security.SecurityContext;
import org.modeshape.jcr.value.Path;
import org.picketbox.config.PicketBoxConfiguration;
import org.picketbox.core.authorization.resources.POJOResource;
import org.picketbox.factories.SecurityFactory;
/**
* A custom security provider which uses some default PicketBox modules.
*
* This is both an {@link org.modeshape.jcr.security.AuthenticationProvider} and an {@link org.modeshape.jcr.security.AuthorizationProvider}
* performing both custom authentication and authorization. If more repository data would be required by this class, it could
* implement {@link org.modeshape.jcr.security.AdvancedAuthorizationProvider} instead of {@link org.modeshape.jcr.security.AuthorizationProvider}
*
* @author Horia Chiorean (hchiorea@redhat.com)
*/
public final class PicketBoxSecurityProvider implements AuthorizationProvider, AuthenticationProvider, SecurityContext {
/**
* The name of the security domain, defined in the security configuration file (see security.conf.xml)
*/
private static final String SECURITY_DOMAIN_NAME = "modeshape-jcr";
private final AuthenticationManager authenticationManager;
private final AuthorizationManager authorizationManager;
private Subject authenticatedSubject;
private Set<String> roles;
public PicketBoxSecurityProvider() {
PicketBoxConfiguration idtrustConfig = new PicketBoxConfiguration();
idtrustConfig.load(PicketBoxSecurityProvider.class.getClassLoader().getResourceAsStream("security.conf.xml"));
this.authenticationManager = SecurityFactory.getAuthenticationManager(SECURITY_DOMAIN_NAME);
if (this.authenticationManager == null) {
throw new IllegalStateException("Authentication Manager is null");
}
this.authorizationManager = SecurityFactory.getAuthorizationManager(SECURITY_DOMAIN_NAME);
if (this.authorizationManager == null) {
throw new IllegalStateException("Authorization Manager is null");
}
// no one is authenticated yet
this.authenticatedSubject = null;
this.roles = null;
}
private PicketBoxSecurityProvider( AuthenticationManager authenticationManager,
AuthorizationManager authorizationManager,
Subject authenticatedSubject ) {
assert authenticatedSubject != null;
this.authenticationManager = authenticationManager;
this.authorizationManager = authorizationManager;
this.authenticatedSubject = authenticatedSubject;
this.roles = roles();
}
@Override
public ExecutionContext authenticate( Credentials credentials, String repositoryName, String workspaceName,
ExecutionContext repositoryContext, Map<String, Object> sessionAttributes ) {
if (!(credentials instanceof SimpleCredentials)) {
return null;
}
final SimpleCredentials simpleCredentials = (SimpleCredentials) credentials;
final Principal principal = new Principal() {
@Override
public String getName() {
return simpleCredentials.getUserID();
}
};
String pass = String.valueOf(simpleCredentials.getPassword());
Subject subject = new Subject();
if (this.authenticationManager.isValid(principal, pass, subject)) {
//we've been successfully authenticated, so we need to set ourselves as a security context in order to be
//able to perform custom authorization
return repositoryContext.with(newProviderWithSubject(subject));
} else {
// were unable to perform authentication
return null;
}
}
@Override
public boolean hasPermission( ExecutionContext context, String repositoryName, String repositorySourceName,
String workspaceName, Path absPath, String... actions ) {
if (absPath == null) {
// we'll let all authenticated users to have rights on the workspaces
return true;
}
try {
// we don't care about the resource, this is just a simple example which checks the roles of the subject
// against the configured roles in the configuration file
authorizationManager.authorize(new POJOResource(absPath), this.authenticatedSubject);
return true;
} catch (AuthorizationException e) {
return false;
}
}
@Override
public boolean isAnonymous() {
return false;
}
@Override
public String getUserName() {
if (authenticatedSubject == null) {
return null;
}
Set<Principal> principals = authenticatedSubject.getPrincipals();
return principals.isEmpty() ? null : principals.iterator().next().getName();
}
@Override
public boolean hasRole( String roleName ) {
// for the purpose of the example we don't care about ModeShape specific roles, so we'll always return true here
return true;
}
@Override
public void logout() {
this.authenticatedSubject = null;
this.roles = null;
}
private Set<String> roles() {
if (authenticatedSubject == null) {
return Collections.emptySet();
}
if (roles != null) {
return roles;
}
Group rolesGroup = null;
for (Group group : authenticatedSubject.getPrincipals(Group.class)) {
if (group.getName().equalsIgnoreCase("roles")) {
rolesGroup = group;
break;
}
}
if (rolesGroup == null) {
return null;
}
Set<String> result = new HashSet<>();
Enumeration<? extends Principal> members = rolesGroup.members();
while (members.hasMoreElements()) {
result.add(members.nextElement().getName());
}
return result;
}
private PicketBoxSecurityProvider newProviderWithSubject( Subject subject ) {
if (subject == null) {
throw new IllegalArgumentException("Null subject");
}
return new PicketBoxSecurityProvider(this.authenticationManager, this.authorizationManager, subject);
}
}