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