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