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

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

There is a newer version: 2.21.0
Show newest version
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.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.
 */
@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) {
      getterRoleNames = security.extractRoleNames(property.getGetter());
      getterRoles = security.extractRoles(getterRoleNames);

      Set temp = security.extractRoleNames(property.getSetter());
      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 its declaring class. */ protected Set extractRoleNames(Method method) { if (method == null) { return noRoleNames; } Set toReturn = extractRoleNames((AnnotatedElement) method); if (noRoleNames == toReturn) { 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 - 2024 Weber Informatics LLC | Privacy Policy