001/* 002 * (C) Copyright 2006-2007 Nuxeo SA (http://nuxeo.com/) and others. 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 * 016 * Contributors: 017 * Nuxeo - initial API and implementation 018 * 019 * $Id: JOOoConvertPluginImpl.java 18651 2007-05-13 20:28:53Z sfermigier $ 020 */ 021 022package org.nuxeo.ecm.platform.ui.web.auth.service; 023 024import java.util.ArrayList; 025import java.util.HashMap; 026import java.util.Iterator; 027import java.util.List; 028import java.util.Map; 029import java.util.regex.Matcher; 030import java.util.regex.Pattern; 031 032import javax.servlet.ServletRequest; 033import javax.servlet.http.HttpServletRequest; 034import javax.servlet.http.HttpSession; 035 036import org.apache.commons.logging.Log; 037import org.apache.commons.logging.LogFactory; 038import org.nuxeo.ecm.platform.api.login.UserIdentificationInfo; 039import org.nuxeo.ecm.platform.api.login.UserIdentificationInfoCallbackHandler; 040import org.nuxeo.ecm.platform.ui.web.auth.CachableUserIdentificationInfo; 041import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPlugin; 042import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPropagator; 043import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationSessionManager; 044import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoCallbackHandlerFactory; 045import org.nuxeo.ecm.platform.ui.web.auth.plugins.DefaultSessionManager; 046import org.nuxeo.ecm.platform.web.common.session.NuxeoHttpSessionMonitor; 047import org.nuxeo.ecm.platform.web.common.vh.VirtualHostHelper; 048import org.nuxeo.runtime.model.ComponentContext; 049import org.nuxeo.runtime.model.ComponentInstance; 050import org.nuxeo.runtime.model.DefaultComponent; 051 052public class PluggableAuthenticationService extends DefaultComponent { 053 054 public static final String NAME = "org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService"; 055 056 public static final String EP_AUTHENTICATOR = "authenticators"; 057 058 public static final String EP_SESSIONMANAGER = "sessionManager"; 059 060 public static final String EP_CHAIN = "chain"; 061 062 public static final String EP_SPECIFIC_CHAINS = "specificChains"; 063 064 public static final String EP_PROPAGATOR = "propagator"; 065 066 public static final String EP_CBFACTORY = "JbossCallbackfactory"; 067 068 public static final String EP_STARTURL = "startURL"; 069 070 public static final String EP_OPENURL = "openUrl"; 071 072 public static final String EP_LOGINSCREEN = "loginScreen"; 073 074 private static final Log log = LogFactory.getLog(PluggableAuthenticationService.class); 075 076 private Map<String, AuthenticationPluginDescriptor> authenticatorsDescriptors; 077 078 private Map<String, NuxeoAuthenticationPlugin> authenticators; 079 080 private Map<String, NuxeoAuthenticationSessionManager> sessionManagers; 081 082 // NB: not used. Remove? 083 private NuxeoAuthenticationSessionManager defaultSessionManager; 084 085 private NuxeoAuthenticationPropagator propagator; 086 087 private NuxeoCallbackHandlerFactory cbhFactory; 088 089 private List<String> authChain; 090 091 private final Map<String, SpecificAuthChainDescriptor> specificAuthChains = new HashMap<String, SpecificAuthChainDescriptor>(); 092 093 private final List<OpenUrlDescriptor> openUrls = new ArrayList<OpenUrlDescriptor>(); 094 095 private final List<String> startupURLs = new ArrayList<String>(); 096 097 private LoginScreenConfigRegistry loginScreenConfigRegistry; 098 099 @Override 100 public void activate(ComponentContext context) { 101 authenticatorsDescriptors = new HashMap<String, AuthenticationPluginDescriptor>(); 102 authChain = new ArrayList<String>(); 103 authenticators = new HashMap<String, NuxeoAuthenticationPlugin>(); 104 sessionManagers = new HashMap<String, NuxeoAuthenticationSessionManager>(); 105 defaultSessionManager = new DefaultSessionManager(); 106 loginScreenConfigRegistry = new LoginScreenConfigRegistry(); 107 } 108 109 @Override 110 public void deactivate(ComponentContext context) { 111 authenticatorsDescriptors = null; 112 authenticators = null; 113 authChain = null; 114 sessionManagers = null; 115 defaultSessionManager = null; 116 loginScreenConfigRegistry = null; 117 } 118 119 @Override 120 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 121 122 if (extensionPoint.equals(EP_AUTHENTICATOR)) { 123 AuthenticationPluginDescriptor descriptor = (AuthenticationPluginDescriptor) contribution; 124 if (authenticatorsDescriptors.containsKey(descriptor.getName())) { 125 mergeDescriptors(descriptor); 126 log.debug("merged AuthenticationPluginDescriptor: " + descriptor.getName()); 127 } else { 128 authenticatorsDescriptors.put(descriptor.getName(), descriptor); 129 log.debug("registered AuthenticationPluginDescriptor: " + descriptor.getName()); 130 } 131 132 // create the new instance 133 AuthenticationPluginDescriptor actualDescriptor = authenticatorsDescriptors.get(descriptor.getName()); 134 try { 135 NuxeoAuthenticationPlugin authPlugin = actualDescriptor.getClassName().newInstance(); 136 authPlugin.initPlugin(actualDescriptor.getParameters()); 137 authenticators.put(actualDescriptor.getName(), authPlugin); 138 } catch (InstantiationException e) { 139 log.error( 140 "Unable to create AuthPlugin for : " + actualDescriptor.getName() + "Error : " + e.getMessage(), 141 e); 142 } catch (IllegalAccessException e) { 143 log.error( 144 "Unable to create AuthPlugin for : " + actualDescriptor.getName() + "Error : " + e.getMessage(), 145 e); 146 } 147 148 } else if (extensionPoint.equals(EP_CHAIN)) { 149 AuthenticationChainDescriptor chainContrib = (AuthenticationChainDescriptor) contribution; 150 log.debug("New authentication chain powered by " + contributor.getName()); 151 authChain.clear(); 152 authChain.addAll(chainContrib.getPluginsNames()); 153 } else if (extensionPoint.equals(EP_OPENURL)) { 154 OpenUrlDescriptor openUrlContrib = (OpenUrlDescriptor) contribution; 155 openUrls.add(openUrlContrib); 156 } else if (extensionPoint.equals(EP_STARTURL)) { 157 StartURLPatternDescriptor startupURLContrib = (StartURLPatternDescriptor) contribution; 158 startupURLs.addAll(startupURLContrib.getStartURLPatterns()); 159 } else if (extensionPoint.equals(EP_PROPAGATOR)) { 160 AuthenticationPropagatorDescriptor propagationContrib = (AuthenticationPropagatorDescriptor) contribution; 161 162 // create the new instance 163 try { 164 propagator = propagationContrib.getClassName().newInstance(); 165 } catch (InstantiationException e) { 166 log.error("Unable to create propagator", e); 167 } catch (IllegalAccessException e) { 168 log.error("Unable to create propagator", e); 169 } 170 } else if (extensionPoint.equals(EP_CBFACTORY)) { 171 CallbackHandlerFactoryDescriptor cbhfContrib = (CallbackHandlerFactoryDescriptor) contribution; 172 173 // create the new instance 174 try { 175 cbhFactory = cbhfContrib.getClassName().newInstance(); 176 } catch (InstantiationException e) { 177 log.error("Unable to create callback handler factory", e); 178 } catch (IllegalAccessException e) { 179 log.error("Unable to create callback handler factory", e); 180 } 181 } else if (extensionPoint.equals(EP_SESSIONMANAGER)) { 182 SessionManagerDescriptor smContrib = (SessionManagerDescriptor) contribution; 183 if (smContrib.enabled) { 184 try { 185 NuxeoAuthenticationSessionManager sm = smContrib.getClassName().newInstance(); 186 sessionManagers.put(smContrib.getName(), sm); 187 } catch (ReflectiveOperationException e) { 188 log.error("Unable to create session manager", e); 189 } 190 } else { 191 sessionManagers.remove(smContrib.getName()); 192 } 193 } else if (extensionPoint.equals(EP_SPECIFIC_CHAINS)) { 194 SpecificAuthChainDescriptor desc = (SpecificAuthChainDescriptor) contribution; 195 specificAuthChains.put(desc.name, desc); 196 } else if (extensionPoint.equals(EP_LOGINSCREEN)) { 197 LoginScreenConfig newConfig = (LoginScreenConfig) contribution; 198 registerLoginScreenConfig(newConfig); 199 } 200 } 201 202 @Override 203 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 204 205 if (extensionPoint.equals(EP_AUTHENTICATOR)) { 206 AuthenticationPluginDescriptor descriptor = (AuthenticationPluginDescriptor) contribution; 207 authenticatorsDescriptors.remove(descriptor.getName()); 208 log.debug("unregistered AuthenticationPlugin: " + descriptor.getName()); 209 } else if (extensionPoint.equals(EP_LOGINSCREEN)) { 210 LoginScreenConfig newConfig = (LoginScreenConfig) contribution; 211 unregisterLoginScreenConfig(newConfig); 212 } 213 } 214 215 private void mergeDescriptors(AuthenticationPluginDescriptor newContrib) { 216 AuthenticationPluginDescriptor oldDescriptor = authenticatorsDescriptors.get(newContrib.getName()); 217 218 // Enable/Disable 219 oldDescriptor.setEnabled(newContrib.getEnabled()); 220 221 // Merge parameters 222 Map<String, String> oldParameters = oldDescriptor.getParameters(); 223 oldParameters.putAll(newContrib.getParameters()); 224 oldDescriptor.setParameters(oldParameters); 225 226 // override LoginLModule 227 if (newContrib.getLoginModulePlugin() != null && newContrib.getLoginModulePlugin().length() > 0) { 228 oldDescriptor.setLoginModulePlugin(newContrib.getLoginModulePlugin()); 229 } 230 231 oldDescriptor.setStateful(newContrib.getStateful()); 232 233 if (newContrib.getClassName() != null) { 234 oldDescriptor.setClassName(newContrib.getClassName()); 235 } 236 237 oldDescriptor.setNeedStartingURLSaving(newContrib.getNeedStartingURLSaving()); 238 } 239 240 // Service API 241 242 public List<String> getStartURLPatterns() { 243 return startupURLs; 244 } 245 246 public List<String> getAuthChain() { 247 return authChain; 248 } 249 250 public List<String> getAuthChain(HttpServletRequest request) { 251 252 if (specificAuthChains == null || specificAuthChains.isEmpty()) { 253 return authChain; 254 } 255 256 SpecificAuthChainDescriptor desc = getAuthChainDescriptor(request); 257 258 if (desc != null) { 259 return desc.computeResultingChain(authChain); 260 } else { 261 return authChain; 262 } 263 } 264 265 public boolean doHandlePrompt(HttpServletRequest request) { 266 if (specificAuthChains == null || specificAuthChains.isEmpty()) { 267 return true; 268 } 269 270 SpecificAuthChainDescriptor desc = getAuthChainDescriptor(request); 271 272 return desc != null ? desc.doHandlePrompt() : SpecificAuthChainDescriptor.DEFAULT_HANDLE_PROMPT_VALUE; 273 274 } 275 276 private SpecificAuthChainDescriptor getAuthChainDescriptor(HttpServletRequest request) { 277 String specificAuthChainName = getSpecificAuthChainName(request); 278 SpecificAuthChainDescriptor desc = specificAuthChains.get(specificAuthChainName); 279 return desc; 280 } 281 282 public String getSpecificAuthChainName(HttpServletRequest request) { 283 for (String specificAuthChainName : specificAuthChains.keySet()) { 284 SpecificAuthChainDescriptor desc = specificAuthChains.get(specificAuthChainName); 285 286 List<Pattern> urlPatterns = desc.getUrlPatterns(); 287 if (!urlPatterns.isEmpty()) { 288 // test on URI 289 String requestUrl = request.getRequestURI(); 290 for (Pattern pattern : urlPatterns) { 291 Matcher m = pattern.matcher(requestUrl); 292 if (m.matches()) { 293 return specificAuthChainName; 294 } 295 } 296 } 297 298 Map<String, Pattern> headerPattern = desc.getHeaderPatterns(); 299 300 for (String headerName : headerPattern.keySet()) { 301 String headerValue = request.getHeader(headerName); 302 if (headerValue != null) { 303 Matcher m = headerPattern.get(headerName).matcher(headerValue); 304 if (m.matches()) { 305 return specificAuthChainName; 306 } 307 } 308 } 309 } 310 return null; 311 } 312 313 public UserIdentificationInfoCallbackHandler getCallbackHandler(UserIdentificationInfo userIdent) { 314 if (cbhFactory == null) { 315 return new UserIdentificationInfoCallbackHandler(userIdent); 316 } 317 return cbhFactory.createCallbackHandler(userIdent); 318 } 319 320 public NuxeoAuthenticationPropagator.CleanupCallback propagateUserIdentificationInformation( 321 CachableUserIdentificationInfo cachableUserIdent) { 322 if (propagator != null) { 323 return propagator.propagateUserIdentificationInformation(cachableUserIdent); 324 } 325 return null; 326 } 327 328 public List<NuxeoAuthenticationPlugin> getPluginChain() { 329 List<NuxeoAuthenticationPlugin> result = new ArrayList<NuxeoAuthenticationPlugin>(); 330 331 for (String pluginName : authChain) { 332 if (authenticatorsDescriptors.containsKey(pluginName) 333 && authenticatorsDescriptors.get(pluginName).getEnabled()) { 334 if (authenticators.containsKey(pluginName)) { 335 result.add(authenticators.get(pluginName)); 336 } 337 } 338 } 339 return result; 340 } 341 342 public NuxeoAuthenticationPlugin getPlugin(String pluginName) { 343 if (authenticatorsDescriptors.containsKey(pluginName) 344 && authenticatorsDescriptors.get(pluginName).getEnabled()) { 345 if (authenticators.containsKey(pluginName)) { 346 return authenticators.get(pluginName); 347 } 348 } 349 return null; 350 } 351 352 public AuthenticationPluginDescriptor getDescriptor(String pluginName) { 353 if (authenticatorsDescriptors.containsKey(pluginName)) { 354 return authenticatorsDescriptors.get(pluginName); 355 } else { 356 log.error("Plugin " + pluginName + " not registered or not created"); 357 return null; 358 } 359 } 360 361 public void invalidateSession(ServletRequest request) { 362 boolean done = false; 363 if (!sessionManagers.isEmpty()) { 364 Iterator<NuxeoAuthenticationSessionManager> it = sessionManagers.values().iterator(); 365 while (it.hasNext() && !(done = it.next().invalidateSession(request))) { 366 } 367 } 368 if (!done) { 369 HttpServletRequest httpRequest = (HttpServletRequest) request; 370 HttpSession session = httpRequest.getSession(false); 371 if (session != null) { 372 session.invalidate(); 373 } 374 } 375 } 376 377 public HttpSession reinitSession(HttpServletRequest httpRequest) { 378 if (!sessionManagers.isEmpty()) { 379 for (String smName : sessionManagers.keySet()) { 380 NuxeoAuthenticationSessionManager sm = sessionManagers.get(smName); 381 sm.onBeforeSessionReinit(httpRequest); 382 } 383 } 384 385 HttpSession session = httpRequest.getSession(true); 386 387 if (!sessionManagers.isEmpty()) { 388 for (String smName : sessionManagers.keySet()) { 389 NuxeoAuthenticationSessionManager sm = sessionManagers.get(smName); 390 sm.onAfterSessionReinit(httpRequest); 391 } 392 } 393 return session; 394 } 395 396 public boolean canBypassRequest(ServletRequest request) { 397 if (!sessionManagers.isEmpty()) { 398 for (String smName : sessionManagers.keySet()) { 399 NuxeoAuthenticationSessionManager sm = sessionManagers.get(smName); 400 if (sm.canBypassRequest(request)) { 401 return true; 402 } 403 } 404 } 405 return false; 406 } 407 408 public boolean needResetLogin(ServletRequest request) { 409 if (!sessionManagers.isEmpty()) { 410 for (NuxeoAuthenticationSessionManager sm : sessionManagers.values()) { 411 if (sm.needResetLogin(request)) { 412 return true; 413 } 414 } 415 } 416 return false; 417 } 418 419 public String getBaseURL(ServletRequest request) { 420 return VirtualHostHelper.getBaseURL(request); 421 } 422 423 public void onAuthenticatedSessionCreated(ServletRequest request, HttpSession session, 424 CachableUserIdentificationInfo cachebleUserInfo) { 425 426 NuxeoHttpSessionMonitor.instance().associatedUser(session, cachebleUserInfo.getPrincipal().getName()); 427 428 if (!sessionManagers.isEmpty()) { 429 for (String smName : sessionManagers.keySet()) { 430 NuxeoAuthenticationSessionManager sm = sessionManagers.get(smName); 431 sm.onAuthenticatedSessionCreated(request, session, cachebleUserInfo); 432 } 433 } 434 } 435 436 public List<OpenUrlDescriptor> getOpenUrls() { 437 return openUrls; 438 } 439 440 public LoginScreenConfig getLoginScreenConfig() { 441 return loginScreenConfigRegistry.getConfig(); 442 } 443 444 /** 445 * @since 10.10 446 */ 447 public void registerLoginScreenConfig(LoginScreenConfig config) { 448 loginScreenConfigRegistry.addContribution(config); 449 } 450 451 /** 452 * @since 10.10 453 */ 454 public void unregisterLoginScreenConfig(LoginScreenConfig config) { 455 loginScreenConfigRegistry.removeContribution(config); 456 } 457 458}