ContextRegistrator.java revision 2ae41f94c30465830758177491494f918a7a79bc
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk/*
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk *
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * Copyright (c) 2011-2012 ForgeRock AS. All rights reserved.
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk *
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * The contents of this file are subject to the terms
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * of the Common Development and Distribution License
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * (the License). You may not use this file except in
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * compliance with the License.
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk *
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * You can obtain a copy of the License at
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * http://forgerock.org/license/CDDLv1.0.html
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * See the License for the specific language governing
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * permission and limitations under the License.
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk *
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * When distributing Covered Code, include this CDDL
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * Header Notice in each file and include the License file
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * at http://forgerock.org/license/CDDLv1.0.html
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * If applicable, add the following below the CDDL Header,
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * with the fields enclosed by brackets [] replaced by
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * your own identifying information:
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * "Portions Copyrighted [year] [name of copyright owner]"
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk */
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkpackage org.forgerock.openidm.http;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport java.io.IOException;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport java.io.InputStream;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport java.net.URL;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport java.util.ArrayList;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport java.util.Dictionary;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport java.util.Enumeration;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport java.util.Hashtable;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport java.util.List;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport org.apache.felix.scr.annotations.Activate;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport org.apache.felix.scr.annotations.Component;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport org.apache.felix.scr.annotations.ConfigurationPolicy;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport org.apache.felix.scr.annotations.Deactivate;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport org.apache.felix.scr.annotations.Reference;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport org.apache.felix.scr.annotations.Service;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport org.ops4j.pax.web.extender.whiteboard.ErrorPageMapping;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport org.ops4j.pax.web.extender.whiteboard.ExtenderConstants;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport org.ops4j.pax.web.extender.whiteboard.FilterMapping;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport org.ops4j.pax.web.extender.whiteboard.ListenerMapping;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport org.ops4j.pax.web.extender.whiteboard.ResourceMapping;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport org.ops4j.pax.web.extender.whiteboard.ServletMapping;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport org.ops4j.pax.web.extender.whiteboard.WelcomeFileMapping;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport org.ops4j.pax.web.service.WebContainer;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport org.osgi.framework.BundleContext;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport org.osgi.framework.ServiceRegistration;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport org.osgi.service.component.ComponentContext;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport org.osgi.service.http.HttpContext;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport org.slf4j.Logger;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkimport org.slf4j.LoggerFactory;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk/**
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * Http context to share amongst OpenIDM servlets to allow for applying uniform
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * security handling
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk *
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * @author aegloff
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk */
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk@Component(name = "org.forgerock.openidm.http.context", immediate = true,
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk policy = ConfigurationPolicy.IGNORE)
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk@Service
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenkpublic final class ContextRegistrator {
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk final static Logger logger = LoggerFactory.getLogger(ContextRegistrator.class);
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk public static final String OPENIDM = "openidm";
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk @Reference
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk WebContainer httpService;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk HttpContext httpContext;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk static ComponentContext context;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk List<SecurityConfigurator> securityConfigurators = new ArrayList<SecurityConfigurator>();
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk /**
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * Allow access for the fragments
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk *
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * @return bundle context if activated, null otherwise
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk */
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk public static BundleContext getBundleContext() {
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk if (context != null) {
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk return context.getBundleContext();
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk } else {
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk return null;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk }
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk }
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk @Activate
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk protected void activate(ComponentContext context) throws Exception {
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk this.context = context;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk httpContext = httpService.createDefaultHttpContext();
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk Dictionary<String, Object> contextProps = new Hashtable<String, Object>();
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk contextProps.put("openidm.contextid", "shared");
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk contextProps.put(ExtenderConstants.PROPERTY_HTTP_CONTEXT_ID, OPENIDM);
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk // TODO: Consider the HttpContextMapping it allows to configure the path
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk context.getBundleContext().registerService(HttpContext.class.getName(), httpContext,
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk contextProps);
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk logger.debug("Registered OpenIDM shared http context");
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk // Apply the pluggable security configurations on the httpContext
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk initSecurityConfigurators();
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk activateSecurityConfigurators(context, httpContext);
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk }
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk @Deactivate
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk protected void deactivate(ComponentContext context) {
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk deactivateSecurityConfigurators(context, httpContext);
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk }
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk /**
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * Loads and instantiates the pluggable Security Configurators
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk *
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * To allow for pluggability with fragments a simple convention is used to
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * find security configurator(s); 1. A properties file contains a property
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * security.configurator.class of a class in the bundle fragment that
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * implements SecurityConfigurator and has a no-arg constructor 2. The
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * properties file is name <prefix>securityconfigurator.properties and
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * placed somewhere in the bundle or attached fragments
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk */
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk private void initSecurityConfigurators() {
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk Enumeration<URL> entries =
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk context.getBundleContext().getBundle().findEntries("/",
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk "*securityconfigurator.properties", true);
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk while (entries != null && entries.hasMoreElements()) {
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk URL entry = entries.nextElement();
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk logger.trace("Handle properties file at {}", entry.getPath());
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk InputStream is = null;
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk java.util.Properties props = new java.util.Properties();
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk try {
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk is = entry.openStream();
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk props.load(is);
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk } catch (IOException ex) {
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk logger.warn("Failed to load security extension properties file", ex);
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk try {
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk is.close();
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk } catch (Exception cex) {
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk logger.warn("Failure during close of properties file.", cex);
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk }
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk }
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk logger.trace("Loaded {}: {}", entry.getPath(), props);
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk if (props != null) {
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk String clazzName = (String) props.get("security.configurator.class");
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk logger.debug("Initiating security configurator for class: {}", clazzName);
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk SecurityConfigurator configurator = instantiateSecurityConfigurator(clazzName);
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk if (configurator != null) {
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk securityConfigurators.add(configurator);
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk }
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk }
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk }
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk }
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk /**
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * @param clazzName
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * name of SecurityConfigurator to load and instantiate
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * @return the security configurator instance if it was successfully
2a9ee4116a7df31d9482821f64c837315c8e2aa0jeff.schenk * instantiated, null if not. Logs any failures
*/
SecurityConfigurator instantiateSecurityConfigurator(String clazzName) {
SecurityConfigurator configurator = null;
Class configuratorClazz = null;
try {
configuratorClazz = context.getBundleContext().getBundle().loadClass(clazzName);
logger.debug("Loaded configurator class {}", clazzName);
} catch (ClassNotFoundException ex) {
logger.debug("Security configurator not present: {}", clazzName);
}
try {
if (configuratorClazz != null) {
Object instance = configuratorClazz.newInstance();
logger.debug("Instantiated configurator {}", instance);
configurator = (SecurityConfigurator) instance;
}
} catch (Exception ex) {
logger.warn("Failed to load security configurator class {}", clazzName, ex);
}
return configurator;
}
/**
* Activate security configurators if present to enable security
*
* @param context
* the component context of the main bundle
* @param httpContext
* the shared http context to configure
*/
private void activateSecurityConfigurators(ComponentContext context, HttpContext httpContext) {
for (SecurityConfigurator configurator : securityConfigurators) {
configurator.activate(httpService, httpContext, context);
logger.info("Activated security configurator {}", configurator.getClass().getName());
}
}
/**
* Deactivate security configurators if present to cleanup
*
* @param context
* the component context of the main bundle
* @param httpContext
* the shared http context to configure
*/
private void deactivateSecurityConfigurators(ComponentContext context, HttpContext httpContext) {
for (SecurityConfigurator configurator : securityConfigurators) {
configurator.deactivate(httpService, httpContext, context);
logger.debug("Deactivated security configurator {}", configurator.getClass().getName());
}
}
public ServiceRegistration registerServletMapping(ServletMapping mapping) {
return context.getBundleContext().registerService(ServletMapping.class.getName(), mapping,
null);
}
public ServiceRegistration registerFilterMapping(FilterMapping mapping) {
return context.getBundleContext().registerService(FilterMapping.class.getName(), mapping,
null);
}
public ServiceRegistration registerListenerMapping(ListenerMapping mapping) {
return context.getBundleContext().registerService(ListenerMapping.class.getName(), mapping,
null);
}
public ServiceRegistration registerWelcomeFileMapping(WelcomeFileMapping mapping) {
return context.getBundleContext().registerService(WelcomeFileMapping.class.getName(),
mapping, null);
}
public ServiceRegistration registerResourceMapping(ResourceMapping mapping) {
return context.getBundleContext().registerService(ResourceMapping.class.getName(), mapping,
null);
}
public ServiceRegistration registerExtenderConstants(ExtenderConstants mapping) {
return context.getBundleContext().registerService(ExtenderConstants.class.getName(),
mapping, null);
}
public ServiceRegistration registerErrorPageMapping(ErrorPageMapping mapping) {
return context.getBundleContext().registerService(ErrorPageMapping.class.getName(),
mapping, null);
}
public String getHttpContextId() {
return OPENIDM;
}
}