/* * Copyright 2016 Red Hat, Inc. and/or its affiliates. * * 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. */ package org.uberfire.security.impl.authz; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import org.jboss.errai.security.shared.api.Group; import org.jboss.errai.security.shared.api.Role; import org.jboss.errai.security.shared.api.identity.User; import org.uberfire.security.Resource; import org.uberfire.security.ResourceAction; import org.uberfire.security.ResourceType; import org.uberfire.security.authz.AuthorizationPolicy; import org.uberfire.security.authz.AuthorizationResult; import org.uberfire.security.authz.Permission; import org.uberfire.security.authz.PermissionCollection; import org.uberfire.security.authz.PermissionManager; import org.uberfire.security.authz.PermissionType; import org.uberfire.security.authz.PermissionTypeRegistry; import org.uberfire.security.authz.VotingAlgorithm; import org.uberfire.security.authz.VotingStrategy; import static org.uberfire.security.authz.AuthorizationResult.ACCESS_ABSTAIN; import static org.uberfire.security.authz.AuthorizationResult.ACCESS_DENIED; import static org.uberfire.security.authz.AuthorizationResult.ACCESS_GRANTED; @ApplicationScoped public class DefaultPermissionManager implements PermissionManager { private PermissionTypeRegistry permissionTypeRegistry; private AuthorizationPolicy authorizationPolicy = new DefaultAuthorizationPolicy(); private DefaultAuthzResultCache cache; private VotingStrategy defaultVotingStrategy = VotingStrategy.PRIORITY; private Map<VotingStrategy, VotingAlgorithm> votingAlgorithmMap = new HashMap<>(); @Inject public DefaultPermissionManager(PermissionTypeRegistry permissionTypeRegistry) { this(permissionTypeRegistry, new DefaultAuthzResultCache()); } public DefaultPermissionManager() { this(new DefaultPermissionTypeRegistry(), new DefaultAuthzResultCache()); } public DefaultPermissionManager(PermissionTypeRegistry permissionTypeRegistry, DefaultAuthzResultCache cache) { this.permissionTypeRegistry = permissionTypeRegistry; this.cache = cache; setVotingAlgorithm(VotingStrategy.AFFIRMATIVE, new AffirmativeBasedVoter()); setVotingAlgorithm(VotingStrategy.CONSENSUS, new ConsensusBasedVoter()); setVotingAlgorithm(VotingStrategy.UNANIMOUS, new UnanimousBasedVoter()); } public AuthorizationPolicy getAuthorizationPolicy() { return authorizationPolicy; } public void setAuthorizationPolicy(AuthorizationPolicy authorizationPolicy) { this.authorizationPolicy = authorizationPolicy != null ? authorizationPolicy : new DefaultAuthorizationPolicy(); this.cache.clear(); } @Override public AuthorizationPolicyBuilder newAuthorizationPolicy() { return new AuthorizationPolicyBuilder(permissionTypeRegistry); } @Override public VotingStrategy getDefaultVotingStrategy() { return defaultVotingStrategy; } @Override public void setDefaultVotingStrategy(VotingStrategy votingStrategy) { defaultVotingStrategy = votingStrategy; } public VotingAlgorithm getVotingAlgorithm(VotingStrategy votingStrategy) { return votingAlgorithmMap.get(votingStrategy); } public void setVotingAlgorithm(VotingStrategy votingStrategy, VotingAlgorithm votingAlgorithm) { votingAlgorithmMap.put(votingStrategy, votingAlgorithm); } @Override public Permission createPermission(String name, boolean granted) { PermissionType permissionType = permissionTypeRegistry.resolve(name); return permissionType.createPermission(name, granted); } @Override public Permission createPermission(Resource resource, ResourceAction action, boolean granted) { // Does the resource have a type? // YES => check the resource action f.i: "project.read.myprojectid" if (resource.getResourceType() != null && !resource.isType(ResourceType.UNKNOWN.getName())) { PermissionType permissionType = permissionTypeRegistry.resolve(resource.getResourceType().getName()); return permissionType.createPermission(resource, action, granted); } // NO => just check the resource identifier return createPermission(resource.getIdentifier(), granted); } @Override public Permission createPermission(ResourceType resourceType, ResourceAction action, boolean granted) { PermissionType permissionType = permissionTypeRegistry.resolve(resourceType.getName()); return permissionType.createPermission(resourceType, action, granted); } @Override public AuthorizationResult checkPermission(Permission permission, User user) { return checkPermission(permission, user, defaultVotingStrategy); } @Override public AuthorizationResult checkPermission(Permission permission, User user, VotingStrategy votingStrategy) { if (authorizationPolicy == null || permission == null) { return ACCESS_ABSTAIN; } AuthorizationResult result = cache.get(user, permission); if (result == null) { result = _checkPermission(permission, user, votingStrategy == null ? defaultVotingStrategy : votingStrategy); cache.put(user, permission, result); } return result; } protected AuthorizationResult _checkPermission(Permission permission, User user, VotingStrategy votingStrategy) { if (VotingStrategy.PRIORITY.equals(votingStrategy)) { PermissionCollection userPermissions = resolvePermissions(user, VotingStrategy.PRIORITY); return _checkPermission(permission, userPermissions); } else { List<AuthorizationResult> permList = _checkRoleAndGroupPermissions(permission, user); VotingAlgorithm votingAlgorithm = votingAlgorithmMap.get(votingStrategy); return votingAlgorithm.vote(permList); } } protected List<AuthorizationResult> _checkRoleAndGroupPermissions(Permission permission, User user) { List<AuthorizationResult> result = new ArrayList<>(); if (user.getRoles() != null) { for (Role role : user.getRoles()) { PermissionCollection collection = authorizationPolicy.getPermissions(role); AuthorizationResult _partialResult = _checkPermission(permission, collection); result.add(_partialResult); } } if (user.getGroups() != null) { for (Group group : user.getGroups()) { PermissionCollection collection = authorizationPolicy.getPermissions(group); AuthorizationResult _partialResult = _checkPermission(permission, collection); result.add(_partialResult); } } return result; } protected AuthorizationResult _checkPermission(Permission permission, PermissionCollection collection) { if (collection == null) { return ACCESS_ABSTAIN; } Permission existing = collection.get(permission.getName()); if (existing != null) { return existing.getResult().equals(permission.getResult()) ? ACCESS_GRANTED : ACCESS_DENIED; } if (collection.implies(permission)) { return ACCESS_GRANTED; } Permission inverted = permission.clone(); inverted.setResult(inverted.getResult().invert()); if (collection.implies(inverted)) { return ACCESS_DENIED; } return ACCESS_ABSTAIN; } @Override public String resolveResourceId(Permission permission) { PermissionType permissionType = permissionTypeRegistry.resolve(permission.getName()); return permissionType.resolveResourceId(permission); } @Override public PermissionCollection resolvePermissions(User user, VotingStrategy votingStrategy) { if (user == null) { return new DefaultPermissionCollection(); } switch (votingStrategy) { case AFFIRMATIVE: return resolvePermissionsAffirmative(user); case CONSENSUS: return resolvePermissionsConsensus(user); case UNANIMOUS: return resolvePermissionsUnanimous(user); default: return resolvePermissionsPriority(user); } } private PermissionCollection resolvePermissionsAffirmative(User user) { // TODO PermissionCollection result = new DefaultPermissionCollection(); return result; } private PermissionCollection resolvePermissionsConsensus(User user) { // TODO PermissionCollection result = new DefaultPermissionCollection(); return result; } private PermissionCollection resolvePermissionsUnanimous(User user) { // TODO PermissionCollection result = new DefaultPermissionCollection(); return result; } /** * Get all the permissions assigned to any of the user's roles/groups plus the default permissions * ({@link AuthorizationPolicy#getPermissions()}) and it creates a single permission collection where * the permission are added by priority. * @param user The target user * @return An unified permission collection */ private PermissionCollection resolvePermissionsPriority(User user) { if (authorizationPolicy == null) { return null; } // Get the default permissions as lowest priority PermissionCollection result = authorizationPolicy.getPermissions(); int[] priority = new int[]{Integer.MIN_VALUE}; // Overwrite the default permissions with those defined for the user's roles & groups result = mergeRolePermissions(user, result, priority); result = mergeGroupPermissions(user, result, priority); return result; } /** * Merge the target collection with the permissions assigned to the given user's roles */ private PermissionCollection mergeRolePermissions(User user, PermissionCollection target, int[] lastPriority) { PermissionCollection result = target; if (user.getRoles() != null) { for (Role role : user.getRoles()) { PermissionCollection collection = authorizationPolicy.getPermissions(role); int priority = authorizationPolicy.getPriority(role); int comparator = resolve(priority, lastPriority[0]); result = result.merge(collection, comparator); if (priority > lastPriority[0]) { lastPriority[0] = priority; } } } return result; } /** * Merge the target collection with the permissions assigned to the given user's groups */ private PermissionCollection mergeGroupPermissions(User user, PermissionCollection target, int[] lastPriority) { PermissionCollection result = target; if (user.getGroups() != null) { for (Group group : user.getGroups()) { PermissionCollection collection = authorizationPolicy.getPermissions(group); int priority = authorizationPolicy.getPriority(group); int comparator = resolve(priority, lastPriority[0]); result = result.merge(collection, comparator); if (priority > lastPriority[0]) { lastPriority[0] = priority; } } } return result; } private int resolve(int p1, int p2) { if (p1 == p2) { return 0; } if (p1 > p2) { return 1; } return -1; } }