Skip to content

Commit

Permalink
feat: validation for admin-meta pwd and login
Browse files Browse the repository at this point in the history
  • Loading branch information
Johaney-s committed Dec 20, 2022
1 parent 2f9b26f commit e9ee26f
Show file tree
Hide file tree
Showing 9 changed files with 322 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -267,45 +267,72 @@ public interface PerunTranslation extends Messages {
@DefaultMessage("Login must<ul><li>start with a lower-case letter<li>be 2-15 characters long<li>consist only of<ul><li>lower-case non-accented letters<li>digits<li>hyphens and underscores</ul></ul>")
public String einfraLoginHelp();

@DefaultMessage("Login must<ul><li>start with a lower-case letter<li>be 2-15 characters long<li>consist only of<ul><li>lower-case non-accented letters<li>digits<li>hyphens and underscores</ul></ul>")
public String adminMetaLoginHelp();

@DefaultMessage("Login must <b>be 2-15 characters long!</b>")
public String einfraLoginLength();

@DefaultMessage("Login must <b>be 2-15 characters long!</b>")
public String adminMetaLoginLength();

@DefaultMessage("Login must <b>start with lower-case letter!</b>")
public String einfraLoginStart();

@DefaultMessage("Login must <b>start with lower-case letter!</b>")
public String adminMetaLoginStart();

@DefaultMessage("Login can contain only<ul><li>lower-cased non-accented letters<li>digits<li>hyphens and underscores</ul>")
public String einfraLoginFormat();

@DefaultMessage("Login can contain only<ul><li>lower-cased non-accented letters<li>digits<li>hyphens and underscores</ul>")
public String adminMetaLoginFormat();

@DefaultMessage("Password must <ul><li>contain only printing (non-accented) characters<li>be at least 10 characters long<li>consist of at least 3 of 4 character groups<ul><li>lower-case letters<li>upper-case letters<li>digits<li>special characters</ul></ul>")
public String einfraPasswordHelp();

@DefaultMessage("Password must <ul><li>contain only printing (non-accented) characters<li>be at least 14 characters long<li>consist of at least 3 of 4 character groups<ul><li>lower-case letters<li>upper-case letters<li>digits<li>special characters</ul></ul>")
public String muAdmPasswordHelp();

@DefaultMessage("Password must <ul><li>contain only printing (non-accented) characters<li>be at least 10 characters long<li>consist of at least 3 of 4 character groups<ul><li>lower-case letters<li>upper-case letters<li>digits<li>special characters</ul></ul>")
public String adminMetaPasswordHelp();

@DefaultMessage("Password must be <b>at least 10 characters</b> long!")
public String einfraPasswordLength();

@DefaultMessage("Password must be <b>at least 14 characters</b> long!")
public String muAdmPasswordLength();

@DefaultMessage("Password must be <b>at least 10 characters</b> long!")
public String adminMetaPasswordLength();

@DefaultMessage("Password <b>can`t contain accented characters (diacritics)</b> or non-printing and control characters!")
public String einfraPasswordFormat();

@DefaultMessage("Password <b>can`t contain accented characters (diacritics)</b> or non-printing and control characters!")
public String muAdmPasswordFormat();

@DefaultMessage("Password <b>can`t contain accented characters (diacritics)</b> or non-printing and control characters!")
public String adminMetaPasswordFormat();

@DefaultMessage("Password must consist of <b>at least 3 of 4</b> character groups<ul><li>lower-case letters</li><li>upper-case letters</li><li>digits</li><li>special characters</li></ul>")
public String einfraPasswordStrength();

@DefaultMessage("Password must consist of <b>at least 3 of 4</b> character groups<ul><li>lower-case letters</li><li>upper-case letters</li><li>digits</li><li>special characters</li></ul>")
public String muAdmPasswordStrength();

@DefaultMessage("Password must consist of <b>at least 3 of 4</b> character groups<ul><li>lower-case letters</li><li>upper-case letters</li><li>digits</li><li>special characters</li></ul>")
public String adminMetaPasswordStrength();

@DefaultMessage("Password <b>can`t contain login, name or surname</b>, not even backwards!")
public String einfraPasswordStrengthForNameLogin();

@DefaultMessage("Password <b>can`t contain login</b>, not even backwards!")
public String muAdmPasswordStrengthForNameLogin();

@DefaultMessage("Password <b>can`t contain login, name or surname</b>, not even backwards!")
public String adminMetaPasswordStrengthForNameLogin();

@DefaultMessage("New password can`t be empty!")
public String passwordCantBeEmpty();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,15 @@ muAdmPasswordFormat=Heslo <b>nesmí obsahovat diakritiku</b> nebo řídící a f
muAdmPasswordStrengthForNameLogin=Heslo <b>nesmí obsahovat login</b> a to ani pozpátku!
muAdmPasswordStrength=Heslo musí obsahovat <b>alespoň 3 ze 4</b> kategorií znaků<ul><li>malá písmena</li><li>velká písmena</li><li>číslice</li><li>speciální znaky</li></ul>
muAdmPasswordHelp=Heslo musí<ul><li>obsahovat pouze tisknutelné znaky (bez diakritiky)<li>mít délku 14 a více znaků<li>obsahovat alespoň 3 ze 4 kategorií znaků<ul><li>malá písmena<li>velká písmena<li>číslice<li>speciální znaky</ul></ul>
adminMetaLoginHelp=Uživatelské jméno musí<ul><li>začínat malým písmenem<li>být 2-15 znaků dlouhé<li>skládat se pouze z<ul><li>malých písmen<li>číslic<li>pomlček a podtržítek</ul></ul>
adminMetaLoginLength=Uživatelské jméno musí <b>být dlouhé 2-15 znaků!</b>
adminMetaLoginStart=Uživatelské jméno musí <b>začínat malým písmenem!</b>
adminMetaLoginFormat=Uživatelské jméno smí obsahovat pouze<ul><li>malá písmena bez diakritiky<li>číslice<li>pomlčky a podtržítka</ul>
adminMetaPasswordHelp=Heslo musí<ul><li>obsahovat pouze tisknutelné znaky (bez diakritiky)<li>mít délku 10 a více znaků<li>obsahovat alespoň 3 ze 4 kategorií znaků<ul><li>malá písmena<li>velká písmena<li>číslice<li>speciální znaky</ul></ul>
adminMetaPasswordLength=Heslo musí mít <b>alespoň 10 znaků!</b>
adminMetaPasswordFormat=Heslo <b>nesmí obsahovat diakritiku</b> nebo řídící a formátovací znaky!</b>
adminMetaPasswordStrength=Heslo musí obsahovat <b>alespoň 3 ze 4</b> kategorií znaků<ul><li>malá písmena</li><li>velká písmena</li><li>číslice</li><li>speciální znaky</li></ul>
adminMetaPasswordStrengthForNameLogin=Heslo <b>nesmí obsahovat login, jméno nebo příjmení</b> a to ani pozpátku!
passwordCantBeEmpty=Nové heslo nesmí být prázdné!
passwordMismatch=Hesla se neshodují!
secondPasswordIsEmpty=Zopakujte nové heslo pro kontrolu
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public interface PerunPwdResetTranslation extends PerunTranslation {
@DefaultMessage("Reset password")
public String submitPwdResetButton();

@DefaultMessage("Passwords doesn`t match!")
@DefaultMessage("Passwords don`t match!")
public String passwordsDoesnMatch();

@DefaultMessage("Password can`t be empty!")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ public interface PerunRegistrarTranslation extends PerunTranslation {
@DefaultMessage("Password <b>can`t be empty!</b>")
public String passEmpty();

@DefaultMessage("Passwords doesn`t match!")
@DefaultMessage("Passwords don`t match!")
public String passMismatch();

@DefaultMessage("Confirm password!")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import cz.metacentrum.perun.wui.registrar.widgets.items.validators.MuAdmPasswordValidator;
import cz.metacentrum.perun.wui.registrar.widgets.items.validators.PasswordValidator;
import cz.metacentrum.perun.wui.registrar.widgets.items.validators.PerunFormItemValidator;
import cz.metacentrum.perun.wui.registrar.widgets.items.validators.AdminMetaPasswordValidator;
import cz.metacentrum.perun.wui.widgets.boxes.ExtendedPasswordTextBox;
import org.gwtbootstrap3.client.ui.InputGroup;
import org.gwtbootstrap3.client.ui.InputGroupAddon;
Expand Down Expand Up @@ -38,6 +39,8 @@ public Password(PerunForm form, ApplicationFormItemData item, String lang) {
this.validator = new EinfraPasswordValidator();
} else if (item.getFormItem() != null && Objects.equals("urn:perun:user:attribute-def:def:login-namespace:mu-adm", item.getFormItem().getPerunDestinationAttribute())) {
this.validator = new MuAdmPasswordValidator();
} else if (item.getFormItem() != null && Objects.equals("urn:perun:user:attribute-def:def:login-namespace:admin-meta", item.getFormItem().getPerunDestinationAttribute())) {
this.validator = new AdminMetaPasswordValidator();
} else {
this.validator = new PasswordValidator();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,16 @@ protected Widget initFormItem() {
}
}

// replace admin-meta help texts to support HTML formatting
if (this.getItemData().getFormItem() != null &&
"urn:perun:user:attribute-def:def:login-namespace:admin-meta".equals(this.getItemData().getFormItem().getPerunDestinationAttribute())) {
if (this instanceof Password) {
help.setHTML(translation.adminMetaPasswordHelp());
} else if (this instanceof Username) {
help.setHTML(translation.adminMetaLoginHelp());
}
}

widgetWithTexts.add(help);

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import cz.metacentrum.perun.wui.model.PerunException;
import cz.metacentrum.perun.wui.model.beans.ApplicationFormItemData;
import cz.metacentrum.perun.wui.registrar.widgets.PerunForm;
import cz.metacentrum.perun.wui.registrar.widgets.items.validators.AdminMetaUsernameValidator;
import cz.metacentrum.perun.wui.registrar.widgets.items.validators.EinfraUsernameValidator;
import cz.metacentrum.perun.wui.registrar.widgets.items.validators.PerunFormItemValidator;
import cz.metacentrum.perun.wui.registrar.widgets.items.validators.UsernameValidator;
Expand Down Expand Up @@ -42,6 +43,8 @@ public Username(PerunForm form, ApplicationFormItemData item, String lang) {
super(form, item, lang);
if (item.getFormItem() != null && Objects.equals("urn:perun:user:attribute-def:def:login-namespace:einfra", item.getFormItem().getPerunDestinationAttribute())) {
this.validator = new EinfraUsernameValidator();
} else if (item.getFormItem() != null && Objects.equals("urn:perun:user:attribute-def:def:login-namespace:admin-meta", item.getFormItem().getPerunDestinationAttribute())) {
this.validator = new AdminMetaUsernameValidator();
} else {
this.validator = new UsernameValidator();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
package cz.metacentrum.perun.wui.registrar.widgets.items.validators;

import com.google.gwt.regexp.shared.RegExp;
import cz.metacentrum.perun.wui.registrar.widgets.items.Password;
import cz.metacentrum.perun.wui.registrar.widgets.items.PerunFormItem;
import cz.metacentrum.perun.wui.registrar.widgets.items.Username;
import org.gwtbootstrap3.client.ui.constants.ValidationState;

import java.util.List;
import java.util.Objects;

/**
* Specific password validator for admin-meta namespace
*
* TODO: We must revalidate this form item when person`s name changes after entering password value !!
*/
public class AdminMetaPasswordValidator extends PasswordValidator {

@Override
public boolean validateLocal(Password password) {

if (password.isRequired() && isNullOrEmpty(password.getValue())) {
setResult(Result.EMPTY_PASSWORD);
password.setRawStatus(getTransl().passEmpty(), ValidationState.ERROR);
return false;
}

// limit only to ASCII printable chars
RegExp regExp2 = RegExp.compile("^[\\x20-\\x7E]{1,}$");
if(regExp2.exec(password.getValue()) == null){
setResult(Result.INVALID_FORMAT);
password.setRawStatus(getTransl().adminMetaPasswordFormat(), ValidationState.ERROR);
return false;
}

// Check that password contains at least 3 of 4 character groups

RegExp regExpDigit = RegExp.compile("^.*[0-9].*$");
RegExp regExpLower = RegExp.compile("^.*[a-z].*$");
RegExp regExpUpper = RegExp.compile("^.*[A-Z].*$");
RegExp regExpSpec = RegExp.compile("^.*[\\x20-\\x2F\\x3A-\\x40\\x5B-\\x60\\x7B-\\x7E].*$"); // FIXME - are those correct printable specific chars?

int matchCounter = 0;
if (regExpDigit.exec(password.getValue()) != null) matchCounter++;
if (regExpLower.exec(password.getValue()) != null) matchCounter++;
if (regExpUpper.exec(password.getValue()) != null) matchCounter++;
if (regExpSpec.exec(password.getValue()) != null) matchCounter++;

if(matchCounter < 3){
setResult(Result.INVALID_FORMAT);
password.setRawStatus(getTransl().adminMetaPasswordStrength(), ValidationState.ERROR);
return false;
}

if (!checkOnNamesAndLogins(getLoginValue(password), password)) return false;
if (!checkOnNamesAndLogins(getName(password), password)) return false;
if (!checkOnNamesAndLogins(getFirstName(password), password)) return false;
if (!checkOnNamesAndLogins(getSurname(password), password)) return false;

// check length
if (password.getValue().length() < 10) {
setResult(Result.INVALID_FORMAT);
password.setRawStatus(getTransl().adminMetaPasswordLength(), ValidationState.ERROR);
return false;
}

// check typos
if (!password.getPassword().getValue().equals(password.getPasswordSecond().getValue())) {
if (isNullOrEmpty(password.getPasswordSecond().getValue())) {
setResult(Result.SECOND_PASSWORD_EMPTY);
password.setStatus(getTransl().secondPassEmpty(), ValidationState.WARNING);
return false;
}
setResult(Result.PASSWORD_MISSMATCH);
password.setStatus(getTransl().passMismatch(), ValidationState.ERROR);
return false;
}

password.setStatus(ValidationState.SUCCESS);
return true;

}

private String getLoginValue(Password password) {

List<PerunFormItem> items = password.getForm().getPerunFormItems();
for (PerunFormItem item : items) {
if (item instanceof Username) {
if (item.getItemData().getFormItem() != null && Objects.equals("urn:perun:user:attribute-def:def:login-namespace:admin-meta", item.getItemData().getFormItem().getPerunDestinationAttribute())) {
return item.getValue();
}
}
}
return null;

}

private String getName(Password password) {

List<PerunFormItem> items = password.getForm().getPerunFormItems();
for (PerunFormItem item : items) {
if (item.getItemData().getFormItem() != null && Objects.equals("urn:perun:user:attribute-def:core:displayName", item.getItemData().getFormItem().getPerunDestinationAttribute())) {
return item.getValue();
}
}
return null;

}

private String getFirstName(Password password) {

List<PerunFormItem> items = password.getForm().getPerunFormItems();
for (PerunFormItem item : items) {
if (item.getItemData().getFormItem() != null && Objects.equals("urn:perun:user:attribute-def:core:firstName", item.getItemData().getFormItem().getPerunDestinationAttribute())) {
return item.getValue();
}
}
return null;

}

private String getSurname(Password password) {

List<PerunFormItem> items = password.getForm().getPerunFormItems();
for (PerunFormItem item : items) {
if (item.getItemData().getFormItem() != null && Objects.equals("urn:perun:user:attribute-def:core:lastName", item.getItemData().getFormItem().getPerunDestinationAttribute())) {
return item.getValue();
}
}
return null;

}

public static String reverse(String string) {
if (string == null || string.isEmpty() || string.length() == 1) return string;
return string.charAt(string.length()-1)+reverse(string.substring(1, string.length()-1))+string.charAt(0);
}

/**
* Return FALSE if password contains "login"
*
* @param string
* @param password
* @return
*/
public boolean checkOnNamesAndLogins(String string, Password password) {

// do not check too small string parts !!
if (string == null || string.length() < 3) return true;

if (string.split("\\s").length > 1) {

// consist of more pieces - check them individually - each piece must be relevant
String[] splitedString = string.split("\\s");
for (String s : splitedString) {

// too small part, skip
if (s == null || s.length() < 3) continue;

// check part
if (password.getValue().toLowerCase().contains(s.toLowerCase()) ||
password.getValue().toLowerCase().contains(reverse(s.toLowerCase())) ||
normalizeString(password.getValue()).contains(normalizeString(s)) ||
normalizeString(password.getValue()).contains(normalizeString(reverse(s)))) {
setResult(Result.INVALID_FORMAT);
password.setRawStatus(getTransl().adminMetaPasswordStrengthForNameLogin(), ValidationState.ERROR);
return false;
}

}

}

// check also whole string
if (password.getValue().toLowerCase().contains(string.toLowerCase()) ||
password.getValue().toLowerCase().contains(reverse(string.toLowerCase())) ||
normalizeString(password.getValue()).contains(normalizeString(string)) ||
normalizeString(password.getValue()).contains(normalizeString(reverse(string)))) {
setResult(Result.INVALID_FORMAT);
password.setRawStatus(getTransl().adminMetaPasswordStrengthForNameLogin(), ValidationState.ERROR);
return false;
}

return true;

}

private String normalizeString(String string) {
String result = normalizeStringToNFD(string);
result = result.replaceAll("\\s","");
return result;
}

private final native String normalizeStringToNFD(String input) /*-{
if (typeof input.normalize !== "undefined") {
// convert to normal decomposed form and replace all combining-diacritics marks
return input.normalize('NFD').replace(/[\u0300-\u036f]/g, "").toLowerCase();
}
// just lowercase
return input.toLowerCase();
}-*/;

}
Loading

0 comments on commit e9ee26f

Please sign in to comment.