001/* 002 * Licensed to DuraSpace under one or more contributor license agreements. 003 * See the NOTICE file distributed with this work for additional information 004 * regarding copyright ownership. 005 * 006 * DuraSpace licenses this file to you under the Apache License, 007 * Version 2.0 (the "License"); you may not use this file except in 008 * compliance with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.fcrepo.auth.common; 019 020import java.security.Principal; 021import java.util.Collections; 022import java.util.HashSet; 023import java.util.Map; 024import java.util.Set; 025 026import javax.jcr.Credentials; 027import javax.servlet.http.HttpServletRequest; 028 029import org.modeshape.jcr.ExecutionContext; 030import org.modeshape.jcr.api.ServletCredentials; 031import org.modeshape.jcr.security.AuthenticationProvider; 032import org.slf4j.Logger; 033import org.slf4j.LoggerFactory; 034 035/** 036 * Authenticates ModeShape logins where JAX-RS credentials are supplied. Capable 037 * of authenticating whether or not container has performed user authentication. 038 * This is a singleton with an injected policy enforcement point. The singleton 039 * pattern allows ModeShape to obtain this instance via classname configuration. 040 * 041 * @author Gregory Jansen 042 */ 043public final class ServletContainerAuthenticationProvider implements 044 AuthenticationProvider { 045 046 private static ServletContainerAuthenticationProvider instance = null; 047 048 private ServletContainerAuthenticationProvider() { 049 instance = this; 050 } 051 052 /** 053 * User role for Fedora's admin users 054 */ 055 public static final String FEDORA_ADMIN_ROLE = "fedoraAdmin"; 056 057 /** 058 * User role for Fedora's ordinary users 059 */ 060 public static final String FEDORA_USER_ROLE = "fedoraUser"; 061 062 private static final Logger LOGGER = LoggerFactory 063 .getLogger(ServletContainerAuthenticationProvider.class); 064 065 private Set<PrincipalProvider> principalProviders = Collections.emptySet(); 066 067 private FedoraAuthorizationDelegate fad; 068 069 /** 070 * Provides the singleton bean to ModeShape via reflection based on class 071 * name. 072 * 073 * @return a AuthenticationProvider 074 */ 075 public static synchronized AuthenticationProvider getInstance() { 076 if (instance != null) { 077 return instance; 078 } 079 instance = new ServletContainerAuthenticationProvider(); 080 LOGGER.warn("Security is MINIMAL, no Policy Enforcement Point configured."); 081 return instance; 082 } 083 084 /** 085 * @return the principalProviders 086 */ 087 public Set<PrincipalProvider> getPrincipalProviders() { 088 return principalProviders; 089 } 090 091 /** 092 * @param principalProviders the principalProviders to set 093 */ 094 public void setPrincipalProviders( 095 final Set<PrincipalProvider> principalProviders) { 096 this.principalProviders = principalProviders; 097 } 098 099 /** 100 * Authenticate the user that is using the supplied credentials. 101 * <p> 102 * If the credentials given establish that the authenticated user has the fedoraAdmin role, construct an 103 * ExecutionContext with FedoraAdminSecurityContext as the SecurityContext. Otherwise, construct an 104 * ExecutionContext with FedoraUserSecurityContext as the SecurityContext. 105 * </p> 106 * <p> 107 * If the authenticated user does not have the fedoraAdmin role, session attributes will be assigned in the 108 * sessionAttributes map: 109 * </p> 110 * <ul> 111 * <li>FEDORA_SERVLET_REQUEST will be assigned the ServletRequest instance associated with credentials.</li> 112 * <li>FEDORA_ALL_PRINCIPALS will be assigned the union of all principals obtained from configured 113 * PrincipalProvider instances plus the authenticated user's principal; FEDORA_ALL_PRINCIPALS will be assigned the 114 * singleton set containing the fad.getEveryonePrincipal() principal otherwise.</li> 115 * </ul> 116 */ 117 @Override 118 public ExecutionContext authenticate(final Credentials credentials, 119 final String repositoryName, final String workspaceName, 120 final ExecutionContext repositoryContext, 121 final Map<String, Object> sessionAttributes) { 122 LOGGER.debug("Trying to authenticate: {}; FAD: {}", credentials, fad); 123 124 if (!(credentials instanceof ServletCredentials)) { 125 return null; 126 } 127 128 final HttpServletRequest servletRequest = 129 ((ServletCredentials) credentials).getRequest(); 130 Principal userPrincipal = servletRequest.getUserPrincipal(); 131 132 if (userPrincipal != null && servletRequest.isUserInRole(FEDORA_ADMIN_ROLE)) { 133 // check if delegation is configured 134 final Principal delegatedPrincipal = getDelegatedPrincipal(credentials); 135 if (delegatedPrincipal != null) { 136 // replace the userPrincipal with the delegated principal 137 // then fall through to the normal user processing 138 userPrincipal = delegatedPrincipal; 139 LOGGER.info("Admin user is delegating to {}", userPrincipal); 140 141 } else { 142 // delegation is configured, but there is no delegated user set in the header of this request 143 LOGGER.debug("Returning admin user"); 144 return repositoryContext.with(new FedoraAdminSecurityContext(userPrincipal.getName())); 145 } 146 } 147 148 if (userPrincipal != null) { 149 LOGGER.debug("Found user-principal: {}.", userPrincipal.getName()); 150 151 sessionAttributes.put( 152 FedoraAuthorizationDelegate.FEDORA_SERVLET_REQUEST, 153 servletRequest); 154 155 sessionAttributes.put( 156 FedoraAuthorizationDelegate.FEDORA_USER_PRINCIPAL, 157 userPrincipal); 158 159 final Set<Principal> principals = collectPrincipals(credentials); 160 principals.add(userPrincipal); 161 principals.add(fad.getEveryonePrincipal()); 162 163 sessionAttributes.put( 164 FedoraAuthorizationDelegate.FEDORA_ALL_PRINCIPALS, 165 principals); 166 167 LOGGER.debug("All principals: {}", principals); 168 169 } else { 170 LOGGER.debug("No user-principal found."); 171 172 sessionAttributes.put(FedoraAuthorizationDelegate.FEDORA_USER_PRINCIPAL, 173 fad.getEveryonePrincipal()); 174 175 sessionAttributes.put( 176 FedoraAuthorizationDelegate.FEDORA_ALL_PRINCIPALS, 177 Collections.singleton(fad.getEveryonePrincipal())); 178 179 } 180 181 return repositoryContext.with(new FedoraUserSecurityContext( 182 userPrincipal, fad)); 183 } 184 185 private Principal getDelegatedPrincipal(final Credentials credentials) { 186 for (final PrincipalProvider provider : this.getPrincipalProviders()) { 187 if (provider instanceof DelegateHeaderPrincipalProvider) { 188 return ((DelegateHeaderPrincipalProvider) provider).getDelegate(credentials); 189 } 190 } 191 return null; 192 } 193 194 /** 195 * @return the authorization delegate 196 */ 197 public FedoraAuthorizationDelegate getFad() { 198 return fad; 199 } 200 201 /** 202 * @param fad the authorization delegate to set 203 */ 204 public void setFad(final FedoraAuthorizationDelegate fad) { 205 this.fad = fad; 206 } 207 208 private Set<Principal> collectPrincipals(final Credentials credentials) { 209 final Set<Principal> principals = new HashSet<>(); 210 211 // TODO add exception handling for principal providers 212 for (final PrincipalProvider p : this.getPrincipalProviders()) { 213 // if the provider is DelegateHeader, it is either already processed (if logged user has fedora admin role) 214 // or should be ignored completely (the user was not in admin role, so on-behalf-of header must be ignored) 215 if (!(p instanceof DelegateHeaderPrincipalProvider)) { 216 final Set<Principal> ps = p.getPrincipals(credentials); 217 218 if (ps != null) { 219 principals.addAll(p.getPrincipals(credentials)); 220 } 221 } 222 } 223 224 return principals; 225 } 226}