HTTP Context processing

HTTP Context processing

This page describes a new method (see PAXWEB-1167) of processing the web applications configured by other bundles.

The term web application is not as strict as in JavaEE environment. In OSGi and with PAX-WEB in particular, web applications are web contexts created by individual bundles. A web context is represented by an instance of javax.servlet.ServletContext and while In JavaEE environment one WAR == one web context, in OSGi a bundle can have more than one web context.

The key that selects a web application (web context) for given bundle is an instance of org.osgi.service.http.HttpContext and it's effectively a string identifier and by default it's ... "default".

A case of CXF

CXF's cxf-rt-transport-http bundle is used to register a org.apache.cxf.transport.servlet.CXFNonSpringServlet servlet using:

... alias = (String)getProp(properties, "org.apache.cxf.servlet.context", "/cxf"); HttpContext context = httpService.createDefaultHttpContext(); try { LOG.log(Level.INFO, "Registering new instance of \"" + alias + "\" servlet"); httpService.registerServlet(alias, servlet, sprops, context); } catch (Exception e) { LOG.log(Level.WARNING, "Error registering CXF OSGi servlet " + e.getMessage(), e); }

The most important invocation is org.osgi.service.http.HttpService#registerServlet.

This generic (non PAX-WEB specific) call has several implications:

  • A "default" org.osgi.service.http.HttpContext is used

  • There's no way to specify context properties (as in web.xml using <context-param> elements)

  • There's no way to specify login configuration and security constraints (as in web.xml using <login-config> and <security-constraint> elements)

  • The servlet is registered in HTTP Service which is bundle-scoped to cxf-rt-transport-http bundle - that's because pax-web-runtime bundle registers HTTP Service as service factory

We can't argue with the last statement, but first 3 ones may be a limitation.

Keycloak

Keycloak is an open source Identity and Access Management solution aimed at modern applications and services. It makes it easy to secure applications and services with little to no code. We won't go into details of configuring and managing Keycloak server instance. The only important thing (here) is that Keycloak provides integration mechanisms with different libraries, products and technologies. One of the integration points is related to PAX-WEB and new authentication hook that allows integration of external security providers. See PAXWEB-1161 for more details.

Keycloak provides an implementation of org.ops4j.pax.web.service.AuthenticatorService for 3 supported containers: Jetty, Tomcat and Undertow.

In simplest words - Keycloak, when integrated with PAX-WEB using org.ops4j.pax.web.service.AuthenticatorService may handle this configuration from web.xml:

<login-config> <auth-method>KEYCLOAK</auth-method> </login-config>

And this invocation of org.ops4j.pax.web.service.WebContainer (which is an extension of standard org.osgi.service.http.HttpService):

http.registerLoginConfig("KEYCLOAK", "realm-name", null, null, httpContext);

Changing other/existing contexts

The problem with CXF is that Keycloak had to do some special tricks involving re-registration of /cxf servlet in different (bundle-scoped to org.keycloak.keycloak-osgi-adapter bundle) web context after registering KEYCLOAK login configuration and security constraints of choice.

The solution

With PAXWEB-1167 there's no need to use and apply dedicated tricks to alter existing servlet registrations (like the one done by cxf-rt-transport-http). Now pax-web registers special ManagedServiceFactory (see configuration admin for details) that tracks org.ops4j.pax.web.context factory PIDs and uses the configuration to alter existing web contexts.

Here's sample configuration that contains all that is currently handled:

# for which bundle do we want to acquire bundle-scoped org.ops4j.pax.web.service.WebContainer service? bundle.symbolicName = org.apache.cxf.cxf-rt-transports-http # what's the ID of org.osgi.service.http.HttpContext we want to get from # org.ops4j.pax.web.service.WebContainer.createDefaultHttpContext(String contextId)? context.id = default # WEB-INF/web.xml's: # <context-param> # <param-name>keycloak.config.resolver</param-name> # <param-value>org.keycloak.adapters.osgi.PathBasedKeycloakConfigResolver</param-value> # </context-param> # PAX-WEB's org.ops4j.pax.web.service.WebContainer.setContextParam() context.param.keycloak.config.resolver = org.keycloak.adapters.osgi.PathBasedKeycloakConfigResolver context.param.param2 = value2 context.param.param3 = value3 # ... # WEB-INF/web.xml's: # <login-config> # <auth-method>KEYCLOAK</auth-method> # <realm-name>_does_not_matter</realm-name> # <!-- # <form-login-config> # <form-login-page>/login</form-login-page> # <form-error-page>/logout</form-error-page> # </form-login-config> # --> # </login-config> # PAX-WEB's org.ops4j.pax.web.service.WebContainer.registerLoginConfig() login.config.authMethod = KEYCLOAK login.config.realmName = _does_not_matter #login.config.formLoginPage = /login #login.config.formErrorPage = /logout # WEB-INF/web.xml's: # <security-constraint> # <web-resource-collection> # <web-resource-name>secured</web-resource-name> # <url-pattern>/info</url-pattern> # <http-method>GET</http-method> # </web-resource-collection> # <auth-constraint> # <role-name>admin</role-name> # </auth-constraint> # </security-constraint> # <security-constraint> # ... # </security-constraint> # ... # # <security-role> # <role-name>admin</role-name> # </security-role> # <security-role> # ... # </security-role> # ... # PAX-WEB's org.ops4j.pax.web.service.WebContainer.registerConstraintMapping() security.constraint.1.url = /cxf security.constraint.1.method = GET security.constraint.1.roles = admin, superuser, ... security.constraint.2.url = /other security.constraint.2.roles = admin # ...

First and most important configuration property is bundle.symbolicName which is used to track a bundle by its symbolic name. When the bundle is in ACTIVE state, pax-web-runtime can get given bundle's scoped instance of org.ops4j.pax.web.service.WebContainer. Using this instance we can alter a web context identified by org.osgi.service.http.HttpContext that's selected using context.id property. That's enough to register 3 additional types of web items - which can be configured using web.xml:

  • context parameters using context.param.paramName = paramValue

  • login configuration

  • security constraints

This configuration allows us to configure security mechanisms applied for example for /cxf - not only KEYCLOAK mechanism is supported - in Karaf environment we can just set BASIC mechanism and we will have automatic support for Karaf JAAS login modules that are handled directly by PAX-WEB.