Implementing Basic Auth for a servlet in an OSGI environment

You will first need to get a reference to the OSGI HTTP Service. You can do this through a declarative service. This post will concentrate on steps after getting a reference to the HTTP Service.

Note: The complete class for this post is located here

When registering a servlet through the OSGI HTTP Service, it provides you with an option to provide an implementation of HTTPContext.


httpService.registerServlet(alias, new MyServlet(), initParams, null);

When we implement the HTTPContext interface we can implement three methods. Out of these three (3) handleSecurity will be called before serving a request to ermmm… check for security.


public class BasicAuthSecuredContext implements HttpContext{
    @Override
    public boolean handleSecurity(HttpServletRequest request, HttpServletResponse response) throws IOException {
        return false;
    }

    @Override
    public URL getResource(String s) {
        return null;  
    }

    @Override
    public String getMimeType(String s) {
        return null;
    }
}

So, when implementing this, I’m borrowing a lot of content from the OSGI HTTPContext documentation and the HTTP Authentication spec. They are a must read, if you are interested in learning a lot, diving into details, etc. or you can just read the rest of this post.

First, it is a big no-no to do basic auth unless https is used. If it’s not there we let the user know it’s forbidden territory. Let’s go ahead and do that.

if (!request.getScheme().equals("https")) {
    response.sendError(HttpServletResponse.SC_FORBIDDEN);
    return false;
}

Next, let’s check for the Authorization header. If that’s not there we let them know, they need that shit to be there. Or we just say they’re unauthorized. Let’s do that now.

if (request.getHeader("Authorization") == null) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
}

Ok, two tests passed. Now, we do some real work. Let’s extract the header decode it, and do “not so proper” authentication.

    protected boolean authenticated(HttpServletRequest request) {
        String authzHeader = request.getHeader("Authorization");
        String usernameAndPassword = new String(Base64.decodeBase64(authzHeader.substring(6).getBytes()));

        int userNameIndex = usernameAndPassword.indexOf(":");
        String username = usernameAndPassword.substring(0, userNameIndex);
        String password = usernameAndPassword.substring(userNameIndex + 1);
        // Now, do the authentication against in the way you want, ex: ldap, db stored uname/pw
        // Here I will do lame hard coded credential check. HIGHLY NOT RECOMMENDED! 
        return ((username.equals("username") && password.equals("password"));
    }

Let’s integrate this method, into the handleSecurity method. Notice how a meaningful error message is set to the response (line 14) when the security fails. This prevents the user from guessing, and they knows what exactly went wrong. Ermm, at least, if they know HTTP error codes they would know exactly what went wrong.

    @Override
    public boolean handleSecurity(HttpServletRequest request, HttpServletResponse response) throws IOException {
        if (!request.getScheme().equals("https")) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
            return false;
        }
        if (request.getHeader("Authorization") == null) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }
        if (authenticated(request)) {
            return true;
        } else {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }
    }

That’s it. Now, pass this object when registering the servlet,

httpService.registerServlet(alias, new MyServlet(), initParams, new BasicAuthSecuredContext());

…and behold the power of basic auth in OSGI servlets!

Fixing the Class Not Found Exception when running a CXF JAX-RS service

If you are trying to deploy a CXF JAX-RS service inside an OSGI container, you can be haunted by the exception given below. But, surprisingly, the fix could be pretty simple 2 step process.

Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: com.sun.ws.rs.ext.RuntimeDelegateImpl
	at javax.ws.rs.ext.RuntimeDelegate.findDelegate(RuntimeDelegate.java:122)
	at javax.ws.rs.ext.RuntimeDelegate.getInstance(RuntimeDelegate.java:91)
	at javax.ws.rs.core.MediaType.<clinit>(MediaType.java:44)
	... 56 more
Caused by: java.lang.ClassNotFoundException: com.sun.ws.rs.ext.RuntimeDelegateImpl
	at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:513)
	at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:429)
	at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:417)
	at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:107)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:169)
	at javax.ws.rs.ext.FactoryFinder.newInstance(FactoryFinder.java:62)
	at javax.ws.rs.ext.FactoryFinder.find(FactoryFinder.java:155)
	at javax.ws.rs.ext.RuntimeDelegate.findDelegate(RuntimeDelegate.java:105)
	... 58 more

First, make sure you follow the steps to register your servlet through the osgi HTTP service as pointed out in the minimal osgi sample.

Then, the reason most probably would be a non-OSGIfied jsr 311 implementation present. Replace this jar with an OSGI compatible implementation located at org.apache.servicemix.specs.jsr311-api-1.1.1-1.9.0.jar.

That should (hopefully) solve your problem.

Useful commands for debugging OSGI issues and constraints

These are a few commands that I find very useful when faced with OSGI issues.

1. ss / ss <string> (abbr. for short status)

Lists down the state of all installed bundles or if followed by a string, does a wildcard match of that string. Useful for initial investigation of checking whether a bundle is active, installed or not present at all.

osgi> ss bam

Framework is launched.


id State Bundle
143 RESOLVED org.wso2.bam.styles_2.0.0.SNAPSHOT
Master=290
150 ACTIVE org.wso2.carbon.bam.analyzer.stub_4.0.0.SNAPSHOT
151 ACTIVE org.wso2.carbon.bam.core.stub_4.0.0.SNAPSHOT
152 ACTIVE org.wso2.carbon.bam.gadgetgenwizard_4.0.0.SNAPSHOT
153 ACTIVE org.wso2.carbon.bam.gadgetgenwizard.stub_4.0.0.SNAPSHOT
154 ACTIVE org.wso2.carbon.bam.gadgetgenwizard.ui_4.0.0.SNAPSHOT
155 ACTIVE org.wso2.carbon.bam.presentation.stub_4.0.0.SNAPSHOT
156 ACTIVE org.wso2.carbon.bam2.core_4.0.0.SNAPSHOT
157 ACTIVE org.wso2.carbon.bam2.core.ui_4.0.0.SNAPSHOT
158 ACTIVE org.wso2.carbon.bam2.presentation_4.0.0.SNAPSHOT
159 ACTIVE org.wso2.carbon.bam2.receiver_4.0.0.SNAPSHOT
160 ACTIVE org.wso2.carbon.bam2.service_4.0.0.SNAPSHOT


2. b  <id> (abbr. for bundle)

Displays details for the specified bundles. Useful for checking what your bundle exports and imports when narrowing down situations like uses constraints. Also, helps with the OSGI services used.

osgi> b 152
org.wso2.carbon.bam.gadgetgenwizard_4.0.0.SNAPSHOT [152]
Id=152, Status=ACTIVE Data Root=/Users/mackie/source-checkouts/carbon/platform/trunk/products/bam2/modules/distribution/product/target/wso2bam-2.0.0-SNAPSHOT/repository/components/configuration/org.eclipse.osgi/bundles/152/data
No registered services.
Services in use:
{org.wso2.carbon.utils.ConfigurationContextService}={service.id=123}
{org.wso2.carbon.base.api.ServerConfigurationService}={service.id=83}
Exported packages
org.wso2.carbon.bam.gadgetgenwizard.internal; version="4.0.0.SNAPSHOT"[exported]
org.wso2.carbon.bam.gadgetgenwizard.service; version="4.0.0.SNAPSHOT"[exported]
Imported packages
org.apache.commons.logging; version="1.1.1"
org.wso2.carbon.utils; version="4.0.0.SNAPSHOT"
org.wso2.carbon.user.core; version="4.0.0.SNAPSHOT"
org.wso2.carbon.registry.core.session; version="1.0.1"
org.wso2.carbon.registry.core.exceptions; version="1.0.1"
org.wso2.carbon.registry.core; version="1.0.1"
org.wso2.carbon.registry.common.services; version="1.0.1"
org.wso2.carbon.base.api; version="1.0.0"
org.osgi.service.component; version="1.1.0"
org.apache.commons.io; version="2.0.0"
org.apache.axiom.om.impl.jaxp; version="1.2.11.wso2v1"
org.apache.axiom.om.impl.builder; version="1.2.11.wso2v1"
org.apache.axiom.om; version="1.2.11.wso2v1"
javax.xml.transform.stream; version="0.0.0"
javax.xml.transform; version="0.0.0"
javax.xml.stream; version="1.0.1"
javax.xml.namespace; version="0.0.0"
net.sf.saxon; version="9.0.0.x"
No fragment bundles
Named class space
org.wso2.carbon.bam.gadgetgenwizard; bundle-version="4.0.0.SNAPSHOT"[provided]
No required bundles

3. p <package> (abbr. for packages)

Shows the bundles that export and import the specified packages. Extremely useful in debugging most OSGI issues.

osgi> p org.wso2.carbon.utils
org.wso2.carbon.utils; version="4.0.0.SNAPSHOT"
axis2_1.6.1.wso2v5 [19] imports
org.wso2.carbon.analytics.hive_4.0.0.SNAPSHOT [145] imports
org.wso2.carbon.application.deployer_4.0.0.SNAPSHOT [147] imports
org.wso2.carbon.bam.gadgetgenwizard_4.0.0.SNAPSHOT [152] imports
org.wso2.carbon.bam2.core_4.0.0.SNAPSHOT [156] imports
org.wso2.carbon.bam2.receiver_4.0.0.SNAPSHOT [159] imports
org.wso2.carbon.cassandra.dataaccess_4.0.0.SNAPSHOT [163] imports
org.wso2.carbon.cassandra.mgt_4.0.0.SNAPSHOT [164] imports
org.wso2.carbon.cluster.mgt.core_4.0.0.SNAPSHOT [169] imports
org.wso2.carbon.coordination.core_4.0.0.SNAPSHOT [172] imports
org.wso2.carbon.core_4.0.0.SNAPSHOT [173] imports
org.wso2.carbon.core.bootup.validator_4.0.0.SNAPSHOT [174] imports
org.wso2.carbon.core.services_4.0.0.SNAPSHOT [177] imports
org.wso2.carbon.dashboard_4.0.0.SNAPSHOT [178] imports
org.wso2.carbon.dashboard.common_4.0.0.SNAPSHOT [179] imports
org.wso2.carbon.dashboard.dashboardpopulator_4.0.0.SNAPSHOT [180] imports
org.wso2.carbon.dashboard.ui_4.0.0.SNAPSHOT [182] imports
org.wso2.carbon.datasource_4.0.0.SNAPSHOT [183] imports
org.wso2.carbon.event.client_4.0.0.SNAPSHOT [187] imports
org.wso2.carbon.event.common_4.0.0.SNAPSHOT [189] imports
org.wso2.carbon.event.core_4.0.0.SNAPSHOT [190] imports
org.wso2.carbon.event.ws_4.0.0.SNAPSHOT [191] imports
org.wso2.carbon.jaggery.app.mgt_1.0.0.SNAPSHOT [222] imports
org.wso2.carbon.jaggery.app.mgt.ui_1.0.0.SNAPSHOT [224] imports
org.wso2.carbon.jaggery.deployer_1.0.0.SNAPSHOT [226] imports
org.wso2.carbon.logging.service_4.0.0.SNAPSHOT [232] imports
org.wso2.carbon.ndatasource.core_4.0.0.SNAPSHOT [236] imports
org.wso2.carbon.ntask.core_4.0.0.SNAPSHOT [239] imports
org.wso2.carbon.registry.common_4.0.0.SNAPSHOT [246] imports
org.wso2.carbon.registry.core_4.0.0.SNAPSHOT [248] imports
org.wso2.carbon.registry.resource.ui_4.0.0.SNAPSHOT [254] imports
org.wso2.carbon.registry.server_4.0.0.SNAPSHOT [258] imports
org.wso2.carbon.registry.servlet_4.0.0.SNAPSHOT [259] imports
org.wso2.carbon.reporting.template.core_4.0.0.SNAPSHOT [263] imports
org.wso2.carbon.security.mgt_4.0.0.SNAPSHOT [273] imports
org.wso2.carbon.security.mgt.ui_4.0.0.SNAPSHOT [275] imports
org.wso2.carbon.server.admin_4.0.0.SNAPSHOT [276] imports
org.wso2.carbon.server.admin.ui_4.0.0.SNAPSHOT [279] imports
org.wso2.carbon.service.mgt_4.0.0.SNAPSHOT [280] imports
org.wso2.carbon.transport.http_4.0.0.SNAPSHOT [285] imports
org.wso2.carbon.transport.https_4.0.0.SNAPSHOT [286] imports
org.wso2.carbon.transport.mgt_4.0.0.SNAPSHOT [287] imports
org.wso2.carbon.ui_4.0.0.SNAPSHOT [290] imports
org.wso2.carbon.user.core_4.0.0.SNAPSHOT [295] imports
org.wso2.carbon.user.mgt.ui_4.0.0.SNAPSHOT [299] imports
org.wso2.carbon.webapp.mgt_4.0.0.SNAPSHOT [301] imports
org.wso2.carbon.wsdl2form_4.0.0.SNAPSHOT [302] imports

4. diag <bid> (abbr. for diagnose)

Shows any unsatisfied constraints of the bundle.

osgi> diag 159
reference:file:plugins/org.wso2.carbon.bam2.receiver_4.0.0.SNAPSHOT.jar [159]
No unresolved constraints.

 

5. ls (abbr. for list services)

Lists down the state of all OSGI services. In this list the most important would be identifying the unsatisfied components as in component 20 below.

osgi> ls
All Components:
ID State Component Name Located in bundle
1 Registered org.eclipse.equinox.frameworkadmin.equinox org.eclipse.equinox.frameworkadmin.equinox(bid=108)
2 Active org.eclipse.equinox.p2.artifact.repository org.eclipse.equinox.p2.artifact.repository(bid=114)
3 Active org.eclipse.equinox.p2.core.eventbus org.eclipse.equinox.p2.core(bid=116)
4 Active org.eclipse.equinox.p2.di.agentProvider org.eclipse.equinox.p2.core(bid=116)
5 Registered org.eclipse.equinox.p2.director org.eclipse.equinox.p2.director(bid=117)
6 Active org.eclipse.equinox.p2.planner org.eclipse.equinox.p2.director(bid=117)
7 Active org.eclipse.equinox.p2.engine.registry org.eclipse.equinox.p2.engine(bid=120)
8 Active org.eclipse.equinox.p2.engine org.eclipse.equinox.p2.engine(bid=120)
9 Active org.eclipse.equinox.p2.garbagecollector org.eclipse.equinox.p2.garbagecollector(bid=122)
10 Active org.eclipse.equinox.p2.metadata.repository org.eclipse.equinox.p2.metadata.repository(bid=125)
11 Registered org.eclipse.equinox.p2.repository org.eclipse.equinox.p2.repository(bid=128)
12 Registered org.eclipse.equinox.p2.transport.ecf org.eclipse.equinox.p2.transport.ecf(bid=132)
13 Registered org.eclipse.equinox.p2.updatechecker org.eclipse.equinox.p2.updatechecker(bid=133)
14 Registered org.eclipse.equinox.simpleconfigurator.manipulator org.eclipse.equinox.simpleconfigurator.manipulator(bid=138)
15 Active bam.hive.component org.wso2.carbon.analytics.hive(bid=145)
16 Active application.deployer.dscomponent org.wso2.carbon.application.deployer(bid=147)
17 Active gadgetgenwizard.component org.wso2.carbon.bam.gadgetgenwizard(bid=152)
18 Active bam.utils.component org.wso2.carbon.bam2.core(bid=156)
19 Active bam.presentation.component org.wso2.carbon.bam2.presentation(bid=158)
20 Unsatisfied bam.receiver.component org.wso2.carbon.bam2.receiver(bid=159)
21 Active org.wso2.carbon.cassandra.dataaccess.component org.wso2.carbon.cassandra.dataaccess(bid=163)
22 Active org.wso2.carbon.cassandra.mgt.component org.wso2.carbon.cassandra.mgt(bid=164)

6. comp <component id> or ls -c <bundleid>

Lists component specific information regarding OSGI declarative services. Useful for debugging issues with declarative services.

osgi> ls -c 152
Components in bundle org.wso2.carbon.bam.gadgetgenwizard:
ID Component details
17 Component[
name = gadgetgenwizard.component
factory = null
autoenable = true
immediate = true
implementation = org.wso2.carbon.bam.gadgetgenwizard.internal.GadgetGenWizardServiceComponent
state = Unsatisfied
properties = {service.pid=gadgetgenwizard.component}
serviceFactory = false
serviceInterface = null
references = {
Reference[name = config.context.service, interface = org.wso2.carbon.utils.ConfigurationContextService, policy = dynamic, cardinality = 1..1, target = null, bind = setConfigurationContextService, unbind = unsetConfigurationContextService]
Reference[name = server.configuration, interface = org.wso2.carbon.base.api.ServerConfigurationService, policy = dynamic, cardinality = 1..1, target = null, bind = setServerConfiguration, unbind = unsetServerConfiguration]
}
located in bundle = org.wso2.carbon.bam.gadgetgenwizard_4.0.0.SNAPSHOT [152]
]
Dynamic information :
The component is satisfied
All component references are satisfied
Component configurations :
Configuration properties:
service.pid = gadgetgenwizard.component
component.name = gadgetgenwizard.component
component.id = 16
Instances:
org.eclipse.equinox.internal.ds.impl.ComponentInstanceImpl@3fabb84d
Bound References:
String[org.wso2.carbon.utils.ConfigurationContextService]
-> org.wso2.carbon.utils.ConfigurationContextService@22d0e7e3
String[org.wso2.carbon.base.api.ServerConfigurationService]
-> org.wso2.carbon.base.ServerConfiguration@4127f9f0