Package org.sonatype.security.realms

Source Code of org.sonatype.security.realms.XmlAuthenticatingRealm

/*
* Sonatype Nexus (TM) Open Source Version
* Copyright (c) 2007-2014 Sonatype, Inc.
* All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
*
* This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
* which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
* of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
* Eclipse Foundation. All other trademarks are the property of their respective owners.
*/
package org.sonatype.security.realms;

import javax.enterprise.inject.Typed;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.sonatype.security.model.CUser;
import org.sonatype.security.realms.tools.ConfigurationManager;
import org.sonatype.security.realms.tools.ConfigurationManagerAction;
import org.sonatype.security.usermanagement.UserNotFoundException;

import com.google.common.base.Throwables;
import org.apache.shiro.authc.AccountException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.PasswordMatcher;
import org.apache.shiro.authc.credential.PasswordService;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.realm.Realm;
import org.eclipse.sisu.Description;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* An Authentication Realm backed by an XML file see the security-model-xml module. This model defines users, roles,
* and
* privileges. This realm ONLY handles authentication.
*
* @author Brian Demers
*/
@Singleton
@Typed(Realm.class)
@Named(XmlAuthenticatingRealm.ROLE)
@Description("Xml Authenticating Realm")
public class XmlAuthenticatingRealm
    extends AuthenticatingRealm
    implements Realm
{
  private static final Logger logger = LoggerFactory.getLogger(XmlAuthenticatingRealm.class);

  public static final String ROLE = "XmlAuthenticatingRealm";

  private ConfigurationManager configuration;

  private PasswordService passwordService;

  private final int MAX_LEGACY_PASSWORD_LENGTH = 40;

  @Inject
  public XmlAuthenticatingRealm(@Named("default") ConfigurationManager configuration,
                                PasswordService passwordService)
  {
    this.configuration = configuration;
    this.passwordService = passwordService;

    PasswordMatcher passwordMatcher = new PasswordMatcher();
    passwordMatcher.setPasswordService(this.passwordService);
    setCredentialsMatcher(passwordMatcher);
    setName(ROLE);
    setAuthenticationCachingEnabled(true);
  }

  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
      throws AuthenticationException
  {
    UsernamePasswordToken upToken = (UsernamePasswordToken) token;

    CUser user;
    try {
      user = configuration.readUser(upToken.getUsername());
    }
    catch (UserNotFoundException e) {
      throw new AccountException("User '" + upToken.getUsername() + "' cannot be retrieved.", e);
    }

    if (user.getPassword() == null) {
      throw new AccountException("User '" + upToken.getUsername() + "' has no password, cannot authenticate.");
    }

    if (CUser.STATUS_ACTIVE.equals(user.getStatus())) {
      //Check for legacy user that has unsalted password hash
      //Update if legacy user, and valid credentials were specified
      if (this.isLegacyUser(user) && this.isValidCredentials(upToken, user)) {
        this.reHashPassword(user, new String(upToken.getPassword()));
      }

      return this.createAuthenticationInfo(user);
    }
    else if (CUser.STATUS_DISABLED.equals(user.getStatus())) {
      throw new DisabledAccountException("User '" + upToken.getUsername() + "' is disabled.");
    }
    else {
      throw new AccountException("User '" + upToken.getUsername() + "' is in illegal status '"
          + user.getStatus() + "'.");
    }
  }

  /*
   * Re-hash user password, and persist changes
   *
   * @param user to update
   * @param password cleartext password to hash
   */
  private void reHashPassword(final CUser user, final String password) {
    //Store current values to rollback if update fails
    final String currentPasswordHash = user.getPassword();

    try {
      configuration.runWrite(new ConfigurationManagerAction()
      {
        @Override
        public void run()
            throws Exception
        {
          try {
            user.setPassword(passwordService.encryptPassword(password));
            configuration.updateUser(user);
            configuration.save();
          }
          catch (Exception e) {
            //Update failed, rollback to previous values
            user.setPassword(currentPasswordHash);
            logger.error("Unable to update hash for user {}", user.getId(), e);
          }
        }
      });
    }
    catch (Exception e) {
      throw Throwables.propagate(e);
    }
  }

  /*
   * Checks to see if the credentials in token match the credentials stored on
   * user
   *
   * @param token the username/password token containing the credentials to
   * verify
   * @param the user object containing the stored credentials
   * @return true if credentials match, false otherwise
   */
  private boolean isValidCredentials(UsernamePasswordToken token, CUser user) {
    boolean credentialsValid = false;

    AuthenticationInfo info = this.createAuthenticationInfo(user);
    CredentialsMatcher matcher = this.getCredentialsMatcher();
    if (matcher != null) {
      if (matcher.doCredentialsMatch(token, info)) {
        credentialsValid = true;
      }
    }

    return credentialsValid;
  }

  /*
   * Checks to see if the specified user is a legacy user
   * A legacy user has an unsalted password
   *
   * @param user to check
   * @return true if legacy user, false otherwise
   */
  private boolean isLegacyUser(CUser user) {
    //Legacy users have a shorter, unsalted, SHA1 or MD5 based hash
    return user.getPassword().length() <= this.MAX_LEGACY_PASSWORD_LENGTH;
  }

  /*
   * Creates an authentication info object
   * using the credentials of the provided user
   *
   * @param user
   * @return authentication info object based on user credentials
   */
  private AuthenticationInfo createAuthenticationInfo(CUser user) {
    return new SimpleAuthenticationInfo(user.getId(), user.getPassword().toCharArray(), getName());
  }
}
TOP

Related Classes of org.sonatype.security.realms.XmlAuthenticatingRealm

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.