import javax.security.auth.*;
import javax.security.auth.spi.*;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;
import java.security.Principal;

import java.io.*;
import java.util.*;


/**
 *	Login module que comprueba usuario y password.
 */
public class PasswordLoginModule implements LoginModule {
	
	private Subject mSujeto;
	private CallbackHandler mCallbackHandler;

	private boolean mLoginExito = false;
	private boolean mCommitExito = false;

	private String mUsuario;
	private char[] mPassword;

	private Principal mPrincipal;

	/**
	 *	Inicializar.
	 */
	public void initialize(Subject sujeto,CallbackHandler callbackHandler,
		Map estadoCompartido,Map opciones) {
			mSujeto = sujeto;
			mCallbackHandler = callbackHandler;
			mLoginExito = false;
			mCommitExito = false;
			mUsuario = null;
			clearPassword();
	}

	/**
	 *	Intentar login.
	 *
	 *	usuario: "user"
	 *	password: "password"
	 */
	public boolean login() throws LoginException {
		if (mCallbackHandler == null) {
			throw new LoginException("CallbackHandler no definido");
		}

                // Crear dos callbacks: uno para usuario y el otro para password.
		Callback[] callbacks = new Callback[2];
		callbacks[0] = new NameCallback("Usuario");
		callbacks[1] = new PasswordCallback("Password", false);

		try {
                        // Llamar al callbackhandler para rellenar informacion
			mCallbackHandler.handle(callbacks);
			mUsuario = ((NameCallback)callbacks[0]).getName();
			char[] tempPassword = ((PasswordCallback)callbacks[1]).getPassword();
			mPassword = new char[tempPassword.length];
			System.arraycopy(tempPassword, 0, mPassword, 0, tempPassword.length);
			// Borrar password en el callback
			((PasswordCallback)callbacks[1]).clearPassword();
		} catch (IOException ioe) {
			throw new LoginException(ioe.toString());
		} catch (UnsupportedCallbackException uce) {
			throw new LoginException(uce.toString());
		}

        // Validar usuario y password
		if (
			"user".equals(mUsuario) &&
			mPassword.length == 8 &&
			mPassword[0] == 'p' &&
			mPassword[1] == 'a' &&
			mPassword[2] == 's' &&
			mPassword[3] == 's' &&
			mPassword[4] == 'w' &&
			mPassword[5] == 'o' &&
			mPassword[6] == 'r' &&
			mPassword[7] == 'd'
			) {

			// Usuario y password son correctos
			mLoginExito = true;
			return true;
		} else {
			// Fallo de autentificación. Borrar estado y lanzar excepción
			mLoginExito = false;
			mUsuario = null;
			clearPassword();
			throw new FailedLoginException("Password Incorrecto");
		}
	}

	/*
	 *	Llamar si el login tiene éxito
	 */
	public boolean commit() throws LoginException {
		if (mLoginExito == false) {
			return false;
		}
		// Login con éxito: crear Principal y añadirlo al Subject
		mPrincipal = new ImplPrincipal(mUsuario);
		if (!(mSujeto.getPrincipals().contains(mPrincipal))) {
			mSujeto.getPrincipals().add(mPrincipal);
		}
		// Si queremos que el Subject contenga credenciales
		// este es el momento para añadirlas.

		// Borrar usuario y password.
		mUsuario = null;
		clearPassword();
		mCommitExito = true;
		return true;
	}

	/**
	 *	Llamar si el login falla
	 */
	public boolean abort() throws LoginException {
		// Si login falla, devolver false
		if (mLoginExito == false) {
			return false;
		} else if (mLoginExito == true && mCommitExito == false) {
			// Nuestro login tuvo éxito pero otros fallaron
			mLoginExito = false;
			mUsuario = null;
			clearPassword();
			mPrincipal = null;
		} else {
			// Nosotros hicimos commit pero alguien falló
			logout();
		}
		return true;
	}

	/**
	 *	Logout
	 */
	public boolean logout() throws LoginException {
		// Borrar principal del usuario
		mSujeto.getPrincipals().remove(mPrincipal);
		mLoginExito = false;
		mCommitExito = false;
		mUsuario = null;
		clearPassword();
		mPrincipal = null;
		return true;
	}

	/**
	 *	Borrar el password
	 */
	private void clearPassword() {
		if (mPassword == null) {
			return;
		}
		for (int i=0;i<mPassword.length;i++) {
			mPassword[i] = ' ';
		}
		mPassword = null;
	}

}