/**
 * © David Attias 2015
 */
package io.spacedog.client.credentials;

import java.util.Optional;
import java.util.Random;

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.xml.bind.DatatypeConverter;

import io.spacedog.utils.Check;

public class Passwords {

	public static final String PASSWORD_DEFAULT_REGEX = ".{6,}";

	private static final Random random = new Random();

	public static String checkAndHash(String password) {
		return checkAndHash(password, PASSWORD_DEFAULT_REGEX);
	}

	public static String checkAndHash(String password, String regex) {
		check(password, Optional.of(regex));
		return hash(password);
	}

	public static void check(String password) {
		Optional<String> none = Optional.empty();
		check(password, none);
	}

	public static void check(String password, Optional<String> regex) {
		Check.matchRegex(regex.orElse(PASSWORD_DEFAULT_REGEX), password, "password");
	}

	public static String random() {
		return Long.toString(random.nextLong());
	}

	/**
	 * ******* README ******* For now, I use hard coded salt and iteration number
	 * (1000). If I decide to change this, I have to maintain this algo and hard
	 * coded values until all password hashed this way are hashed another way. Salt
	 * and iterations could be saved in datastore close to the hashed password.
	 */
	public static String hash(String password) {
		try {
			PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), "hjyuetcslhhjgl".getBytes(), 1000, 64 * 8);
			SecretKeyFactory skf;
			skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
			return DatatypeConverter.printHexBinary(skf.generateSecret(spec).getEncoded());
		} catch (Throwable t) {
			throw new RuntimeException("failed to hash password", t);
		}
	}

}

@@ -116,16 +116,48 @@
 .Random;
+%0Aimport java.util.regex.Pattern;
 %0A%0Aimport
@@ -292,21 +292,58 @@
 g.utils.
-Check
+Exceptions;%0Aimport io.spacedog.utils.Utils
 ;%0A%0Apubli
@@ -917,36 +917,40 @@
 egex
+Opt
 ) %7B%0A%09%09
-Check.matchR
+String r
 egex
-(
+ = 
 regex
+Opt
 .orE
@@ -980,30 +980,215 @@
 GEX)
-, password, %22password%22
+;%0A%09%09if (Utils.isNull(password) %7C%7C !Pattern.matches(regex, password))%0A%09%09%09throw Exceptions.illegalArgumentWithCode(%22non-compliant-password%22, //%0A%09%09%09%09%09%22password isn't compliant to %5B%25s%5D regular expression%22, regex
 );%0A%09

Add non-compliant-password exception code