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