prevent cast class exception if a user starts a SAML 1 flow, leaves in the middle...
[java-idp.git] / src / main / java / 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.Map;
21 import java.util.concurrent.locks.Lock;
22
23 import javax.servlet.ServletRequest;
24 import javax.servlet.http.HttpServletRequest;
25
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28 import org.springframework.context.ApplicationContext;
29
30 import edu.internet2.middleware.shibboleth.common.config.BaseReloadableService;
31 import edu.internet2.middleware.shibboleth.common.profile.AbstractErrorHandler;
32 import edu.internet2.middleware.shibboleth.common.profile.ProfileHandler;
33 import edu.internet2.middleware.shibboleth.common.profile.ProfileHandlerManager;
34 import edu.internet2.middleware.shibboleth.common.profile.provider.AbstractRequestURIMappedProfileHandler;
35 import edu.internet2.middleware.shibboleth.common.service.ServiceException;
36 import edu.internet2.middleware.shibboleth.idp.authn.LoginHandler;
37
38 /**
39  * Implementation of a {@link ProfileHandlerManager} that maps the request path, without the servlet context, to a
40  * profile handler and adds support for authentication handlers.
41  */
42 public class IdPProfileHandlerManager extends BaseReloadableService implements ProfileHandlerManager {
43
44     /** Class logger. */
45     private final Logger log = LoggerFactory.getLogger(IdPProfileHandlerManager.class);
46
47     /** Handler used for errors. */
48     private AbstractErrorHandler errorHandler;
49
50     /** Map of request paths to profile handlers. */
51     private Map<String, AbstractRequestURIMappedProfileHandler> profileHandlers;
52
53     /** Map of authentication methods to login handlers. */
54     private Map<String, LoginHandler> loginHandlers;
55
56     /** Constructor. */
57     public IdPProfileHandlerManager() {
58         super();
59         profileHandlers = new HashMap<String, AbstractRequestURIMappedProfileHandler>();
60         loginHandlers = new HashMap<String, LoginHandler>();
61     }
62
63     /** {@inheritDoc} */
64     public AbstractErrorHandler getErrorHandler() {
65         return errorHandler;
66     }
67
68     /**
69      * Sets the error handler.
70      * 
71      * @param handler error handler
72      */
73     public void setErrorHandler(AbstractErrorHandler handler) {
74         if (handler == null) {
75             throw new IllegalArgumentException("Error handler may not be null");
76         }
77         errorHandler = handler;
78     }
79
80     /** {@inheritDoc} */
81     public ProfileHandler getProfileHandler(ServletRequest request) {
82         ProfileHandler handler;
83
84         String requestPath = ((HttpServletRequest) request).getPathInfo();
85         log.debug("{}: Looking up profile handler for request path: {}", getId(), requestPath);
86
87         Lock readLock = getReadWriteLock().readLock();
88         readLock.lock();
89         try{
90             handler = profileHandlers.get(requestPath);
91         }finally{
92             readLock.unlock();
93         }
94
95         if (handler != null) {
96             log.debug("{}: Located profile handler of the following type for the request path: {}", getId(), handler
97                     .getClass().getName());
98         } else {
99             log.debug("{}: No profile handler registered for request path {}", getId(), requestPath);
100         }
101         return handler;
102     }
103
104     /**
105      * Gets the registered profile handlers.
106      * 
107      * @return registered profile handlers
108      */
109     public Map<String, AbstractRequestURIMappedProfileHandler> getProfileHandlers() {
110         return profileHandlers;
111     }
112
113     /**
114      * Gets the registered authentication handlers.
115      * 
116      * @return registered authentication handlers
117      */
118     public Map<String, LoginHandler> getLoginHandlers() {
119         return loginHandlers;
120     }
121
122     /** {@inheritDoc} */
123     protected void onNewContextCreated(ApplicationContext newServiceContext) throws ServiceException {
124         log.debug("{}: Loading new configuration into service", getId());
125         AbstractErrorHandler oldErrorHandler = errorHandler;
126         Map<String, AbstractRequestURIMappedProfileHandler> oldProfileHandlers = profileHandlers;
127         Map<String, LoginHandler> oldLoginHandlers = loginHandlers;
128
129         try {
130             loadNewErrorHandler(newServiceContext);
131             loadNewProfileHandlers(newServiceContext);
132             loadNewLoginHandlers(newServiceContext);
133         } catch (Exception e) {
134             errorHandler = oldErrorHandler;
135             profileHandlers = oldProfileHandlers;
136             loginHandlers = oldLoginHandlers;
137             throw new ServiceException(getId() + " configuration is not valid, retaining old configuration", e);
138         }
139     }
140
141     /**
142      * Reads the new error handler from the newly created application context and loads it into this manager.
143      * 
144      * @param newServiceContext newly created application context
145      */
146     protected void loadNewErrorHandler(ApplicationContext newServiceContext) {
147         String[] errorBeanNames = newServiceContext.getBeanNamesForType(AbstractErrorHandler.class);
148         log.debug("{}: Loading {} new error handler.", getId(), errorBeanNames.length);
149
150         errorHandler = (AbstractErrorHandler) newServiceContext.getBean(errorBeanNames[0]);
151         log.debug("{}: Loaded new error handler of type: {}", getId(), errorHandler.getClass().getName());
152     }
153
154     /**
155      * Reads the new profile handlers from the newly created application context and loads it into this manager.
156      * 
157      * @param newServiceContext newly created application context
158      */
159     protected void loadNewProfileHandlers(ApplicationContext newServiceContext) {
160         String[] profileBeanNames = newServiceContext.getBeanNamesForType(AbstractRequestURIMappedProfileHandler.class);
161         log.debug("{}: Loading {} new profile handlers.", getId(), profileBeanNames.length);
162
163         Map<String, AbstractRequestURIMappedProfileHandler> newProfileHandlers = new HashMap<String, AbstractRequestURIMappedProfileHandler>();
164         AbstractRequestURIMappedProfileHandler<?, ?> profileHandler;
165         for (String profileBeanName : profileBeanNames) {
166             profileHandler = (AbstractRequestURIMappedProfileHandler) newServiceContext.getBean(profileBeanName);
167             for (String requestPath : profileHandler.getRequestPaths()) {
168                 newProfileHandlers.put(requestPath, profileHandler);
169                 log.debug("{}: Loaded profile handler for handling requests to request path {}", getId(), requestPath);
170             }
171         }
172         profileHandlers = newProfileHandlers;
173     }
174
175     /**
176      * Reads the new authentication handlers from the newly created application context and loads it into this manager.
177      * 
178      * @param newServiceContext newly created application context
179      */
180     protected void loadNewLoginHandlers(ApplicationContext newServiceContext) {
181         String[] authnBeanNames = newServiceContext.getBeanNamesForType(LoginHandler.class);
182         log.debug("{}: Loading {} new authentication handlers.", getId(), authnBeanNames.length);
183
184         Map<String, LoginHandler> newLoginHandlers = new HashMap<String, LoginHandler>();
185         LoginHandler authnHandler;
186         for (String authnBeanName : authnBeanNames) {
187             authnHandler = (LoginHandler) newServiceContext.getBean(authnBeanName);
188             log.debug("{}: Loading authentication handler of type supporting authentication methods: {}", getId(),
189                     authnHandler.getSupportedAuthenticationMethods());
190
191             for (String authnMethod : authnHandler.getSupportedAuthenticationMethods()) {
192                 newLoginHandlers.put(authnMethod, authnHandler);
193             }
194         }
195         loginHandlers = newLoginHandlers;
196     }
197 }