/*
* RHQ Management Platform
* Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.enterprise.server.auth;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerConfig;
import javax.ejb.TimerService;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.interceptor.ExcludeDefaultInterceptors;
import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.security.auth.login.LoginContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.crypto.CryptoUtil;
import org.jboss.security.auth.callback.UsernamePasswordHandler;
import org.rhq.core.domain.auth.Principal;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.authz.Permission;
import org.rhq.core.domain.authz.Role;
import org.rhq.core.domain.common.composite.SystemSetting;
import org.rhq.core.domain.common.composite.SystemSettings;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.criteria.RoleCriteria;
import org.rhq.core.domain.criteria.SavedSearchCriteria;
import org.rhq.core.domain.criteria.SubjectCriteria;
import org.rhq.core.domain.resource.group.ResourceGroup;
import org.rhq.core.domain.search.SavedSearch;
import org.rhq.core.domain.server.PersistenceUtility;
import org.rhq.core.domain.util.PageControl;
import org.rhq.core.domain.util.PageList;
import org.rhq.enterprise.server.RHQConstants;
import org.rhq.enterprise.server.alert.AlertNotificationManagerLocal;
import org.rhq.enterprise.server.authz.AuthorizationManagerLocal;
import org.rhq.enterprise.server.authz.PermissionException;
import org.rhq.enterprise.server.authz.RequiredPermission;
import org.rhq.enterprise.server.authz.RoleManagerLocal;
import org.rhq.enterprise.server.content.RepoManagerLocal;
import org.rhq.enterprise.server.core.CustomJaasDeploymentServiceMBean;
import org.rhq.enterprise.server.exception.LoginException;
import org.rhq.enterprise.server.resource.group.LdapGroupManagerLocal;
import org.rhq.enterprise.server.resource.group.ResourceGroupManagerLocal;
import org.rhq.enterprise.server.search.SavedSearchManagerLocal;
import org.rhq.enterprise.server.system.SystemManagerLocal;
import org.rhq.enterprise.server.util.CriteriaQueryGenerator;
import org.rhq.enterprise.server.util.CriteriaQueryRunner;
/**
* Provides functionality to access and manipulate subjects and principals, mainly for authentication purposes.
*
* @author John Mazzitelli
*/
@Stateless
public class SubjectManagerBean implements SubjectManagerLocal, SubjectManagerRemote {
private final Log log = LogFactory.getLog(SubjectManagerBean.class);
@PersistenceContext(unitName = RHQConstants.PERSISTENCE_UNIT_NAME)
private EntityManager entityManager;
@EJB
private AuthorizationManagerLocal authorizationManager;
@EJB
//@IgnoreDependency
private ResourceGroupManagerLocal resourceGroupManager;
@EJB
//@IgnoreDependency
private LdapGroupManagerLocal ldapManager;
@EJB
private SystemManagerLocal systemManager;
@EJB
//@IgnoreDependency
private AlertNotificationManagerLocal alertNotificationManager;
@EJB
//@IgnoreDependency
private RoleManagerLocal roleManager;
@EJB
//@IgnoreDependency
private RepoManagerLocal repoManager;
@EJB
private SavedSearchManagerLocal savedSearchManager;
@Resource
private TimerService timerService;
private SessionManager sessionManager = SessionManager.getInstance();
public void scheduleSessionPurgeJob() {
// each time the webapp is reloaded, we don't want to create duplicate jobs
Collection<Timer> timers = timerService.getTimers();
for (Timer existingTimer : timers) {
if (log.isDebugEnabled()) {
log.debug("Found timer - attempting to cancel: " + existingTimer.toString());
}
try {
existingTimer.cancel();
} catch (Exception e) {
log.warn("Failed in attempting to cancel timer: " + existingTimer.toString());
}
}
// timer that will trigger every 60 seconds
timerService.createIntervalTimer(60000L, 60000L, new TimerConfig(null, false));
}
@Timeout
// we don't need transactioning here
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void purgeTimedOutSessions() {
try {
sessionManager.purgeTimedOutSessions();
} catch (Throwable t) {
log.error("Failed to purge timed out sessions - will try again later. Cause: " + t);
}
}
/**
* @see org.rhq.enterprise.server.auth.SubjectManagerLocal#loadUserConfiguration(Integer)
*/
public Subject loadUserConfiguration(Integer subjectId) {
Subject subject = entityManager.find(Subject.class, subjectId);
Configuration config = subject.getUserConfiguration();
if ((config != null) && (config.getProperties() != null)) {
config.getProperties().size(); // force it to load
}
if (subject.getRoles() != null) {
subject.getRoles().size();
}
return subject;
}
/**
* @see org.rhq.enterprise.server.auth.SubjectManagerLocal#updateSubject(Subject, Subject)
*/
public Subject updateSubject(Subject whoami, Subject subjectToModify) {
// let a user change his own details
Set<Permission> globalPermissions = authorizationManager.getExplicitGlobalPermissions(whoami);
if (!whoami.equals(subjectToModify) && !globalPermissions.contains(Permission.MANAGE_SECURITY)) {
throw new PermissionException("You [" + whoami.getName() + "] do not have permission to update user ["
+ subjectToModify.getName() + "].");
}
if (authorizationManager.isSystemSuperuser(subjectToModify) && !subjectToModify.getFactive()) {
throw new PermissionException("You cannot disable system user [" + subjectToModify.getName()
+ "] - it must always be active.");
}
// Reset the roles, LDAP roles, and owned groups according to the current settings as this method will not
// update them. To update assigned roles, use the 3-param createSubject() or use RoleManagerLocal.
Subject currentSubject = entityManager.find(Subject.class, subjectToModify.getId());
subjectToModify.setRoles(currentSubject.getRoles());
subjectToModify.setLdapRoles(currentSubject.getLdapRoles());
subjectToModify.setOwnedGroups(currentSubject.getOwnedGroups());
return entityManager.merge(subjectToModify);
}
@RequiredPermission(Permission.MANAGE_SECURITY)
public Subject createSubject(Subject whoami, Subject subjectToCreate, String password) throws SubjectException,
EntityExistsException {
if (getSubjectByName(subjectToCreate.getName()) != null) {
throw new EntityExistsException("A user named [" + subjectToCreate.getName() + "] already exists.");
}
if (subjectToCreate.getFsystem()) {
throw new SubjectException("Cannot create new system users: " + subjectToCreate.getName());
}
entityManager.persist(subjectToCreate);
createPrincipal(whoami, subjectToCreate.getName(), password);
return subjectToCreate;
}
public Subject updateSubject(Subject whoami, Subject subjectToModify, String newPassword) {
// let a user change his own details
Set<Permission> globalPermissions = authorizationManager.getExplicitGlobalPermissions(whoami);
boolean isSecurityManager = globalPermissions.contains(Permission.MANAGE_SECURITY);
if (!whoami.equals(subjectToModify) && !isSecurityManager) {
throw new PermissionException("You [" + whoami.getName() + "] do not have permission to update user ["
+ subjectToModify.getName() + "].");
}
boolean subjectToModifyIsSystemSuperuser = authorizationManager.isSystemSuperuser(subjectToModify);
if (!subjectToModify.getFactive() && subjectToModifyIsSystemSuperuser) {
throw new PermissionException("You cannot disable the system user [" + subjectToModify.getName() + "].");
}
Subject attachedSubject = getSubjectById(subjectToModify.getId());
if (attachedSubject == null) {
throw new IllegalArgumentException("No user exists with id [" + subjectToModify.getId() + "].");
}
if (!attachedSubject.getName().equals(subjectToModify.getName())) {
throw new IllegalArgumentException("You cannot change a user's username.");
}
Set<Role> newRoles = subjectToModify.getRoles();
if (newRoles != null) {
Set<Role> currentRoles = new HashSet<Role>(roleManager.findRolesBySubject(subjectToModify.getId(),
PageControl.getUnlimitedInstance()));
boolean rolesChanged = !(newRoles.containsAll(currentRoles) && currentRoles.containsAll(newRoles));
if (rolesChanged) {
int[] newRoleIds = new int[newRoles.size()];
int i = 0;
for (Role role : newRoles) {
newRoleIds[i++] = role.getId();
}
roleManager.setAssignedSubjectRoles(whoami, subjectToModify.getId(), newRoleIds);
}
}
boolean ldapRolesModified = false;
Set<Role> newLdapRoles = subjectToModify.getLdapRoles();
if (newLdapRoles == null) {
newLdapRoles = Collections.emptySet();
}
if (newLdapRoles != null) {
RoleCriteria subjectLdapRolesCriteria = new RoleCriteria();
subjectLdapRolesCriteria.addFilterLdapSubjectId(subjectToModify.getId());
subjectLdapRolesCriteria.clearPaging();//disable paging as the code assumes all the results will be returned.
PageList<Role> currentLdapRoles = roleManager.findRolesByCriteria(whoami, subjectLdapRolesCriteria);
ldapRolesModified = !(currentLdapRoles.containsAll(newLdapRoles) && newLdapRoles
.containsAll(currentLdapRoles));
}
boolean isUserWithPrincipal = isUserWithPrincipal(subjectToModify.getName());
if (ldapRolesModified) {
if (!isSecurityManager) {
throw new PermissionException("You cannot change the LDAP roles assigned to ["
+ subjectToModify.getName() + "] - only a user with the MANAGE_SECURITY permission can do so.");
} else if (isUserWithPrincipal) {
throw new PermissionException("You cannot set LDAP roles on non-LDAP user ["
+ subjectToModify.getName() + "].");
}
// TODO: Update LDAP roles.
}
if (newPassword != null) {
if (!isUserWithPrincipal(subjectToModify.getName())) {
throw new IllegalArgumentException("You cannot set a password for an LDAP user.");
}
changePasswordInternal(subjectToModify.getName(), newPassword);
}
return entityManager.merge(subjectToModify);
}
/**
* @see org.rhq.enterprise.server.auth.SubjectManagerLocal#getOverlord()
*/
public Subject getOverlord() {
return sessionManager.getOverlord();
}
/**
* @see org.rhq.enterprise.server.auth.SubjectManagerRemote#getSubjectByName(String)
*/
public Subject getSubjectByName(String username) {
//TODO: this method needs to be modified to require a Subject and probably MANAGE_SECURITY
// permissions to defend against unrestricted access to subjects.
SubjectCriteria c = new SubjectCriteria();
c.addFilterName(username);
//to return the right user and to be deterministic the criteria should be strict.
c.setStrict(true);
PageList<Subject> result = findSubjectsByCriteria(getOverlord(), c);
return result.isEmpty() ? null : result.get(0);
}
/**
* @see org.rhq.enterprise.server.auth.SubjectManagerLocal#createSubject(Subject, Subject)
*/
@RequiredPermission(Permission.MANAGE_SECURITY)
public Subject createSubject(Subject whoami, Subject subject) throws SubjectException {
// Make sure there's not an existing subject with the same name.
if (getSubjectByName(subject.getName()) != null) {
throw new EntityExistsException("A user named [" + subject.getName() + "] already exists.");
}
if (subject.getFsystem()) {
throw new SubjectException("Cannot create new system subjects: " + subject.getName());
}
// we are ignoring roles - anything the caller gave us is thrown out
subject.setRoles(null);
subject.setLdapRoles(null);
subject.setOwnedGroups(null);
Configuration configuration = subject.getUserConfiguration();
if (configuration != null) {
configuration = entityManager.merge(configuration);
subject.setUserConfiguration(configuration);
}
entityManager.persist(subject);
return subject;
}
/**
* @see org.rhq.enterprise.server.auth.SubjectManagerLocal#getSubjectById(int)
*/
public Subject getSubjectById(int id) {
Subject subject = entityManager.find(Subject.class, id);
return subject;
}
public Subject login(String username, String password) throws LoginException {
return _login(username, password, true, true);
}
public Subject loginLocal(String username, String password) throws LoginException {
return _login(username, password, true, false);
}
private Subject _login(String username, String password, boolean checkRoles, boolean remote) throws LoginException {
if (password == null) {
throw new LoginException("No password was given");
}
// Use the JAAS modules to perform the auth.
_checkAuthentication(username, password);
// User is authenticated!
Subject subject = getSubjectByName(username);
if (subject != null) {//regular JDBC user
if (!subject.getFactive()) {
throw new LoginException("User account has been disabled.");
}
if (checkRoles) {
// fetch the roles
int rolesNumber = subject.getRoles().size();
if (rolesNumber == 0) {
if (systemManager.isLoginWithoutRolesEnabled()) {
if (log.isInfoEnabled()) {
log.info("Letting in user [" + subject.getName() + "] without any assigned roles.");
}
} else {
throw new LoginException("There are no preconfigured roles for user [" + subject.getName()
+ "]");
}
}
}
} else {
// There is no subject in the database yet.
// If LDAP authentication is enabled and we cannot find the subject,
// it means we must have authenticated via LDAP, not JDBC (otherwise,
// how else can there be a Principal without a Subject?). In the
// case of LDAP authenticated without having a Subject, it means the
// user is logging in for the first time and must go through a special
// GUI workflow to create a subject record. Let's create a dummy
// placeholder subject in here for now.
boolean isLdapAuthenticationEnabled = isLdapAuthenticationEnabled();
if (isLdapAuthenticationEnabled) {
if (remote) {
throw new IllegalStateException(
"Use the web UI for the first log in and fill all the necessary information.");
}
subject = new Subject();
subject.setId(0);
subject.setName(username);
subject.setFactive(true);
subject.setFsystem(false);
} else {
// LDAP is not enabled, so how in the world did we authenticate? This should never happen
throw new IllegalStateException(
"Somehow you authenticated with a principal that has no associated subject. Your account is invalid.");
}
}
// make sure to return the session-activated subject
subject = sessionManager.put(subject);
return subject;
}
public Subject checkAuthentication(String username, String password) {
try {
_checkAuthentication(username, password);
return getSubjectByName(username);
} catch (LoginException e) {
return null;
}
}
private void _checkAuthentication(String username, String password) throws LoginException {
try {
UsernamePasswordHandler handler = new UsernamePasswordHandler(username, password.toCharArray());
LoginContext loginContext;
loginContext = new LoginContext(CustomJaasDeploymentServiceMBean.RHQ_USER_SECURITY_DOMAIN, handler);
loginContext.login();
loginContext.getSubject().getPrincipals().iterator().next();
loginContext.logout();
} catch (javax.security.auth.login.LoginException e) {
throw new LoginException(e.getMessage());
}
}
/**This method is applied to Subject instances that may require LDAP auth/authz processing.
* Called from both SLSB and SubjectGWTServiceImpl and:
* -if Subject passed in has Principal(not LDAP account) then we immediately return Subject as no processing needed.
* -if Subject for LDAP account
*
* @param subject Authenticated subject.
* @return same or new Subject returned from LDAP processing.
* @throws LoginException
*/
public Subject processSubjectForLdap(Subject subject, String subjectPassword) throws LoginException {
if (subject != null) {//null check
//if user has principal then bail as LDAP processing not required
boolean userHasPrincipal = isUserWithPrincipal(subject.getName());
if (log.isDebugEnabled()) {
log.debug("Processing subject '" + subject.getName() + "' for LDAP check, userHasPrincipal:"
+ userHasPrincipal);
}
//if user has principal then return as non-ldap user
if (userHasPrincipal) {
return subject; //bail. No further checking required.
} else {//Start LDAP check.
boolean isLdapAuthenticationEnabled = isLdapAuthenticationEnabled();
if (isLdapAuthenticationEnabled) {//we can proceed with LDAP checking
//check that session is valid. RHQ auth has already occurred. Security check required to initiate following
//spinder BZ:682755: 3/10/11: can't use isValidSessionId() as it also compares subject.id which is changing during case insensitive
// and new registration. This worked before because HTTP get took longer to invalidate sessions.
Subject sessionSubject;
try {
sessionSubject = sessionManager.getSubject(subject.getSessionId());
} catch (SessionNotFoundException e) {
throw new LoginException("User session not valid. Login to proceed.");
} catch (SessionTimeoutException e) {
throw new LoginException("User session not valid. Login to proceed.");
}
if (!subject.getName().equals(sessionSubject.getName())) {
throw new LoginException("User session not valid. Login to proceed.");
}
//Subject.id == 0 then is registration or case insensitive check and subject update.
if (subject.getId() == 0) {
//i)case insensitive check or ii)ldap new user registration.
//BZ-586435: insert case insensitivity for usernames with ldap auth
// locate first matching subject and attach.
SubjectCriteria subjectCriteria = new SubjectCriteria();
subjectCriteria.setCaseSensitive(false);
subjectCriteria.setStrict(true);
subjectCriteria.fetchRoles(false);
subjectCriteria.fetchConfiguration(false);
subjectCriteria.addFilterName(subject.getName());
//BZ-798465: spinder 3/1/12 we now need to pass in overlord because of BZ-786159
// We've verified that this user has valid session, and is using ldap. Safe to elevate search here.
PageList<Subject> subjectsLocated = findSubjectsByCriteria(getOverlord(), subjectCriteria);
//if subject variants located then take the first one with a principal otherwise do nothing
//To defend against the case where they create an account with the same name but not
//case as an rhq sysadmin or higher perms, then make them relogin with same creds entered.
if ((!subjectsLocated.isEmpty())
&& (!subjectsLocated.get(0).getName().equals(subject.getName()))) {//then case insensitive username matches found. Try to use instead.
Subject ldapSubject = subjectsLocated.get(0);
String msg = "Located existing ldap account with different case for ["
+ ldapSubject.getName() + "]. "
+ "Attempting to authenticate with that account instead.";
if (log.isInfoEnabled()) {
log.info(msg);
}
logout(subject.getSessionId().intValue());
subject = _login(ldapSubject.getName(), subjectPassword, false, false);
Integer sessionId = subject.getSessionId();
if (log.isDebugEnabled()) {
log.debug("Logged in as [" + ldapSubject.getName() + "] with session id [" + sessionId
+ "]");
}
} else {//then this is a registration request. insert overlord registration and login
//we've verified that this user has valid session, requires registration and that ldap is configured.
Subject superuser = getOverlord();
// create the subject, but don't add a principal since LDAP will handle authentication
if (log.isDebugEnabled()) {
log.debug("registering new LDAP-authenticated subject [" + subject.getName() + "]");
}
createSubject(superuser, subject);
subject.setFactive(true);
// nuke the temporary session and establish a new
// one for this subject.. must be done before pulling the
// new subject in order to do it with his own credentials
logout(subject.getSessionId().intValue());
subject = _login(subject.getName(), subjectPassword, false, false);
prepopulateLdapFields(subject);
//insert empty configuration to start
Configuration newUserConfig = new Configuration();
//set flag on user so that we know registration is still required.
PropertySimple simple = new PropertySimple("isNewUser", true);
newUserConfig.put(simple);
subject.setUserConfiguration(newUserConfig);
}
}
//Subject.id guaranteed to be > 0 then iii)authorization updates for ldap groups necessary
//BZ-580127: only do group authz check if one or both of group filter fields is set
if (isLdapAuthorizationEnabled()) {
List<String> groupNames = new ArrayList<String>(ldapManager.findAvailableGroupsFor(subject
.getName()));
if (groupNames.isEmpty()) {
if (systemManager.isLoginWithoutRolesEnabled()) {
if (log.isInfoEnabled()) {
log.info("Letting in user [" + subject.getName() + "] without any assigned roles.");
}
} else {
// there are no LDAP groups so don't even bother with assignRolesToLdapSubject and fail fast.
throw new LoginException("Subject [" + subject.getName()
+ "] is authenticated for LDAP, but there are no preconfigured roles for them.");
}
} else {
if (log.isDebugEnabled()) {
log.debug("Updating LDAP authorization data for user [" + subject.getName()
+ "] with LDAP groups " + groupNames + "...");
}
ldapManager.assignRolesToLdapSubject(subject.getId(), groupNames);
if (!systemManager.isLoginWithoutRolesEnabled() && subject.getRoles().isEmpty()) {
throw new LoginException("Subject [" + subject.getName()
+ "] is authenticated for LDAP, but there are no preconfigured roles for them.");
}
}
}
} else {//ldap not configured. Somehow authenticated for LDAP without ldap being configured. Error. Bail
throw new LoginException("Subject[" + subject.getName()
+ "] is authenticated for LDAP, but LDAP is not configured.");
}
}
}
return subject;
}
/**
* @see org.rhq.enterprise.server.auth.SubjectManagerRemote#logout(Subject)
*/
public void logout(Subject subject) {
try {
// make sure the Subject is valid by pairing the name and sessionId
Subject s = getSubjectByNameAndSessionId(subject.getName(), subject.getSessionId());
sessionManager.invalidate(s.getSessionId());
} catch (Exception e) {
// ignore invalid logout request
}
}
/**
* @see org.rhq.enterprise.server.auth.SubjectManagerLocal#logout(int)
*/
public void logout(int sessionId) {
sessionManager.invalidate(sessionId);
}
/**
* @see org.rhq.enterprise.server.auth.SubjectManagerLocal#createPrincipal(Subject, String, String)
*/
@RequiredPermission(Permission.MANAGE_SECURITY)
public void createPrincipal(Subject whoami, String username, String password) throws SubjectException {
Principal principal = new Principal(username, CryptoUtil.createPasswordHash("MD5", "base64", null, null,
password));
createPrincipal(whoami, principal);
}
/**
* @see org.rhq.enterprise.server.auth.SubjectManagerLocal#createPrincipal(Subject, Principal)
*/
@RequiredPermission(Permission.MANAGE_SECURITY)
public void createPrincipal(Subject whoami, Principal principal) throws SubjectException {
try {
entityManager.persist(principal);
} catch (Exception e) {
throw new SubjectException("Failed to create " + principal + ".", e);
}
}
/**
* @see org.rhq.enterprise.server.auth.SubjectManagerLocal#changePassword(Subject, String, String)
*/
public void changePassword(Subject whoami, String username, String password) {
// a user can change his/her own password, as can a user with the appropriate permission
if (!whoami.getName().equals(username)
&& !authorizationManager.hasGlobalPermission(whoami, Permission.MANAGE_SECURITY)) {
throw new PermissionException("You do not have permission to change the password for user [" + username
+ "]");
}
changePasswordInternal(username, password);
return;
}
private void changePasswordInternal(String username, String password) {
Query query = entityManager.createNamedQuery(Principal.QUERY_FIND_BY_USERNAME);
query.setParameter("principal", username);
Principal principal = (Principal) query.getSingleResult();
String passwordHash = CryptoUtil.createPasswordHash("MD5", CryptoUtil.BASE64_ENCODING, null, null, password);
principal.setPassword(passwordHash);
}
/**
* @see org.rhq.enterprise.server.auth.SubjectManagerLocal#isUserWithPrincipal(String)
*/
public boolean isUserWithPrincipal(String username) {
try {
Query q = entityManager.createNamedQuery(Principal.QUERY_FIND_BY_USERNAME);
q.setParameter("principal", username);
Principal principal = (Principal) q.getSingleResult();
return (principal != null);
} catch (NoResultException e) {
return false;
}
}
/**
* @see org.rhq.enterprise.server.auth.SubjectManagerLocal#findAllUsersWithPrincipals()
*/
@SuppressWarnings("unchecked")
public Collection<String> findAllUsersWithPrincipals() {
Query q = entityManager.createNamedQuery(Principal.QUERY_FIND_ALL_USERS);
List<Principal> principals = q.getResultList();
List<String> users = new ArrayList<String>();
for (Principal p : principals) {
users.add(p.getPrincipal());
}
return users;
}
/**
* @see org.rhq.enterprise.server.auth.SubjectManagerLocal#loginUnauthenticated(String)
*/
public Subject loginUnauthenticated(String username) throws LoginException {
// we currently use a shared session for overlord. ensure we use the shared session if the overlord user is
// requested.
if ("admin".equals(username)) {
return getOverlord();
}
Subject subject = getSubjectByName(username);
if (subject == null) {
throw new LoginException("User account does not exist. [" + username + "]");
}
if (!subject.getFactive()) {
throw new LoginException("User account has been disabled. [" + username + "]");
}
// make sure we return the Subject returned from this call, which may differ from the one passed in
subject = sessionManager.put(subject, 1000L * 60 * 2); // 2mins only
return subject;
}
/**
* @see org.rhq.enterprise.server.auth.SubjectManagerLocal#deleteUsers(Subject, int[])
*/
@RequiredPermission(Permission.MANAGE_SECURITY)
public void deleteUsers(Subject subject, int[] subjectIds) {
for (Integer doomedSubjectId : subjectIds) {
Subject doomedSubject = getSubjectById(doomedSubjectId);
if (subject.getName().equals(doomedSubject.getName())) {
throw new PermissionException("You cannot remove yourself: " + doomedSubject.getName());
}
if (authorizationManager.isSystemSuperuser(doomedSubject)) {
throw new PermissionException("You cannot delete a system root user - they must always exist");
}
Set<Role> roles = doomedSubject.getRoles();
doomedSubject.setRoles(new HashSet<Role>()); // clean out roles
for (Role doomedRoleRelationship : roles) {
doomedRoleRelationship.removeSubject(doomedSubject);
}
// TODO: we need to reassign ownership of things this user used to own
// if this user was authenticated via JDBC and thus has a principal, remove it
if (isUserWithPrincipal(doomedSubject.getName())) {
deletePrincipal(doomedSubject);
}
// one more thing, delete any owned groups
List<ResourceGroup> ownedGroups = doomedSubject.getOwnedGroups();
if (null != ownedGroups && !ownedGroups.isEmpty()) {
int size = ownedGroups.size();
int[] ownedGroupIds = new int[size];
for (int i = 0; (i < size); ++i) {
ownedGroupIds[i] = ownedGroups.get(i).getId();
}
try {
resourceGroupManager.deleteResourceGroups(subject, ownedGroupIds);
} catch (Throwable t) {
if (log.isDebugEnabled()) {
log.error("Error deleting owned group " + Arrays.toString(ownedGroupIds), t);
} else {
log.error("Error deleting owned group " + Arrays.toString(ownedGroupIds) + ": "
+ t.getMessage());
}
}
}
// Delete searches saved by this user
SavedSearchCriteria savedSearchCriteria = new SavedSearchCriteria();
savedSearchCriteria.addFilterSubjectId(doomedSubjectId);
savedSearchCriteria.clearPaging();
PageList<SavedSearch> savedSearches = savedSearchManager.findSavedSearchesByCriteria(subject,
savedSearchCriteria);
for (SavedSearch savedSearch : savedSearches) {
savedSearchManager.deleteSavedSearch(subject, savedSearch.getId());
}
alertNotificationManager.cleanseAlertNotificationBySubject(doomedSubject.getId());
repoManager.removeOwnershipOfSubject(doomedSubject.getId());
entityManager.remove(doomedSubject);
}
return;
}
/**
* @see org.rhq.enterprise.server.auth.SubjectManagerRemote#deleteSubjects(org.rhq.core.domain.auth.Subject, int[])
* TODO: A wrapper method for deleteUsers, exposed in remote, both should be merged at some point.
*/
public void deleteSubjects(Subject sessionSubject, int[] subjectIds) {
deleteUsers(sessionSubject, subjectIds);
}
/**
* Delete a user's principal from the internal database.
*
*
* @param subject The user whose principal is to be deleted
*
* @throws PermissionException if the caller tried to delete a system superuser
*/
private void deletePrincipal(Subject subject) throws PermissionException {
if (authorizationManager.isSystemSuperuser(subject)) {
throw new PermissionException("You cannot delete the principal for the root user [" + subject.getName()
+ "]");
}
Query q = entityManager.createNamedQuery(Principal.QUERY_FIND_BY_USERNAME);
q.setParameter("principal", subject.getName());
Principal principal = (Principal) q.getSingleResult();
entityManager.remove(principal);
}
/**
* @see org.rhq.enterprise.server.auth.SubjectManagerLocal#getSubjectBySessionId(int)
*/
public Subject getSubjectBySessionId(int sessionId) throws Exception {
Subject subject = sessionManager.getSubject(sessionId);
return subject;
}
/**
* Adds more security in the remote api call by requiring matching username
*/
public Subject getSubjectByNameAndSessionId(String username, int sessionId) throws Exception {
Subject subject = getSubjectBySessionId(sessionId);
if (!username.equals(subject.getName())) {
throw new SessionNotFoundException();
}
return subject;
}
/**
* @see org.rhq.enterprise.server.auth.SubjectManagerLocal#isValidSessionId(int, String, int)
*/
// we exclude the default interceptors because the required permissions interceptor calls into this
@ExcludeDefaultInterceptors
public boolean isValidSessionId(int session, String username, int userid) {
try {
Subject sessionSubject = sessionManager.getSubject(session);
return username.equals(sessionSubject.getName()) && userid == sessionSubject.getId();
} catch (Exception e) {
return false;
}
}
@RequiredPermission(Permission.MANAGE_SECURITY)
@SuppressWarnings("unchecked")
public PageList<Subject> findAvailableSubjectsForRole(Subject whoami, Integer roleId, Integer[] pendingSubjectIds,
PageControl pc) {
pc.initDefaultOrderingField("s.name");
String queryName;
if ((pendingSubjectIds == null) || (pendingSubjectIds.length == 0)) {
queryName = Subject.QUERY_FIND_AVAILABLE_SUBJECTS_FOR_ROLE;
} else {
queryName = Subject.QUERY_FIND_AVAILABLE_SUBJECTS_FOR_ROLE_WITH_EXCLUDES;
}
Query countQuery = PersistenceUtility.createCountQuery(entityManager, queryName, "distinct s");
Query query = PersistenceUtility.createQueryWithOrderBy(entityManager, queryName, pc);
countQuery.setParameter("roleId", roleId);
query.setParameter("roleId", roleId);
if ((pendingSubjectIds != null) && (pendingSubjectIds.length > 0)) {
List<Integer> pendingIdsList = Arrays.asList(pendingSubjectIds);
countQuery.setParameter("excludes", pendingIdsList);
query.setParameter("excludes", pendingIdsList);
}
long count = (Long) countQuery.getSingleResult();
List<Subject> subjects = query.getResultList();
// eagerly load in the roles - can't use left-join due to PersistenceUtility usage; perhaps use EAGER
for (Subject subject : subjects) {
subject.getRoles().size();
}
return new PageList<Subject>(subjects, (int) count, pc);
}
public PageList<Subject> findSubjectsByCriteria(Subject subject, SubjectCriteria criteria) {
CriteriaQueryGenerator generator = new CriteriaQueryGenerator(subject, criteria);
CriteriaQueryRunner<Subject> queryRunner = new CriteriaQueryRunner<Subject>(criteria, generator, entityManager);
PageList<Subject> subjects = queryRunner.execute();
boolean canViewUsers = (authorizationManager.isSystemSuperuser(subject)
|| authorizationManager.hasGlobalPermission(subject, Permission.MANAGE_SECURITY) || authorizationManager
.hasGlobalPermission(subject, Permission.VIEW_USERS));
if (!canViewUsers) {
if (subjects.contains(subject)) {
Subject attachedSubject = subjects.get(subjects.indexOf(subject));
subjects.clear();
subjects.add(attachedSubject);
} else {
subjects.clear();
}
subjects.setTotalSize(subjects.size());
}
return subjects;
}
private boolean isLdapAuthenticationEnabled() {
SystemSettings systemSettings = systemManager.getUnmaskedSystemSettings(true);
String value = systemSettings.get(SystemSetting.LDAP_BASED_JAAS_PROVIDER);
return (value != null) ? Boolean.valueOf(value) : false;
}
private boolean isLdapAuthorizationEnabled() {
SystemSettings systemSettings = systemManager.getUnmaskedSystemSettings(true);
String groupFilter = systemSettings.get(SystemSetting.LDAP_GROUP_FILTER);
String groupMember = systemSettings.get(SystemSetting.LDAP_GROUP_MEMBER);
return ((groupFilter != null) && (groupFilter.trim().length() > 0))
|| ((groupMember != null) && (groupMember.trim().length() > 0));
}
private void prepopulateLdapFields(Subject subject) {
// Note: A schema defining the standard LDAP attributes can be found here:
// http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=servers/slapd/schema/core.ldif
Map<String, String> ldapUserAttributes = ldapManager.findLdapUserDetails(subject.getName());
String givenName = (ldapUserAttributes.get("givenName") != null) ? ldapUserAttributes.get("givenName")
: ldapUserAttributes.get("gn");
subject.setFirstName(givenName);
String surname = (ldapUserAttributes.get("sn") != null) ? ldapUserAttributes.get("sn") : ldapUserAttributes
.get("surname");
subject.setLastName(surname);
String telephoneNumber = ldapUserAttributes.get("telephoneNumber");
subject.setPhoneNumber(telephoneNumber);
String mail = (ldapUserAttributes.get("mail") != null) ? ldapUserAttributes.get("mail") : ldapUserAttributes
.get("rfc822Mailbox");
subject.setEmailAddress(mail);
String organizationalUnit = (ldapUserAttributes.get("ou") != null) ? ldapUserAttributes.get("ou")
: ldapUserAttributes.get("organizationalUnitName");
subject.setDepartment(organizationalUnit);
return;
}
}