/*
* (C) Copyright 2006-2007 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* Nuxeo - initial API and implementation
*
* $Id: JOOoConvertPluginImpl.java 18651 2007-05-13 20:28:53Z sfermigier $
*/
package org.nuxeo.ecm.platform.ui.web.auth.service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.platform.api.login.UserIdentificationInfo;
import org.nuxeo.ecm.platform.api.login.UserIdentificationInfoCallbackHandler;
import org.nuxeo.ecm.platform.ui.web.auth.CachableUserIdentificationInfo;
import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthPreFilter;
import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPlugin;
import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPropagator;
import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationSessionManager;
import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoCallbackHandlerFactory;
import org.nuxeo.ecm.platform.ui.web.auth.plugins.DefaultSessionManager;
import org.nuxeo.ecm.platform.web.common.session.NuxeoHttpSessionMonitor;
import org.nuxeo.ecm.platform.web.common.vh.VirtualHostHelper;
import org.nuxeo.runtime.api.login.LoginAs;
import org.nuxeo.runtime.model.ComponentContext;
import org.nuxeo.runtime.model.ComponentInstance;
import org.nuxeo.runtime.model.DefaultComponent;
public class PluggableAuthenticationService extends DefaultComponent {
public static final String NAME = "org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService";
public static final String EP_AUTHENTICATOR = "authenticators";
public static final String EP_SESSIONMANAGER = "sessionManager";
public static final String EP_CHAIN = "chain";
public static final String EP_SPECIFIC_CHAINS = "specificChains";
public static final String EP_PROPAGATOR = "propagator";
public static final String EP_CBFACTORY = "JbossCallbackfactory";
public static final String EP_STARTURL = "startURL";
public static final String EP_OPENURL = "openUrl";
public static final String EP_PREFILTER = "preFilter";
public static final String EP_LOGINSCREEN = "loginScreen";
private static final Log log = LogFactory.getLog(PluggableAuthenticationService.class);
private Map<String, AuthenticationPluginDescriptor> authenticatorsDescriptors;
private Map<String, NuxeoAuthenticationPlugin> authenticators;
private Map<String, AuthPreFilterDescriptor> preFiltersDesc;
private List<NuxeoAuthPreFilter> preFilters;
private Map<String, NuxeoAuthenticationSessionManager> sessionManagers;
// NB: not used. Remove?
private NuxeoAuthenticationSessionManager defaultSessionManager;
private NuxeoAuthenticationPropagator propagator;
private NuxeoCallbackHandlerFactory cbhFactory;
private List<String> authChain;
private final Map<String, SpecificAuthChainDescriptor> specificAuthChains = new HashMap<String, SpecificAuthChainDescriptor>();
private final List<OpenUrlDescriptor> openUrls = new ArrayList<OpenUrlDescriptor>();
private final List<String> startupURLs = new ArrayList<String>();
private LoginScreenConfigRegistry loginScreenConfigRegistry;
@Override
public void activate(ComponentContext context) {
authenticatorsDescriptors = new HashMap<String, AuthenticationPluginDescriptor>();
authChain = new ArrayList<String>();
authenticators = new HashMap<String, NuxeoAuthenticationPlugin>();
sessionManagers = new HashMap<String, NuxeoAuthenticationSessionManager>();
defaultSessionManager = new DefaultSessionManager();
loginScreenConfigRegistry = new LoginScreenConfigRegistry();
}
@Override
public void deactivate(ComponentContext context) {
authenticatorsDescriptors = null;
authenticators = null;
authChain = null;
sessionManagers = null;
defaultSessionManager = null;
loginScreenConfigRegistry = null;
}
@Override
public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
if (extensionPoint.equals(EP_AUTHENTICATOR)) {
AuthenticationPluginDescriptor descriptor = (AuthenticationPluginDescriptor) contribution;
if (authenticatorsDescriptors.containsKey(descriptor.getName())) {
mergeDescriptors(descriptor);
log.debug("merged AuthenticationPluginDescriptor: " + descriptor.getName());
} else {
authenticatorsDescriptors.put(descriptor.getName(), descriptor);
log.debug("registered AuthenticationPluginDescriptor: " + descriptor.getName());
}
// create the new instance
AuthenticationPluginDescriptor actualDescriptor = authenticatorsDescriptors.get(descriptor.getName());
try {
NuxeoAuthenticationPlugin authPlugin = actualDescriptor.getClassName().newInstance();
authPlugin.initPlugin(actualDescriptor.getParameters());
authenticators.put(actualDescriptor.getName(), authPlugin);
} catch (InstantiationException e) {
log.error(
"Unable to create AuthPlugin for : " + actualDescriptor.getName() + "Error : " + e.getMessage(),
e);
} catch (IllegalAccessException e) {
log.error(
"Unable to create AuthPlugin for : " + actualDescriptor.getName() + "Error : " + e.getMessage(),
e);
}
} else if (extensionPoint.equals(EP_CHAIN)) {
AuthenticationChainDescriptor chainContrib = (AuthenticationChainDescriptor) contribution;
log.debug("New authentication chain powered by " + contributor.getName());
authChain.clear();
authChain.addAll(chainContrib.getPluginsNames());
} else if (extensionPoint.equals(EP_OPENURL)) {
OpenUrlDescriptor openUrlContrib = (OpenUrlDescriptor) contribution;
openUrls.add(openUrlContrib);
} else if (extensionPoint.equals(EP_STARTURL)) {
StartURLPatternDescriptor startupURLContrib = (StartURLPatternDescriptor) contribution;
startupURLs.addAll(startupURLContrib.getStartURLPatterns());
} else if (extensionPoint.equals(EP_PROPAGATOR)) {
AuthenticationPropagatorDescriptor propagationContrib = (AuthenticationPropagatorDescriptor) contribution;
// create the new instance
try {
propagator = propagationContrib.getClassName().newInstance();
} catch (InstantiationException e) {
log.error("Unable to create propagator", e);
} catch (IllegalAccessException e) {
log.error("Unable to create propagator", e);
}
} else if (extensionPoint.equals(EP_CBFACTORY)) {
CallbackHandlerFactoryDescriptor cbhfContrib = (CallbackHandlerFactoryDescriptor) contribution;
// create the new instance
try {
cbhFactory = cbhfContrib.getClassName().newInstance();
} catch (InstantiationException e) {
log.error("Unable to create callback handler factory", e);
} catch (IllegalAccessException e) {
log.error("Unable to create callback handler factory", e);
}
} else if (extensionPoint.equals(EP_SESSIONMANAGER)) {
SessionManagerDescriptor smContrib = (SessionManagerDescriptor) contribution;
if (smContrib.enabled) {
try {
NuxeoAuthenticationSessionManager sm = smContrib.getClassName().newInstance();
sessionManagers.put(smContrib.getName(), sm);
} catch (ReflectiveOperationException e) {
log.error("Unable to create session manager", e);
}
} else {
sessionManagers.remove(smContrib.getName());
}
} else if (extensionPoint.equals(EP_SPECIFIC_CHAINS)) {
SpecificAuthChainDescriptor desc = (SpecificAuthChainDescriptor) contribution;
specificAuthChains.put(desc.name, desc);
} else if (extensionPoint.equals(EP_PREFILTER)) {
AuthPreFilterDescriptor desc = (AuthPreFilterDescriptor) contribution;
if (preFiltersDesc == null) {
preFiltersDesc = new HashMap<String, AuthPreFilterDescriptor>();
}
if (desc.enabled) {
preFiltersDesc.put(desc.getName(), desc);
} else {
preFiltersDesc.remove(desc.getName());
}
} else if (extensionPoint.equals(EP_LOGINSCREEN)) {
LoginScreenConfig newConfig = (LoginScreenConfig) contribution;
loginScreenConfigRegistry.addContribution(newConfig);
}
}
@Override
public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
if (extensionPoint.equals(EP_AUTHENTICATOR)) {
AuthenticationPluginDescriptor descriptor = (AuthenticationPluginDescriptor) contribution;
authenticatorsDescriptors.remove(descriptor.getName());
log.debug("unregistered AuthenticationPlugin: " + descriptor.getName());
} else if (extensionPoint.equals(EP_LOGINSCREEN)) {
LoginScreenConfig newConfig = (LoginScreenConfig) contribution;
loginScreenConfigRegistry.removeContribution(newConfig);
}
}
private void mergeDescriptors(AuthenticationPluginDescriptor newContrib) {
AuthenticationPluginDescriptor oldDescriptor = authenticatorsDescriptors.get(newContrib.getName());
// Enable/Disable
oldDescriptor.setEnabled(newContrib.getEnabled());
// Merge parameters
Map<String, String> oldParameters = oldDescriptor.getParameters();
oldParameters.putAll(newContrib.getParameters());
oldDescriptor.setParameters(oldParameters);
// override LoginLModule
if (newContrib.getLoginModulePlugin() != null && newContrib.getLoginModulePlugin().length() > 0) {
oldDescriptor.setLoginModulePlugin(newContrib.getLoginModulePlugin());
}
oldDescriptor.setStateful(newContrib.getStateful());
if (newContrib.getClassName() != null) {
oldDescriptor.setClassName(newContrib.getClassName());
}
oldDescriptor.setNeedStartingURLSaving(newContrib.getNeedStartingURLSaving());
}
// Service API
public List<String> getStartURLPatterns() {
return startupURLs;
}
public List<String> getAuthChain() {
return authChain;
}
public List<String> getAuthChain(HttpServletRequest request) {
if (specificAuthChains == null || specificAuthChains.isEmpty()) {
return authChain;
}
SpecificAuthChainDescriptor desc = getAuthChainDescriptor(request);
if (desc != null) {
return desc.computeResultingChain(authChain);
} else {
return authChain;
}
}
public boolean doHandlePrompt(HttpServletRequest request) {
if (specificAuthChains == null || specificAuthChains.isEmpty()) {
return true;
}
SpecificAuthChainDescriptor desc = getAuthChainDescriptor(request);
return desc != null ? desc.doHandlePrompt() : SpecificAuthChainDescriptor.DEFAULT_HANDLE_PROMPT_VALUE;
}
private SpecificAuthChainDescriptor getAuthChainDescriptor(HttpServletRequest request) {
String specificAuthChainName = getSpecificAuthChainName(request);
SpecificAuthChainDescriptor desc = specificAuthChains.get(specificAuthChainName);
return desc;
}
public String getSpecificAuthChainName(HttpServletRequest request) {
for (String specificAuthChainName : specificAuthChains.keySet()) {
SpecificAuthChainDescriptor desc = specificAuthChains.get(specificAuthChainName);
List<Pattern> urlPatterns = desc.getUrlPatterns();
if (!urlPatterns.isEmpty()) {
// test on URI
String requestUrl = request.getRequestURI();
for (Pattern pattern : urlPatterns) {
Matcher m = pattern.matcher(requestUrl);
if (m.matches()) {
return specificAuthChainName;
}
}
}
Map<String, Pattern> headerPattern = desc.getHeaderPatterns();
for (String headerName : headerPattern.keySet()) {
String headerValue = request.getHeader(headerName);
if (headerValue != null) {
Matcher m = headerPattern.get(headerName).matcher(headerValue);
if (m.matches()) {
return specificAuthChainName;
}
}
}
}
return null;
}
public UserIdentificationInfoCallbackHandler getCallbackHandler(UserIdentificationInfo userIdent) {
if (cbhFactory == null) {
return new UserIdentificationInfoCallbackHandler(userIdent);
}
return cbhFactory.createCallbackHandler(userIdent);
}
public NuxeoAuthenticationPropagator.CleanupCallback propagateUserIdentificationInformation(
CachableUserIdentificationInfo cachableUserIdent) {
if (propagator != null) {
return propagator.propagateUserIdentificationInformation(cachableUserIdent);
}
return null;
}
public List<NuxeoAuthenticationPlugin> getPluginChain() {
List<NuxeoAuthenticationPlugin> result = new ArrayList<NuxeoAuthenticationPlugin>();
for (String pluginName : authChain) {
if (authenticatorsDescriptors.containsKey(pluginName)
&& authenticatorsDescriptors.get(pluginName).getEnabled()) {
if (authenticators.containsKey(pluginName)) {
result.add(authenticators.get(pluginName));
}
}
}
return result;
}
public NuxeoAuthenticationPlugin getPlugin(String pluginName) {
if (authenticatorsDescriptors.containsKey(pluginName)
&& authenticatorsDescriptors.get(pluginName).getEnabled()) {
if (authenticators.containsKey(pluginName)) {
return authenticators.get(pluginName);
}
}
return null;
}
public AuthenticationPluginDescriptor getDescriptor(String pluginName) {
if (authenticatorsDescriptors.containsKey(pluginName)) {
return authenticatorsDescriptors.get(pluginName);
} else {
log.error("Plugin " + pluginName + " not registered or not created");
return null;
}
}
public void invalidateSession(ServletRequest request) {
boolean done = false;
if (!sessionManagers.isEmpty()) {
Iterator<NuxeoAuthenticationSessionManager> it = sessionManagers.values().iterator();
while (it.hasNext() && !(done = it.next().invalidateSession(request))) {
}
}
if (!done) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpSession session = httpRequest.getSession(false);
if (session != null) {
session.invalidate();
}
}
}
public HttpSession reinitSession(HttpServletRequest httpRequest) {
if (!sessionManagers.isEmpty()) {
for (String smName : sessionManagers.keySet()) {
NuxeoAuthenticationSessionManager sm = sessionManagers.get(smName);
sm.onBeforeSessionReinit(httpRequest);
}
}
HttpSession session = httpRequest.getSession(true);
if (!sessionManagers.isEmpty()) {
for (String smName : sessionManagers.keySet()) {
NuxeoAuthenticationSessionManager sm = sessionManagers.get(smName);
sm.onAfterSessionReinit(httpRequest);
}
}
return session;
}
public boolean canBypassRequest(ServletRequest request) {
if (!sessionManagers.isEmpty()) {
for (String smName : sessionManagers.keySet()) {
NuxeoAuthenticationSessionManager sm = sessionManagers.get(smName);
if (sm.canBypassRequest(request)) {
return true;
}
}
}
return false;
}
public boolean needResetLogin(ServletRequest request) {
if (!sessionManagers.isEmpty()) {
for (NuxeoAuthenticationSessionManager sm : sessionManagers.values()) {
if (sm.needResetLogin(request)) {
return true;
}
}
}
return false;
}
public String getBaseURL(ServletRequest request) {
return VirtualHostHelper.getBaseURL(request);
}
public void onAuthenticatedSessionCreated(ServletRequest request, HttpSession session,
CachableUserIdentificationInfo cachebleUserInfo) {
NuxeoHttpSessionMonitor.instance().associatedUser(session, cachebleUserInfo.getPrincipal().getName());
if (!sessionManagers.isEmpty()) {
for (String smName : sessionManagers.keySet()) {
NuxeoAuthenticationSessionManager sm = sessionManagers.get(smName);
sm.onAuthenticatedSessionCreated(request, session, cachebleUserInfo);
}
}
}
public List<OpenUrlDescriptor> getOpenUrls() {
return openUrls;
}
// preFilter management
public synchronized void initPreFilters() {
if (preFiltersDesc != null) {
List<AuthPreFilterDescriptor> sortableDesc = new ArrayList<AuthPreFilterDescriptor>();
sortableDesc.addAll(preFiltersDesc.values());
Collections.sort(sortableDesc);
preFilters = new ArrayList<NuxeoAuthPreFilter>();
for (AuthPreFilterDescriptor desc : sortableDesc) {
try {
NuxeoAuthPreFilter preFilter = (NuxeoAuthPreFilter) desc.getClassName().newInstance();
preFilters.add(preFilter);
} catch (ReflectiveOperationException e) {
log.error("Unable to create preFilter " + desc.getName() + " and class" + desc.getClassName(), e);
}
}
}
}
public List<NuxeoAuthPreFilter> getPreFilters() {
if (preFilters == null || preFilters.isEmpty()) {
return null;
}
return preFilters;
}
@SuppressWarnings("unchecked")
@Override
public <T> T getAdapter(Class<T> adapter) {
if (LoginAs.class == adapter) {
return (T) new LoginAsImpl();
}
return super.getAdapter(adapter);
}
public LoginScreenConfig getLoginScreenConfig() {
return loginScreenConfigRegistry.getConfig();
}
}