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

com.aoindustries.aoserv.client.PasswordChecker Maven / Gradle / Ivy

There is a newer version: 1.92.0
Show newest version
/*
 * aoserv-client - Java client for the AOServ platform.
 * Copyright (C) 2000-2013, 2016  AO Industries, Inc.
 *     [email protected]
 *     7262 Bull Pen Cir
 *     Mobile, AL 36695
 *
 * This file is part of aoserv-client.
 *
 * aoserv-client 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.
 *
 * aoserv-client 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 aoserv-client.  If not, see .
 */
package com.aoindustries.aoserv.client;

import static com.aoindustries.aoserv.client.ApplicationResources.accessor;
import com.aoindustries.aoserv.client.validator.UserId;
import com.aoindustries.io.IoUtils;
import com.aoindustries.util.EncodingUtils;
import com.aoindustries.util.zip.CorrectedGZIPInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Performs password checking for all password protected
 * services.
 *
 * @author  AO Industries, Inc.
 */
final public class PasswordChecker {

	/**
	 * The different ways months may be represented in the English
	 */
	private static final String[] months={
		"jan","january",
		"feb","february",
		"mar","march",
		"apr","april",
		"may",
		"jun","june",
		"jul","july",
		"aug","august",
		"sep","september",
		"nov","november",
		"dec","december"
	};

	private static final String GOOD_KEY = "PasswordChecker.good";

	/**
	 * The categories that are checked.
	 */
	private static final String[] categoryKeys={
		"PasswordChecker.category.length",
		"PasswordChecker.category.characters",
		"PasswordChecker.category.case",
		"PasswordChecker.category.dates",
		"PasswordChecker.category.dictionary"
	};
	public static final int NUM_CATEGORIES=categoryKeys.length;

	private static byte[] cachedWords;

	public static class Result {
		private String category;
		private String result;

		private Result(String category) {
			this.category = category;
			this.result = accessor.getMessage(GOOD_KEY);
		}

		public String getCategory() {
			return category;
		}

		public String getResult() {
			return result;
		}

		@Override
		public String toString() {
			return category+": "+result;
		}
	}

	private PasswordChecker() {}

	public static List getAllGoodResults() {
		List results = new ArrayList<>(NUM_CATEGORIES);
		for(int c=0;c checkPassword(UserId username, String password, PasswordStrength strength) throws IOException {
		if(strength==null) throw new IllegalArgumentException("strength==null");
		List results = getAllGoodResults();
		int passwordLen = password.length();
		if (passwordLen > 0) {
			/*
			 * Check the length of the password
			 *
			 * Must be at least eight characters
			 */
			if (passwordLen < (strength==PasswordStrength.SUPER_LAX?6:8)) results.get(0).result = accessor.getMessage(strength==PasswordStrength.SUPER_LAX ? "PasswordChecker.length.atLeastSix" : "PasswordChecker.length.atLeastEight");

			/*
			 * Gather password stats
			 */
			int lowercount = 0;
			int uppercount = 0;
			int numbercount = 0;
			int specialcount = 0;
			int ch;
			for (int c = 0; c < passwordLen; c++) {
				if ((ch = password.charAt(c)) >= 'A' && ch <= 'Z') uppercount++;
				else if (ch >= 'a' && ch <= 'z') lowercount++;
				else if (ch >= '0' && ch <= '9') numbercount++;
				else if (ch <= ' ') specialcount++;
			}

			/*
			 * Must use numbers and/or punctuation
			 *
			 * 1) Must contain numbers/punctuation
			 * 2) Must not be all numbers
			 * 3) Must not contain a space
			 */
			if ((numbercount + specialcount) == passwordLen) results.get(1).result = accessor.getMessage("PasswordChecker.characters.notOnlyNumbers");
			else if (strength!=PasswordStrength.SUPER_LAX && (lowercount + uppercount + specialcount) == passwordLen) results.get(1).result = accessor.getMessage("PasswordChecker.characters.numbersAndPunctuation");
			else if (password.indexOf(' ')!=-1) results.get(1).result = accessor.getMessage("PasswordChecker.characters.notContainSpace");

			/*
			 * Must use different cases
			 *
			 * If more than one letter exists, force different case
			 */
			if (
				strength!=PasswordStrength.SUPER_LAX
				&& (
					(lowercount > 1 && uppercount == 0)
					|| (uppercount > 1 && lowercount == 0)
					|| (lowercount == 0 && uppercount == 0)
				)
			) results.get(2).result = accessor.getMessage("PasswordChecker.case.capitalAndLower");

			/*
			 * Generate the backwards version of the password
			 */
			String backwards;
			{
				char[] backwards_ca = new char[passwordLen];
				for (int c = 0; c < passwordLen; c++) backwards_ca[c] = password.charAt(passwordLen - c - 1);
				backwards = new String(backwards_ca);
			}

			/*
			 * Must not be the same as your username
			 */
			if(username!=null && username.toString().equalsIgnoreCase(password)) {
				results.get(4).result = accessor.getMessage("PasswordChecker.dictionary.notSameAsUsername");
			}

			/*
			 * Must not contain a date
			 * 

* Does not contain, forwards or backwards and case insensitive (FBCI), jan ... dec
* Removed -> Does not contain the two digit representation of the current year, last year, or * Removed -> next year, and a month 1-12 (strict mode only) */ if (strength!=PasswordStrength.SUPER_LAX) { String lowerf = password.toLowerCase(); String lowerb = backwards.toLowerCase(); boolean goodb = true; int len = months.length; for (int c = 0; c < len; c++) { String month = months[c].toLowerCase(); if (lowerf.indexOf(month) != -1 || lowerb.indexOf(month) != -1) { goodb = false; break; } } if (!goodb) results.get(3).result = accessor.getMessage("PasswordChecker.dates.noDate"); if(results.get(4).result.equals(accessor.getMessage(GOOD_KEY))) { /* * Dictionary check * * Must not contain a dictionary word, forward or backword and case insensitive, * that is longer than half the length of the password if strict or 2 characters if not strict. */ byte[] words = getDictionary(); int max_allowed_dict_len = strength==PasswordStrength.STRICT ? 3 : (passwordLen / 2); // Search through each dictionary word int wordslen = words.length; int pos = 0; String longest = ""; boolean longest_forwards = true; Loop : while (pos < wordslen) { // Find the beginning of the next word while (pos < wordslen && words[pos] <= ' ') pos++; // Search to the end of the word int startpos = pos; while (pos < wordslen && words[pos] > ' ') pos++; // Get the word int wordlen = pos - startpos; if (wordlen > max_allowed_dict_len) { if (indexOfIgnoreCase(password, words, startpos, wordlen) != -1) { if (longest_forwards ? (wordlen > longest.length()) : (wordlen >= longest.length())) { longest = new String(words, startpos, wordlen); longest_forwards = true; } } else if (indexOfIgnoreCase(backwards, words, startpos, wordlen) != -1) { if (wordlen > longest.length()) { longest = new String(words, startpos, wordlen); longest_forwards = false; } } } } if (longest.length() > 0) { results.get(4).result = accessor.getMessage("PasswordChecker.dictionary.basedOnWord", longest); } } } } else results.get(0).result = accessor.getMessage("PasswordChecker.length.noPassword"); return results; } /** * TODO: Need to pull the values from ApplicationResources here based on locales. * public static String checkPasswordDescribe(String username, String password, boolean strict, boolean superLax) { String[] results=checkPassword(username, password, strict, superLax); StringBuilder SB=new StringBuilder(); for(int c=0;c0) SB.append('\n'); SB.append(categories[c]).append(": ").append(desc); } } return SB.length()==0?null:SB.toString(); } */ private synchronized static byte[] getDictionary() throws IOException { if(cachedWords==null) { InputStream in=new CorrectedGZIPInputStream(PasswordChecker.class.getResourceAsStream("linux.words.gz")); try { ByteArrayOutputStream bout=new ByteArrayOutputStream(); try { IoUtils.copy(in, bout); } finally { bout.flush(); bout.close(); } cachedWords=bout.toByteArray(); } finally { in.close(); } } return cachedWords; } public static boolean hasResults(List results) { if(results==null) return false; String good = accessor.getMessage(GOOD_KEY); for(Result result : results) { if(!result.result.equals(good)) return true; } return false; } public static int indexOfIgnoreCase(String string,byte[] buffer,int wordstart,int wordlen) { int endpos=string.length()-wordlen; int wordend=wordstart+wordlen; Loop: for(int c=0;c<=endpos;c++) { int spos=c; for(int wpos=wordstart;wpos='A'&&ch1<='Z') ch1+='a'-'A'; if(ch2>='A'&&ch2<='Z') ch2+='a'-'A'; if(ch1!=ch2) continue Loop; } return c; } return -1; } public static String yearOf(int year) { if(year>=0&&year<=9) return "0"+year; return String.valueOf(year); } private static final String EOL = System.getProperty("line.separator"); /** * Prints the results. */ public static void printResults(List results, Appendable out) throws IOException { for(Result result : results) { out.append(result.getCategory()); out.append(": "); out.append(result.getResult()); out.append(EOL); } } /** * Prints the results in HTML format. */ public static void printResultsHtml(List results, Appendable out) throws IOException { out.append("

\n"); for(Result result : results) { out.append(" \n"); } out.append("
"); EncodingUtils.encodeHtml(result.getCategory(), out); out.append(":"); EncodingUtils.encodeHtml(result.getResult(), out); out.append("
\n"); } /** * Gets the results in HTML format. */ public static String getResultsHtml(List results) throws IOException { StringBuilder out = new StringBuilder(); printResultsHtml(results, out); return out.toString(); } /** * Gets the results as a String. */ public static String getResultsString(List results) { StringBuilder SB = new StringBuilder(); for(Result result : results) { SB .append(result.getCategory()) .append(": ") .append(result.getResult()) .append('\n'); } return SB.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy