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 final Principal userPrincipal = servletRequest.getUserPrincipal(); 129 130 if (userPrincipal != null && 131 servletRequest.isUserInRole(FEDORA_ADMIN_ROLE)) { 132 LOGGER.debug("Returning admin user"); 133 134 return repositoryContext.with(new FedoraAdminSecurityContext( 135 userPrincipal.getName())); 136 } 137 138 if (userPrincipal != null) { 139 LOGGER.debug("Found user-principal: {}.", userPrincipal.getName()); 140 141 sessionAttributes.put( 142 FedoraAuthorizationDelegate.FEDORA_SERVLET_REQUEST, 143 servletRequest); 144 145 sessionAttributes.put( 146 FedoraAuthorizationDelegate.FEDORA_USER_PRINCIPAL, 147 userPrincipal); 148 149 final Set<Principal> principals = collectPrincipals(credentials); 150 principals.add(userPrincipal); 151 principals.add(fad.getEveryonePrincipal()); 152 153 sessionAttributes.put( 154 FedoraAuthorizationDelegate.FEDORA_ALL_PRINCIPALS, 155 principals); 156 157 } else { 158 LOGGER.debug("No user-principal found."); 159 160 sessionAttributes.put(FedoraAuthorizationDelegate.FEDORA_USER_PRINCIPAL, 161 fad.getEveryonePrincipal()); 162 163 sessionAttributes.put( 164 FedoraAuthorizationDelegate.FEDORA_ALL_PRINCIPALS, 165 Collections.singleton(fad.getEveryonePrincipal())); 166 167 } 168 169 return repositoryContext.with(new FedoraUserSecurityContext( 170 userPrincipal, fad)); 171 } 172 173 /** 174 * @return the authorization delegate 175 */ 176 public FedoraAuthorizationDelegate getFad() { 177 return fad; 178 } 179 180 /** 181 * @param fad the authorization delegate to set 182 */ 183 public void setFad(final FedoraAuthorizationDelegate fad) { 184 this.fad = fad; 185 } 186 187 private Set<Principal> collectPrincipals(final Credentials credentials) { 188 final Set<Principal> principals = new HashSet<>(); 189 190 // TODO add exception handling for principal providers 191 for (final PrincipalProvider p : this.getPrincipalProviders()) { 192 final Set<Principal> ps = p.getPrincipals(credentials); 193 194 if (ps != null) { 195 principals.addAll(p.getPrincipals(credentials)); 196 } 197 } 198 199 return principals; 200 } 201}