KEYCLOAK-7724 User Profile default validations
This commit is contained in:
parent
4dacbb9e0b
commit
32f2f095fe
20 changed files with 545 additions and 49 deletions
|
@ -41,6 +41,8 @@ public abstract class AbstractNumberValidator extends AbstractSimpleValidator im
|
|||
|
||||
public static final String MESSAGE_INVALID_NUMBER = "error-invalid-number";
|
||||
public static final String MESSAGE_NUMBER_OUT_OF_RANGE = "error-number-out-of-range";
|
||||
public static final String MESSAGE_NUMBER_OUT_OF_RANGE_TOO_SMALL = "error-number-out-of-range-too-small";
|
||||
public static final String MESSAGE_NUMBER_OUT_OF_RANGE_TOO_BIG = "error-number-out-of-range-too-big";
|
||||
|
||||
public static final String KEY_MIN = "min";
|
||||
public static final String KEY_MAX = "max";
|
||||
|
@ -111,18 +113,31 @@ public abstract class AbstractNumberValidator extends AbstractSimpleValidator im
|
|||
Number max = getMinMaxConfig(config, KEY_MAX);
|
||||
|
||||
if (min != null && isFirstGreaterThanToSecond(min, number)) {
|
||||
context.addError(new ValidationError(getId(), inputHint, MESSAGE_NUMBER_OUT_OF_RANGE, min, max));
|
||||
context.addError(new ValidationError(getId(), inputHint, selectRangeErrorMessage(config), min, max));
|
||||
return;
|
||||
}
|
||||
|
||||
if (max != null && isFirstGreaterThanToSecond(number, max)) {
|
||||
context.addError(new ValidationError(getId(), inputHint, MESSAGE_NUMBER_OUT_OF_RANGE, min, max));
|
||||
context.addError(new ValidationError(getId(), inputHint, selectRangeErrorMessage(config), min, max));
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select error message depending on the allowed range interval bound configuration.
|
||||
*/
|
||||
protected String selectRangeErrorMessage(ValidatorConfig config) {
|
||||
if (!config.containsKey(KEY_MAX)) {
|
||||
return MESSAGE_NUMBER_OUT_OF_RANGE_TOO_SMALL;
|
||||
} else if (!config.containsKey(KEY_MIN)) {
|
||||
return MESSAGE_NUMBER_OUT_OF_RANGE_TOO_BIG;
|
||||
} else {
|
||||
return MESSAGE_NUMBER_OUT_OF_RANGE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationResult validateConfig(KeycloakSession session, ValidatorConfig config) {
|
||||
Set<ValidationError> errors = new LinkedHashSet<>();
|
||||
|
|
|
@ -45,6 +45,8 @@ public class LengthValidator extends AbstractStringValidator implements Configur
|
|||
public static final String ID = "length";
|
||||
|
||||
public static final String MESSAGE_INVALID_LENGTH = "error-invalid-length";
|
||||
public static final String MESSAGE_INVALID_LENGTH_TOO_SHORT = "error-invalid-length-too-short";
|
||||
public static final String MESSAGE_INVALID_LENGTH_TOO_LONG = "error-invalid-length-too-long";
|
||||
|
||||
public static final String KEY_MIN = "min";
|
||||
public static final String KEY_MAX = "max";
|
||||
|
@ -85,17 +87,30 @@ public class LengthValidator extends AbstractStringValidator implements Configur
|
|||
int length = value.length();
|
||||
|
||||
if (config.containsKey(KEY_MIN) && length < min.intValue()) {
|
||||
context.addError(new ValidationError(ID, inputHint, MESSAGE_INVALID_LENGTH, min, max));
|
||||
context.addError(new ValidationError(ID, inputHint, selectErrorMessage(config), min, max));
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.containsKey(KEY_MAX) && length > max.intValue()) {
|
||||
context.addError(new ValidationError(ID, inputHint, MESSAGE_INVALID_LENGTH, min, max));
|
||||
context.addError(new ValidationError(ID, inputHint, selectErrorMessage(config), min, max));
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Select error message depending on the allowed length interval bound configuration.
|
||||
*/
|
||||
protected String selectErrorMessage(ValidatorConfig config) {
|
||||
if (!config.containsKey(KEY_MAX)) {
|
||||
return MESSAGE_INVALID_LENGTH_TOO_SHORT;
|
||||
} else if (!config.containsKey(KEY_MIN)) {
|
||||
return MESSAGE_INVALID_LENGTH_TOO_LONG;
|
||||
} else {
|
||||
return MESSAGE_INVALID_LENGTH;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationResult validateConfig(KeycloakSession session, ValidatorConfig config) {
|
||||
|
||||
|
|
|
@ -42,20 +42,29 @@ public class PatternValidator extends AbstractStringValidator implements Configu
|
|||
|
||||
public static final PatternValidator INSTANCE = new PatternValidator();
|
||||
|
||||
public static final String KEY_PATTERN = "pattern";
|
||||
public static final String CFG_PATTERN = "pattern";
|
||||
|
||||
public static final String MESSAGE_NO_MATCH = "error-pattern-no-match";
|
||||
|
||||
public static final String CFG_ERROR_MESSAGE = "error-message";
|
||||
|
||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(KEY_PATTERN);
|
||||
property.setName(CFG_PATTERN);
|
||||
property.setLabel("RegExp pattern");
|
||||
property.setHelpText("RegExp pattern the value must match. Java Pattern syntax is used.");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property);
|
||||
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(CFG_ERROR_MESSAGE);
|
||||
property.setLabel("Error message key");
|
||||
property.setHelpText("Key of the error message in i18n bundle. Dafault message key is " + MESSAGE_NO_MATCH);
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -65,10 +74,10 @@ public class PatternValidator extends AbstractStringValidator implements Configu
|
|||
|
||||
@Override
|
||||
protected void doValidate(String value, String inputHint, ValidationContext context, ValidatorConfig config) {
|
||||
Pattern pattern = config.getPattern(KEY_PATTERN);
|
||||
Pattern pattern = config.getPattern(CFG_PATTERN);
|
||||
|
||||
if (!pattern.matcher(value).matches()) {
|
||||
context.addError(new ValidationError(ID, inputHint, MESSAGE_NO_MATCH, config.getString(KEY_PATTERN)));
|
||||
context.addError(new ValidationError(ID, inputHint, config.getStringOrDefault(CFG_ERROR_MESSAGE, MESSAGE_NO_MATCH), config.getString(CFG_PATTERN)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,17 +85,17 @@ public class PatternValidator extends AbstractStringValidator implements Configu
|
|||
public ValidationResult validateConfig(KeycloakSession session, ValidatorConfig config) {
|
||||
Set<ValidationError> errors = new LinkedHashSet<>();
|
||||
|
||||
if (config == null || config == ValidatorConfig.EMPTY || !config.containsKey(KEY_PATTERN)) {
|
||||
errors.add(new ValidationError(ID, KEY_PATTERN, ValidatorConfigValidator.MESSAGE_CONFIG_MISSING_VALUE));
|
||||
if (config == null || config == ValidatorConfig.EMPTY || !config.containsKey(CFG_PATTERN)) {
|
||||
errors.add(new ValidationError(ID, CFG_PATTERN, ValidatorConfigValidator.MESSAGE_CONFIG_MISSING_VALUE));
|
||||
} else {
|
||||
Object maybePattern = config.get(KEY_PATTERN);
|
||||
Object maybePattern = config.get(CFG_PATTERN);
|
||||
try {
|
||||
Pattern pattern = config.getPattern(KEY_PATTERN);
|
||||
Pattern pattern = config.getPattern(CFG_PATTERN);
|
||||
if (pattern == null) {
|
||||
errors.add(new ValidationError(ID, KEY_PATTERN, ValidatorConfigValidator.MESSAGE_CONFIG_INVALID_VALUE, maybePattern));
|
||||
errors.add(new ValidationError(ID, CFG_PATTERN, ValidatorConfigValidator.MESSAGE_CONFIG_INVALID_VALUE, maybePattern));
|
||||
}
|
||||
} catch (PatternSyntaxException pse) {
|
||||
errors.add(new ValidationError(ID, KEY_PATTERN, ValidatorConfigValidator.MESSAGE_CONFIG_INVALID_VALUE, maybePattern));
|
||||
errors.add(new ValidationError(ID, CFG_PATTERN, ValidatorConfigValidator.MESSAGE_CONFIG_INVALID_VALUE, maybePattern, pse.getMessage()));
|
||||
}
|
||||
}
|
||||
return new ValidationResult(errors);
|
||||
|
|
|
@ -60,6 +60,12 @@ public class BuiltinValidatorsTest {
|
|||
|
||||
// test value trimming disabled in config
|
||||
Assert.assertTrue("trim disabled but performed", validator.validate("t ", "name", configFromMap(ImmutableMap.of(LengthValidator.KEY_MIN, 2, LengthValidator.KEY_TRIM_DISABLED, true))).isValid());
|
||||
|
||||
//test correct error message selection
|
||||
Assert.assertEquals(LengthValidator.MESSAGE_INVALID_LENGTH_TOO_SHORT,validator.validate("", "name", ValidatorConfig.builder().config(LengthValidator.KEY_MIN, 1).build()).getErrors().iterator().next().getMessage());
|
||||
Assert.assertEquals(LengthValidator.MESSAGE_INVALID_LENGTH,validator.validate("", "name", ValidatorConfig.builder().config(LengthValidator.KEY_MIN, 1).config(LengthValidator.KEY_MAX, 10).build()).getErrors().iterator().next().getMessage());
|
||||
Assert.assertEquals(LengthValidator.MESSAGE_INVALID_LENGTH_TOO_LONG,validator.validate("aaa", "name", ValidatorConfig.builder().config(LengthValidator.KEY_MAX, 1).build()).getErrors().iterator().next().getMessage());
|
||||
Assert.assertEquals(LengthValidator.MESSAGE_INVALID_LENGTH,validator.validate("aaa", "name", ValidatorConfig.builder().config(LengthValidator.KEY_MIN, 1).config(LengthValidator.KEY_MAX, 2).build()).getErrors().iterator().next().getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -243,6 +249,11 @@ public class BuiltinValidatorsTest {
|
|||
Assert.assertTrue(validator.validate("100.1", "name", ValidatorConfig.builder().config(DoubleValidator.KEY_MIN, 10.1).config(DoubleValidator.KEY_MAX, 100.1).build()).isValid());
|
||||
Assert.assertFalse(validator.validate("100.2", "name", ValidatorConfig.builder().config(DoubleValidator.KEY_MIN, 10.1).config(DoubleValidator.KEY_MAX, 100.1).build()).isValid());
|
||||
|
||||
//test correct error message selection
|
||||
Assert.assertEquals(DoubleValidator.MESSAGE_NUMBER_OUT_OF_RANGE_TOO_SMALL,validator.validate("10", "name", ValidatorConfig.builder().config(DoubleValidator.KEY_MIN, 100).build()).getErrors().iterator().next().getMessage());
|
||||
Assert.assertEquals(DoubleValidator.MESSAGE_NUMBER_OUT_OF_RANGE,validator.validate("10", "name", ValidatorConfig.builder().config(DoubleValidator.KEY_MIN, 100).config(DoubleValidator.KEY_MAX, 1000).build()).getErrors().iterator().next().getMessage());
|
||||
Assert.assertEquals(DoubleValidator.MESSAGE_NUMBER_OUT_OF_RANGE,validator.validate("10000", "name", ValidatorConfig.builder().config(DoubleValidator.KEY_MIN, 100).config(DoubleValidator.KEY_MAX, 1000).build()).getErrors().iterator().next().getMessage());
|
||||
Assert.assertEquals(DoubleValidator.MESSAGE_NUMBER_OUT_OF_RANGE_TOO_BIG,validator.validate("10000", "name", ValidatorConfig.builder().config(DoubleValidator.KEY_MAX, 1000).build()).getErrors().iterator().next().getMessage());
|
||||
|
||||
}
|
||||
|
||||
|
@ -318,28 +329,34 @@ public class BuiltinValidatorsTest {
|
|||
Assert.assertFalse(validator.validate(Arrays.asList("10", new Object()), "notANumberPresent").isValid());
|
||||
|
||||
// min only
|
||||
Assert.assertTrue(validator.validate("10", "name", ValidatorConfig.builder().config(DoubleValidator.KEY_MIN, 1).build()).isValid());
|
||||
Assert.assertFalse(validator.validate("10", "name", ValidatorConfig.builder().config(DoubleValidator.KEY_MIN, 100).build()).isValid());
|
||||
Assert.assertTrue(validator.validate("10", "name", ValidatorConfig.builder().config(IntegerValidator.KEY_MIN, 1).build()).isValid());
|
||||
Assert.assertFalse(validator.validate("10", "name", ValidatorConfig.builder().config(IntegerValidator.KEY_MIN, 100).build()).isValid());
|
||||
// min behavior around empty values
|
||||
Assert.assertFalse(validator.validate(null, "name", ValidatorConfig.builder().config(DoubleValidator.KEY_MIN, 1).build()).isValid());
|
||||
Assert.assertFalse(validator.validate("", "name", ValidatorConfig.builder().config(DoubleValidator.KEY_MIN, 1).build()).isValid());
|
||||
Assert.assertFalse(validator.validate(" ", "name", ValidatorConfig.builder().config(DoubleValidator.KEY_MIN, 1).build()).isValid());
|
||||
Assert.assertTrue(validator.validate(null, "name", ValidatorConfig.builder().config(DoubleValidator.KEY_MIN, 1).config(valConfigIgnoreEmptyValues).build()).isValid());
|
||||
Assert.assertTrue(validator.validate("", "name", ValidatorConfig.builder().config(DoubleValidator.KEY_MIN, 1).config(valConfigIgnoreEmptyValues).build()).isValid());
|
||||
Assert.assertTrue(validator.validate(" ", "name", ValidatorConfig.builder().config(DoubleValidator.KEY_MIN, 1).config(valConfigIgnoreEmptyValues).build()).isValid());
|
||||
Assert.assertFalse(validator.validate(null, "name", ValidatorConfig.builder().config(IntegerValidator.KEY_MIN, 1).build()).isValid());
|
||||
Assert.assertFalse(validator.validate("", "name", ValidatorConfig.builder().config(IntegerValidator.KEY_MIN, 1).build()).isValid());
|
||||
Assert.assertFalse(validator.validate(" ", "name", ValidatorConfig.builder().config(IntegerValidator.KEY_MIN, 1).build()).isValid());
|
||||
Assert.assertTrue(validator.validate(null, "name", ValidatorConfig.builder().config(IntegerValidator.KEY_MIN, 1).config(valConfigIgnoreEmptyValues).build()).isValid());
|
||||
Assert.assertTrue(validator.validate("", "name", ValidatorConfig.builder().config(IntegerValidator.KEY_MIN, 1).config(valConfigIgnoreEmptyValues).build()).isValid());
|
||||
Assert.assertTrue(validator.validate(" ", "name", ValidatorConfig.builder().config(IntegerValidator.KEY_MIN, 1).config(valConfigIgnoreEmptyValues).build()).isValid());
|
||||
|
||||
// max only
|
||||
Assert.assertFalse(validator.validate("10", "name", ValidatorConfig.builder().config(DoubleValidator.KEY_MAX, 1).build()).isValid());
|
||||
Assert.assertTrue(validator.validate("10", "name", ValidatorConfig.builder().config(DoubleValidator.KEY_MAX, 100).build()).isValid());
|
||||
Assert.assertFalse(validator.validate("10", "name", ValidatorConfig.builder().config(IntegerValidator.KEY_MAX, 1).build()).isValid());
|
||||
Assert.assertTrue(validator.validate("10", "name", ValidatorConfig.builder().config(IntegerValidator.KEY_MAX, 100).build()).isValid());
|
||||
|
||||
// min and max
|
||||
Assert.assertFalse(validator.validate("9", "name", ValidatorConfig.builder().config(DoubleValidator.KEY_MIN, 10).config(DoubleValidator.KEY_MAX, 100).build()).isValid());
|
||||
Assert.assertTrue(validator.validate("10", "name", ValidatorConfig.builder().config(DoubleValidator.KEY_MIN, 10).config(DoubleValidator.KEY_MAX, 100).build()).isValid());
|
||||
Assert.assertTrue(validator.validate("100", "name", ValidatorConfig.builder().config(DoubleValidator.KEY_MIN, 10).config(DoubleValidator.KEY_MAX, 100).build()).isValid());
|
||||
Assert.assertFalse(validator.validate("101", "name", ValidatorConfig.builder().config(DoubleValidator.KEY_MIN, 10).config(DoubleValidator.KEY_MAX, 100).build()).isValid());
|
||||
Assert.assertFalse(validator.validate("9", "name", ValidatorConfig.builder().config(IntegerValidator.KEY_MIN, 10).config(IntegerValidator.KEY_MAX, 100).build()).isValid());
|
||||
Assert.assertTrue(validator.validate("10", "name", ValidatorConfig.builder().config(IntegerValidator.KEY_MIN, 10).config(IntegerValidator.KEY_MAX, 100).build()).isValid());
|
||||
Assert.assertTrue(validator.validate("100", "name", ValidatorConfig.builder().config(IntegerValidator.KEY_MIN, 10).config(IntegerValidator.KEY_MAX, 100).build()).isValid());
|
||||
Assert.assertFalse(validator.validate("101", "name", ValidatorConfig.builder().config(IntegerValidator.KEY_MIN, 10).config(IntegerValidator.KEY_MAX, 100).build()).isValid());
|
||||
|
||||
Assert.assertTrue(validator.validate(Long.MIN_VALUE, "name").isValid());
|
||||
Assert.assertTrue(validator.validate(Long.MAX_VALUE, "name").isValid());
|
||||
|
||||
//test correct error message selection
|
||||
Assert.assertEquals(IntegerValidator.MESSAGE_NUMBER_OUT_OF_RANGE_TOO_SMALL,validator.validate("10", "name", ValidatorConfig.builder().config(IntegerValidator.KEY_MIN, 100).build()).getErrors().iterator().next().getMessage());
|
||||
Assert.assertEquals(IntegerValidator.MESSAGE_NUMBER_OUT_OF_RANGE,validator.validate("10", "name", ValidatorConfig.builder().config(IntegerValidator.KEY_MIN, 100).config(IntegerValidator.KEY_MAX, 1000).build()).getErrors().iterator().next().getMessage());
|
||||
Assert.assertEquals(IntegerValidator.MESSAGE_NUMBER_OUT_OF_RANGE,validator.validate("10000", "name", ValidatorConfig.builder().config(IntegerValidator.KEY_MIN, 100).config(IntegerValidator.KEY_MAX, 1000).build()).getErrors().iterator().next().getMessage());
|
||||
Assert.assertEquals(IntegerValidator.MESSAGE_NUMBER_OUT_OF_RANGE_TOO_BIG,validator.validate("10000", "name", ValidatorConfig.builder().config(IntegerValidator.KEY_MAX, 1000).build()).getErrors().iterator().next().getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -384,15 +401,19 @@ public class BuiltinValidatorsTest {
|
|||
Validator validator = Validators.patternValidator();
|
||||
|
||||
// Pattern object in the configuration
|
||||
ValidatorConfig config = configFromMap(Collections.singletonMap(PatternValidator.KEY_PATTERN, Pattern.compile("^start-.*-end$")));
|
||||
ValidatorConfig config = configFromMap(Collections.singletonMap(PatternValidator.CFG_PATTERN, Pattern.compile("^start-.*-end$")));
|
||||
Assert.assertTrue(validator.validate("start-1234-end", "value", config).isValid());
|
||||
Assert.assertFalse(validator.validate("start___end", "value", config).isValid());
|
||||
|
||||
// String in the configuration
|
||||
config = configFromMap(Collections.singletonMap(PatternValidator.KEY_PATTERN, "^start-.*-end$"));
|
||||
config = configFromMap(Collections.singletonMap(PatternValidator.CFG_PATTERN, "^start-.*-end$"));
|
||||
Assert.assertTrue(validator.validate("start-1234-end", "value", config).isValid());
|
||||
Assert.assertFalse(validator.validate("start___end", "value", config).isValid());
|
||||
|
||||
//custom error message
|
||||
config = ValidatorConfig.builder().config(PatternValidator.CFG_PATTERN, "^start-.*-end$").config(PatternValidator.CFG_ERROR_MESSAGE, "customError").build();
|
||||
Assert.assertEquals("customError", validator.validate("start___end", "value", config).getErrors().iterator().next().getMessage());
|
||||
|
||||
// null and empty values handling
|
||||
Assert.assertFalse(validator.validate(null, "value", config).isValid());
|
||||
Assert.assertFalse(validator.validate("", "value", config).isValid());
|
||||
|
|
|
@ -86,7 +86,7 @@ public class ValidatorTest {
|
|||
Assert.assertNotNull(error);
|
||||
Assert.assertEquals(LengthValidator.ID, error.getValidatorId());
|
||||
Assert.assertEquals(inputHint, error.getInputHint());
|
||||
Assert.assertEquals(LengthValidator.MESSAGE_INVALID_LENGTH, error.getMessage());
|
||||
Assert.assertEquals(LengthValidator.MESSAGE_INVALID_LENGTH_TOO_SHORT, error.getMessage());
|
||||
Assert.assertEquals(new Integer(2), error.getMessageParameters()[0]);
|
||||
|
||||
Assert.assertTrue(result.hasErrorsForValidatorId(LengthValidator.ID));
|
||||
|
|
|
@ -302,10 +302,11 @@ public abstract class AbstractUserProfileProvider<U extends UserProfileProvider>
|
|||
new AttributeValidatorMetadata(DuplicateUsernameValidator.ID),
|
||||
new AttributeValidatorMetadata(UsernameMutationValidator.ID)).setAttributeDisplayName("${username}");
|
||||
|
||||
metadata.addAttribute(UserModel.EMAIL, -1, new AttributeValidatorMetadata(BlankAttributeValidator.ID, BlankAttributeValidator.createConfig(Messages.MISSING_EMAIL, false)),
|
||||
new AttributeValidatorMetadata(EmailValidator.ID, ValidatorConfig.builder().config(EmailValidator.IGNORE_EMPTY_VALUE, true).build()),
|
||||
metadata.addAttribute(UserModel.EMAIL, -1,
|
||||
new AttributeValidatorMetadata(BlankAttributeValidator.ID, BlankAttributeValidator.createConfig(Messages.MISSING_EMAIL, false)),
|
||||
new AttributeValidatorMetadata(DuplicateEmailValidator.ID),
|
||||
new AttributeValidatorMetadata(EmailExistsAsUsernameValidator.ID)).setAttributeDisplayName("${email}");
|
||||
new AttributeValidatorMetadata(EmailExistsAsUsernameValidator.ID))
|
||||
.setAttributeDisplayName("${email}");
|
||||
|
||||
List<AttributeValidatorMetadata> readonlyValidators = new ArrayList<>();
|
||||
|
||||
|
@ -326,8 +327,9 @@ public abstract class AbstractUserProfileProvider<U extends UserProfileProvider>
|
|||
metadata.addAttribute(UserModel.USERNAME, -2, AbstractUserProfileProvider::editUsernameCondition,
|
||||
AbstractUserProfileProvider::readUsernameCondition, new AttributeValidatorMetadata(BrokeringFederatedUsernameHasValueValidator.ID)).setAttributeDisplayName("${username}");
|
||||
|
||||
metadata.addAttribute(UserModel.EMAIL, -1, new AttributeValidatorMetadata(BlankAttributeValidator.ID, BlankAttributeValidator.createConfig(Messages.MISSING_EMAIL, true)),
|
||||
new AttributeValidatorMetadata(EmailValidator.ID)).setAttributeDisplayName("${email}");
|
||||
metadata.addAttribute(UserModel.EMAIL, -1,
|
||||
new AttributeValidatorMetadata(BlankAttributeValidator.ID, BlankAttributeValidator.createConfig(Messages.MISSING_EMAIL, true)))
|
||||
.setAttributeDisplayName("${email}");
|
||||
|
||||
List<AttributeValidatorMetadata> readonlyValidators = new ArrayList<>();
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ import org.keycloak.userprofile.validator.BlankAttributeValidator;
|
|||
import org.keycloak.userprofile.validator.ImmutableAttributeValidator;
|
||||
import org.keycloak.validate.AbstractSimpleValidator;
|
||||
import org.keycloak.validate.ValidatorConfig;
|
||||
import org.keycloak.validate.validators.EmailValidator;
|
||||
|
||||
/**
|
||||
* {@link UserProfileProvider} loading configuration from the changeable JSON file stored in component config. Parsed
|
||||
|
@ -142,6 +143,13 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
|
|||
decoratedMetadata.addAttribute(UserModel.FIRST_NAME, 1, new AttributeValidatorMetadata(BlankAttributeValidator.ID, BlankAttributeValidator.createConfig(
|
||||
Messages.MISSING_FIRST_NAME, metadata.getContext() == UserProfileContext.IDP_REVIEW))).setAttributeDisplayName("${firstName}");
|
||||
decoratedMetadata.addAttribute(UserModel.LAST_NAME, 2, new AttributeValidatorMetadata(BlankAttributeValidator.ID, BlankAttributeValidator.createConfig(Messages.MISSING_LAST_NAME, metadata.getContext() == UserProfileContext.IDP_REVIEW))).setAttributeDisplayName("${lastName}");
|
||||
|
||||
//add email format validator to legacy profile
|
||||
List<AttributeMetadata> em = decoratedMetadata.getAttribute(UserModel.EMAIL);
|
||||
for(AttributeMetadata e: em) {
|
||||
e.addValidator(new AttributeValidatorMetadata(EmailValidator.ID, ValidatorConfig.builder().config(EmailValidator.IGNORE_EMPTY_VALUE, true).build()));
|
||||
}
|
||||
|
||||
return decoratedMetadata;
|
||||
}
|
||||
return decoratedMetadata;
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.userprofile.validator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.keycloak.provider.ConfiguredProvider;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.validate.AbstractStringValidator;
|
||||
import org.keycloak.validate.ValidationContext;
|
||||
import org.keycloak.validate.ValidationError;
|
||||
import org.keycloak.validate.ValidatorConfig;
|
||||
|
||||
/**
|
||||
* This validator disallowing bunch of characters we really not to expect in names of persons (fist, middle, last names).
|
||||
* <p>
|
||||
* Validates against hardcoded RegEx pattern - accepts plain string and collection of strings, for basic behavior
|
||||
* like null/blank values handling and collections support see {@link AbstractStringValidator}.
|
||||
*/
|
||||
public class PersonNameProhibitedCharactersValidator extends AbstractStringValidator implements ConfiguredProvider {
|
||||
|
||||
public static final String ID = "person-name-prohibited-characters";
|
||||
|
||||
public static final PersonNameProhibitedCharactersValidator INSTANCE = new PersonNameProhibitedCharactersValidator();
|
||||
|
||||
protected static final Pattern PATTERN = Pattern.compile("^[^<>&\"\\v$%!#?§;*~/\\\\|^=\\[\\]{}()\\p{Cntrl}]+$");
|
||||
|
||||
public static final String MESSAGE_NO_MATCH = "error-person-name-invalid-character";
|
||||
|
||||
public static final String CFG_ERROR_MESSAGE = "error-message";
|
||||
|
||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(CFG_ERROR_MESSAGE);
|
||||
property.setLabel("Error message key");
|
||||
property.setHelpText("Key of the error message in i18n bundle. Dafault message key is " + MESSAGE_NO_MATCH);
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doValidate(String value, String inputHint, ValidationContext context, ValidatorConfig config) {
|
||||
if (!PATTERN.matcher(value).matches()) {
|
||||
context.addError(new ValidationError(ID, inputHint, config.getStringOrDefault(CFG_ERROR_MESSAGE, MESSAGE_NO_MATCH)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Basic person name (First, Middle, Last name) validator disallowing bunch of characters we really do not expect in names.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.userprofile.validator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.keycloak.provider.ConfiguredProvider;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.validate.AbstractStringValidator;
|
||||
import org.keycloak.validate.ValidationContext;
|
||||
import org.keycloak.validate.ValidationError;
|
||||
import org.keycloak.validate.ValidatorConfig;
|
||||
|
||||
/**
|
||||
* This validator disallowing bunch of characters we really not to expect in username.
|
||||
* <p>
|
||||
* Validates against hardcoded RegEx pattern - accepts plain string and collection of strings, for basic behavior
|
||||
* like null/blank values handling and collections support see {@link AbstractStringValidator}.
|
||||
*/
|
||||
public class UsernameProhibitedCharactersValidator extends AbstractStringValidator implements ConfiguredProvider {
|
||||
|
||||
public static final String ID = "username-prohibited-characters";
|
||||
|
||||
public static final UsernameProhibitedCharactersValidator INSTANCE = new UsernameProhibitedCharactersValidator();
|
||||
|
||||
protected static final Pattern PATTERN = Pattern.compile("^[^<>&\"'\\s\\v\\h$%!#?§,;:*~/\\\\|^=\\[\\]{}()`\\p{Cntrl}]+$");
|
||||
|
||||
public static final String MESSAGE_NO_MATCH = "error-username-invalid-character";
|
||||
|
||||
public static final String CFG_ERROR_MESSAGE = "error-message";
|
||||
|
||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(CFG_ERROR_MESSAGE);
|
||||
property.setLabel("Error message key");
|
||||
property.setHelpText("Key of the error message in i18n bundle. Dafault message key is " + MESSAGE_NO_MATCH);
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doValidate(String value, String inputHint, ValidationContext context, ValidatorConfig config) {
|
||||
if (!PATTERN.matcher(value).matches()) {
|
||||
context.addError(new ValidationError(ID, inputHint, config.getStringOrDefault(CFG_ERROR_MESSAGE, MESSAGE_NO_MATCH)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Basic Username validator disallowing bunch of characters we really do not expect in username.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
}
|
|
@ -11,3 +11,5 @@ org.keycloak.userprofile.validator.RegistrationUsernameExistsValidator
|
|||
org.keycloak.userprofile.validator.RegistrationEmailAsUsernameEmailValueValidator
|
||||
org.keycloak.userprofile.validator.BrokeringFederatedUsernameHasValueValidator
|
||||
org.keycloak.userprofile.validator.ImmutableAttributeValidator
|
||||
org.keycloak.userprofile.validator.UsernameProhibitedCharactersValidator
|
||||
org.keycloak.userprofile.validator.PersonNameProhibitedCharactersValidator
|
|
@ -2,11 +2,19 @@
|
|||
"attributes": [
|
||||
{
|
||||
"name": "username",
|
||||
"displayName": "${username}"
|
||||
"displayName": "${username}",
|
||||
"validations": {
|
||||
"length": { "min": 3, "max": 255 },
|
||||
"username-prohibited-characters": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "email",
|
||||
"displayName": "${email}"
|
||||
"displayName": "${email}",
|
||||
"validations": {
|
||||
"email" : {},
|
||||
"length": { "max": 255 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "firstName",
|
||||
|
@ -15,6 +23,10 @@
|
|||
"permissions": {
|
||||
"view": ["admin", "user"],
|
||||
"edit": ["admin", "user"]
|
||||
},
|
||||
"validations": {
|
||||
"length": { "max": 255 },
|
||||
"person-name-prohibited-characters": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -24,6 +36,10 @@
|
|||
"permissions": {
|
||||
"view": ["admin", "user"],
|
||||
"edit": ["admin", "user"]
|
||||
},
|
||||
"validations": {
|
||||
"length": { "max": 255 },
|
||||
"person-name-prohibited-characters": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.userprofile.validator;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Vlastimil Elias <velias@redhat.com>
|
||||
*/
|
||||
public class PersonNameProhibitedCharactersValidatorTest {
|
||||
|
||||
@Test
|
||||
public void allowed() {
|
||||
// letters and numbers
|
||||
assertValid("a");
|
||||
assertValid("A");
|
||||
assertValid("z");
|
||||
assertValid("Z");
|
||||
assertValid("0");
|
||||
assertValid("9");
|
||||
//other than ASCII alphabets
|
||||
assertValid("\u010D");
|
||||
assertValid("\u01B1");
|
||||
assertValid("\u0397");
|
||||
assertValid("\u98CE\u7720");
|
||||
|
||||
// symbols we want to be allowed
|
||||
assertValid(" ");
|
||||
assertValid(".");
|
||||
assertValid("-");
|
||||
assertValid("_");
|
||||
assertValid("@");
|
||||
assertValid("'");
|
||||
assertValid(":");
|
||||
assertValid(",");
|
||||
|
||||
assertValid("as tr");
|
||||
|
||||
//crazy but existing name ;-)
|
||||
assertValid("X \u00C6 A-12");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disallowed() {
|
||||
|
||||
// white and control characters
|
||||
assertInvalid("\t");
|
||||
assertInvalid("\n");
|
||||
assertInvalid("\f");
|
||||
assertInvalid("\r");
|
||||
assertInvalid("\u0000");
|
||||
|
||||
//symbols dangerous for distinct technologies or really unnecessary in names
|
||||
//potential path traversals
|
||||
assertInvalid("/");
|
||||
assertInvalid("\\");
|
||||
//html/javascript dangerous
|
||||
assertInvalid("<");
|
||||
assertInvalid(">");
|
||||
assertInvalid("\"");
|
||||
assertInvalid("&");
|
||||
//other symbols not expected in names and potentially dangerous for other technologies
|
||||
assertInvalid("*");
|
||||
assertInvalid("$");
|
||||
assertInvalid("%");
|
||||
assertInvalid("#");
|
||||
assertInvalid("(");
|
||||
assertInvalid(")");
|
||||
assertInvalid("{");
|
||||
assertInvalid("}");
|
||||
assertInvalid("|");
|
||||
assertInvalid("~");
|
||||
assertInvalid("^");
|
||||
assertInvalid("!");
|
||||
assertInvalid("?");
|
||||
assertInvalid(";");
|
||||
assertInvalid("§");
|
||||
assertInvalid("=");
|
||||
|
||||
//unexpected character between expected
|
||||
assertInvalid("as\ttr");
|
||||
assertInvalid("\tastr");
|
||||
assertInvalid("astr\t");
|
||||
}
|
||||
|
||||
private void assertValid(String value) {
|
||||
Assert.assertTrue(PersonNameProhibitedCharactersValidator.INSTANCE.validate(value).isValid());
|
||||
}
|
||||
|
||||
private void assertInvalid(String value) {
|
||||
Assert.assertFalse(PersonNameProhibitedCharactersValidator.INSTANCE.validate(value).isValid());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.userprofile.validator;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @author Vlastimil Elias <velias@redhat.com>
|
||||
*/
|
||||
public class UsernameProhibitedCharactersValidatorTest {
|
||||
|
||||
@Test
|
||||
public void allowed() {
|
||||
// letters and numbers
|
||||
assertValid("a");
|
||||
assertValid("A");
|
||||
assertValid("z");
|
||||
assertValid("Z");
|
||||
assertValid("0");
|
||||
assertValid("9");
|
||||
assertValid("\u010D");
|
||||
assertValid("\u01B1");
|
||||
assertValid("\u0397");
|
||||
|
||||
// symbols we want to be allowed
|
||||
assertValid(".");
|
||||
assertValid("-");
|
||||
assertValid("_");
|
||||
assertValid("@");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disallowed() {
|
||||
|
||||
// white and control characters
|
||||
assertInvalid(" ");
|
||||
assertInvalid("\t");
|
||||
assertInvalid("\n");
|
||||
assertInvalid("\f");
|
||||
assertInvalid("\r");
|
||||
assertInvalid("\u0000");
|
||||
|
||||
//symbols dangerous for distinct technologies or really unnecessary in username
|
||||
//potential path traversals
|
||||
assertInvalid("/");
|
||||
assertInvalid("\\");
|
||||
//html/javascript dangerous
|
||||
assertInvalid("<");
|
||||
assertInvalid(">");
|
||||
assertInvalid("'");
|
||||
assertInvalid("\"");
|
||||
assertInvalid("&");
|
||||
//other symbols not expected in username and potentially dangerous for other technologies
|
||||
assertInvalid("*");
|
||||
assertInvalid("$");
|
||||
assertInvalid("%");
|
||||
assertInvalid("#");
|
||||
assertInvalid("(");
|
||||
assertInvalid(")");
|
||||
assertInvalid("{");
|
||||
assertInvalid("}");
|
||||
assertInvalid("|");
|
||||
assertInvalid("`");
|
||||
assertInvalid("~");
|
||||
assertInvalid("^");
|
||||
assertInvalid("!");
|
||||
assertInvalid("?");
|
||||
assertInvalid(":");
|
||||
assertInvalid(",");
|
||||
assertInvalid(";");
|
||||
assertInvalid("§");
|
||||
assertInvalid("=");
|
||||
|
||||
//unexpected character between expected
|
||||
assertInvalid("as tr");
|
||||
assertInvalid("\tastr");
|
||||
assertInvalid("astr\t");
|
||||
}
|
||||
|
||||
private void assertValid(String value) {
|
||||
Assert.assertTrue(UsernameProhibitedCharactersValidator.INSTANCE.validate(value).isValid());
|
||||
}
|
||||
|
||||
private void assertInvalid(String value) {
|
||||
Assert.assertFalse(UsernameProhibitedCharactersValidator.INSTANCE.validate(value).isValid());
|
||||
}
|
||||
|
||||
}
|
|
@ -111,7 +111,6 @@ public class RequiredActionUpdateProfileTest extends AbstractTestRealmKeycloakTe
|
|||
events.expectRequiredAction(EventType.UPDATE_PROFILE).detail(Details.PREVIOUS_FIRST_NAME, "Tom").detail(Details.UPDATED_FIRST_NAME, "New first")
|
||||
.detail(Details.PREVIOUS_LAST_NAME, "Brady").detail(Details.UPDATED_LAST_NAME, "New last")
|
||||
.detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com")
|
||||
.detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com")
|
||||
.assertEvent();
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
|
|
|
@ -320,13 +320,16 @@ public class RegisterTest extends AbstractTestRealmKeycloakTest {
|
|||
loginPage.clickRegister();
|
||||
registerPage.assertCurrent();
|
||||
|
||||
registerPage.register("firstName", "lastName", "registerUserSuccess@email", "registerUserSuccess", "password", "password");
|
||||
//contains few special characters we want to be sure they are allowed in username
|
||||
String username = "register.U-se@rS_uccess";
|
||||
|
||||
registerPage.register("firstName", "lastName", "registerUserSuccess@email", username, "password", "password");
|
||||
|
||||
appPage.assertCurrent();
|
||||
assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
String userId = events.expectRegister("registerUserSuccess", "registerUserSuccess@email").assertEvent().getUserId();
|
||||
assertUserRegistered(userId, "registerusersuccess", "registerusersuccess@email");
|
||||
String userId = events.expectRegister(username, "registerUserSuccess@email").assertEvent().getUserId();
|
||||
assertUserRegistered(userId, username.toLowerCase(), "registerusersuccess@email");
|
||||
}
|
||||
|
||||
private void assertUserRegistered(String userId, String username, String email) {
|
||||
|
|
|
@ -758,7 +758,7 @@ public class UserProfileTest extends AbstractUserProfileTest {
|
|||
fail("Should fail validation");
|
||||
} catch (ValidationException ve) {
|
||||
assertTrue(ve.isAttributeOnError(UserModel.USERNAME));
|
||||
assertTrue(ve.hasError(LengthValidator.MESSAGE_INVALID_LENGTH));
|
||||
assertTrue(ve.hasError(LengthValidator.MESSAGE_INVALID_LENGTH_TOO_SHORT));
|
||||
}
|
||||
|
||||
attributes.put(UserModel.USERNAME, "user");
|
||||
|
@ -769,7 +769,7 @@ public class UserProfileTest extends AbstractUserProfileTest {
|
|||
|
||||
provider.setConfiguration(null);
|
||||
|
||||
attributes.put(UserModel.USERNAME, "us");
|
||||
attributes.put(UserModel.USERNAME, "user");
|
||||
attributes.put(UserModel.FIRST_NAME, "Joe");
|
||||
attributes.put(UserModel.LAST_NAME, "Doe");
|
||||
|
||||
|
|
|
@ -380,12 +380,19 @@ error-invalid-value=Invalid value.
|
|||
error-invalid-blank=Please specify value.
|
||||
error-empty=Please specify value.
|
||||
error-invalid-length=Attribute {0} must have a length between {1} and {2}.
|
||||
error-invalid-length-too-short=Attribute {0} must have minimal length of {1}.
|
||||
error-invalid-length-too-long=Attribute {0} must have maximal length of {2}.
|
||||
error-invalid-email=Invalid email address.
|
||||
error-invalid-number=Invalid number.
|
||||
error-number-out-of-range=Attribute {0} must be a number between {1} and {2}.
|
||||
error-number-out-of-range-too-small=Attribute {0} must have minimal value of {1}.
|
||||
error-number-out-of-range-too-big=Attribute {0} must have maximal value of {2}.
|
||||
error-pattern-no-match=Invalid value.
|
||||
error-invalid-uri=Invalid URL.
|
||||
error-invalid-uri-scheme=Invalid URL scheme.
|
||||
error-invalid-uri-fragment=Invalid URL fragment.
|
||||
error-user-attribute-required=Please specify attribute {0}.
|
||||
error-invalid-date=Invalid date.
|
||||
error-user-attribute-read-only=The field {0} is read only.
|
||||
error-username-invalid-character=Username contains invalid character.
|
||||
error-person-name-invalid-character=Name contains invalid character.
|
||||
|
|
|
@ -45,14 +45,19 @@ error-invalid-value=Invalid value.
|
|||
error-invalid-blank=Please specify value.
|
||||
error-empty=Please specify value.
|
||||
error-invalid-length=Attribute {0} must have a length between {1} and {2}.
|
||||
error-invalid-length-too-short=Attribute {0} must have minimal length of {1}.
|
||||
error-invalid-length-too-long=Attribute {0} must have maximal length of {2}.
|
||||
error-invalid-email=Invalid email address.
|
||||
error-invalid-number=Invalid number.
|
||||
error-number-out-of-range=Attribute {0} must be a number between {1} and {2}.
|
||||
error-number-out-of-range-too-small=Attribute {0} must have minimal value of {1}.
|
||||
error-number-out-of-range-too-big=Attribute {0} must have maximal value of {2}.
|
||||
error-pattern-no-match=Invalid value.
|
||||
error-invalid-uri=Invalid URL.
|
||||
error-invalid-uri-scheme=Invalid URL scheme.
|
||||
error-invalid-uri-fragment=Invalid URL fragment.
|
||||
error-user-attribute-required=Please specify attribute {0}.
|
||||
error-invalid-date=Invalid date.
|
||||
|
||||
error-invalid-date=Attribute {0} is invalid date.
|
||||
error-user-attribute-read-only=Attribute {0} is read only.
|
||||
error-username-invalid-character={0} contains invalid character.
|
||||
error-person-name-invalid-character={0} contains invalid character.
|
|
@ -212,15 +212,22 @@ error-invalid-value=Invalid value.
|
|||
error-invalid-blank=Please specify value.
|
||||
error-empty=Please specify value.
|
||||
error-invalid-length=Length must be between {1} and {2}.
|
||||
error-invalid-length-too-short=Minimal length is {1}.
|
||||
error-invalid-length-too-long=Maximal length is {2}.
|
||||
error-invalid-email=Invalid email address.
|
||||
error-invalid-number=Invalid number.
|
||||
error-number-out-of-range=Number must be between {1} and {2}.
|
||||
error-number-out-of-range-too-small=Number must have minimal value of {1}.
|
||||
error-number-out-of-range-too-big=Number must have maximal value of {2}.
|
||||
error-pattern-no-match=Invalid value.
|
||||
error-invalid-uri=Invalid URL.
|
||||
error-invalid-uri-scheme=Invalid URL scheme.
|
||||
error-invalid-uri-fragment=Invalid URL fragment.
|
||||
error-user-attribute-required=Please specify this field.
|
||||
error-invalid-date=Invalid date.
|
||||
error-user-attribute-read-only=This field is read only.
|
||||
error-username-invalid-character=Value contains invalid character.
|
||||
error-person-name-invalid-character=Value contains invalid character.
|
||||
|
||||
invalidPasswordExistingMessage=Invalid existing password.
|
||||
invalidPasswordBlacklistedMessage=Invalid password: password is blacklisted.
|
||||
|
|
|
@ -124,11 +124,18 @@ error-invalid-value=''{0}'' has invalid value.
|
|||
error-invalid-blank=Please specify value of ''{0}''.
|
||||
error-empty=Please specify value of ''{0}''.
|
||||
error-invalid-length=''{0}'' must have a length between {1} and {2}.
|
||||
error-invalid-length-too-short=''{0}'' must have minimal length of {1}.
|
||||
error-invalid-length-too-long=''{0}'' must have maximal length of {2}.
|
||||
error-invalid-email=Invalid email address.
|
||||
error-invalid-number=''{0}'' is invalid number.
|
||||
error-number-out-of-range=''{0}'' must be a number between {1} and {2}.
|
||||
error-number-out-of-range-too-small=''{0}'' must have minimal value of {1}.
|
||||
error-number-out-of-range-too-big=''{0}'' must have maximal value of {2}.
|
||||
error-pattern-no-match=''{0}'' doesn''t match required format.
|
||||
error-invalid-uri=''{0}'' is invalid URL.
|
||||
error-invalid-uri-scheme=''{0}'' has invalid URL scheme.
|
||||
error-invalid-uri-fragment=''{0}'' is invalid URL fragment.
|
||||
error-user-attribute-required=Please specify ''{0}''.
|
||||
error-invalid-date=''{0}'' is invalid date.
|
||||
error-username-invalid-character=''{0}'' contains invalid character.
|
||||
error-person-name-invalid-character='{0}' contains invalid character.
|
||||
|
|
Loading…
Reference in a new issue