2 * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package edu.internet2.middleware.shibboleth.idp.profile;
19 import java.util.HashMap;
20 import java.util.List;
22 import java.util.Timer;
23 import java.util.concurrent.locks.Lock;
25 import javax.servlet.ServletRequest;
26 import javax.servlet.http.HttpServletRequest;
28 import org.apache.log4j.Logger;
29 import org.opensaml.util.resource.Resource;
30 import org.opensaml.xml.util.Pair;
31 import org.springframework.context.ApplicationContext;
33 import edu.internet2.middleware.shibboleth.common.config.BaseReloadableService;
34 import edu.internet2.middleware.shibboleth.common.profile.AbstractErrorHandler;
35 import edu.internet2.middleware.shibboleth.common.profile.ProfileHandler;
36 import edu.internet2.middleware.shibboleth.common.profile.ProfileHandlerManager;
37 import edu.internet2.middleware.shibboleth.common.profile.provider.AbstractRequestURIMappedProfileHandler;
38 import edu.internet2.middleware.shibboleth.idp.authn.LoginHandler;
39 import edu.internet2.middleware.shibboleth.idp.authn.LoginContext;
42 * Implementation of a {@link ProfileHandlerManager} that maps the request path, without the servlet context, to a
43 * profile handler and adds support for authentication handlers.
45 public class IdPProfileHandlerManager extends BaseReloadableService implements ProfileHandlerManager {
48 private final Logger log = Logger.getLogger(IdPProfileHandlerManager.class);
50 /** Handler used for errors. */
51 private AbstractErrorHandler errorHandler;
53 /** Map of request paths to profile handlers. */
54 private Map<String, AbstractRequestURIMappedProfileHandler> profileHandlers;
56 /** Map of authentication methods to authentication handlers. */
57 private Map<String, LoginHandler> authenticationHandlers;
60 * Constructor. Configuration resources are not monitored for changes.
62 * @param configurations configuration resources for this service
64 public IdPProfileHandlerManager(List<Resource> configurations) {
65 super(configurations);
66 profileHandlers = new HashMap<String, AbstractRequestURIMappedProfileHandler>();
67 authenticationHandlers = new HashMap<String, LoginHandler>();
73 * @param timer timer resource polling tasks are scheduled with
74 * @param configurations configuration resources for this service
75 * @param pollingFrequency the frequency, in milliseconds, to poll the policy resources for changes, must be greater
78 public IdPProfileHandlerManager(List<Resource> configurations, Timer timer, long pollingFrequency) {
79 super(timer, configurations, pollingFrequency);
80 profileHandlers = new HashMap<String, AbstractRequestURIMappedProfileHandler>();
81 authenticationHandlers = new HashMap<String, LoginHandler>();
85 public AbstractErrorHandler getErrorHandler() {
90 * Sets the error handler.
92 * @param handler error handler
94 public void setErrorHandler(AbstractErrorHandler handler) {
95 if (handler == null) {
96 throw new IllegalArgumentException("Error handler may not be null");
98 errorHandler = handler;
102 public ProfileHandler getProfileHandler(ServletRequest request) {
103 ProfileHandler handler;
105 String requestPath = ((HttpServletRequest) request).getPathInfo();
106 if (log.isDebugEnabled()) {
107 log.debug(getId() + ": Looking up profile handler for request path: " + requestPath);
109 Lock readLock = getReadWriteLock().readLock();
111 handler = profileHandlers.get(requestPath);
114 if (handler != null) {
115 if (log.isDebugEnabled()) {
116 log.debug(getId() + ": Located profile handler of the following type for request path "
117 + requestPath + ": " + handler.getClass().getName());
120 if (log.isDebugEnabled()) {
121 log.debug(getId() + ": No profile handler registered for request path " + requestPath);
128 * Gets the registered profile handlers.
130 * @return registered profile handlers
132 public Map<String, AbstractRequestURIMappedProfileHandler> getProfileHandlers() {
133 return profileHandlers;
137 * Gets the authentication handler appropriate for the given loging context. The mechanism used to determine the
138 * "appropriate" handler is implementation specific.
140 * @param loginContext current login context
142 * @return authentication method URI and handler appropriate for given login context
144 public Pair<String, LoginHandler> getAuthenticationHandler(LoginContext loginContext) {
145 if (loginContext == null) {
149 if (log.isDebugEnabled()) {
150 log.debug(getId() + ": Looking up authentication method for relying party "
151 + loginContext.getRelyingPartyId());
153 List<String> requestedMethods = loginContext.getRequestedAuthenticationMethods();
154 if (requestedMethods != null) {
155 LoginHandler candidateHandler;
156 for (String requestedMethod : requestedMethods) {
157 if (log.isDebugEnabled()) {
158 log.debug(getId() + ": Checking for authentication handler for method " + requestedMethod
159 + " which was requested for relying party " + loginContext.getRelyingPartyId());
161 candidateHandler = authenticationHandlers.get(requestedMethod);
162 if (candidateHandler != null) {
163 if (log.isDebugEnabled()) {
164 log.debug(getId() + ": Authentication handler for method " + requestedMethod
165 + " for relying party " + loginContext.getRelyingPartyId()
166 + " found. Checking if it meets othe criteria.");
168 if(loginContext.getPassiveAuth() && !candidateHandler.supportsPassive()){
169 if (log.isDebugEnabled()) {
170 log.debug(getId() + ": Authentication handler for method " + requestedMethod
171 + " for relying party " + loginContext.getRelyingPartyId()
172 + " does not meet required support for passive auth. Skipping it");
177 if (log.isDebugEnabled()) {
178 log.debug(getId() + ": Authentication handler for method " + requestedMethod
179 + " for relying party " + loginContext.getRelyingPartyId()
180 + " meets all requirements, using it.");
182 return new Pair<String, LoginHandler>(requestedMethod, candidateHandler);
186 log.error(getId() + ": No requested authentication methods for relying party "
187 + loginContext.getRelyingPartyId());
194 * Gets the registered authentication handlers.
196 * @return registered authentication handlers
198 public Map<String, LoginHandler> getAuthenticationHandlers() {
199 return authenticationHandlers;
203 protected void newContextCreated(ApplicationContext newServiceContext) {
204 if (log.isDebugEnabled()) {
205 log.debug(getId() + ": Loading new configuration into service");
207 Lock writeLock = getReadWriteLock().writeLock();
209 loadNewErrorHandler(newServiceContext);
210 loadNewProfileHandlers(newServiceContext);
211 loadNewAuthenticationHandlers(newServiceContext);
216 * Reads the new error handler from the newly created application context and loads it into this manager.
218 * @param newServiceContext newly created application context
220 protected void loadNewErrorHandler(ApplicationContext newServiceContext) {
221 String[] errorBeanNames = newServiceContext.getBeanNamesForType(AbstractErrorHandler.class);
222 if (log.isDebugEnabled()) {
223 log.debug(getId() + ": Loading " + errorBeanNames.length + " new error handler.");
226 errorHandler = (AbstractErrorHandler) newServiceContext.getBean(errorBeanNames[0]);
227 if (log.isDebugEnabled()) {
228 log.debug(getId() + ": Loaded new error handler of type: " + errorHandler.getClass().getName());
233 * Reads the new profile handlers from the newly created application context and loads it into this manager.
235 * @param newServiceContext newly created application context
237 protected void loadNewProfileHandlers(ApplicationContext newServiceContext) {
238 String[] profileBeanNames = newServiceContext.getBeanNamesForType(AbstractRequestURIMappedProfileHandler.class);
239 if (log.isDebugEnabled()) {
240 log.debug(getId() + ": Loading " + profileBeanNames.length + " new profile handlers.");
243 profileHandlers.clear();
244 AbstractRequestURIMappedProfileHandler<?,?> profileHandler;
245 for (String profileBeanName : profileBeanNames) {
246 profileHandler = (AbstractRequestURIMappedProfileHandler) newServiceContext.getBean(profileBeanName);
247 for (String requestPath : profileHandler.getRequestPaths()) {
248 profileHandlers.put(requestPath, profileHandler);
249 if (log.isDebugEnabled()) {
250 log.debug(getId() + ": Loaded profile handler of type "
251 + profileHandler.getClass().getName() + " handling requests to request path "
259 * Reads the new authentication handlers from the newly created application context and loads it into this manager.
261 * @param newServiceContext newly created application context
263 protected void loadNewAuthenticationHandlers(ApplicationContext newServiceContext) {
264 String[] authnBeanNames = newServiceContext.getBeanNamesForType(LoginHandler.class);
265 if (log.isDebugEnabled()) {
266 log.debug(getId() + ": Loading " + authnBeanNames.length + " new authentication handlers.");
269 authenticationHandlers.clear();
270 LoginHandler authnHandler;
271 for (String authnBeanName : authnBeanNames) {
272 authnHandler = (LoginHandler) newServiceContext.getBean(authnBeanName);
273 if (log.isDebugEnabled()) {
274 log.debug(getId() + ": Loading authentication handler of type "
275 + authnHandler.getClass().getName() + " supporting authentication methods: "
276 + authnHandler.getSupportedAuthenticationMethods());
278 for (String authnMethod : authnHandler.getSupportedAuthenticationMethods()) {
279 authenticationHandlers.put(authnMethod, authnHandler);