/**
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
*
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
package org.openmrs.aop;
import java.lang.reflect.Method;
import java.util.Collection;
import org.apache.commons.lang.StringUtils;
import org.openmrs.User;
import org.openmrs.annotation.AuthorizedAnnotationAttributes;
import org.openmrs.api.APIAuthenticationException;
import org.openmrs.api.context.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.MethodBeforeAdvice;
/**
* This class provides the authorization AOP advice performed before every service layer method
* call.
*/
public class AuthorizationAdvice implements MethodBeforeAdvice {
/**
* Logger for this class and subclasses
*/
protected final Logger log = LoggerFactory.getLogger(AuthorizationAdvice.class);
/**
* Allows us to check whether a user is authorized to access a particular method.
*
* @param method
* @param args
* @param target
* @throws Throwable
* @should notify listeners about checked privileges
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
if (log.isDebugEnabled()) {
log.debug("Calling authorization advice before " + method.getName());
}
if (log.isDebugEnabled()) {
User user = Context.getAuthenticatedUser();
log.debug("User " + user);
if (user != null) {
log.debug("has roles " + user.getAllRoles());
}
}
AuthorizedAnnotationAttributes attributes = new AuthorizedAnnotationAttributes();
Collection<String> privileges = attributes.getAttributes(method);
boolean requireAll = attributes.getRequireAll(method);
// Only execute if the "secure" method has authorization attributes
// Iterate through required privileges and return only if the user has
// one of them
if (!privileges.isEmpty()) {
for (String privilege : privileges) {
// skip null privileges
if (privilege == null || privilege.isEmpty()) {
return;
}
if (log.isDebugEnabled()) {
log.debug("User has privilege " + privilege + "? " + Context.hasPrivilege(privilege));
}
if (Context.hasPrivilege(privilege)) {
if (!requireAll) {
// if not all required, the first one that they have
// causes them to "pass"
return;
}
} else {
if (requireAll) {
// if all are required, the first miss causes them
// to "fail"
throwUnauthorized(Context.getAuthenticatedUser(), method, privilege);
}
}
}
if (!requireAll) {
// If there's no match, then we know there are privileges and
// that the user didn't have any of them. The user is not
// authorized to access the method
throwUnauthorized(Context.getAuthenticatedUser(), method, privileges);
}
} else if (attributes.hasAuthorizedAnnotation(method) && !Context.isAuthenticated()) {
throwUnauthorized(Context.getAuthenticatedUser(), method);
}
}
/**
* Throws an APIAuthorization exception stating why the user failed
*
* @param user authenticated user
* @param method acting method
* @param attrs Collection of String privilege names that the user must have
*/
private void throwUnauthorized(User user, Method method, Collection<String> attrs) {
if (log.isDebugEnabled()) {
log.debug("User " + user + " is not authorized to access " + method.getName());
}
throw new APIAuthenticationException(Context.getMessageSourceService().getMessage("error.privilegesRequired",
new Object[] { StringUtils.join(attrs, ",") }, null));
}
/**
* Throws an APIAuthorization exception stating why the user failed
*
* @param user authenticated user
* @param method acting method
* @param attrs privilege names that the user must have
*/
private void throwUnauthorized(User user, Method method, String attr) {
if (log.isDebugEnabled()) {
log.debug("User " + user + " is not authorized to access " + method.getName());
}
throw new APIAuthenticationException(Context.getMessageSourceService().getMessage("error.privilegesRequired",
new Object[] { attr }, null));
}
/**
* Throws an APIAuthorization exception stating why the user failed
*
* @param user authenticated user
* @param method acting method
*/
private void throwUnauthorized(User user, Method method) {
if (log.isDebugEnabled()) {
log.debug("User " + user + " is not authorized to access " + method.getName());
}
throw new APIAuthenticationException(Context.getMessageSourceService().getMessage("error.aunthenticationRequired"));
}
}