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