/* SecurityUtil.java {{IS_NOTE Purpose: Description: History: Sep 17, 2009 10:48:23 AM, Created by henrichen }}IS_NOTE Copyright (C) 2009 Potix Corporation. All Rights Reserved. Partial Copyright 2004, 2005, 2006 Acegi Technology Pty Limited {{IS_RIGHT This program is distributed under GPL Version 3.0 in the hope that it will be useful, but WITHOUT ANY WARRANTY. This implementation is based on codes of the Spring Security taglibs written by Ben Alex, Thomas Champagne, Francois Beausoleil, et al. As what Apache License required, we have to include following license statement. ----- Licensed under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. }}IS_RIGHT */ package leasetool.ui.zk.security.spring.util; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.security.acls.domain.DefaultPermissionFactory; import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl; import org.springframework.security.acls.domain.PermissionFactory; import org.springframework.security.acls.domain.SidRetrievalStrategyImpl; import org.springframework.security.acls.model.Acl; import org.springframework.security.acls.model.AclService; import org.springframework.security.acls.model.NotFoundException; import org.springframework.security.acls.model.ObjectIdentity; import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy; import org.springframework.security.acls.model.Permission; import org.springframework.security.acls.model.Sid; import org.springframework.security.acls.model.SidRetrievalStrategy; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.GrantedAuthorityImpl; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.zkoss.spring.SpringUtil; import org.zkoss.zk.ui.UiException; /** * Utility class for ZK spring security. * * Modified by Gesuino Napoli April 16,2011 * Now when the method isAccessible is called no NullPointerException is throws * Added the initialization of the PermissionFactory in the initializeIfRequired() method * * @author henrichen */ public class SecurityUtilACL { private static AclService _aclService; private static ApplicationContext _applicationContext; private static ObjectIdentityRetrievalStrategy _objectIdentityRetrievalStrategy; private static SidRetrievalStrategy _sidRetrievalStrategy; private static PermissionFactory permissionFactory; /** * Return true if the current Authentication has one of the specified * permissions to the presented domain object instance. * * @param hasPermission A comma separated list of integers, each * representing a required bit mask permission from a subclass of * {@link org.springframework.security.acl.basic.AbstractBasicAclEntry}. * @param domainObject The actual domain object instance for which permissions * are being evaluated. * @return true if current Authentication has one of the specified permission * to the presented domain object instance. */ public static boolean isAccessible(String hasPermission, Object domainObject) { if (hasPermission == null || "".equals(hasPermission)) { return false; } initializeIfRequired(); final List requiredPermissions = parsePermissions(hasPermission); Object resolvedDomainObject = domainObject; if (resolvedDomainObject == null) { // Of course they have access to a null object! return true; } if (SecurityContextHolder.getContext().getAuthentication() == null) { //SecurityContextHolder did not return a non-null Authentication object, so skipping tag body return false; } List sids = _sidRetrievalStrategy.getSids(SecurityContextHolder.getContext().getAuthentication()); ObjectIdentity oid = _objectIdentityRetrievalStrategy.getObjectIdentity(resolvedDomainObject); // Obtain aclEntrys applying to the current Authentication object try { final Acl acl = _aclService.readAclById(oid, sids); return acl.isGranted(requiredPermissions, sids, false); } catch (NotFoundException nfe) { return false; } } /** * Return true if the authenticated principal is granted NONE of the roles * specified in authorities. * * @param authorities A comma separated list of roles which the user must * have been granted NONE. * @return true if the authenticated principal is granted authorities * of NONE the specified roles. */ @SuppressWarnings("unchecked") public static boolean isNoneGranted(String authorities) { if (null == authorities || "".equals(authorities)) { return false; } final Collection granted = getPrincipalAuthorities(); final Set grantedCopy = retainAll(granted, parseAuthoritiesString(authorities)); return grantedCopy.isEmpty(); } /** * Return true if the authenticated principal is granted ALL of the roles * specified in authorities. * * @param authorities A comma separated list of roles which the user must have * been granted ALL. * @return true true if the authenticated principal is granted authorities * of ALL the specified roles. */ public static boolean isAllGranted(String authorities) { if (null == authorities || "".equals(authorities)) { return false; } final Collection granted = getPrincipalAuthorities(); boolean isAllGranted = granted.containsAll(parseAuthoritiesString(authorities)); return isAllGranted; } /** * Return true if the authenticated principal is granted ANY of the roles * specified in authorities. * * @param authorities A comma separated list of roles which the user must have * been granted ANY. * @return true true if the authenticated principal is granted authorities * of ALL the specified roles. */ @SuppressWarnings("unchecked") public static boolean isAnyGranted(String authorities) { if (null == authorities || "".equals(authorities)) { return false; } final Collection granted = getPrincipalAuthorities(); final Set grantedCopy = retainAll(granted, parseAuthoritiesString(authorities)); return !grantedCopy.isEmpty(); } /** * Return the current Authentication object. */ public static Authentication getAuthentication() { if ((SecurityContextHolder.getContext() != null) && (SecurityContextHolder.getContext() instanceof SecurityContext)) { final Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null && auth.getPrincipal() != null) { return auth; } } return null; } /** * Return the evaluated result per the given property of the current * Authentication object. * * @param property Property of the Authentication object which would be * evaluated. Supports nested properties. For example if the principal * object is an instance of UserDetails, the property "principal.username" * will return the username. Alternatively, using "name" will call getName * method on the Authentication object directly. * * @return the evaluated result of the current Authentication object per the * given property. */ public static Object getAuthentication(String property) { // determine the value by... if (property != null) { if ((SecurityContextHolder.getContext() == null) || !(SecurityContextHolder.getContext() instanceof SecurityContext) || (SecurityContextHolder.getContext().getAuthentication() == null)) { return null; } Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth.getPrincipal() == null) { return null; } try { BeanWrapperImpl wrapper = new BeanWrapperImpl(auth); return wrapper.getPropertyValue(property); } catch (BeansException e) { throw new UiException(e); } } return null; } private static List parsePermissions(String permissionsString) { final Set permissions = new HashSet(); final StringTokenizer tokenizer; tokenizer = new StringTokenizer(permissionsString, ",", false); while (tokenizer.hasMoreTokens()) { String permission = tokenizer.nextToken(); try { permissions.add(permissionFactory.buildFromMask(Integer.valueOf(permission))); } catch (NumberFormatException nfe) { // Not an integer mask. Try using a name permissions.add(permissionFactory.buildFromName(permission)); } } return new ArrayList(permissions); } @SuppressWarnings("unchecked") private static void initializeIfRequired() { if (_applicationContext != null) { return; } _applicationContext = SpringUtil.getApplicationContext(); Map map = new HashMap(); ApplicationContext context = _applicationContext; while (context != null) { map.putAll(context.getBeansOfType(AclService.class)); context = context.getParent(); } if (map.size() != 1) { throw new UiException( "Found incorrect number of AclService instances in application context - you must have only have one!"); } _aclService = (AclService) map.values().iterator().next(); map = _applicationContext.getBeansOfType(SidRetrievalStrategy.class); if (map.size() == 0) { _sidRetrievalStrategy = new SidRetrievalStrategyImpl(); } else if (map.size() == 1) { _sidRetrievalStrategy = (SidRetrievalStrategy) map.values().iterator().next(); } else { throw new UiException("Found incorrect number of SidRetrievalStrategy instances in application " + "context - you must have only have one!"); } map = _applicationContext.getBeansOfType(ObjectIdentityRetrievalStrategy.class); if (map.size() == 0) { _objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl(); } else if (map.size() == 1) { _objectIdentityRetrievalStrategy = (ObjectIdentityRetrievalStrategy) map.values().iterator().next(); } else { throw new UiException("Found incorrect number of ObjectIdentityRetrievalStrategy instances in " + "application context - you must have only have one!"); } map = _applicationContext.getBeansOfType(PermissionFactory.class); if (map.size() == 0) { permissionFactory = new DefaultPermissionFactory(); } else if (map.size() == 1) { permissionFactory = (PermissionFactory) map.values().iterator().next(); } else { throw new UiException("Found incorrect number of PermissionFactory instances in application " + "context - you must have only have one!"); } } @SuppressWarnings("unchecked") private static Set authoritiesToRoles(Collection c) { final Set target = new HashSet(); for (Iterator iterator = c.iterator(); iterator.hasNext();) { GrantedAuthority authority = (GrantedAuthority) iterator.next(); if (null == authority.getAuthority()) { throw new IllegalArgumentException( "Cannot process GrantedAuthority objects which return null from getAuthority() - attempting to process " + authority.toString()); } target.add(authority.getAuthority()); } return target; } private static Collection getPrincipalAuthorities() { Authentication currentUser = SecurityContextHolder.getContext().getAuthentication(); if (null == currentUser) { return Collections.emptyList(); } if ((null == currentUser.getAuthorities()) || (currentUser.getAuthorities().size() < 1)) { return Collections.emptyList(); } Collection granted = currentUser.getAuthorities(); return granted; } private static Collection parseAuthoritiesString(String authorizationsString) { final Collection requiredAuthorities = new ArrayList(); final String[] roles = authorizationsString.split(","); for (int i = 0; i < roles.length; i++) { String role = roles[i].trim(); requiredAuthorities.add(new GrantedAuthorityImpl(role)); } return requiredAuthorities; } /** * Find the common authorities between the current authentication's {@link GrantedAuthority} and the ones * that have been specified in the tag's ifAny, ifNot or ifAllGranted attributes.

We need to manually * iterate over both collections, because the granted authorities might not implement {@link * Object#equals(Object)} and {@link Object#hashCode()} in the same way as {@link GrantedAuthorityImpl}, thereby * invalidating {@link Collection#retainAll(java.util.Collection)} results.

*

* CAVEAT: This method will not work if the granted authorities * returns a null string as the return value of {@link * org.springframework.security.GrantedAuthority#getAuthority()}. *

*

Reported by rawdave, on Fri Feb 04, 2005 2:11 pm in the Spring Security forum.

* * @param granted The authorities granted by the authentication. May be any implementation of {@link * GrantedAuthority} that does not return null from {@link * org.springframework.security.GrantedAuthority#getAuthority()}. * @param required A {@link Set} of {@link GrantedAuthorityImpl}s that have been built using ifAny, ifAll or * ifNotGranted. * * @return A set containing only the common authorities between granted and required. */ @SuppressWarnings("unchecked") private static Set retainAll(final Collection granted, final Collection required) { Set grantedRoles = authoritiesToRoles(granted); Set requiredRoles = authoritiesToRoles(required); grantedRoles.retainAll(requiredRoles); return rolesToAuthorities(grantedRoles, granted); } @SuppressWarnings("unchecked") private static Set rolesToAuthorities(Set grantedRoles, Collection granted) { Set target = new HashSet(); for (Iterator iterator = grantedRoles.iterator(); iterator.hasNext();) { String role = (String) iterator.next(); for (Iterator grantedIterator = granted.iterator(); grantedIterator.hasNext();) { GrantedAuthority authority = (GrantedAuthority) grantedIterator.next(); if (authority.getAuthority().equals(role)) { target.add(authority); break; } } } return target; } }