/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.java.sip.communicator.util;
import java.util.regex.*;
/**
* Simple password quality meter. The JavaScript version found at
* http://www.geekwisdom.com/js/passwordmeter.js was used as a base. Provides a
* method to compute the relative score of the password that can be used to
* model a progress bar, for example.
*
* @author Dmitri Melnikov
*/
public class PasswordQualityMeter
{
/**
* Maximum possible points.
*/
public static final int TOTAL_POINTS = 42;
/**
* Assesses the strength of the password.
*
* @param pass the password to assess
* @return the score for this password between 0 and TOTAL_POINTS
*/
public int assessPassword(String pass)
{
int score = 0;
if (pass == null || pass.length() == 0)
return score;
score += assessLength(pass);
score += assessLetters(pass);
score += assessNumbers(pass);
score += assessSpecials(pass);
return score;
}
/**
* Assesses password length:
* level 0 (3 point): less than 5 characters
* level 1 (6 points): between 5 and 7 characters
* level 2 (12 points): between 8 and 15 characters
* level 3 (18 points): 16 or more characters
*
* @param pass the password to assess
* @return the score based on the length
*/
private int assessLength(String pass)
{
int len = pass.length();
if (len < 5)
return 3;
else if (len >= 5 && len < 8)
return 6;
else if (len >= 8 && len < 16)
return 12;
// len >= 16
return 18;
}
/**
* Assesses letter cases:
* level 0 (0 points): no letters
* level 1 (5 points): all letters are either lower or upper case
* level 2 (7 points): letters are mixed case
*
* @param pass the password to assess
* @return the score based on the letters
*/
private int assessLetters(String pass)
{
boolean lower = matches(pass, "[a-z]+");
boolean upper = matches(pass, "[A-Z]+");
if (lower && upper)
return 7;
if (lower || upper)
return 5;
return 0;
}
/**
* Assesses number count:
* level 0 (0 points): no numbers exist
* level 1 (5 points): one or two number exists
* level 1 (7 points): 3 or more numbers exists
*
* @param pass the password to assess
* @return the score based on the numbers
*/
private int assessNumbers(String pass)
{
int found = countMatches(pass, "\\d");
if (found < 1)
return 0;
else if (found >= 1 && found < 3)
return 5;
return 7;
}
/**
* Assesses special character count.
* Here special characters are non-word and non-space ones.
* level 0 (0 points): no special characters
* level 1 (5 points): one special character exists
* level 2 (10 points): more than one special character exists
*
* @param pass the password to assess
* @return the score based on special characters
*/
private int assessSpecials(String pass)
{
int found = countMatches(pass, "[^\\w\\s]");
if (found < 1)
return 0;
else if (found <= 1 && found < 2)
return 5;
return 10;
}
/**
* Counts the number of matches of a given pattern in a given string.
*
* @param str the string to search in
* @param pattern the pattern to search for
* @return number of matches of patter in str
*/
private int countMatches(String str, String pattern)
{
Pattern p = Pattern.compile(pattern);
Matcher matcher = p.matcher(str);
int found = 0;
while (matcher.find())
found++;
return found;
}
/**
* Wrapper around @link{Pattern} and @link{Matcher} classes.
*
* @param str the string to search in
* @param pattern the pattern to search for
* @return true if pattern has been found in str.
*/
private boolean matches(String str, String pattern)
{
return Pattern.compile(pattern).matcher(str).find();
}
}