Merge in new logging system.
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / idp / profile / IdPProfileHandlerManager.java
1 /*
2  * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package edu.internet2.middleware.shibboleth.idp.profile;
18
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Timer;
23 import java.util.concurrent.locks.Lock;
24
25 import javax.servlet.ServletRequest;
26 import javax.servlet.http.HttpServletRequest;
27
28 import org.opensaml.util.resource.Resource;
29 import org.opensaml.xml.util.Pair;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32 import org.springframework.context.ApplicationContext;
33
34 import edu.internet2.middleware.shibboleth.common.config.BaseReloadableService;
35 import edu.internet2.middleware.shibboleth.common.profile.AbstractErrorHandler;
36 import edu.internet2.middleware.shibboleth.common.profile.ProfileHandler;
37 import edu.internet2.middleware.shibboleth.common.profile.ProfileHandlerManager;
38 import edu.internet2.middleware.shibboleth.common.profile.provider.AbstractRequestURIMappedProfileHandler;
39 import edu.internet2.middleware.shibboleth.idp.authn.LoginHandler;
40 import edu.internet2.middleware.shibboleth.idp.authn.LoginContext;
41
42 /**
43  * Implementation of a {@link ProfileHandlerManager} that maps the request path, without the servlet context, to a
44  * profile handler and adds support for authentication handlers.
45  */
46 public class IdPProfileHandlerManager extends BaseReloadableService implements ProfileHandlerManager {
47
48     /** Class logger. */
49     private final Logger log = LoggerFactory.getLogger(IdPProfileHandlerManager.class);
50
51     /** Handler used for errors. */
52     private AbstractErrorHandler errorHandler;
53
54     /** Map of request paths to profile handlers. */
55     private Map<String, AbstractRequestURIMappedProfileHandler> profileHandlers;
56
57     /** Map of authentication methods to authentication handlers. */
58     private Map<String, LoginHandler> authenticationHandlers;
59
60     /**
61      * Constructor. Configuration resources are not monitored for changes.
62      * 
63      * @param configurations configuration resources for this service
64      */
65     public IdPProfileHandlerManager(List<Resource> configurations) {
66         super(configurations);
67         profileHandlers = new HashMap<String, AbstractRequestURIMappedProfileHandler>();
68         authenticationHandlers = new HashMap<String, LoginHandler>();
69     }
70
71     /**
72      * Constructor.
73      * 
74      * @param timer timer resource polling tasks are scheduled with
75      * @param configurations configuration resources for this service
76      * @param pollingFrequency the frequency, in milliseconds, to poll the policy resources for changes, must be greater
77      *            than zero
78      */
79     public IdPProfileHandlerManager(List<Resource> configurations, Timer timer, long pollingFrequency) {
80         super(timer, configurations, pollingFrequency);
81         profileHandlers = new HashMap<String, AbstractRequestURIMappedProfileHandler>();
82         authenticationHandlers = new HashMap<String, LoginHandler>();
83     }
84
85     /** {@inheritDoc} */
86     public AbstractErrorHandler getErrorHandler() {
87         return errorHandler;
88     }
89
90     /**
91      * Sets the error handler.
92      * 
93      * @param handler error handler
94      */
95     public void setErrorHandler(AbstractErrorHandler handler) {
96         if (handler == null) {
97             throw new IllegalArgumentException("Error handler may not be null");
98         }
99         errorHandler = handler;
100     }
101
102     /** {@inheritDoc} */
103     public ProfileHandler getProfileHandler(ServletRequest request) {
104         ProfileHandler handler;
105
106         String requestPath = ((HttpServletRequest) request).getPathInfo();
107         log.debug("{}: Looking up profile handler for request path: {}", getId(), requestPath);
108
109         Lock readLock = getReadWriteLock().readLock();
110         readLock.lock();
111         handler = profileHandlers.get(requestPath);
112         readLock.unlock();
113
114         if (handler != null) {
115             log.debug("{}: Located profile handler of the following type for the request path: {}", getId(), handler
116                     .getClass().getName());
117         } else {
118             log.debug("{}: No profile handler registered for request path {}", getId(), requestPath);
119         }
120         return handler;
121     }
122
123     /**
124      * Gets the registered profile handlers.
125      * 
126      * @return registered profile handlers
127      */
128     public Map<String, AbstractRequestURIMappedProfileHandler> getProfileHandlers() {
129         return profileHandlers;
130     }
131
132     /**
133      * Gets the authentication handler appropriate for the given loging context. The mechanism used to determine the
134      * "appropriate" handler is implementation specific.
135      * 
136      * @param loginContext current login context
137      * 
138      * @return authentication method URI and handler appropriate for given login context
139      */
140     public Pair<String, LoginHandler> getAuthenticationHandler(LoginContext loginContext) {
141         log.debug("{}: Looking up authentication method for relying party {}", getId(), loginContext
142                 .getRelyingPartyId());
143
144         List<String> requestedMethods = loginContext.getRequestedAuthenticationMethods();
145         if (requestedMethods != null) {
146             LoginHandler candidateHandler;
147             for (String requestedMethod : requestedMethods) {
148                 log.debug(getId() + "{}: Checking for authentication handler for method {}", getId(), requestedMethod);
149                 candidateHandler = authenticationHandlers.get(requestedMethod);
150                 if (candidateHandler != null) {
151                     log.debug(getId()
152                             + "{}: Authentication handler for method {} found.  Checking if it meets othe criteria.",
153                             getId(), requestedMethod);
154
155                     if (loginContext.getPassiveAuth() && !candidateHandler.supportsPassive()) {
156                         log.debug("{}: Authentication handler for method {} does not meet required support for passive auth.  Skipping it",
157                                         getId(), requestedMethod);
158                         continue;
159                     }
160
161                     log.debug(getId() + "{}: Authentication handler for method {}  meets all requirements, using it.",
162                             getId(), requestedMethod);
163                     return new Pair<String, LoginHandler>(requestedMethod, candidateHandler);
164                 }
165             }
166         } else {
167             log.error("{}: No requested authentication methods for relying party {}", getId(), loginContext
168                     .getRelyingPartyId());
169         }
170
171         return null;
172     }
173
174     /**
175      * Gets the registered authentication handlers.
176      * 
177      * @return registered authentication handlers
178      */
179     public Map<String, LoginHandler> getAuthenticationHandlers() {
180         return authenticationHandlers;
181     }
182
183     /** {@inheritDoc} */
184     protected void newContextCreated(ApplicationContext newServiceContext) {
185         log.debug("{}: Loading new configuration into service", getId());
186         Lock writeLock = getReadWriteLock().writeLock();
187         writeLock.lock();
188         loadNewErrorHandler(newServiceContext);
189         loadNewProfileHandlers(newServiceContext);
190         loadNewAuthenticationHandlers(newServiceContext);
191         writeLock.unlock();
192     }
193
194     /**
195      * Reads the new error handler from the newly created application context and loads it into this manager.
196      * 
197      * @param newServiceContext newly created application context
198      */
199     protected void loadNewErrorHandler(ApplicationContext newServiceContext) {
200         String[] errorBeanNames = newServiceContext.getBeanNamesForType(AbstractErrorHandler.class);
201         log.debug("{}: Loading {} new error handler.", getId(), errorBeanNames.length);
202
203         errorHandler = (AbstractErrorHandler) newServiceContext.getBean(errorBeanNames[0]);
204         log.debug("{}: Loaded new error handler of type: {}", getId(), errorHandler.getClass().getName());
205     }
206
207     /**
208      * Reads the new profile handlers from the newly created application context and loads it into this manager.
209      * 
210      * @param newServiceContext newly created application context
211      */
212     protected void loadNewProfileHandlers(ApplicationContext newServiceContext) {
213         String[] profileBeanNames = newServiceContext.getBeanNamesForType(AbstractRequestURIMappedProfileHandler.class);
214         log.debug("{}: Loading {} new profile handlers.", getId(), profileBeanNames.length);
215
216         profileHandlers.clear();
217         AbstractRequestURIMappedProfileHandler<?, ?> profileHandler;
218         for (String profileBeanName : profileBeanNames) {
219             profileHandler = (AbstractRequestURIMappedProfileHandler) newServiceContext.getBean(profileBeanName);
220             for (String requestPath : profileHandler.getRequestPaths()) {
221                 profileHandlers.put(requestPath, profileHandler);
222                 log.debug("{}: Loaded profile handler for handling requests to request path {}", getId(), requestPath);
223             }
224         }
225     }
226
227     /**
228      * Reads the new authentication handlers from the newly created application context and loads it into this manager.
229      * 
230      * @param newServiceContext newly created application context
231      */
232     protected void loadNewAuthenticationHandlers(ApplicationContext newServiceContext) {
233         String[] authnBeanNames = newServiceContext.getBeanNamesForType(LoginHandler.class);
234         log.debug("{}: Loading {} new authentication handlers.", getId(), authnBeanNames.length);
235
236         authenticationHandlers.clear();
237         LoginHandler authnHandler;
238         for (String authnBeanName : authnBeanNames) {
239             authnHandler = (LoginHandler) newServiceContext.getBean(authnBeanName);
240             log.debug("{}: Loading authentication handler of type supporting authentication methods: {}", getId(),
241                     authnHandler.getSupportedAuthenticationMethods());
242
243             for (String authnMethod : authnHandler.getSupportedAuthenticationMethods()) {
244                 authenticationHandlers.put(authnMethod, authnHandler);
245             }
246         }
247     }
248 }