All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.vt.middleware.password.AbstractSequenceRule Maven / Gradle / Ivy

The newest version!
/*
  $Id: AbstractSequenceRule.java 2704 2013-04-24 21:30:32Z dfisher $

  Copyright (C) 2003-2013 Virginia Tech.
  All rights reserved.

  SEE LICENSE FOR MORE INFORMATION

  Author:  Middleware Services
  Email:   [email protected]
  Version: $Revision: 2704 $
  Updated: $Date: 2013-04-24 17:30:32 -0400 (Wed, 24 Apr 2013) $
*/
package edu.vt.middleware.password;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Provide common implementation for keyboard sequence rules.
 *
 * @author  Middleware Services
 * @version  $Revision: 2704 $ $Date: 2013-04-24 17:30:32 -0400 (Wed, 24 Apr 2013) $
 */
public abstract class AbstractSequenceRule implements Rule
{

  /** Error code for sequence validation failures. */
  public static final String ERROR_CODE = "ILLEGAL_SEQUENCE";

  /** Default length of keyboard sequence, value is {@value}. */
  public static final int DEFAULT_SEQUENCE_LENGTH = 5;

  /** Minimum length of keyboard sequence, value is {@value}. */
  public static final int MINIMUM_SEQUENCE_LENGTH = 3;

  /** Number of characters in sequence to match. */
  protected int sequenceLength = DEFAULT_SEQUENCE_LENGTH;

  /** Whether or not to wrap a sequence when searching for matches. */
  protected boolean wrapSequence;


  /** {@inheritDoc} */
  @Override
  public RuleResult validate(final PasswordData passwordData)
  {
    final RuleResult result = new RuleResult(true);
    final String password = passwordData.getPassword().getText();
    final int max = password.length() - sequenceLength + 1;
    Sequence sequence;
    int position;
    char c;
    for (int i = 0; i < getSequenceCount(); i++) {
      for (int j = 0; j < max; j++) {
        sequence = newSequence(getSequence(i), password.charAt(j));
        if (sequence != null) {
          position = j;
          while (sequence.forward()) {
            c = password.charAt(++position);
            if (c == sequence.currentLower() || c == sequence.currentUpper()) {
              sequence.addMatchCharacter(c);
            } else {
              break;
            }
          }
          if (sequence.matchCount() == sequenceLength) {
            recordFailure(result, sequence.matchString());
          }
          sequence.reset();
          position = j;
          while (sequence.backward()) {
            c = password.charAt(++position);
            if (c == sequence.currentLower() || c == sequence.currentUpper()) {
              sequence.addMatchCharacter(c);
            } else {
              break;
            }
          }
          if (sequence.matchCount() == sequenceLength) {
            recordFailure(result, sequence.matchString());
          }
        }
      }
    }
    return result;
  }


  /** {@inheritDoc} */
  @Override
  public String toString()
  {
    return
      String.format(
        "%s@%h::length=%d,wrap=%s",
        getClass().getName(),
        hashCode(),
        sequenceLength,
        wrapSequence);
  }


  /**
   * Sets the sequence length.
   *
   * @param  sl  sequence length
   */
  protected void setSequenceLength(final int sl)
  {
    if (sl < MINIMUM_SEQUENCE_LENGTH) {
      throw new IllegalArgumentException(
        String.format(
          "sequence length must be >= %s",
          MINIMUM_SEQUENCE_LENGTH));
    }
    sequenceLength = sl;
  }


  /**
   * Returns the sequence of character pairs for which to search.
   *
   * @param  n  provides support for multiple character sequences that are
   * indexed from 0 to n.
   *
   * @return  character sequence.
   */
  protected abstract char[][] getSequence(final int n);


  /**
   * Returns the number of character sequences used in this implementation.
   *
   * @return  number of character sequences.
   */
  protected abstract int getSequenceCount();


  /**
   * Creates an iterator that iterates over a character sequence positioned at
   * the first matching character, if any, in the given password.
   *
   * @param  chars  sequence of upper/lowercase character pairs.
   * @param  first  first character to match in character sequence.
   *
   * @return  forward sequence iterator.
   */
  private Sequence newSequence(final char[][] chars, final char first)
  {
    for (int i = 0; i < chars.length; i++) {
      if (first == chars[i][0] || first == chars[i][1]) {
        final Sequence s = new Sequence(chars, i, sequenceLength, wrapSequence);
        s.addMatchCharacter(first);
        return s;
      }
    }
    return null;
  }


  /**
   * Records a validation failure.
   *
   * @param  result  rule result holding failure details.
   * @param  match  illegal string matched in the password that caused failure.
   */
  private void recordFailure(final RuleResult result, final String match)
  {
    final Map m = new LinkedHashMap();
    m.put("sequence", match);
    result.setValid(false);
    result.getDetails().add(new RuleResultDetail(ERROR_CODE, m));
  }


  /**
   * Contains convenience methods for iterating over a sequence of
   * upper/lowercase pairs of characters and stores matched characters.
   *
   * @author  Middleware Services
   * @version  $Revision: 2704 $
   */
  private class Sequence
  {

    /** Sequence of upper/lower character pairs. */
    private final char[][] chars;

    /** 0-based iterator start position. */
    private final int start;

    /** Number of characters to iterate over. */
    private final int length;

    /** Index upper bound. */
    private int ubound;

    /** Index lower bound. */
    private int lbound;

    /** Current 0-based iterator position. */
    private int position;

    /** Stores matched characters. */
    private final StringBuilder matches;


    /**
     * Creates a new sequence.
     *
     * @param  characters  sequence of characters
     * @param  startIndex  in the characters array
     * @param  count  length of this sequence
     * @param  wrap  whether this sequence wraps
     */
    public Sequence(
      final char[][] characters,
      final int startIndex,
      final int count,
      final boolean wrap)
    {
      chars = characters;
      start = startIndex;
      length = count;
      lbound = start - length;
      ubound = start + length;
      if (lbound < 0 && !wrap) {
        lbound = 0;
      }
      if (ubound >= characters.length && !wrap) {
        ubound = characters.length;
      }
      position = start;
      matches = new StringBuilder(length);
    }


    /**
     * Advances the iterator one unit in the forward direction.
     *
     * @return  true if characters remain, false otherwise.
     */
    public boolean forward()
    {
      return ++position < ubound;
    }


    /**
     * Advances the iterator one unit in the backward direction.
     *
     * @return  true if characters remain, false otherwise.
     */
    public boolean backward()
    {
      return --position > lbound;
    }


    /**
     * Resets the sequence to its original position and discards all but the
     * initial match character.
     */
    public void reset()
    {
      position = start;
      matches.delete(1, length);
    }


    /**
     * Returns the lowercase character at the current iterator position.
     *
     * @return  lowercase character at current position.
     */
    public char currentLower()
    {
      final int i;
      if (position < 0) {
        i = chars.length + position;
      } else if (position >= chars.length) {
        i = position - chars.length;
      } else {
        i = position;
      }
      return chars[i][0];
    }


    /**
     * Returns the uppercase character at the current iterator position.
     *
     * @return  uppercase character at current position.
     */
    public char currentUpper()
    {
      final int i;
      if (position < 0) {
        i = chars.length + position;
      } else if (position >= chars.length) {
        i = position - chars.length;
      } else {
        i = position;
      }
      return chars[i][1];
    }


    /**
     * Adds the given character to the set of matched characters.
     *
     * @param  c  match character.
     */
    public void addMatchCharacter(final char c)
    {
      matches.append(c);
    }


    /**
     * Returns the number of matched characters.
     *
     * @return  matched character count.
     */
    public int matchCount()
    {
      return matches.length();
    }


    /**
     * Returns the string of matched characters.
     *
     * @return  match string.
     */
    public String matchString()
    {
      return matches.toString();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy