Package org.jspresso.framework.security.auth.spi

Source Code of org.jspresso.framework.security.auth.spi.DevelopmentLoginModule

/*
* Copyright (c) 2005-2011 Vincent Vandenschrick. All rights reserved.
*
*  This file is part of the Jspresso framework.
*
*  Jspresso is free software: you can redistribute it and/or modify
*  it under the terms of the GNU Lesser General Public License as published by
*  the Free Software Foundation, either version 3 of the License, or
*  (at your option) any later version.
*
*  Jspresso is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public License
*  along with Jspresso.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.jspresso.framework.security.auth.spi;

import java.io.IOException;
import java.security.acl.Group;
import java.util.Map;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.TextOutputCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

import org.jboss.security.SimpleGroup;
import org.jboss.security.SimplePrincipal;
import org.jspresso.framework.security.SecurityHelper;
import org.jspresso.framework.security.UserPrincipal;
import org.jspresso.framework.util.security.LoginUtils;

/**
* A development login module with configuration parametrized user, password,
* roles and custom properties.
*
* @version $LastChangedRevision: 4321 $
* @author Vincent Vandenschrick
*/
public class DevelopmentLoginModule implements LoginModule {

  private static final String CUSTOM_PROPERTY_OPT = "custom";
  private static final String PASSWORD_OPT        = "password";
  private static final String ROLES_OPT           = "roles";
  private static final String USER_OPT            = "user";

  private CallbackHandler     callbackHandler;
  private boolean             commitSucceeded     = false;
  private Map<String, ?>      options;
  private char[]              password;
  private Subject             subject;
  private boolean             succeeded           = false;
  private String              suffix;
  private String              username;
  private UserPrincipal       userPrincipal;

  /**
   * <p>
   * This method is called if the LoginContext's overall authentication failed.
   * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules did
   * not succeed).
   * <p>
   * If this LoginModule's own authentication attempt succeeded (checked by
   * retrieving the private state saved by the <code>login</code> and
   * <code>commit</code> methods), then this method cleans up any state that was
   * originally saved.
   * <p>
   *
   * @return false if this LoginModule's own login and/or commit attempts
   *         failed, and true otherwise.
   */
  @Override
  public boolean abort() {
    if (!succeeded) {
      Callback[] callbacks = new Callback[1];
      callbacks[0] = new TextOutputCallback(TextOutputCallback.ERROR,
          LoginUtils.LOGIN_FAILED);
      try {
        callbackHandler.handle(callbacks);
      } catch (IOException ex) {
        // NO-OP.
      } catch (UnsupportedCallbackException ex) {
        // NO-OP.
      }
      return false;
    } else if (succeeded && !commitSucceeded) {
      // login succeeded but overall authentication failed
      succeeded = false;
      username = null;
      suffix = "";
      if (password != null) {
        for (int i = 0; i < password.length; i++) {
          password[i] = ' ';
        }
        password = null;
      }
      userPrincipal = null;
    } else {
      // overall authentication succeeded and commit succeeded,
      // but someone else's commit failed
      logout();
    }
    return true;
  }

  /**
   * <p>
   * This method is called if the LoginContext's overall authentication
   * succeeded (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL
   * LoginModules succeeded).
   * <p>
   * If this LoginModule's own authentication attempt succeeded (checked by
   * retrieving the private state saved by the <code>login</code> method), then
   * this method associates a <code>UserPrincipal</code> with the
   * <code>Subject</code> located in the <code>LoginModule</code>. If this
   * LoginModule's own authentication attempted failed, then this method removes
   * any state that was originally saved.
   * <p>
   *
   * @return true if this LoginModule's own login and commit attempts succeeded,
   *         or false otherwise.
   */
  @Override
  public boolean commit() {
    if (!succeeded) {
      return false;
    }
    // assume the user we authenticated is the DemoPrincipal
    userPrincipal = new UserPrincipal(username);
    for (Map.Entry<String, ?> option : options.entrySet()) {
      if (option.getKey().startsWith(CUSTOM_PROPERTY_OPT + suffix)) {
        userPrincipal.putCustomProperty(option.getKey().substring(
            (CUSTOM_PROPERTY_OPT + suffix).length() + 1), option.getValue());
      }
    }
    if (!subject.getPrincipals().contains(userPrincipal)) {
      subject.getPrincipals().add(userPrincipal);
    }

    String roles = (String) options.get(ROLES_OPT + suffix);
    if (roles != null) {
      Group rolesGroup = new SimpleGroup(SecurityHelper.ROLES_GROUP_NAME);
      String[] rolesArray = roles.split(",");
      for (String role : rolesArray) {
        rolesGroup.addMember(new SimplePrincipal(role));
      }
      subject.getPrincipals().add(rolesGroup);
    }

    // in any case, clean out state
    username = null;
    suffix = "";
    for (int i = 0; i < password.length; i++) {
      password[i] = ' ';
    }
    password = null;

    commitSucceeded = true;
    return true;
  }

  /**
   * Initialize this <code>LoginModule</code>.
   * <p>
   *
   * @param aSubject
   *          the <code>Subject</code> to be authenticated.
   *          <p>
   * @param aCallbackHandler
   *          a <code>CallbackHandler</code> for communicating with the end user
   *          (prompting for user names and passwords, for example).
   *          <p>
   * @param aSharedState
   *          shared <code>LoginModule</code> state.
   *          <p>
   * @param aOptions
   *          options specified in the login <code>Configuration</code> for this
   *          particular <code>LoginModule</code>.
   */
  @Override
  public void initialize(Subject aSubject, CallbackHandler aCallbackHandler,
      Map<String, ?> aSharedState, Map<String, ?> aOptions) {

    subject = aSubject;
    callbackHandler = aCallbackHandler;
    options = aOptions;
  }

  /**
   * Authenticate the user by prompting for a user name and password.
   * <p>
   *
   * @return true in all cases since this <code>LoginModule</code> should not be
   *         ignored.
   * @exception LoginException
   *              if the authentication fails.
   *              <p>
   * @exception LoginException
   *              if this <code>LoginModule</code> is unable to perform the
   *              authentication.
   */
  @Override
  public boolean login() throws LoginException {

    // prompt for a user name and password
    if (callbackHandler == null) {
      throw new LoginException("Error: no CallbackHandler available "
          + "to garner authentication information from the user");
    }

    Callback[] callbacks = new Callback[3];
    callbacks[0] = new NameCallback(LoginUtils.USER);
    callbacks[1] = new PasswordCallback(LoginUtils.PASSWORD, false);
    callbacks[2] = new TextOutputCallback(TextOutputCallback.INFORMATION,
        LoginUtils.CRED_MESSAGE);

    try {
      callbackHandler.handle(callbacks);
      username = ((NameCallback) callbacks[0]).getName();
      char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword();
      if (tmpPassword == null) {
        // treat a NULL password as an empty password
        tmpPassword = new char[0];
      }
      password = new char[tmpPassword.length];
      System.arraycopy(tmpPassword, 0, password, 0, tmpPassword.length);
      ((PasswordCallback) callbacks[1]).clearPassword();

    } catch (java.io.IOException ioe) {
      throw new LoginException(ioe.toString());
    } catch (UnsupportedCallbackException uce) {
      throw new LoginException("Error: " + uce.getCallback().toString()
          + " not available to garner authentication information "
          + "from the user");
    }

    // verify the username/password
    boolean usernameCorrect = checkUserName();
    String modulePassword = (String) options.get(PASSWORD_OPT + suffix);
    if (usernameCorrect) {
      if (modulePassword == null) {
        succeeded = true;
      } else {
        if (modulePassword.length() == password.length) {
          succeeded = true;
          for (int i = 0; succeeded && i < modulePassword.length(); i++) {
            if (modulePassword.charAt(i) != password[i]) {
              succeeded = false;
            }
          }
        }
      }
      return true;
    }
    succeeded = false;
    username = null;
    suffix = "";
    for (int i = 0; i < password.length; i++) {
      password[i] = ' ';
    }
    password = null;
    if (!usernameCorrect) {
      throw new FailedLoginException(LoginUtils.USER_FAILED);
    }
    throw new FailedLoginException(LoginUtils.PASSWORD_FAILED);
  }

  /**
   * Logout the user.
   * <p>
   * This method removes the <code>DemoPrincipal</code> that was added by the
   * <code>commit</code> method.
   * <p>
   *
   * @return true in all cases since this <code>LoginModule</code> should not be
   *         ignored.
   */
  @Override
  public boolean logout() {
    subject.getPrincipals().remove(userPrincipal);
    succeeded = false;
    succeeded = commitSucceeded;
    username = null;
    suffix = "";
    if (password != null) {
      for (int i = 0; i < password.length; i++) {
        password[i] = ' ';
      }
      password = null;
    }
    userPrincipal = null;
    return true;
  }

  private boolean checkUserName() {
    boolean usernameCorrect = false;
    boolean atLeastOneUserEntryFound = false;
    for (Map.Entry<String, ?> optionEntry : options.entrySet()) {
      if (optionEntry.getKey().startsWith(USER_OPT)) {
        atLeastOneUserEntryFound = true;
        if (optionEntry.getValue().equals(username)) {
          usernameCorrect = true;
          suffix = optionEntry.getKey().substring(USER_OPT.length());
        }
      }
    }
    if (atLeastOneUserEntryFound) {
      return usernameCorrect;
    }
    return true;
  }
}
TOP

Related Classes of org.jspresso.framework.security.auth.spi.DevelopmentLoginModule

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.