/** * The contents of this file are subject to the OpenMRS Public License * Version 1.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://license.openmrs.org * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * Copyright (C) OpenMRS, LLC. All Rights Reserved. */ package org.openmrs.api.context; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.Vector; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openmrs.Location; import org.openmrs.Role; import org.openmrs.User; import org.openmrs.api.APIAuthenticationException; import org.openmrs.api.db.ContextDAO; import org.openmrs.util.LocaleUtility; import org.openmrs.util.OpenmrsConstants; import org.openmrs.util.RoleConstants; /** * Represents an OpenMRS <code>User Context</code> which stores the current user information. Only * one <code>User</code> may be authenticated within a UserContext at any given time. The * UserContext should not be accessed directly, but rather used through the <code>Context</code>. * This class should be kept light-weight. There is one instance of this class per user that is * logged into the system. * * @see org.openmrs.api.context.Context */ public class UserContext { /** * Logger - shared by entire class */ private static final Log log = LogFactory.getLog(UserContext.class); /** * User object containing details about the authenticated user */ private User user = null; /** * User's permission proxies */ private List<String> proxies = new Vector<String>(); /** * User's locale */ private Locale locale = null; /** * Cached Role given to all authenticated users */ private Role authenticatedRole = null; /** * Cache Role given to all users */ private Role anonymousRole = null; /** * User's defined location */ private Location location = null; /** * Default public constructor */ public UserContext() { } /** * Authenticate the user to this UserContext. * * @see org.openmrs.api.context.Context#authenticate(String,String) * @param username String login name * @param password String login password * @param contextDAO ContextDAO implementation to use for authentication * @return User that has been authenticated * @throws ContextAuthenticationException */ public User authenticate(String username, String password, ContextDAO contextDAO) throws ContextAuthenticationException { if (log.isDebugEnabled()) log.debug("Authenticating with username: " + username); this.user = contextDAO.authenticate(username, password); setUserLocation(); if (log.isDebugEnabled()) log.debug("Authenticated as: " + this.user); return this.user; } /** * Refresh the authenticated user object in this UserContext. This should be used when updating * information in the database about the current user and it needs to be reflecting in the * (cached) {@link #getAuthenticatedUser()} User object. * * @since 1.5 */ public void refreshAuthenticatedUser() { if (log.isDebugEnabled()) log.debug("Refreshing authenticated user"); if (user != null) { user = Context.getUserService().getUser(user.getUserId()); //update the stored location in the user's session setUserLocation(); } } /** * Change current authentication to become another user. (You can only do this if you're already * authenticated as a superuser.) * * @param systemId * @return The new user that this context has been set to. (null means no change was made) * @throws ContextAuthenticationException */ public User becomeUser(String systemId) throws ContextAuthenticationException { if (!Context.getAuthenticatedUser().isSuperUser()) throw new APIAuthenticationException("You must be a superuser to assume another user's identity"); if (log.isDebugEnabled()) log.debug("Turning the authenticated user into user with systemId: " + systemId); User userToBecome = Context.getUserService().getUserByUsername(systemId); if (userToBecome == null) throw new ContextAuthenticationException("User not found with systemId: " + systemId); // hydrate the user object if (userToBecome.getAllRoles() != null) userToBecome.getAllRoles().size(); if (userToBecome.getUserProperties() != null) userToBecome.getUserProperties().size(); if (userToBecome.getPrivileges() != null) userToBecome.getPrivileges().size(); this.user = userToBecome; //update the user's location setUserLocation(); if (log.isDebugEnabled()) log.debug("Becoming user: " + user); return userToBecome; } /** * @return "active" user who has been authenticated, otherwise <code>null</code> */ public User getAuthenticatedUser() { return user; } /** * @return true if user has been authenticated in this UserContext */ public boolean isAuthenticated() { return user != null; } /** * logs out the "active" (authenticated) user within this UserContext * * @see #authenticate */ public void logout() { log.debug("setting user to null on logout"); user = null; } /** * Gives the given privilege to all calls to hasPrivilege. This method was visualized as being * used as follows (try/finally is important): * * <pre> * try { * Context.addProxyPrivilege("AAA"); * Context.get*Service().methodRequiringAAAPrivilege(); * } * finally { * Context.removeProxyPrivilege("AAA"); * } * </pre> * * @param privilege to give to users */ public void addProxyPrivilege(String privilege) { if (log.isDebugEnabled()) log.debug("Adding proxy privilege: " + privilege); proxies.add(privilege); } /** * Will remove one instance of privilege from the privileges that are currently proxied * * @param privilege Privilege to remove in string form */ public void removeProxyPrivilege(String privilege) { if (log.isDebugEnabled()) log.debug("Removing proxy privilege: " + privilege); if (proxies.contains(privilege)) proxies.remove(privilege); } /** * @param locale new locale for this context */ public void setLocale(Locale locale) { this.locale = locale; } /** * @return current locale for this context */ public Locale getLocale() { if (locale == null) locale = LocaleUtility.getDefaultLocale(); return locale; } /** * Gets all the roles for the (un)authenticated user. Anonymous and Authenticated roles are * appended if necessary * * @return all expanded roles for a user * @throws Exception */ public Set<Role> getAllRoles() throws Exception { return getAllRoles(getAuthenticatedUser()); } /** * Gets all the roles for a user. Anonymous and Authenticated roles are appended if necessary * * @param user * @return all expanded roles for a user * @should not fail with null user * @should add anonymous role to all users * @should add authenticated role to all authenticated users * @should return same roles as user getAllRoles method */ public Set<Role> getAllRoles(User user) throws Exception { Set<Role> roles = new HashSet<Role>(); // add the Anonymous Role roles.add(getAnonymousRole()); // add the Authenticated role if (user != null && getAuthenticatedUser() != null && getAuthenticatedUser().equals(user)) { roles.addAll(user.getAllRoles()); roles.add(getAuthenticatedRole()); } return roles; } /** * Tests whether or not currently authenticated user has a particular privilege * * @param privilege * @return true if authenticated user has given privilege * @should authorize if authenticated user has specified privilege * @should authorize if authenticated role has specified privilege * @should authorize if proxied user has specified privilege * @should authorize if anonymous user has specified privilege * @should not authorize if authenticated user does not have specified privilege * @should not authorize if authenticated role does not have specified privilege * @should not authorize if proxied user does not have specified privilege * @should not authorize if anonymous user does not have specified privilege */ public boolean hasPrivilege(String privilege) { // if a user has logged in, check their privileges if (isAuthenticated()) { // check user's privileges if (getAuthenticatedUser().hasPrivilege(privilege)) return true; if (getAuthenticatedRole().hasPrivilege(privilege)) return true; } if (log.isDebugEnabled()) log.debug("Checking '" + privilege + "' against proxies: " + proxies); // check proxied privileges for (String s : proxies) if (s.equals(privilege)) return true; if (getAnonymousRole().hasPrivilege(privilege)) return true; // default return value return false; } /** * Convenience method to get the Role in the system designed to be given to all users * * @return Role * @should fail if database doesn't contain anonymous role */ private Role getAnonymousRole() { if (anonymousRole != null) return anonymousRole; anonymousRole = Context.getUserService().getRole(RoleConstants.ANONYMOUS); if (anonymousRole == null) { throw new RuntimeException("Database out of sync with code: " + RoleConstants.ANONYMOUS + " role does not exist"); } return anonymousRole; } /** * Convenience method to get the Role in the system designed to be given to all users that have * authenticated in some manner * * @return Role * @should fail if database doesn't contain authenticated role */ private Role getAuthenticatedRole() { if (authenticatedRole != null) return authenticatedRole; authenticatedRole = Context.getUserService().getRole(RoleConstants.AUTHENTICATED); if (authenticatedRole == null) { throw new RuntimeException("Database out of sync with code: " + RoleConstants.AUTHENTICATED + " role does not exist"); } return authenticatedRole; } /** * @return current location for this user context if any is set * @since 1.9 */ public Location getLocation() { return this.location; } /** * @param location the location to set to * @since 1.9 */ public void setLocation(Location location) { this.location = location; } /** * Convenience method that sets the default location of the currently authenticated user using * the value of the user's default location property */ private void setUserLocation() { if (this.user != null) { String locationId = this.user.getUserProperty(OpenmrsConstants.USER_PROPERTY_DEFAULT_LOCATION); if (StringUtils.isNotBlank(locationId)) { //only go ahead if it has actually changed OR if wasn't set before if (this.location == null || !this.location.getName().equalsIgnoreCase(locationId)) { try { this.location = Context.getLocationService().getLocation(Integer.valueOf(locationId)); } catch (NumberFormatException e) { //Drop the stored value since we have no match for the set id if (this.location != null) this.location = null; log.warn("The value of the default Location property of the user with id:" + this.user.getUserId() + " should be an integer", e); } } } else { if (this.location != null) this.location = null; } } } }