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