package hirondelle.predict.pub.resetpassword;
import static hirondelle.web4j.util.Consts.FAILS;
import hirondelle.predict.util.Captcha;
import hirondelle.web4j.model.Check;
import hirondelle.web4j.model.Id;
import hirondelle.web4j.model.ModelCtorException;
import hirondelle.web4j.model.ModelUtil;
import hirondelle.web4j.security.SafeText;
/** Model object for resetting a user's password to a new value. */
public final class ResetPassword {
/**
* Full constructor. All params are required.
*
* <P>The CAPTCHA fields must be present, and must be a valid response.
* @param aEmail Valid email address (required)
* @param aNonce one-time nonce used to as a 'handshake' between app and the owner of an email address
* @param aPassword Login password, 6..50 chars (required)
* @param aPasswordConfirm must be same as password (required)
* @param aCaptchaChallenge (required)
* @param aCaptchaResponse (required)
* @param aIpAddress the client IP address (required)
*/
public ResetPassword(
SafeText aEmail,
SafeText aNonce,
SafeText aPassword,
SafeText aPasswordConfirm,
SafeText aCaptchaChallenge,
SafeText aCaptchaResponse,
Id aIpAddress
) throws ModelCtorException {
fEmail = aEmail;
fNonce = aNonce;
fPassword = aPassword;
fPasswordConfirm = aPasswordConfirm;
fCaptchaChallenge = aCaptchaChallenge;
fCaptchaResponse = aCaptchaResponse;
fIpAddress = aIpAddress;
validateState();
}
public SafeText getEmail() { return fEmail; }
public SafeText getNonce() { return fNonce; }
public SafeText getPassword() { return fPassword; }
public SafeText getPasswordConfirm() { return fPasswordConfirm; }
/** Intended for debugging only. Passwords are not emitted. */
@Override public String toString(){
StringBuilder builder = new StringBuilder();
builder.append(ResetPassword.class);
builder.append(" Email : " + fEmail);
return builder.toString();
}
@Override public boolean equals(Object aThat){
Boolean result = ModelUtil.quickEquals(this, aThat);
if ( result == null ) {
ResetPassword that = (ResetPassword)aThat;
result = ModelUtil.equalsFor(this.getSignificantFields(), that.getSignificantFields());
}
return result;
}
@Override public int hashCode(){
if ( fHashCode == 0 ) {
fHashCode = ModelUtil.hashCodeFor(getSignificantFields());
}
return fHashCode;
}
// PRIVATE //
private final SafeText fEmail;
private final SafeText fNonce;
private final SafeText fPassword;
private final SafeText fPasswordConfirm;
private final SafeText fCaptchaChallenge;
private final SafeText fCaptchaResponse;
private final Id fIpAddress;
private int fHashCode;
private void validateState() throws ModelCtorException {
ModelCtorException ex = new ModelCtorException();
if ( FAILS == Check.required(fNonce) ) {
ex.add("Nonce is not present.");
}
if ( FAILS == Check.required(fEmail, Check.email()) ) {
ex.add("Valid email address is required.");
}
if ( FAILS == Check.required(fPassword, Check.range(6, 50)) ) {
ex.add("Password is required, minimum 6 characters.");
}
if ( FAILS == Check.required(fPasswordConfirm, Check.range(6, 50)) ) {
ex.add("Password confirmation is required, must match the password supplied above.");
}
if( bothPasswordsPresent() && passwordsDontMatch() ){
ex.add("Password confirmation doesn't match the original password.");
}
if( FAILS == isCaptchaPresent() ){
ex.add("Please type in the fuzzy characters (CAPTCHA)");
}
else if( FAILS == isCaptchaValid() ){
ex.add("CAPTCHA invalid. Please type in the fuzzy characters (CAPTCHA) again.");
}
if ( ! ex.isEmpty() ) throw ex;
}
private Object[] getSignificantFields(){
return new Object[] {fEmail, fNonce, fPassword, fPasswordConfirm};
}
private boolean isCaptchaPresent(){
return Check.required(fCaptchaChallenge) && Check.required(fCaptchaResponse);
}
private boolean isCaptchaValid(){
return Captcha.isCaptchaValid(
fIpAddress.getRawString(),
fCaptchaChallenge.getRawString(),
fCaptchaResponse.getRawString()
);
}
private boolean bothPasswordsPresent(){
return Check.required(fPassword) && Check.required(fPasswordConfirm);
}
private boolean passwordsDontMatch(){
return ! fPassword.equals(fPasswordConfirm);
}
}