/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.security.authorization.modules.web; import java.io.IOException; import java.security.CodeSource; import java.security.Permission; import java.security.Policy; import java.security.Principal; import java.security.ProtectionDomain; import java.util.Map; import java.util.Set; import javax.security.auth.Subject; import javax.security.jacc.WebResourcePermission; import javax.security.jacc.WebRoleRefPermission; import javax.security.jacc.WebUserDataPermission; import javax.servlet.http.HttpServletRequest; import org.jboss.security.PicketBoxLogger; import org.jboss.security.PicketBoxMessages; import org.jboss.security.authorization.AuthorizationContext; import org.jboss.security.authorization.PolicyRegistration; import org.jboss.security.authorization.Resource; import org.jboss.security.authorization.ResourceKeys; import org.jboss.security.authorization.modules.AbstractJACCModuleDelegate; import org.jboss.security.authorization.modules.AuthorizationModuleDelegate; import org.jboss.security.authorization.resources.WebResource; import org.jboss.security.identity.Role; import org.jboss.security.identity.RoleGroup; //$Id: WebJACCPolicyModuleDelegate.java 62923 2007-05-09 03:08:14Z anil.saldhana@jboss.com $ /** * JACC based authorization module helper that deals with the web layer * authorization decisions * @author <a href="mailto:Anil.Saldhana@jboss.org">Anil Saldhana</a> * @since July 7, 2006 * @version $Revision: 62923 $ */ public class WebJACCPolicyModuleDelegate extends AbstractJACCModuleDelegate { private Policy policy = Policy.getPolicy(); private HttpServletRequest request = null; private CodeSource webCS = null; private String canonicalRequestURI = null; /** * @see AuthorizationModuleDelegate#authorize(org.jboss.security.authorization.Resource, javax.security.auth.Subject, org.jboss.security.identity.RoleGroup) */ @SuppressWarnings("unchecked") public int authorize(Resource resource, Subject callerSubject, RoleGroup role) { if(resource instanceof WebResource == false) throw PicketBoxMessages.MESSAGES.invalidType(WebResource.class.getName()); WebResource webResource = (WebResource) resource; //Get the context map Map<String,Object> map = resource.getMap(); if(map == null) throw PicketBoxMessages.MESSAGES.invalidNullProperty("resourceMap"); //Get the Request Object request = (HttpServletRequest) webResource.getServletRequest(); webCS = webResource.getCodeSource(); this.canonicalRequestURI = webResource.getCanonicalRequestURI(); String roleName = (String)map.get(ResourceKeys.ROLENAME); Principal principal = (Principal)map.get(ResourceKeys.HASROLE_PRINCIPAL); Set<Principal> roles = (Set<Principal>)map.get(ResourceKeys.PRINCIPAL_ROLES); String servletName = webResource.getServletName(); Boolean resourceCheck = checkBooleanValue((Boolean)map.get(ResourceKeys.RESOURCE_PERM_CHECK)); Boolean userDataCheck = checkBooleanValue((Boolean)map.get(ResourceKeys.USERDATA_PERM_CHECK)); Boolean roleRefCheck = checkBooleanValue((Boolean)map.get(ResourceKeys.ROLEREF_PERM_CHECK)); validatePermissionChecks(resourceCheck,userDataCheck,roleRefCheck); boolean decision = false; try { if(resourceCheck) decision = this.hasResourcePermission(callerSubject, role); else if(userDataCheck) decision = this.hasUserDataPermission(); else if(roleRefCheck) decision = this.hasRole(principal, roleName, roles, servletName); else PicketBoxLogger.LOGGER.debugInvalidWebJaccCheck(); } catch(IOException ioe) { PicketBoxLogger.LOGGER.debugIgnoredException(ioe); } return decision ? AuthorizationContext.PERMIT : AuthorizationContext.DENY; } /** * @see AuthorizationModuleDelegate#setPolicyRegistrationManager(PolicyRegistration) */ public void setPolicyRegistrationManager(PolicyRegistration authzM) { this.policyRegistration = authzM; } //**************************************************************************** // PRIVATE METHODS //**************************************************************************** /** See if the given JACC permission is implied using the caller as * obtained from either the * PolicyContext.getContext(javax.security.auth.Subject.container) or * the info associated with the requestPrincipal. * * @param perm - the JACC permission to check * @param requestPrincpal - the http request getPrincipal * @param caller the authenticated subject obtained by establishSubjectContext * @return true if the permission is allowed, false otherwise */ private boolean checkPolicy(Permission perm, Principal requestPrincpal, Subject caller, Role role) { // Get the caller principals, its null if there is no caller Principal[] principals = getPrincipals(caller,role); return checkPolicy(perm, principals); } /** See if the given permission is implied by the Policy. This calls * Policy.implies(pd, perm) with the ProtectionDomain built from the * active CodeSource set by the JaccContextValve, and the given * principals. * * @param perm - the JACC permission to evaluate * @param principals - the possibly null set of principals for the caller * @return true if the permission is allowed, false otherwise */ private boolean checkPolicy(Permission perm, Principal[] principals) { ProtectionDomain pd = new ProtectionDomain(webCS, null, null, principals); return policy.implies(pd, perm); } /** * Ensure that the bool is a valid value * @param bool * @return bool or Boolean.FALSE (when bool is null) */ private Boolean checkBooleanValue(Boolean bool) { if(bool == null) return Boolean.FALSE; return bool; } /** * Perform hasResourcePermission Check * @param caller * @param role * @return * @throws IOException */ private boolean hasResourcePermission(Subject caller, Role role) throws IOException { Principal requestPrincipal = request.getUserPrincipal(); WebResourcePermission perm = new WebResourcePermission(this.canonicalRequestURI, request.getMethod()); boolean allowed = checkPolicy(perm, requestPrincipal, caller, role ); if (PicketBoxLogger.LOGGER.isTraceEnabled()) { PicketBoxLogger.LOGGER.traceHasResourcePermission(perm.toString(), allowed); } return allowed; } /** * Perform hasRole check * @param principal * @param roleName * @param roles * @return */ private boolean hasRole(Principal principal, String roleName, Set<Principal> roles, String servletName) { if(servletName == null) throw PicketBoxMessages.MESSAGES.invalidNullArgument("servletName"); WebRoleRefPermission perm = new WebRoleRefPermission(servletName, roleName); Principal[] principals = {principal}; if( roles != null ) { principals = new Principal[roles.size()]; roles.toArray(principals); } boolean allowed = checkPolicy(perm, principals); PicketBoxLogger.LOGGER.traceHasRolePermission(perm.toString(), allowed); return allowed; } /** * Perform hasUserDataPermission check for the realm. * If this module returns false, the base class (Realm) will * make the decision as to whether a redirection to the ssl * port needs to be done * @return * @throws IOException */ private boolean hasUserDataPermission() throws IOException { WebUserDataPermission perm = new WebUserDataPermission(this.canonicalRequestURI, request.getMethod()); boolean ok = false; try { Principal[] principals = null; ok = checkPolicy(perm, principals); } catch(Exception e) { PicketBoxLogger.LOGGER.debugIgnoredException(e); } if (PicketBoxLogger.LOGGER.isTraceEnabled()) { PicketBoxLogger.LOGGER.traceHasUserDataPermission(perm.toString(), ok); } return ok; } /** * Validate that the access check is made only for one of the * following * @param resourceCheck * @param userDataCheck * @param roleRefCheck */ private void validatePermissionChecks(Boolean resourceCheck, Boolean userDataCheck, Boolean roleRefCheck) { if((resourceCheck == Boolean.TRUE && userDataCheck == Boolean.TRUE && roleRefCheck == Boolean.TRUE ) || (resourceCheck == Boolean.TRUE && userDataCheck == Boolean.TRUE) || (userDataCheck == Boolean.TRUE && roleRefCheck == Boolean.TRUE)) throw PicketBoxMessages.MESSAGES.invalidPermissionChecks(); } }