package com.hwlcn.security.realm; import com.hwlcn.cache.Cache; import com.hwlcn.cache.CacheManager; import com.hwlcn.security.authc.credential.CredentialsMatcher; import com.hwlcn.security.authz.*; import com.hwlcn.security.authz.permission.*; import com.hwlcn.security.subject.PrincipalCollection; import com.hwlcn.security.util.CollectionUtils; import com.hwlcn.security.util.Initializable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; public abstract class AuthorizingRealm extends AuthenticatingRealm implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware { private static final Logger log = LoggerFactory.getLogger(AuthorizingRealm.class); private static final String DEFAULT_AUTHORIZATION_CACHE_SUFFIX = ".authorizationCache"; private static final AtomicInteger INSTANCE_COUNT = new AtomicInteger(); private boolean authorizationCachingEnabled; private Cache<Object, AuthorizationInfo> authorizationCache; private String authorizationCacheName; private PermissionResolver permissionResolver; private RolePermissionResolver permissionRoleResolver; public AuthorizingRealm() { this(null, null); } public AuthorizingRealm(CacheManager cacheManager) { this(cacheManager, null); } public AuthorizingRealm(CredentialsMatcher matcher) { this(null, matcher); } public AuthorizingRealm(CacheManager cacheManager, CredentialsMatcher matcher) { super(); if (cacheManager != null) setCacheManager(cacheManager); if (matcher != null) setCredentialsMatcher(matcher); this.authorizationCachingEnabled = true; this.permissionResolver = new WildcardPermissionResolver(); int instanceNumber = INSTANCE_COUNT.getAndIncrement(); this.authorizationCacheName = getClass().getName() + DEFAULT_AUTHORIZATION_CACHE_SUFFIX; if (instanceNumber > 0) { this.authorizationCacheName = this.authorizationCacheName + "." + instanceNumber; } } public void setName(String name) { super.setName(name); String authzCacheName = this.authorizationCacheName; if (authzCacheName != null && authzCacheName.startsWith(getClass().getName())) { this.authorizationCacheName = name + DEFAULT_AUTHORIZATION_CACHE_SUFFIX; } } public void setAuthorizationCache(Cache<Object, AuthorizationInfo> authorizationCache) { this.authorizationCache = authorizationCache; } public Cache<Object, AuthorizationInfo> getAuthorizationCache() { return this.authorizationCache; } public String getAuthorizationCacheName() { return authorizationCacheName; } @SuppressWarnings({"UnusedDeclaration"}) public void setAuthorizationCacheName(String authorizationCacheName) { this.authorizationCacheName = authorizationCacheName; } public boolean isAuthorizationCachingEnabled() { return isCachingEnabled() && authorizationCachingEnabled; } @SuppressWarnings({"UnusedDeclaration"}) public void setAuthorizationCachingEnabled(boolean authenticationCachingEnabled) { this.authorizationCachingEnabled = authenticationCachingEnabled; if (authenticationCachingEnabled) { setCachingEnabled(true); } } public PermissionResolver getPermissionResolver() { return permissionResolver; } public void setPermissionResolver(PermissionResolver permissionResolver) { if (permissionResolver == null) throw new IllegalArgumentException("Null PermissionResolver is not allowed"); this.permissionResolver = permissionResolver; } public RolePermissionResolver getRolePermissionResolver() { return permissionRoleResolver; } public void setRolePermissionResolver(RolePermissionResolver permissionRoleResolver) { this.permissionRoleResolver = permissionRoleResolver; } protected void onInit() { super.onInit(); getAvailableAuthorizationCache(); } protected void afterCacheManagerSet() { super.afterCacheManagerSet(); getAvailableAuthorizationCache(); } private Cache<Object, AuthorizationInfo> getAuthorizationCacheLazy() { if (this.authorizationCache == null) { if (log.isDebugEnabled()) { log.debug("No authorizationCache instance set. Checking for a cacheManager..."); } CacheManager cacheManager = getCacheManager(); if (cacheManager != null) { String cacheName = getAuthorizationCacheName(); if (log.isDebugEnabled()) { log.debug("CacheManager [" + cacheManager + "] has been configured. Building " + "authorization cache named [" + cacheName + "]"); } this.authorizationCache = cacheManager.getCache(cacheName); } else { if (log.isInfoEnabled()) { log.info("No cache or cacheManager properties have been set. Authorization cache cannot " + "be obtained."); } } } return this.authorizationCache; } private Cache<Object, AuthorizationInfo> getAvailableAuthorizationCache() { Cache<Object, AuthorizationInfo> cache = getAuthorizationCache(); if (cache == null && isAuthorizationCachingEnabled()) { cache = getAuthorizationCacheLazy(); } return cache; } protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) { if (principals == null) { return null; } AuthorizationInfo info = null; if (log.isTraceEnabled()) { log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]"); } Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache(); if (cache != null) { if (log.isTraceEnabled()) { log.trace("Attempting to retrieve the AuthorizationInfo from cache."); } Object key = getAuthorizationCacheKey(principals); info = cache.get(key); if (log.isTraceEnabled()) { if (info == null) { log.trace("No AuthorizationInfo found in cache for principals [" + principals + "]"); } else { log.trace("AuthorizationInfo found in cache for principals [" + principals + "]"); } } } if (info == null) { info = doGetAuthorizationInfo(principals); if (info != null && cache != null) { if (log.isTraceEnabled()) { log.trace("Caching authorization info for principals: [" + principals + "]."); } Object key = getAuthorizationCacheKey(principals); cache.put(key, info); } } return info; } protected Object getAuthorizationCacheKey(PrincipalCollection principals) { return principals; } protected void clearCachedAuthorizationInfo(PrincipalCollection principals) { if (principals == null) { return; } Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache(); if (cache != null) { Object key = getAuthorizationCacheKey(principals); cache.remove(key); } } protected abstract AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals); private Collection<Permission> getPermissions(AuthorizationInfo info) { Set<Permission> permissions = new HashSet<Permission>(); if (info != null) { Collection<Permission> perms = info.getObjectPermissions(); if (!CollectionUtils.isEmpty(perms)) { permissions.addAll(perms); } perms = resolvePermissions(info.getStringPermissions()); if (!CollectionUtils.isEmpty(perms)) { permissions.addAll(perms); } perms = resolveRolePermissions(info.getRoles()); if (!CollectionUtils.isEmpty(perms)) { permissions.addAll(perms); } } if (permissions.isEmpty()) { return Collections.emptySet(); } else { return Collections.unmodifiableSet(permissions); } } private Collection<Permission> resolvePermissions(Collection<String> stringPerms) { Collection<Permission> perms = Collections.emptySet(); PermissionResolver resolver = getPermissionResolver(); if (resolver != null && !CollectionUtils.isEmpty(stringPerms)) { perms = new LinkedHashSet<Permission>(stringPerms.size()); for (String strPermission : stringPerms) { Permission permission = getPermissionResolver().resolvePermission(strPermission); perms.add(permission); } } return perms; } private Collection<Permission> resolveRolePermissions(Collection<String> roleNames) { Collection<Permission> perms = Collections.emptySet(); RolePermissionResolver resolver = getRolePermissionResolver(); if (resolver != null && !CollectionUtils.isEmpty(roleNames)) { perms = new LinkedHashSet<Permission>(roleNames.size()); for (String roleName : roleNames) { Collection<Permission> resolved = resolver.resolvePermissionsInRole(roleName); if (!CollectionUtils.isEmpty(resolved)) { perms.addAll(resolved); } } } return perms; } public boolean isPermitted(PrincipalCollection principals, String permission) { Permission p = getPermissionResolver().resolvePermission(permission); return isPermitted(principals, p); } public boolean isPermitted(PrincipalCollection principals, Permission permission) { AuthorizationInfo info = getAuthorizationInfo(principals); return isPermitted(permission, info); } private boolean isPermitted(Permission permission, AuthorizationInfo info) { Collection<Permission> perms = getPermissions(info); if (perms != null && !perms.isEmpty()) { for (Permission perm : perms) { if (perm.implies(permission)) { return true; } } } return false; } public boolean[] isPermitted(PrincipalCollection subjectIdentifier, String... permissions) { List<Permission> perms = new ArrayList<Permission>(permissions.length); for (String permString : permissions) { perms.add(getPermissionResolver().resolvePermission(permString)); } return isPermitted(subjectIdentifier, perms); } public boolean[] isPermitted(PrincipalCollection principals, List<Permission> permissions) { AuthorizationInfo info = getAuthorizationInfo(principals); return isPermitted(permissions, info); } protected boolean[] isPermitted(List<Permission> permissions, AuthorizationInfo info) { boolean[] result; if (permissions != null && !permissions.isEmpty()) { int size = permissions.size(); result = new boolean[size]; int i = 0; for (Permission p : permissions) { result[i++] = isPermitted(p, info); } } else { result = new boolean[0]; } return result; } public boolean isPermittedAll(PrincipalCollection subjectIdentifier, String... permissions) { if (permissions != null && permissions.length > 0) { Collection<Permission> perms = new ArrayList<Permission>(permissions.length); for (String permString : permissions) { perms.add(getPermissionResolver().resolvePermission(permString)); } return isPermittedAll(subjectIdentifier, perms); } return false; } public boolean isPermittedAll(PrincipalCollection principal, Collection<Permission> permissions) { AuthorizationInfo info = getAuthorizationInfo(principal); return info != null && isPermittedAll(permissions, info); } protected boolean isPermittedAll(Collection<Permission> permissions, AuthorizationInfo info) { if (permissions != null && !permissions.isEmpty()) { for (Permission p : permissions) { if (!isPermitted(p, info)) { return false; } } } return true; } public void checkPermission(PrincipalCollection subjectIdentifier, String permission) throws AuthorizationException { Permission p = getPermissionResolver().resolvePermission(permission); checkPermission(subjectIdentifier, p); } public void checkPermission(PrincipalCollection principal, Permission permission) throws AuthorizationException { AuthorizationInfo info = getAuthorizationInfo(principal); checkPermission(permission, info); } protected void checkPermission(Permission permission, AuthorizationInfo info) { if (!isPermitted(permission, info)) { String msg = "User is not permitted [" + permission + "]"; throw new UnauthorizedException(msg); } } public void checkPermissions(PrincipalCollection subjectIdentifier, String... permissions) throws AuthorizationException { if (permissions != null) { for (String permString : permissions) { checkPermission(subjectIdentifier, permString); } } } public void checkPermissions(PrincipalCollection principal, Collection<Permission> permissions) throws AuthorizationException { AuthorizationInfo info = getAuthorizationInfo(principal); checkPermissions(permissions, info); } protected void checkPermissions(Collection<Permission> permissions, AuthorizationInfo info) { if (permissions != null && !permissions.isEmpty()) { for (Permission p : permissions) { checkPermission(p, info); } } } public boolean hasRole(PrincipalCollection principal, String roleIdentifier) { AuthorizationInfo info = getAuthorizationInfo(principal); return hasRole(roleIdentifier, info); } protected boolean hasRole(String roleIdentifier, AuthorizationInfo info) { return info != null && info.getRoles() != null && info.getRoles().contains(roleIdentifier); } public boolean[] hasRoles(PrincipalCollection principal, List<String> roleIdentifiers) { AuthorizationInfo info = getAuthorizationInfo(principal); boolean[] result = new boolean[roleIdentifiers != null ? roleIdentifiers.size() : 0]; if (info != null) { result = hasRoles(roleIdentifiers, info); } return result; } protected boolean[] hasRoles(List<String> roleIdentifiers, AuthorizationInfo info) { boolean[] result; if (roleIdentifiers != null && !roleIdentifiers.isEmpty()) { int size = roleIdentifiers.size(); result = new boolean[size]; int i = 0; for (String roleName : roleIdentifiers) { result[i++] = hasRole(roleName, info); } } else { result = new boolean[0]; } return result; } public boolean hasAllRoles(PrincipalCollection principal, Collection<String> roleIdentifiers) { AuthorizationInfo info = getAuthorizationInfo(principal); return info != null && hasAllRoles(roleIdentifiers, info); } private boolean hasAllRoles(Collection<String> roleIdentifiers, AuthorizationInfo info) { if (roleIdentifiers != null && !roleIdentifiers.isEmpty()) { for (String roleName : roleIdentifiers) { if (!hasRole(roleName, info)) { return false; } } } return true; } public void checkRole(PrincipalCollection principal, String role) throws AuthorizationException { AuthorizationInfo info = getAuthorizationInfo(principal); checkRole(role, info); } protected void checkRole(String role, AuthorizationInfo info) { if (!hasRole(role, info)) { String msg = "User does not have role [" + role + "]"; throw new UnauthorizedException(msg); } } public void checkRoles(PrincipalCollection principal, Collection<String> roles) throws AuthorizationException { AuthorizationInfo info = getAuthorizationInfo(principal); checkRoles(roles, info); } public void checkRoles(PrincipalCollection principal, String... roles) throws AuthorizationException { checkRoles(principal, Arrays.asList(roles)); } protected void checkRoles(Collection<String> roles, AuthorizationInfo info) { if (roles != null && !roles.isEmpty()) { for (String roleName : roles) { checkRole(roleName, info); } } } @Override protected void doClearCache(PrincipalCollection principals) { super.doClearCache(principals); clearCachedAuthorizationInfo(principals); } }