/*
 *  Copyright 2013-2050 Emmanuel BRUN (contact@amapj.fr)
 * 
 *  This file is part of AmapJ.
 *  
 *  AmapJ is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.

 *  AmapJ 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with AmapJ.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * 
 */
 package fr.amapj.service.services.authentification;

import java.util.Date;

import org.passay.CharacterRule;
import org.passay.CharacterSequence;
import org.passay.EnglishCharacterData;
import org.passay.EnglishSequenceData;
import org.passay.IllegalSequenceRule;
import org.passay.LengthRule;
import org.passay.PasswordData;
import org.passay.PasswordValidator;
import org.passay.RuleResult;
import org.passay.SequenceData;
import org.passay.WhitespaceRule;

import fr.amapj.common.AmapjRuntimeException;
import fr.amapj.common.DateUtils;
import fr.amapj.model.models.fichierbase.Utilisateur;
import fr.amapj.view.engine.ui.AppConfiguration;

/**
 * Permet de verifier la robustesse des mots de passe 
 */
public class PasswordComplianceService
{
	static public enum CheckComplianceType
	{
		// compliant 
		OK ,
		
		// non compliant , mais l'utilisateur a encore un délai pour changer
		SHOULD_CHANGE_PASSWORD,
		
		// non compliant , et l'utilisateur doit changer maintenant
		MUST_CHANGE_PASSWORD,
	}

	
	/**
	 * 
	 */
	public CheckComplianceType checkComplianceOnLogging(Utilisateur u, String password, String sudo) 
	{
		// Pas de verification en mode sudo 
		if (sudo!=null)
		{
			return CheckComplianceType.OK;
		}
		

		
		// Le mot de passe est bien conforme 
		if (isCompliantPassword(password))
		{
			return CheckComplianceType.OK;
		}

		// Si le mot de passe n'est pas assez robuste 
		
		// On met à jour la date de première demande si elle n'est pas renseignée 
		if (u.dateChangePasswordAsked==null)
		{
			u.dateChangePasswordAsked = DateUtils.getDate();
		}
		
		
		Date ref = DateUtils.addDays(DateUtils.getDate(), -7);
		if (u.dateChangePasswordAsked.before(ref))
		{
			return CheckComplianceType.MUST_CHANGE_PASSWORD;
		}
		else
		{
			return CheckComplianceType.SHOULD_CHANGE_PASSWORD;
		}
	}
	
	
	public String getHtmlExplanationPasswordCompliant()
	{
		String level = AppConfiguration.getConf().getPasswordLevel();
		
		if (level.equals("0"))
		{
			return "Pas de contraintes particulières sur le mot de passe";
		}
		
		if (level.equals("1"))
		{
			return "Le mot de masse doit respecter les contraintes suivantes :"+
					"<ul>"+
						"<li>longueur mimimun 12 caractères, et 24 au maximum</li>"+
						"<li>contenir au moins une majuscule</li>"+
						"<li>contenir au moins une miniscule</li>"+
						"<li>contenir au moins un chiffre</li>"+
						"<li>contenir au moins un caractére spécial </li>"+
						"<li>ne pas contenir des séquences évidentes (abcdef, 12345, ...)</li>"+
					"</ul>";
		}
		
		throw new AmapjRuntimeException("Le password level dans AppConfiguration est inconnu:"+level);
	}

	public boolean isCompliantPassword(String password) 
	{
		String level = AppConfiguration.getConf().getPasswordLevel();
		
		if (level.equals("0"))
		{
			return true;
		}
		
		if (level.equals("1"))
		{
			PasswordValidator validator = new PasswordValidator(
				// length between 12 and 24 characters
				new LengthRule(12, 24),

				// at least one upper-case character
				new CharacterRule(EnglishCharacterData.UpperCase, 1),

				// at least one lower-case character
				new CharacterRule(EnglishCharacterData.LowerCase, 1),

				// at least one digit character
				new CharacterRule(EnglishCharacterData.Digit, 1),

				// at least one symbol (special character)
				new CharacterRule(EnglishCharacterData.Special, 1),

				// define some illegal sequences that will fail when >= 5 chars long
				// alphabetical is of the form 'abcde', numerical is '34567', qwery is 'asdfg'
				// the false parameter indicates that wrapped sequences are allowed; e.g.
				// 'xyzabc'
				new IllegalSequenceRule(EnglishSequenceData.Alphabetical, 5, false),
				new IllegalSequenceRule(EnglishSequenceData.Numerical, 5, false),
				new IllegalSequenceRule(EnglishSequenceData.USQwerty, 5, false),
				new IllegalSequenceRule(FrenchSequenceData.AZERTY, 5, false),

				// no whitespace
				new WhitespaceRule());

			RuleResult result = validator.validate(new PasswordData(new String(password)));
			return result.isValid();
		}
		
		throw new AmapjRuntimeException("Le password level dans AppConfiguration est inconnu:"+level);
	}
	
	
	


	/**
	 */
	static public enum FrenchSequenceData implements SequenceData 
	{
	  AZERTY(
	    "ILLEGAL_AZERTY_SEQUENCE",
	    // rows are no-modifier, shift, alt, shift+alt
	    // dead keys are denoted with the null character /u0000
	    new CharacterSequence[] {
	      new CharacterSequence(
	        "œ&é\"'(-è_çà)=",
	        "Œ1234567890°+"),
	      new CharacterSequence(
	        "azertyuiop^$",
	        "AZERTYUIOP¨£"),
	      new CharacterSequence(
	        "qsdfghjklmù*",
	        "QSDFGHJKLM%µ"),
	      new CharacterSequence(
	        "<wxcvbn,;:!",
	        ">WXCVBN?./§"),
	    });

	  /** Error code. */
	  private final String errorCode;

	  /** Character sequences. */
	  private final CharacterSequence[] sequences;

	  /**
	   * Creates a new english sequence data.
	   *
	   * @param  code  Error code used for message resolution.
	   * @param  seqs  One or more character sequences.
	   */
	  FrenchSequenceData(final String code, final CharacterSequence[] seqs)
	  {
	    errorCode = code;
	    sequences = seqs;
	  }

	  @Override
	  public String getErrorCode()
	  {
	    return errorCode;
	  }

	  @Override
	  public CharacterSequence[] getSequences()
	  {
	    return sequences;
	  }
	}

	
	
	
}