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.login;
020
021import java.io.IOException;
022import java.security.Principal;
023import java.util.Map;
024
025import javax.security.auth.Subject;
026import javax.security.auth.callback.Callback;
027import javax.security.auth.callback.CallbackHandler;
028import javax.security.auth.callback.NameCallback;
029import javax.security.auth.callback.PasswordCallback;
030import javax.security.auth.callback.UnsupportedCallbackException;
031import javax.security.auth.login.LoginException;
032import javax.security.auth.spi.LoginModule;
033
034import org.nuxeo.runtime.api.Framework;
035
036/**
037 * A login module that will use the current registered authenticator to validate a given username / password pair.
038 *
039 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
040 */
041public class AuthenticationLoginModule implements LoginModule {
042
043    protected Subject subject;
044
045    protected CallbackHandler callbackHandler;
046
047    @SuppressWarnings("rawtypes")
048    protected Map sharedState;
049
050    protected Principal principal;
051
052    public Principal authenticate(String[] login) {
053        return Framework.getService(Authenticator.class).authenticate(login[0], login[1]);
054    }
055
056    @Override
057    public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
058            Map<String, ?> options) {
059        this.subject = subject;
060        this.callbackHandler = callbackHandler;
061        this.sharedState = sharedState;
062    }
063
064    protected String[] retrieveLogin() throws LoginException {
065        PasswordCallback pc = new PasswordCallback("Password: ", false);
066        NameCallback nc = new NameCallback("Username: ", "guest");
067        Callback[] callbacks = { nc, pc };
068        try {
069            String[] login = new String[2];
070            callbackHandler.handle(callbacks);
071            login[0] = nc.getName();
072            char[] tmpPassword = pc.getPassword();
073            if (tmpPassword != null) {
074                login[1] = new String(tmpPassword);
075            }
076            pc.clearPassword();
077            return login;
078        } catch (IOException ioe) {
079            throw new LoginException(ioe.toString());
080        } catch (UnsupportedCallbackException uce) {
081            throw new LoginException("Error: " + uce.getCallback().toString()
082                    + " not available to gather authentication information " + "from the user");
083        }
084    }
085
086    @SuppressWarnings("unchecked")
087    @Override
088    public boolean login() throws LoginException {
089        String[] login = retrieveLogin();
090        try {
091            principal = authenticate(login);
092        } catch (RuntimeException e) {
093            LoginException ee = new LoginException("Authentication failed for " + login[0]);
094            ee.initCause(e);
095            throw ee;
096        }
097        if (principal == null) {
098            throw new LoginException("Authentication failed for " + login[0]);
099        }
100        sharedState.put("javax.security.auth.login.name", principal);
101        // sharedState.put("javax.security.auth.login.password", login[1]);
102        return true;
103    }
104
105    @Override
106    public boolean abort() throws LoginException {
107        return true;
108    }
109
110    @Override
111    public boolean commit() throws LoginException {
112        if (principal != null) {
113            subject.getPrincipals().add(principal);
114            return true;
115        } else {
116            return false;
117        }
118    }
119
120    @Override
121    public boolean logout() throws LoginException {
122        if (principal != null) {
123            subject.getPrincipals().remove(principal);
124        }
125        return true;
126    }
127
128}