001/** 002 * Copyright 2015 DuraSpace, Inc. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.fcrepo.auth.webac; 017 018import static java.util.Collections.unmodifiableMap; 019import static org.fcrepo.auth.webac.URIConstants.FOAF_AGENT_VALUE; 020import static org.fcrepo.auth.webac.URIConstants.WEBAC_MODE_CONTROL_VALUE; 021import static org.fcrepo.auth.webac.URIConstants.WEBAC_MODE_READ_VALUE; 022import static org.fcrepo.auth.webac.URIConstants.WEBAC_MODE_WRITE_VALUE; 023import static org.modeshape.jcr.ModeShapePermissions.ADD_NODE; 024import static org.modeshape.jcr.ModeShapePermissions.MODIFY_ACCESS_CONTROL; 025import static org.modeshape.jcr.ModeShapePermissions.READ; 026import static org.modeshape.jcr.ModeShapePermissions.READ_ACCESS_CONTROL; 027import static org.modeshape.jcr.ModeShapePermissions.REGISTER_NAMESPACE; 028import static org.modeshape.jcr.ModeShapePermissions.REMOVE; 029import static org.modeshape.jcr.ModeShapePermissions.REMOVE_CHILD_NODES; 030import static org.modeshape.jcr.ModeShapePermissions.SET_PROPERTY; 031 032import java.security.Principal; 033import java.util.Arrays; 034import java.util.HashMap; 035import java.util.Map; 036import java.util.Set; 037 038import javax.jcr.Session; 039 040import org.fcrepo.auth.roles.common.AbstractRolesAuthorizationDelegate; 041 042import org.slf4j.Logger; 043import org.slf4j.LoggerFactory; 044 045/** 046 * Authorization Delegate responsible for resolving Fedora's permissions using Web Access Control (WebAC) access 047 * control lists. 048 * 049 * @author Peter Eichman 050 * @since Aug 24, 2015 051 */ 052public class WebACAuthorizationDelegate extends AbstractRolesAuthorizationDelegate { 053 054 /** 055 * Class-level logger. 056 */ 057 private static final Logger LOGGER = LoggerFactory.getLogger(WebACAuthorizationDelegate.class); 058 059 private static final Map<String, String> actionMap; 060 061 static { 062 final Map<String, String> map = new HashMap<>(); 063 // WEBAC_MODE_READ Permissions 064 map.put(READ, WEBAC_MODE_READ_VALUE); 065 // WEBAC_MODE_WRITE Permissions 066 map.put(ADD_NODE, WEBAC_MODE_WRITE_VALUE); 067 map.put(REGISTER_NAMESPACE, WEBAC_MODE_WRITE_VALUE); 068 map.put(REMOVE, WEBAC_MODE_WRITE_VALUE); 069 map.put(REMOVE_CHILD_NODES, WEBAC_MODE_WRITE_VALUE); 070 map.put(SET_PROPERTY, WEBAC_MODE_WRITE_VALUE); 071 // WEBAC_MODE_CONTROL Permissions 072 map.put(MODIFY_ACCESS_CONTROL, WEBAC_MODE_CONTROL_VALUE); 073 map.put(READ_ACCESS_CONTROL, WEBAC_MODE_CONTROL_VALUE); 074 actionMap = unmodifiableMap(map); 075 } 076 077 /** 078 * The security principal for every request, that represents the foaf:Agent agent class that is used to designate 079 * "everyone". 080 */ 081 private static final Principal EVERYONE = new Principal() { 082 083 @Override 084 public String getName() { 085 return FOAF_AGENT_VALUE; 086 } 087 088 @Override 089 public String toString() { 090 return getName(); 091 } 092 093 }; 094 095 @Override 096 public boolean rolesHavePermission(final Session userSession, final String absPath, 097 final String[] actions, final Set<String> roles) { 098 099 /* 100 * If any value in the actions Array is NOT also in the roles Set, the request should be denied. 101 * Otherwise, e.g. all of the actions values are contained in the roles set, the request is approved. 102 * 103 * The logic here may not be immediately obvious. The process is thus: 104 * map: map the modeshape action to a webac action 105 * filter: ALL of these actions MUST exist in the roles Set, so if any action 106 * value does NOT exist in the roles Set, we want to know about that 107 * findFirst: If any value makes it through that filter, it is enough to invalidate 108 * the request. This is evaluated lazily, so the first item encountered will 109 * short-circut the processing of the rest of the stream. 110 * isPresent: this returns true if any value passed through the filter, but we 111 * actually want to invert that logic, hence the ! at the beginning of the expression. 112 */ 113 final boolean permit = !Arrays.asList(actions).stream() 114 .map(actionMap::get) 115 .filter(x -> !roles.contains(x)) 116 .findFirst() 117 .isPresent(); 118 119 LOGGER.debug("Request for actions: {}, on path: {}, with roles: {}. Permission={}", 120 actions, 121 absPath, 122 roles, 123 permit); 124 125 return permit; 126 } 127 128 @Override 129 public Principal getEveryonePrincipal() { 130 return EVERYONE; 131 } 132 133}