001/*
002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     bstefanescu
011 */
012package org.nuxeo.runtime.api;
013
014import java.util.Map;
015
016import javax.security.auth.Subject;
017import javax.security.auth.callback.CallbackHandler;
018import javax.security.auth.login.LoginException;
019import javax.security.auth.spi.LoginModule;
020
021/**
022 * A login module wrapper to overcome the class loading issues on OSGi frameworks.
023 * <p>
024 * A login module is specified using the class name - and when doing a login against that module the login context will
025 * get a class instance for the module by using Clas.forName and the current thread context class loader. This means in
026 * an OSGi application the class loader of the caller bundle will be used to resolve the login module. But the caller
027 * bundle may not have any dependency on the login module since it is not aware about which login module implementation
028 * is used underneath - so in most cases the login module class will not be found.
029 * <p>
030 * For this reason all the contributed login modules will be wrapped using this class. As almost any Nuxeo bundle have a
031 * dependency on runtime this class will be visible from almost any Nuxeo bundle. So, the login context will instantiate
032 * this wrapper instead of the real login module and the wrapper will use OSGi logic to instantiate the wrapped login
033 * module. (by using the bundle that contributed the login module)
034 * <p>
035 * <b>IMPORTANT</b>
036 * <p>
037 * This class is in org.nuxeo.runtime.api package to be visible to any bundle that uses Nuxeo runtime. This is because
038 * this package is imported by almost any bundle using Nuxeo runtime - so you don't need to declare the import package
039 * in the OSGi manifest. In the case you don't use runtime but you are doing logins in the context of your bundle you
040 * must import the package <code>org.nuxeo.runtime.api</code> in your manifest.
041 *
042 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
043 */
044public class LoginModuleWrapper implements LoginModule {
045
046    public static final String DELEGATE_CLASS_KEY = LoginModuleWrapper.class.getName() + ".delegate";
047
048    protected LoginModule delegate;
049
050    protected void initDelegate(Map<String, ?> options) {
051        if (delegate == null) {
052            try {
053                Class<?> clazz = (Class<?>) options.get(DELEGATE_CLASS_KEY);
054                delegate = (LoginModule) clazz.newInstance();
055            } catch (NullPointerException e) {
056                throw new RuntimeException("Should be a bug: No DELEGATE_CLASS_KEY found in login module options", e);
057            } catch (ReflectiveOperationException e) {
058                throw new RuntimeException("Invalid login module class: " + options.get(DELEGATE_CLASS_KEY)
059                        + ". Should implement LoginModule.", e);
060            }
061        }
062    }
063
064    @Override
065    public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
066            Map<String, ?> options) {
067        initDelegate(options);
068        delegate.initialize(subject, callbackHandler, sharedState, options);
069    }
070
071    @Override
072    public boolean login() throws LoginException {
073        return delegate.login();
074    }
075
076    @Override
077    public boolean commit() throws LoginException {
078        return delegate.commit();
079    }
080
081    @Override
082    public boolean abort() throws LoginException {
083        return delegate.abort();
084    }
085
086    @Override
087    public boolean logout() throws LoginException {
088        return delegate.logout();
089    }
090
091}