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 preFiltersDesc.put(desc.getName(), desc); 211 } else if (extensionPoint.equals(EP_LOGINSCREEN)) { 212 LoginScreenConfig newConfig = (LoginScreenConfig) contribution; 213 loginScreenConfigRegistry.addContribution(newConfig); 214 } 215 } 216 217 @Override 218 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 219 220 if (extensionPoint.equals(EP_AUTHENTICATOR)) { 221 AuthenticationPluginDescriptor descriptor = (AuthenticationPluginDescriptor) contribution; 222 authenticatorsDescriptors.remove(descriptor.getName()); 223 log.debug("unregistered AuthenticationPlugin: " + descriptor.getName()); 224 } else if (extensionPoint.equals(EP_LOGINSCREEN)) { 225 LoginScreenConfig newConfig = (LoginScreenConfig) contribution; 226 loginScreenConfigRegistry.removeContribution(newConfig); 227 } 228 } 229 230 private void mergeDescriptors(AuthenticationPluginDescriptor newContrib) { 231 AuthenticationPluginDescriptor oldDescriptor = authenticatorsDescriptors.get(newContrib.getName()); 232 233 // Enable/Disable 234 oldDescriptor.setEnabled(newContrib.getEnabled()); 235 236 // Merge parameters 237 Map<String, String> oldParameters = oldDescriptor.getParameters(); 238 oldParameters.putAll(newContrib.getParameters()); 239 oldDescriptor.setParameters(oldParameters); 240 241 // override LoginLModule 242 if (newContrib.getLoginModulePlugin() != null && newContrib.getLoginModulePlugin().length() > 0) { 243 oldDescriptor.setLoginModulePlugin(newContrib.getLoginModulePlugin()); 244 } 245 246 oldDescriptor.setStateful(newContrib.getStateful()); 247 248 if (newContrib.getClassName() != null) { 249 oldDescriptor.setClassName(newContrib.getClassName()); 250 } 251 252 oldDescriptor.setNeedStartingURLSaving(newContrib.getNeedStartingURLSaving()); 253 } 254 255 // Service API 256 257 public List<String> getStartURLPatterns() { 258 return startupURLs; 259 } 260 261 public List<String> getAuthChain() { 262 return authChain; 263 } 264 265 public List<String> getAuthChain(HttpServletRequest request) { 266 267 if (specificAuthChains == null || specificAuthChains.isEmpty()) { 268 return authChain; 269 } 270 271 SpecificAuthChainDescriptor desc = getAuthChainDescriptor(request); 272 273 if (desc != null) { 274 return desc.computeResultingChain(authChain); 275 } else { 276 return authChain; 277 } 278 } 279 280 public boolean doHandlePrompt(HttpServletRequest request) { 281 if (specificAuthChains == null || specificAuthChains.isEmpty()) { 282 return true; 283 } 284 285 SpecificAuthChainDescriptor desc = getAuthChainDescriptor(request); 286 287 return desc != null ? desc.doHandlePrompt() : SpecificAuthChainDescriptor.DEFAULT_HANDLE_PROMPT_VALUE; 288 289 } 290 291 private SpecificAuthChainDescriptor getAuthChainDescriptor(HttpServletRequest request) { 292 String specificAuthChainName = getSpecificAuthChainName(request); 293 SpecificAuthChainDescriptor desc = specificAuthChains.get(specificAuthChainName); 294 return desc; 295 } 296 297 public String getSpecificAuthChainName(HttpServletRequest request) { 298 for (String specificAuthChainName : specificAuthChains.keySet()) { 299 SpecificAuthChainDescriptor desc = specificAuthChains.get(specificAuthChainName); 300 301 List<Pattern> urlPatterns = desc.getUrlPatterns(); 302 if (!urlPatterns.isEmpty()) { 303 // test on URI 304 String requestUrl = request.getRequestURI(); 305 for (Pattern pattern : urlPatterns) { 306 Matcher m = pattern.matcher(requestUrl); 307 if (m.matches()) { 308 return specificAuthChainName; 309 } 310 } 311 } 312 313 Map<String, Pattern> headerPattern = desc.getHeaderPatterns(); 314 315 for (String headerName : headerPattern.keySet()) { 316 String headerValue = request.getHeader(headerName); 317 if (headerValue != null) { 318 Matcher m = headerPattern.get(headerName).matcher(headerValue); 319 if (m.matches()) { 320 return specificAuthChainName; 321 } 322 } 323 } 324 } 325 return null; 326 } 327 328 public UserIdentificationInfoCallbackHandler getCallbackHandler(UserIdentificationInfo userIdent) { 329 if (cbhFactory == null) { 330 return new UserIdentificationInfoCallbackHandler(userIdent); 331 } 332 return cbhFactory.createCallbackHandler(userIdent); 333 } 334 335 public NuxeoAuthenticationPropagator.CleanupCallback propagateUserIdentificationInformation( 336 CachableUserIdentificationInfo cachableUserIdent) { 337 if (propagator != null) { 338 return propagator.propagateUserIdentificationInformation(cachableUserIdent); 339 } 340 return null; 341 } 342 343 public List<NuxeoAuthenticationPlugin> getPluginChain() { 344 List<NuxeoAuthenticationPlugin> result = new ArrayList<NuxeoAuthenticationPlugin>(); 345 346 for (String pluginName : authChain) { 347 if (authenticatorsDescriptors.containsKey(pluginName) 348 && authenticatorsDescriptors.get(pluginName).getEnabled()) { 349 if (authenticators.containsKey(pluginName)) { 350 result.add(authenticators.get(pluginName)); 351 } 352 } 353 } 354 return result; 355 } 356 357 public NuxeoAuthenticationPlugin getPlugin(String pluginName) { 358 if (authenticatorsDescriptors.containsKey(pluginName) && authenticatorsDescriptors.get(pluginName).getEnabled()) { 359 if (authenticators.containsKey(pluginName)) { 360 return authenticators.get(pluginName); 361 } 362 } 363 return null; 364 } 365 366 public AuthenticationPluginDescriptor getDescriptor(String pluginName) { 367 if (authenticatorsDescriptors.containsKey(pluginName)) { 368 return authenticatorsDescriptors.get(pluginName); 369 } else { 370 log.error("Plugin " + pluginName + " not registered or not created"); 371 return null; 372 } 373 } 374 375 public void invalidateSession(ServletRequest request) { 376 boolean done = false; 377 if (!sessionManagers.isEmpty()) { 378 Iterator<NuxeoAuthenticationSessionManager> it = sessionManagers.values().iterator(); 379 while (it.hasNext() && !(done = it.next().invalidateSession(request))) { 380 } 381 } 382 if (!done) { 383 HttpServletRequest httpRequest = (HttpServletRequest) request; 384 HttpSession session = httpRequest.getSession(false); 385 if (session != null) { 386 session.invalidate(); 387 } 388 } 389 } 390 391 public HttpSession reinitSession(HttpServletRequest httpRequest) { 392 if (!sessionManagers.isEmpty()) { 393 for (String smName : sessionManagers.keySet()) { 394 NuxeoAuthenticationSessionManager sm = sessionManagers.get(smName); 395 sm.onBeforeSessionReinit(httpRequest); 396 } 397 } 398 399 HttpSession session = httpRequest.getSession(true); 400 401 if (!sessionManagers.isEmpty()) { 402 for (String smName : sessionManagers.keySet()) { 403 NuxeoAuthenticationSessionManager sm = sessionManagers.get(smName); 404 sm.onAfterSessionReinit(httpRequest); 405 } 406 } 407 return session; 408 } 409 410 public boolean canBypassRequest(ServletRequest request) { 411 if (!sessionManagers.isEmpty()) { 412 for (String smName : sessionManagers.keySet()) { 413 NuxeoAuthenticationSessionManager sm = sessionManagers.get(smName); 414 if (sm.canBypassRequest(request)) { 415 return true; 416 } 417 } 418 } 419 return false; 420 } 421 422 public boolean needResetLogin(ServletRequest request) { 423 if (!sessionManagers.isEmpty()) { 424 for (NuxeoAuthenticationSessionManager sm : sessionManagers.values()) { 425 if (sm.needResetLogin(request)) { 426 return true; 427 } 428 } 429 } 430 return false; 431 } 432 433 public String getBaseURL(ServletRequest request) { 434 return VirtualHostHelper.getBaseURL(request); 435 } 436 437 public void onAuthenticatedSessionCreated(ServletRequest request, HttpSession session, 438 CachableUserIdentificationInfo cachebleUserInfo) { 439 440 NuxeoHttpSessionMonitor.instance().associatedUser(session, cachebleUserInfo.getPrincipal().getName()); 441 442 if (!sessionManagers.isEmpty()) { 443 for (String smName : sessionManagers.keySet()) { 444 NuxeoAuthenticationSessionManager sm = sessionManagers.get(smName); 445 sm.onAuthenticatedSessionCreated(request, session, cachebleUserInfo); 446 } 447 } 448 } 449 450 public List<OpenUrlDescriptor> getOpenUrls() { 451 return openUrls; 452 } 453 454 // preFilter management 455 456 public synchronized void initPreFilters() { 457 458 if (preFiltersDesc != null) { 459 List<AuthPreFilterDescriptor> sortableDesc = new ArrayList<AuthPreFilterDescriptor>(); 460 461 sortableDesc.addAll(preFiltersDesc.values()); 462 463 Collections.sort(sortableDesc); 464 465 preFilters = new ArrayList<NuxeoAuthPreFilter>(); 466 467 for (AuthPreFilterDescriptor desc : sortableDesc) { 468 try { 469 NuxeoAuthPreFilter preFilter = (NuxeoAuthPreFilter) desc.getClassName().newInstance(); 470 preFilters.add(preFilter); 471 } catch (ReflectiveOperationException e) { 472 log.error("Unable to create preFilter " + desc.getName() + " and class" + desc.getClassName(), e); 473 } 474 } 475 } 476 } 477 478 public List<NuxeoAuthPreFilter> getPreFilters() { 479 if (preFilters == null || preFilters.isEmpty()) { 480 return null; 481 } 482 return preFilters; 483 } 484 485 @SuppressWarnings("unchecked") 486 @Override 487 public <T> T getAdapter(Class<T> adapter) { 488 if (LoginAs.class == adapter) { 489 return (T) new LoginAsImpl(); 490 } 491 return super.getAdapter(adapter); 492 } 493 494 public LoginScreenConfig getLoginScreenConfig() { 495 return loginScreenConfigRegistry.getConfig(); 496 } 497 498}