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