All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.getperka.flatpack.security.RolePropertySecurity Maven / Gradle / Ivy

package com.getperka.flatpack.security;

/*
 * #%L
 * FlatPack serialization code
 * %%
 * Copyright (C) 2012 - 2013 Perka Inc.
 * %%
 * 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.
 * #L%
 */

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.Principal;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.inject.Inject;
import javax.inject.Singleton;

import com.getperka.flatpack.HasUuid;
import com.getperka.flatpack.RoleMapper;
import com.getperka.flatpack.ext.PrincipalMapper;
import com.getperka.flatpack.ext.Property;
import com.getperka.flatpack.ext.PropertySecurity;
import com.getperka.flatpack.util.FlatPackCollections;

/**
 * Enforces {@link RolesAllowed} restrictions on entity properties.
 * 

* The guide to access control: *

    *
  • Decorated getters are definitive. *
  • Undecorated public getters will inherit access controls from their declaring class. *
  • Undecorated non-public getters will be inaccessible. *
  • Decorated setters are definitive. *
  • Undecorated setters of any visibility will inherit from the associated getter. *
  • Undecorated setters of any visibility will inherit from the declaring class if there is no * getter for the property. *
*/ @Singleton public class RolePropertySecurity implements PropertySecurity { /** * Used to memoize property information. */ static class PropertyRoles { final Set> getterRoles; final Set getterRoleNames; final Set> setterRoles; final Set setterRoleNames; PropertyRoles(RolePropertySecurity security, Property property) { // Extract the roles, delegating to the enclosing type only if the property is public if (property.getGetter() == null) { getterRoleNames = noRoleNames; } else { getterRoleNames = security.extractRoleNames(property.getGetter(), Modifier.isPublic(property.getGetter().getModifiers())); } getterRoles = security.extractRoles(getterRoleNames); // The setter should inherit role names from the class only if there is no getter Set temp = security.extractRoleNames(property.getSetter(), property.getGetter() == null); if (noRoleNames.equals(temp)) { setterRoles = getterRoles; setterRoleNames = getterRoleNames; } else { setterRoleNames = temp; setterRoles = security.extractRoles(setterRoleNames); } } } private interface AllRolesView {} private interface NoRolesView {} final Set> allRoles = Collections.> singleton(AllRolesView.class); final Set> noRoles = Collections.> singleton(NoRolesView.class); private PrincipalMapper principalMapper; private RoleMapper roleMapper; private final ConcurrentMap propertyRoles = new ConcurrentHashMap(); /** * Requires injection. */ protected RolePropertySecurity() {} @Override public Set getGetterRoleNames(Property property) { return getPropertyRoles(property).getterRoleNames; } @Override public Set getSetterRoleNames(Property property) { return getPropertyRoles(property).setterRoleNames; } @Override public boolean mayGet(Property property, Principal principal, HasUuid target) { PropertyRoles roles = getPropertyRoles(property); // Always enforce @DenyAll if (roles.getterRoles.isEmpty()) { return false; } if (!principalMapper.isAccessEnforced(principal, target)) { return true; } return checkRoles(roles.getterRoles, principalMapper.getRoles(principal)); } @Override public boolean maySet(Property property, Principal principal, HasUuid target, Object newValue) { PropertyRoles roles = getPropertyRoles(property); // Always enforce @DenyAll if (roles.setterRoles.isEmpty()) { return false; } if (!principalMapper.isAccessEnforced(principal, target)) { return true; } return checkRoles(roles.setterRoles, principalMapper.getRoles(principal)); } /** * Returns the role names associated with a method or class. This method never returns * {@code null}, however there are several special return values: *
    *
  • {@link #noRoleNames} indicates that no access information was set *
  • {@link #allRoleNames} indicates that access should be allowed for all roles *
  • An empty set indicates access should be denied for all roles *
*/ protected Set extractRoleNames(AnnotatedElement obj) { // Map no method to none-allowed if (obj == null) { return noRoleNames; } if (obj.isAnnotationPresent(DenyAll.class)) { return Collections.emptySet(); } if (obj.isAnnotationPresent(PermitAll.class)) { return allRoleNames; } RolesAllowed view = obj.getAnnotation(RolesAllowed.class); if (view == null) { return noRoleNames; } Set toReturn = FlatPackCollections.setForIteration(); toReturn.addAll(Arrays.asList(view.value())); if (toReturn.isEmpty()) { return Collections.emptySet(); } return Collections.unmodifiableSet(toReturn); } /** * Extract role information from a method or, optionally, its declaring class. */ protected Set extractRoleNames(Method method, boolean useDeclaring) { if (method == null) { return noRoleNames; } Set toReturn = extractRoleNames(method); if (noRoleNames.equals(toReturn) && useDeclaring) { toReturn = extractRoleNames(method.getDeclaringClass()); } return toReturn; } /** * Determine if any of the given credential roles map onto a required view. Visible for testing. */ boolean checkRoles(Collection> required, Collection credentials) { // Object comparison intentional if (roleMapper == null || required == allRoles) { return true; } if (required == null || required.isEmpty()) { return false; } for (String cred : credentials) { Class credView = roleMapper.mapRole(cred); if (credView == null) { continue; } if (required.contains(credView)) { return true; } for (Class req : required) { if (req.isAssignableFrom(credView)) { return true; } } } return false; } @Inject void inject(PrincipalMapper principalMapper, RoleMapper roleMapper) { this.principalMapper = principalMapper; this.roleMapper = roleMapper; } private Set> extractRoles(Set roleNames) { // Object comparison intentional if (roleMapper == null || roleNames == null || noRoleNames == roleNames) { return noRoles; } if (allRoleNames == roleNames) { return allRoles; } Set> toReturn = FlatPackCollections.setForIteration(); for (String name : roleNames) { Class roleClass = roleMapper.mapRole(name); if (roleClass != null) { toReturn.add(roleClass); } } return Collections.unmodifiableSet(toReturn); } private PropertyRoles getPropertyRoles(Property property) { PropertyRoles toReturn = propertyRoles.get(property); if (toReturn == null) { toReturn = new PropertyRoles(this, property); propertyRoles.put(property, toReturn); } return toReturn; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy