/*
* Copyright 2012 Red Hat, Inc. and/or its affiliates.
*
* 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.jbpm.services.task.identity;
import java.security.Principal;
import java.security.acl.Group;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.Set;
import javax.security.auth.Subject;
import javax.security.jacc.PolicyContext;
import org.jbpm.services.task.identity.adapter.UserGroupAdapter;
import org.kie.api.task.UserGroupCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* JAAS based implementation of user group callback dedicated when using LocalTaskService
* in container such as JBoss AS. It relies on JACC api to collect information on currently
* logged on user when querying for tasks.
* <br/>
* JACC exposes following named context in PolicyContext:<br/>
* <code>javax.security.auth.Subject.container</code>
* <br/>
* This returns <code>Subject</code> instance for currently authenticated user and next principals
* will be examined to find instances of <code>Group</code> and with given rolePrincipleName (by default Roles).
* <br/>
*
* By default it works with JBoss Application Servers as it uses specific principal name to find the groups.
*
*
*/
public class JAASUserGroupCallbackImpl extends AbstractUserGroupInfo implements UserGroupCallback {
private static final Logger logger = LoggerFactory.getLogger(JAASUserGroupCallbackImpl.class);
protected static final String DEFAULT_PROPERTIES_NAME = "classpath:/jbpm.usergroup.callback.properties";
private ServiceLoader<UserGroupAdapter> ugAdapterServiceLoader = ServiceLoader.load(UserGroupAdapter.class);
private static final ThreadLocal<UserGroupAdapter> externalUserGroupAdapterLocal = new ThreadLocal<UserGroupAdapter>();
public static void addExternalUserGroupAdapter(UserGroupAdapter externalUserGroupAdapter) {
if( externalUserGroupAdapterLocal.get() != null ) {
UserGroupAdapter adapter = externalUserGroupAdapterLocal.get();
throw new IllegalStateException("The external UserGroupAdapter has already been set! "
+ "(" + adapter.getClass().getName() + ")");
}
externalUserGroupAdapterLocal.set(externalUserGroupAdapter);
}
public static void clearExternalUserGroupAdapter() {
externalUserGroupAdapterLocal.set(null);
}
private String rolePrincipleName = null;
//no no-arg constructor to prevent cdi from auto deploy
public JAASUserGroupCallbackImpl(boolean activate) {
// use default JBoss AS role principle name
this("Roles");
String propertiesLocation = System.getProperty("jbpm.usergroup.callback.properties");
Properties config = readProperties(propertiesLocation, DEFAULT_PROPERTIES_NAME);
if (config != null) {
this.rolePrincipleName = config.getProperty("jaas.role.principle.name", "Roles");
}
}
public JAASUserGroupCallbackImpl(String rolesPrincipleName) {
this.rolePrincipleName = rolesPrincipleName;
}
public String getRolePrincipleName() {
return rolePrincipleName;
}
public void setRolePrincipleName(String rolePrincipleName) {
this.rolePrincipleName = rolePrincipleName;
}
public boolean existsUser(String userId) {
// allows everything as there is no way to ask JAAS/JACC for users in the domain
return true;
}
public boolean existsGroup(String groupId) {
// allows everything as there is no way to ask JAAS/JACC for groups in the domain
return true;
}
public List<String> getGroupsForUser(String userId) {
List<String> roles = new ArrayList<String>();
try {
Subject subject = getSubjectFromContainer();
if (subject != null) {
Set<Principal> principals = subject.getPrincipals();
if (principals != null) {
logger.debug("Adding roles from JAAS subject");
roles = new ArrayList<String>();
for (Principal principal : principals) {
if (principal instanceof Group && rolePrincipleName.equalsIgnoreCase(principal.getName())) {
Enumeration<? extends Principal> groups = ((Group) principal).members();
while (groups.hasMoreElements()) {
Principal groupPrincipal = (Principal) groups.nextElement();
roles.add(groupPrincipal.getName());
}
break;
}
}
}
} else {
// use adapters
for (UserGroupAdapter adapter : ugAdapterServiceLoader) {
logger.debug("Adding roles from UserGroupAdapter service ({})", adapter.getClass().getSimpleName());
List<String> userRoles = adapter.getGroupsForUser(userId);
if (userRoles != null) {
roles.addAll(userRoles);
}
}
}
UserGroupAdapter adapter = externalUserGroupAdapterLocal.get();
if( adapter != null ) {
logger.debug("Adding roles from external UserGroupAdapter ({})", adapter.getClass().getSimpleName());
List<String> userRoles = adapter.getGroupsForUser(userId);
if (userRoles != null) {
roles.addAll(userRoles);
}
}
} catch (Exception e) {
logger.error("Error when getting user roles for userid:" + userId, e);
}
return roles;
}
protected Subject getSubjectFromContainer() {
try {
return (Subject) PolicyContext.getContext( "javax.security.auth.Subject.container" );
} catch (Exception e) {
return null;
}
}
}