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