/**
* 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.web.taglib;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.tagext.TagSupport;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.User;
import org.openmrs.api.APIException;
import org.openmrs.api.context.Context;
import org.openmrs.api.context.UserContext;
import org.openmrs.util.OpenmrsConstants;
import org.openmrs.web.WebConstants;
import org.openmrs.web.user.UserProperties;
import org.springframework.util.StringUtils;
/**
* Controller for the <openmrs:require> taglib used on jsp pages. This taglib restricts the page
* view to currently logged in (or anonymous) users that have the given privileges. <br/>
* <br/>
* Example use case:
*
* <pre>
* <openmrs:require privilege="Manage Concept Classes" otherwise="/login.htm" redirect="/admin/concepts/conceptClass.form" />
* </pre>
*
* This will demand that the user have the "Manage Concept Classes" privilege. If they don't, kick
* the user back to the "/login.htm" page. Then, after they log in on that page, send the user to
* "/admin/concepts/conceptClass.form".
*/
public class RequireTag extends TagSupport {
public static final long serialVersionUID = 122998L;
private final Log log = LogFactory.getLog(getClass());
private String privilege;
private String allPrivileges;
private String anyPrivilege;
private String otherwise;
private String redirect;
private boolean errorOccurred;
/**
* This is where all the magic happens. The privileges are checked and the user is redirected if
* need be. <br/>
* <br/>
* Returns SKIP_PAGE if the user doesn't have the privilege and SKIP_BODY if it does.
*
* @see javax.servlet.jsp.tagext.TagSupport#doStartTag()
* @should allow user with the privilege
* @should allow user to have any privilege
* @should allow user with all privileges
* @should reject user without the privilege
* @should reject user without any of the privileges
* @should reject user without all of the privileges
*/
public int doStartTag() {
errorOccurred = false;
HttpServletResponse httpResponse = (HttpServletResponse) pageContext.getResponse();
HttpSession httpSession = pageContext.getSession();
HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
String request_ip_addr = request.getLocalAddr();
String session_ip_addr = (String) httpSession.getAttribute(WebConstants.OPENMRS_CLIENT_IP_HTTPSESSION_ATTR);
UserContext userContext = Context.getUserContext();
if (userContext == null && privilege != null) {
log.error("userContext is null. Did this pass through a filter?");
//httpSession.removeAttribute(WebConstants.OPENMRS_CONTEXT_HTTPSESSION_ATTR);
//TODO find correct error to throw
throw new APIException("The context is currently null. Please try reloading the site.");
}
// Parse comma-separated list of privileges in allPrivileges and anyPrivileges attributes
String[] allPrivilegesArray = StringUtils.commaDelimitedListToStringArray(allPrivileges);
String[] anyPrivilegeArray = StringUtils.commaDelimitedListToStringArray(anyPrivilege);
boolean hasPrivilege = hasPrivileges(userContext, privilege, allPrivilegesArray, anyPrivilegeArray);
if (!hasPrivilege) {
errorOccurred = true;
if (userContext.isAuthenticated()) {
httpSession.setAttribute(WebConstants.OPENMRS_ERROR_ATTR, "require.unauthorized");
log.warn("The user: '" + Context.getAuthenticatedUser() + "' has attempted to access: " + redirect
+ " which requires privilege: " + privilege + " or one of: " + allPrivileges + " or any of "
+ anyPrivilege);
} else
httpSession.setAttribute(WebConstants.OPENMRS_MSG_ATTR, "require.login");
} else if (hasPrivilege && userContext.isAuthenticated()) {
// redirect users to password change form
User user = userContext.getAuthenticatedUser();
log.debug("Login redirect: " + redirect);
if (new UserProperties(user.getUserProperties()).isSupposedToChangePassword()
&& !redirect.contains("options.form")) {
httpSession.setAttribute(WebConstants.OPENMRS_ERROR_ATTR, "User.password.change");
errorOccurred = true;
redirect = request.getContextPath() + "/options.form#Change Login Info";
otherwise = redirect;
try {
httpResponse.sendRedirect(redirect);
return SKIP_PAGE;
}
catch (IOException e) {
// oops, cannot redirect
log.error("Unable to redirect for password change: " + redirect, e);
throw new APIException(e);
}
}
}
if (differentIpAddresses(session_ip_addr, request_ip_addr)) {
errorOccurred = true;
// stops warning message in IE when refreshing repeatedly
if ("0.0.0.0".equals(request_ip_addr) == false) {
log.warn("Invalid ip addr: expected " + session_ip_addr + ", but found: " + request_ip_addr);
httpSession.setAttribute(WebConstants.OPENMRS_ERROR_ATTR, "require.ip_addr");
}
}
log.debug("session ip addr: " + session_ip_addr);
if (errorOccurred) {
String url = "";
if (redirect != null && !redirect.equals(""))
url = request.getContextPath() + redirect;
else
url = request.getRequestURI();
if (request.getQueryString() != null)
url = url + "?" + request.getQueryString();
httpSession.setAttribute(WebConstants.OPENMRS_LOGIN_REDIRECT_HTTPSESSION_ATTR, url);
try {
httpResponse.sendRedirect(request.getContextPath() + otherwise);
return SKIP_PAGE;
}
catch (IOException e) {
// oops, cannot redirect
throw new APIException(e);
}
}
return SKIP_BODY;
}
/**
* Determines if the given ip addresses are the same.
*
* @param session_ip_addr
* @param request_ip_addr
* @return true/false whether these IPs are different
*/
private boolean differentIpAddresses(String sessionIpAddr, String requestIpAddr) {
if (sessionIpAddr == null || requestIpAddr == null)
return false;
// IE7 and firefox store "localhost" IP addresses differently.
// To accomodate switching from firefox browing to IE taskpane,
// we assume these addresses to be equivalent
List<String> equivalentAddresses = new ArrayList<String>();
equivalentAddresses.add("127.0.0.1");
equivalentAddresses.add("0.0.0.0");
// if the addresses are equal, all is well
if (sessionIpAddr.equals(requestIpAddr))
return false;
// if they aren't equal, but we consider them to be, also all is well
else if (equivalentAddresses.contains(sessionIpAddr) && equivalentAddresses.contains(requestIpAddr)) {
return false;
}
// the IP addresses were not equal, (don't continue with this user)
return true;
}
/**
* Returns true if all of the following three are true:
* <ul>
* <li>privilege is not defined OR user has privilege</li>
* <li>allPrivileges is not defined OR user has every privilege in allPrivileges</li>
* <li>anyPrivilege is not defined OR user has at least one of the privileges in anyPrivileges</li>
* </ul>
*
* @param userContext current user context
* @param privilege a single required privilege
* @param allPrivilegesArray an array of required privileges
* @param anyPrivilegeArray an array of privileges, at least one of which is required
* @return true if privilege conditions are met
*/
private boolean hasPrivileges(UserContext userContext, String privilege, String[] allPrivilegesArray,
String[] anyPrivilegeArray) {
if (privilege != null && !userContext.hasPrivilege(privilege.trim()))
return false;
if (allPrivilegesArray.length > 0 && !hasAllPrivileges(userContext, allPrivilegesArray))
return false;
if (anyPrivilegeArray.length > 0 && !hasAnyPrivilege(userContext, anyPrivilegeArray))
return false;
return true;
}
/**
* Returns true if user has all privileges
*
* @param userContext current user context
* @param allPrivilegesArray list of privileges
* @return true if user has all of the privileges
*/
private boolean hasAllPrivileges(UserContext userContext, String[] allPrivilegesArray) {
for (String p : allPrivilegesArray)
if (!userContext.hasPrivilege(p.trim()))
return false;
return true;
}
/**
* Returns true if user has any of the privileges
*
* @param userContext current user context
* @param anyPriviegeArray list of privileges
* @return true if user has at least one of the privileges
*/
private boolean hasAnyPrivilege(UserContext userContext, String[] anyPriviegeArray) {
for (String p : anyPriviegeArray)
if (userContext.hasPrivilege(p.trim()))
return true;
return false;
}
/**
* @see javax.servlet.jsp.tagext.TagSupport#doEndTag()
*/
public int doEndTag() {
if (errorOccurred)
return SKIP_PAGE;
else
return EVAL_PAGE;
}
public String getPrivilege() {
return privilege;
}
public void setPrivilege(String privilege) {
this.privilege = privilege;
}
public String getAllPrivileges() {
return allPrivileges;
}
public void setAllPrivileges(String allPrivileges) {
this.allPrivileges = allPrivileges;
}
public String getAnyPrivilege() {
return anyPrivilege;
}
public void setAnyPrivilege(String anyPrivilege) {
this.anyPrivilege = anyPrivilege;
}
public String getOtherwise() {
return otherwise;
}
public void setOtherwise(String otherwise) {
this.otherwise = otherwise;
}
public String getRedirect() {
return redirect;
}
public void setRedirect(String redirect) {
this.redirect = redirect;
}
}