chore: restore from backup
restore the project to new gitea instance
This commit is contained in:
commit
b477a5f984
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
/target/
|
||||
/build/
|
||||
/.classpath
|
||||
/.project
|
||||
/.factorypath
|
||||
/password-authority.iml
|
||||
/.idea/
|
||||
/.settings/
|
||||
|
||||
/validation-authority.iml
|
||||
96
README.md
Normal file
96
README.md
Normal file
@ -0,0 +1,96 @@
|
||||
:small_orange_diamond: WORK IN PROGRESS :small_orange_diamond:
|
||||
|
||||
# Validation Authority
|
||||
|
||||
| characteristic | value | description |
|
||||
| ------ | ------ | ------ |
|
||||
| specification | JSON:API | https://jsonapi.org/format/ |
|
||||
| base url (prod) | https://validationauthority.jkgroller ||
|
||||
| base path | `/v1` | |
|
||||
| content type | `application/vnd.api+json` | optional on `GET` |
|
||||
|
||||
|
||||
## Resources
|
||||
|
||||
### credentials
|
||||
|
||||
Characteristics:
|
||||
|
||||
| characteristic | description |
|
||||
| ------ | ------ |
|
||||
| type | `credentials` |
|
||||
| resource path | `/credentials` |
|
||||
| id | base62 url-safe uuid |
|
||||
| supported methods | `POST` |
|
||||
|
||||
|
||||
Attributes:
|
||||
|
||||
| name | type | allowed methods |
|
||||
| ------ | ------ | ------ |
|
||||
| username | char[] | `POST` |
|
||||
| password | char[] | `POST` |
|
||||
| valid | boolean | `GET` |
|
||||
|
||||
Status Codes:
|
||||
|
||||
| status | description |
|
||||
| ------ | ------ |
|
||||
| `201 CREATED` | For all successful `POST` requests. |
|
||||
| `400 BAD REQUEST` | For all validation failure responses. Collection of `Error` objects returned. See below. |
|
||||
| `405 METHOD NOT ALLOWED` | For any `GET`, `PATCH`, and `DELETE` requests. |
|
||||
| `500 INTERNAL SERVER ERROR` | When things go horribly wrong. Should never occur. |
|
||||
|
||||
|
||||
Error Objects:
|
||||
|
||||
See https://jsonapi.org/format/#errors
|
||||
|
||||
`400 BAD REQUEST` - _Collection_ of error objects.
|
||||
|
||||
_Validation Errors - Username_
|
||||
|
||||
| attribute | type | description/value |
|
||||
| ------ | ------ | ------ |
|
||||
| id | string | base62 url-safe uuid |
|
||||
| title | string | Human readable title for the error. Many errors may share this title. |
|
||||
| description | string | Human readable description for _this instance_ of the problem. |
|
||||
| status | string | `422` |
|
||||
| code | string | base62 resource id of constraint which failed |
|
||||
| source | object | |
|
||||
| pointer |string| `/data/attributes/username`|
|
||||
| meta | map| `accessibilityDescription` = Screen reader compatible description |
|
||||
| | | ?? |
|
||||
|
||||
_Validation Errors - Password_
|
||||
|
||||
| attribute | type | description/value |
|
||||
| ------ | ------ | ------ |
|
||||
| id | string | base62 url-safe uuid |
|
||||
| title | string | Human readable title for the error. Many errors may share this title. |
|
||||
| description | string | Human readable description for _this instance_ of the problem. |
|
||||
| status | string | `422` |
|
||||
| code | string | base62 resource id of constraint which failed |
|
||||
| source | object | |
|
||||
| pointer |string| `/data/attributes/password`|
|
||||
| meta | map| `accessibilityDescription` = Screen reader compatible description |
|
||||
| | | ?? |
|
||||
|
||||
|
||||
`405 METHOD NOT ALLOWED` - Single error object.
|
||||
|
||||
| attribute | type | description/value |
|
||||
| ------ | ------ | ------ |
|
||||
| id | string | base62 url-safe uuid |
|
||||
| title | string | Human readable title for the error. |
|
||||
| status | string | `405` |
|
||||
|
||||
|
||||
`500 INTERNAL SERVER ERRORD` - Single error object.
|
||||
|
||||
| attribute | type | description/value |
|
||||
| ------ | ------ | ------ |
|
||||
| id | string | base62 url-safe uuid |
|
||||
| title | string | Human readable title for the error. |
|
||||
| status | string | `500` |
|
||||
|
||||
140
pom.xml
Normal file
140
pom.xml
Normal file
@ -0,0 +1,140 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Copyright 2021-2022 John Groller
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.jkgroller</groupId>
|
||||
<artifactId>validation-authority</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>Validation Authority API</name>
|
||||
<description>Provides resources used for rule-based validation and generation.</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.2.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>8</java.version>
|
||||
<commons.lang3.version>3.5</commons.lang3.version>
|
||||
<crnk.version>3.2.20200419165537</crnk.version>
|
||||
<commons.io.version>2.5</commons.io.version>
|
||||
<mapstruct.version>1.3.1.Final</mapstruct.version>
|
||||
<passay.version>1.6.0</passay.version>
|
||||
<mycila.license.version>4.0.rc1</mycila.license.version>
|
||||
<friendly.id.version>1.1.0</friendly.id.version>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>JCenter</id>
|
||||
<url>https://jcenter.bintray.com/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>${commons.io.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons.lang3.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.crnk</groupId>
|
||||
<artifactId>crnk-setup-spring-boot2</artifactId>
|
||||
<version>${crnk.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.devskiller.friendly-id</groupId>
|
||||
<artifactId>friendly-id</artifactId>
|
||||
<version>${friendly.id.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.passay</groupId>
|
||||
<artifactId>passay</artifactId>
|
||||
<version>${passay.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>ValidationAuthority</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.mycila</groupId>
|
||||
<artifactId>license-maven-plugin</artifactId>
|
||||
<version>${mycila.license.version}</version>
|
||||
<configuration>
|
||||
<licenseSets>
|
||||
<licenseSet>
|
||||
<header>src/main/resources/licenseTemplate.txt</header>
|
||||
<excludes>
|
||||
<exclude>**/README</exclude>
|
||||
<exclude>src/test/resources/**</exclude>
|
||||
<exclude>src/main/resources/**</exclude>
|
||||
</excludes>
|
||||
</licenseSet>
|
||||
</licenseSets>
|
||||
<properties>
|
||||
<owner>John Groller</owner>
|
||||
</properties>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* @author john@grollerfamily.com
|
||||
* <p>
|
||||
* API providing validation resources.
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class ValidationAuthority {
|
||||
|
||||
/**
|
||||
* Start it up.
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ValidationAuthority.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.config;
|
||||
|
||||
import com.jkgroller.validationauthority.constraints.password.*;
|
||||
import com.jkgroller.validationauthority.constraints.username.UsernameEmailSubstringConstraint;
|
||||
import com.jkgroller.validationauthority.constraints.username.UsernameLeadingTrailingSpaceConstraint;
|
||||
import com.jkgroller.validationauthority.constraints.username.UsernameLengthConstraint;
|
||||
import com.jkgroller.validationauthority.resource.CredentialRuleResource;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Loading up the rule resources for use in the UI. Could be externalized.
|
||||
*/
|
||||
@Configuration
|
||||
public class CredentialRulesConfig {
|
||||
|
||||
@Value("${password.validation.minimumLength}")
|
||||
private int passwordMinimumLength;
|
||||
|
||||
@Value("${password.validation.maximumLength}")
|
||||
private int passwordMaximumLength;
|
||||
|
||||
@Value("${username.validation.minimumLength}")
|
||||
private int usernameMinimumLength;
|
||||
|
||||
@Value("${username.validation.maximumLength}")
|
||||
private int usernameMaximumLength;
|
||||
|
||||
/**
|
||||
* Create the bean.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean(name = "credentialRuleResources")
|
||||
public List<CredentialRuleResource> createCredentialRuleResources() {
|
||||
|
||||
List<CredentialRuleResource> CredentialRuleResources = new ArrayList<CredentialRuleResource>();
|
||||
|
||||
CredentialRuleResources.add(createPasswordPrintableAsciiCharactersRuleResource());
|
||||
CredentialRuleResources.add(createPasswordLengthRuleResource());
|
||||
CredentialRuleResources.add(createPasswordSpecialCharacterRuleResource());
|
||||
CredentialRuleResources.add(createPasswordEmailForbiddenRuleResource());
|
||||
CredentialRuleResources.add(createPasswordUsernameForbiddenRuleResource());
|
||||
CredentialRuleResources.add(createUsernamePrintableAsciiCharactersRuleResource());
|
||||
CredentialRuleResources.add(createUsernameLengthRuleResource());
|
||||
CredentialRuleResources.add(createUsernameEmailForbiddenRuleResource());
|
||||
CredentialRuleResources.add(createUsernameLeadingTrailingSpacesRuleResource());
|
||||
|
||||
return CredentialRuleResources;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create password printable ascii character rules.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private CredentialRuleResource createPasswordPrintableAsciiCharactersRuleResource() {
|
||||
|
||||
CredentialRuleResource passwordPrintableAsciiCharactersRuleResource = new CredentialRuleResource();
|
||||
|
||||
passwordPrintableAsciiCharactersRuleResource.setId("2Znw1zI46fXgYisLoi9hF8");
|
||||
passwordPrintableAsciiCharactersRuleResource.setCode(PasswordEnglishAllowedCharacterConstraint.CODE);
|
||||
passwordPrintableAsciiCharactersRuleResource
|
||||
.setDescription("Only standard English keyboard characters " + "allowed");
|
||||
passwordPrintableAsciiCharactersRuleResource
|
||||
.setAriaDescription("Only standard English keyboard characters " + "allowed");
|
||||
passwordPrintableAsciiCharactersRuleResource.setExplicit(true);
|
||||
passwordPrintableAsciiCharactersRuleResource.setAppliesTo("password");
|
||||
|
||||
return passwordPrintableAsciiCharactersRuleResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create password length rule.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private CredentialRuleResource createPasswordLengthRuleResource() {
|
||||
|
||||
CredentialRuleResource passwordLengthRuleResource = new CredentialRuleResource();
|
||||
|
||||
passwordLengthRuleResource.setId("6USKQgPUolT95cSLTtqC5v");
|
||||
passwordLengthRuleResource.setCode(PasswordLengthConstraint.CODE);
|
||||
passwordLengthRuleResource.setDescription("CHARACTERS");
|
||||
passwordLengthRuleResource.setAriaDescription(
|
||||
"Must be between " + passwordMinimumLength + " and " + passwordMaximumLength + " characters long");
|
||||
passwordLengthRuleResource.setExplicit(true);
|
||||
passwordLengthRuleResource.setImageType("text");
|
||||
passwordLengthRuleResource.setImage("12+");
|
||||
passwordLengthRuleResource.setAppliesTo("password");
|
||||
|
||||
return passwordLengthRuleResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create special characters rule.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private CredentialRuleResource createPasswordSpecialCharacterRuleResource() {
|
||||
|
||||
CredentialRuleResource passwordSpecialCharacterRuleResource = new CredentialRuleResource();
|
||||
|
||||
passwordSpecialCharacterRuleResource.setId("6QVdke9thuBFxyzUxo6JjL");
|
||||
passwordSpecialCharacterRuleResource.setCode(PasswordSpecialCharacterConstraint.CODE);
|
||||
passwordSpecialCharacterRuleResource.setDescription("ANY SPECIAL CHARACTERS");
|
||||
passwordSpecialCharacterRuleResource
|
||||
.setAriaDescription("Must contain at least one non alpha numeric special " + "character");
|
||||
passwordSpecialCharacterRuleResource.setExplicit(true);
|
||||
passwordSpecialCharacterRuleResource.setImageType("text");
|
||||
passwordSpecialCharacterRuleResource.setImage("1+");
|
||||
passwordSpecialCharacterRuleResource.setAppliesTo("password");
|
||||
|
||||
return passwordSpecialCharacterRuleResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create password email forbidden rule.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private CredentialRuleResource createPasswordEmailForbiddenRuleResource() {
|
||||
|
||||
CredentialRuleResource passwordEmailForbiddenRuleResource = new CredentialRuleResource();
|
||||
|
||||
passwordEmailForbiddenRuleResource.setId("3qs3IwQB01YIehyDZOwpuc");
|
||||
passwordEmailForbiddenRuleResource.setCode(PasswordEmailSubstringConstraint.CODE);
|
||||
passwordEmailForbiddenRuleResource.setDescription("EMAIL NOT ALLOWED");
|
||||
passwordEmailForbiddenRuleResource.setAriaDescription("Must not contain email address");
|
||||
passwordEmailForbiddenRuleResource.setExplicit(true);
|
||||
passwordEmailForbiddenRuleResource.setImageType("icon");
|
||||
passwordEmailForbiddenRuleResource.setImage("email");
|
||||
passwordEmailForbiddenRuleResource.setAppliesTo("password");
|
||||
passwordEmailForbiddenRuleResource.setModifiers(new ArrayList<String>() {
|
||||
{
|
||||
add("i");
|
||||
}
|
||||
});
|
||||
|
||||
return passwordEmailForbiddenRuleResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create password username forbidden rule.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private CredentialRuleResource createPasswordUsernameForbiddenRuleResource() {
|
||||
|
||||
CredentialRuleResource passwordUsernameForbiddenRuleResource = new CredentialRuleResource();
|
||||
|
||||
passwordUsernameForbiddenRuleResource.setId("4NAFhwNcUyiNnzH2p6l8H8");
|
||||
passwordUsernameForbiddenRuleResource.setCode(PasswordUsernameConstraint.CODE);
|
||||
passwordUsernameForbiddenRuleResource.setDescription("USERNAME NOT ALLOWED");
|
||||
passwordUsernameForbiddenRuleResource.setAriaDescription("Must not contain user name");
|
||||
passwordUsernameForbiddenRuleResource.setExplicit(false);
|
||||
passwordUsernameForbiddenRuleResource.setAppliesTo("password");
|
||||
|
||||
return passwordUsernameForbiddenRuleResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create username printable ascii characters rule.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private CredentialRuleResource createUsernamePrintableAsciiCharactersRuleResource() {
|
||||
|
||||
CredentialRuleResource usernamePrintableAsciiCharactersRuleResource = new CredentialRuleResource();
|
||||
|
||||
usernamePrintableAsciiCharactersRuleResource.setId("5Wq2qfgtjZfppaAHXy1EQJ");
|
||||
usernamePrintableAsciiCharactersRuleResource.setCode(UsernameEmailSubstringConstraint.CODE);
|
||||
usernamePrintableAsciiCharactersRuleResource
|
||||
.setDescription("Only standard English keyboard characters " + "allowed");
|
||||
usernamePrintableAsciiCharactersRuleResource
|
||||
.setAriaDescription(usernamePrintableAsciiCharactersRuleResource.getDescription());
|
||||
usernamePrintableAsciiCharactersRuleResource.setExplicit(true);
|
||||
usernamePrintableAsciiCharactersRuleResource.setAppliesTo("username");
|
||||
|
||||
return usernamePrintableAsciiCharactersRuleResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create username length rule.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private CredentialRuleResource createUsernameLengthRuleResource() {
|
||||
|
||||
CredentialRuleResource usernameLengthRuleResource = new CredentialRuleResource();
|
||||
|
||||
usernameLengthRuleResource.setId("SUEVxQKsWeWJTH6ESFr9m");
|
||||
usernameLengthRuleResource.setCode(UsernameLengthConstraint.CODE);
|
||||
usernameLengthRuleResource.setDescription("CHARACTERS");
|
||||
usernameLengthRuleResource.setAriaDescription(
|
||||
"Must be between " + usernameMinimumLength + " and " + usernameMaximumLength + "characters long");
|
||||
usernameLengthRuleResource.setExplicit(true);
|
||||
usernameLengthRuleResource.setImageType("text");
|
||||
usernameLengthRuleResource.setImage("12+");
|
||||
usernameLengthRuleResource.setAppliesTo("username");
|
||||
|
||||
return usernameLengthRuleResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create username email forbidden rule.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private CredentialRuleResource createUsernameEmailForbiddenRuleResource() {
|
||||
|
||||
CredentialRuleResource usernameEmailForbiddenRuleResource = new CredentialRuleResource();
|
||||
List<String> modifiers = new ArrayList<String>() {
|
||||
{
|
||||
add("i");
|
||||
}
|
||||
};
|
||||
usernameEmailForbiddenRuleResource.setId("A4dwWPFdAgWSzw2rew3MK");
|
||||
usernameEmailForbiddenRuleResource.setCode(UsernameEmailSubstringConstraint.CODE);
|
||||
usernameEmailForbiddenRuleResource.setDescription("EMAIL NOT ALLOWED");
|
||||
usernameEmailForbiddenRuleResource.setAriaDescription("Must not contain email address");
|
||||
usernameEmailForbiddenRuleResource.setExplicit(true);
|
||||
usernameEmailForbiddenRuleResource.setImageType("icon");
|
||||
usernameEmailForbiddenRuleResource.setImage("email");
|
||||
usernameEmailForbiddenRuleResource.setModifiers(modifiers);
|
||||
usernameEmailForbiddenRuleResource.setAppliesTo("username");
|
||||
|
||||
return usernameEmailForbiddenRuleResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create username leading trailing spaces rule.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private CredentialRuleResource createUsernameLeadingTrailingSpacesRuleResource() {
|
||||
|
||||
CredentialRuleResource usernameLeadingTrailingSpacesRuleResource = new CredentialRuleResource();
|
||||
|
||||
usernameLeadingTrailingSpacesRuleResource.setId("6Ej8sla6VSmbnaIV7wJ8mm");
|
||||
usernameLeadingTrailingSpacesRuleResource.setCode(UsernameLeadingTrailingSpaceConstraint.CODE);
|
||||
usernameLeadingTrailingSpacesRuleResource.setDescription("Username may not begin or end with spaces");
|
||||
usernameLeadingTrailingSpacesRuleResource.setAriaDescription("OUsername may not begin or end with spaces");
|
||||
usernameLeadingTrailingSpacesRuleResource.setExplicit(true);
|
||||
usernameLeadingTrailingSpacesRuleResource.setAppliesTo("username");
|
||||
|
||||
return usernameLeadingTrailingSpacesRuleResource;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.constraints.password;
|
||||
|
||||
import com.jkgroller.validationauthority.resource.CredentialsResource;
|
||||
import io.crnk.core.engine.document.ErrorData;
|
||||
import io.crnk.core.engine.http.HttpStatus;
|
||||
import org.passay.IllegalRegexRule;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @Author john@grollerfamily.com
|
||||
* <p>
|
||||
* Class for building the email substring rule. If an email is detected,
|
||||
* validation fails.
|
||||
*/
|
||||
@Component
|
||||
public class PasswordEmailSubstringConstraint extends IllegalRegexRule {
|
||||
|
||||
public static final String HTTP_STATUS = Integer.toString(HttpStatus.UNPROCESSABLE_ENTITY_422);
|
||||
|
||||
public static final String CODE = "8388";
|
||||
|
||||
/**
|
||||
* Provides the regex to find email addresses.
|
||||
*/
|
||||
public PasswordEmailSubstringConstraint(@Value("${regex.emailAddressSubstring}") String emailRegex) {
|
||||
super(emailRegex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to generate the error object for this rule failure.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ErrorData getErrorData() {
|
||||
|
||||
return ErrorData.builder().setTitle("Password substring failure")
|
||||
.setDetail("Password must not contain email address.")
|
||||
.setSourcePointer(CredentialsResource.SOURCE_POINTER_PASSWORD).setStatus(HTTP_STATUS).setCode(CODE)
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.constraints.password;
|
||||
|
||||
import com.jkgroller.validationauthority.resource.CredentialsResource;
|
||||
import io.crnk.core.engine.document.ErrorData;
|
||||
import io.crnk.core.engine.http.HttpStatus;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.passay.AllowedCharacterRule;
|
||||
import org.passay.EnglishCharacterData;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @Author john@grollerfamily.com
|
||||
* <p>
|
||||
* Provides the allowed character rule... allowing all English
|
||||
* characters.
|
||||
*/
|
||||
@Component
|
||||
public class PasswordEnglishAllowedCharacterConstraint extends AllowedCharacterRule {
|
||||
|
||||
public static final String CODE = "7257";
|
||||
|
||||
public static String HTTP_STATUS = Integer.toString(HttpStatus.UNPROCESSABLE_ENTITY_422);
|
||||
|
||||
/**
|
||||
* All English characters allowed.
|
||||
*/
|
||||
public PasswordEnglishAllowedCharacterConstraint() {
|
||||
|
||||
super((EnglishCharacterData.Alphabetical.getCharacters() + EnglishCharacterData.Digit.getCharacters()
|
||||
+ StringUtils.SPACE + EnglishCharacterData.Special.getCharacters()).toCharArray());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to generate the error object for this rule failure.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ErrorData getErrorData() {
|
||||
|
||||
return ErrorData.builder().setTitle("Character set validation failure.")
|
||||
.setDetail("Password must contain only English characters.")
|
||||
.setSourcePointer(CredentialsResource.SOURCE_POINTER_PASSWORD).setStatus(HTTP_STATUS).setCode(CODE)
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.constraints.password;
|
||||
|
||||
import com.jkgroller.validationauthority.resource.CredentialsResource;
|
||||
import io.crnk.core.engine.document.ErrorData;
|
||||
import io.crnk.core.engine.http.HttpStatus;
|
||||
import org.passay.LengthRule;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @Author john@grollerfamily.com
|
||||
* <p>
|
||||
* Provides the length rule for passwords.
|
||||
*/
|
||||
@Component
|
||||
public class PasswordLengthConstraint extends LengthRule {
|
||||
|
||||
public static String HTTP_STATUS = Integer.toString(HttpStatus.UNPROCESSABLE_ENTITY_422);
|
||||
|
||||
public static String CODE = "2736";
|
||||
|
||||
@Value("${password.validation.minimumLength}")
|
||||
private int minimumLength;
|
||||
|
||||
@Value("${password.validation.maximumLength}")
|
||||
private int maximumLength;
|
||||
|
||||
/**
|
||||
* Minimum length of minimumLength, and max of maximumLength
|
||||
*/
|
||||
public PasswordLengthConstraint(@Value("${password.validation.minimumLength}") int minimumLength,
|
||||
@Value("${password.validation.maximumLength}") int maximumLength) {
|
||||
super(minimumLength, maximumLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to generate the error object for this rule failure.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ErrorData getErrorData() {
|
||||
|
||||
return ErrorData.builder().setTitle("Invalid password length.")
|
||||
.setDetail("Password must be between " + minimumLength + " and " + maximumLength + " characters long" +
|
||||
".").setSourcePointer(CredentialsResource.SOURCE_POINTER_PASSWORD)
|
||||
.setStatus(HTTP_STATUS).setCode(CODE).build();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.constraints.password;
|
||||
|
||||
import com.jkgroller.validationauthority.resource.CredentialsResource;
|
||||
import io.crnk.core.engine.document.ErrorData;
|
||||
import io.crnk.core.engine.http.HttpStatus;
|
||||
import org.passay.CharacterRule;
|
||||
import org.passay.EnglishCharacterData;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @Author john@grollerfamily.com
|
||||
* <p>
|
||||
* Provides the rule regarding special characters.
|
||||
*/
|
||||
@Component
|
||||
public class PasswordSpecialCharacterConstraint extends CharacterRule {
|
||||
|
||||
public static final String ERROR_CODE = "INSUFFICIENT_SPECIAL";
|
||||
|
||||
public static final String HTTP_STATUS = Integer.toString(HttpStatus.UNPROCESSABLE_ENTITY_422);
|
||||
|
||||
public static final String CODE = "4959";
|
||||
|
||||
@Value("${password.validation.numberOfSpecialCharacters}")
|
||||
private int numberOfSpecialCharacters;
|
||||
|
||||
/**
|
||||
* One English special character required.
|
||||
*/
|
||||
public PasswordSpecialCharacterConstraint(
|
||||
@Value("${password.validation.numberOfSpecialCharacters}") int numberOfSpecialCharacters) {
|
||||
super(EnglishCharacterData.Special, numberOfSpecialCharacters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to generate the error object for this rule failure.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ErrorData getErrorData() {
|
||||
|
||||
return ErrorData.builder().setTitle("Password does not meet character requirements.")
|
||||
.setDetail("Password must contain " + numberOfSpecialCharacters + " special character.")
|
||||
.setSourcePointer(CredentialsResource.SOURCE_POINTER_USERNAME).setStatus(HTTP_STATUS).setCode(CODE)
|
||||
.build();
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.constraints.password;
|
||||
|
||||
import com.jkgroller.validationauthority.resource.CredentialsResource;
|
||||
import io.crnk.core.engine.document.ErrorData;
|
||||
import io.crnk.core.engine.http.HttpStatus;
|
||||
import org.passay.MatchBehavior;
|
||||
import org.passay.UsernameRule;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @Author john@grollerfamily.com
|
||||
* <p>
|
||||
* Applies the behavior for detecting how to detect username in
|
||||
* password.
|
||||
*/
|
||||
@Component
|
||||
public class PasswordUsernameConstraint extends UsernameRule {
|
||||
|
||||
public static String HTTP_STATUS = Integer.toString(HttpStatus.UNPROCESSABLE_ENTITY_422);
|
||||
|
||||
public static String CODE = "4690";
|
||||
|
||||
/**
|
||||
* Checking if password contains username.
|
||||
*/
|
||||
public PasswordUsernameConstraint() {
|
||||
super(MatchBehavior.Contains);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to generate the error object for this rule failure.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ErrorData getErrorData() {
|
||||
|
||||
return ErrorData.builder()
|
||||
.setTitle("Password contains an invalid pattern.").setDetail("Password must not contain a username.")
|
||||
.setSourcePointer(CredentialsResource.SOURCE_POINTER_PASSWORD).setStatus(HTTP_STATUS).setCode(CODE)
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.constraints.username;
|
||||
|
||||
import com.jkgroller.validationauthority.resource.CredentialsResource;
|
||||
import io.crnk.core.engine.document.ErrorData;
|
||||
import io.crnk.core.engine.http.HttpStatus;
|
||||
import org.passay.IllegalRegexRule;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @Author john@grollerfamily.com
|
||||
* <p>
|
||||
* Class for building the email substring rule. If an email is detected,
|
||||
* validation fails.
|
||||
*/
|
||||
@Component
|
||||
public class UsernameEmailSubstringConstraint extends IllegalRegexRule {
|
||||
|
||||
public static final String HTTP_STATUS = Integer.toString(HttpStatus.UNPROCESSABLE_ENTITY_422);
|
||||
|
||||
public static final String CODE = "3589";
|
||||
|
||||
/**
|
||||
* Provides the regex to find email addresses.
|
||||
*/
|
||||
public UsernameEmailSubstringConstraint(@Value("${regex.emailAddressSubstring}") String emailRegex) {
|
||||
super(emailRegex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to generate the error object for this rule failure.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ErrorData getErrorData() {
|
||||
|
||||
return ErrorData.builder()
|
||||
.setTitle("Username contains an invalid pattern.")
|
||||
.setDetail("Username must not contain an email address.")
|
||||
.setSourcePointer(CredentialsResource.SOURCE_POINTER_USERNAME).setStatus(HTTP_STATUS).setCode(CODE)
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.constraints.username;
|
||||
|
||||
import com.jkgroller.validationauthority.resource.CredentialsResource;
|
||||
import io.crnk.core.engine.document.ErrorData;
|
||||
import io.crnk.core.engine.http.HttpStatus;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.passay.AllowedCharacterRule;
|
||||
import org.passay.EnglishCharacterData;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @Author john@grollerfamily.com
|
||||
* <p>
|
||||
* Provides the allowed character rule... allowing all English
|
||||
* characters.
|
||||
*/
|
||||
@Component
|
||||
public class UsernameEnglishAllowedCharacterConstraint extends AllowedCharacterRule {
|
||||
|
||||
public static final String HTTP_STATUS = Integer.toString(HttpStatus.UNPROCESSABLE_ENTITY_422);
|
||||
|
||||
public static final String CODE = "3365";
|
||||
|
||||
/**
|
||||
* All English characters allowed.
|
||||
*/
|
||||
public UsernameEnglishAllowedCharacterConstraint() {
|
||||
|
||||
super((EnglishCharacterData.Alphabetical.getCharacters() + EnglishCharacterData.Digit.getCharacters()
|
||||
+ StringUtils.SPACE + EnglishCharacterData.Special.getCharacters()).toCharArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to generate the error object for this rule failure.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ErrorData getErrorData() {
|
||||
|
||||
return ErrorData.builder().setTitle("Username does not meet character requirements.")
|
||||
.setDetail("Username may only contain English keyboard characters.")
|
||||
.setSourcePointer(CredentialsResource.SOURCE_POINTER_USERNAME).setStatus(HTTP_STATUS).setCode(CODE)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.constraints.username;
|
||||
|
||||
import com.jkgroller.validationauthority.resource.CredentialsResource;
|
||||
import io.crnk.core.engine.document.ErrorData;
|
||||
import io.crnk.core.engine.http.HttpStatus;
|
||||
import org.passay.IllegalRegexRule;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @Author john@grollerfamily.com
|
||||
* <p>
|
||||
* Rule for checking leading and trailing spaces for username.
|
||||
*/
|
||||
@Component
|
||||
public class UsernameLeadingTrailingSpaceConstraint extends IllegalRegexRule {
|
||||
|
||||
public static final String HTTP_STATUS = Integer.toString(HttpStatus.UNPROCESSABLE_ENTITY_422);
|
||||
|
||||
public static final String CODE = "0316";
|
||||
|
||||
/**
|
||||
* No leading/trailing spaces constraint.
|
||||
*
|
||||
* @param leadingTrailingRegex
|
||||
*/
|
||||
public UsernameLeadingTrailingSpaceConstraint(
|
||||
@Value("${regex.leadingTrailingSpaces}") String leadingTrailingRegex) {
|
||||
super(leadingTrailingRegex);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Used to generate the error object for this rule failure.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ErrorData getErrorData() {
|
||||
|
||||
return ErrorData.builder()
|
||||
.setTitle("Username contains an invalid pattern.")
|
||||
.setDetail("Username must not contain leading or trailing spaces.")
|
||||
.setSourcePointer(CredentialsResource.SOURCE_POINTER_USERNAME).setStatus(HTTP_STATUS).setCode(CODE)
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.constraints.username;
|
||||
|
||||
import com.jkgroller.validationauthority.resource.CredentialsResource;
|
||||
import io.crnk.core.engine.document.ErrorData;
|
||||
import io.crnk.core.engine.http.HttpStatus;
|
||||
import org.passay.LengthRule;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @Author john@grollerfamily.com
|
||||
* <p>
|
||||
* Provides the length rule for usernames.
|
||||
*/
|
||||
@Component
|
||||
public class UsernameLengthConstraint extends LengthRule {
|
||||
|
||||
public final String HTTP_STATUS = Integer.toString(HttpStatus.UNPROCESSABLE_ENTITY_422);
|
||||
|
||||
public static final String CODE = "9377";
|
||||
|
||||
@Value("${username.validation.minimumLength}")
|
||||
private int minimumLength;
|
||||
|
||||
@Value("${username.validation.maximumLength}")
|
||||
private int maximumLength;
|
||||
|
||||
/**
|
||||
* Minimum length of minimumLength, maximum of maximumLength.
|
||||
*/
|
||||
public UsernameLengthConstraint(@Value("${username.validation.minimumLength}") int minimumLength,
|
||||
@Value("${username.validation.maximumLength}") int maximumLength) {
|
||||
|
||||
super(minimumLength, maximumLength);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to generate the error object for this rule failure.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ErrorData getErrorData() {
|
||||
|
||||
return ErrorData.builder()
|
||||
.setTitle("Invalid username length.")
|
||||
.setDetail("Username must be between " + minimumLength + " and " + maximumLength + " characters " +
|
||||
"long.").setSourcePointer(CredentialsResource.SOURCE_POINTER_USERNAME)
|
||||
.setStatus(HTTP_STATUS).setCode(CODE).build();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.converter;
|
||||
|
||||
import com.jkgroller.validationauthority.resource.CredentialsResource;
|
||||
import com.jkgroller.validationauthority.service.to.ValidateCredentialRequestTO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.NullValuePropertyMappingStrategy;
|
||||
|
||||
/**
|
||||
* @author john@grollerfamily.com
|
||||
* <p>
|
||||
* Uses the Mapstruct library for mapping objects.
|
||||
*/
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface ResourceConverter {
|
||||
|
||||
/**
|
||||
* @param credentialsResource
|
||||
* @return
|
||||
*/
|
||||
@Mapping(target = "username", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT)
|
||||
ValidateCredentialRequestTO credentialsResourceToValidateUsernameRequestTO(
|
||||
CredentialsResource credentialsResource);
|
||||
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.enums;
|
||||
|
||||
import org.passay.CharacterData;
|
||||
|
||||
/**
|
||||
* @Author john@grollerfamily.com
|
||||
* <p>
|
||||
* Enum for qwerty special characters. Not sure this is necessary anymore. It was originally to
|
||||
* limit special characters to the ones listed below... but it's probably fine to use all the special
|
||||
* characters specified by Passay.
|
||||
*
|
||||
*/
|
||||
public enum CommonQwertySpecialCharactersCharacterData implements CharacterData {
|
||||
|
||||
CommonQwertySpecial("INSUFFICIENT_COMMON_QWERTY_SPECIAL", "!@#$%&?");
|
||||
|
||||
private final String errorCode;
|
||||
private final String characters;
|
||||
|
||||
/**
|
||||
* @param code
|
||||
* @param charString
|
||||
*/
|
||||
CommonQwertySpecialCharactersCharacterData(String code, String charString) {
|
||||
this.errorCode = code;
|
||||
this.characters = charString;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getErrorCode() {
|
||||
return this.errorCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getCharacters() {
|
||||
return this.characters;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.exception;
|
||||
|
||||
import io.crnk.core.engine.document.ErrorData;
|
||||
import io.crnk.core.engine.http.HttpStatus;
|
||||
import io.crnk.core.exception.CrnkMappableException;
|
||||
|
||||
/**
|
||||
* @Author john@grollerfamily.com
|
||||
* <p>
|
||||
* Custom MethodNotAllowedException
|
||||
*/
|
||||
public final class MethodNotAllowedException extends CrnkMappableException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -3039598962652245954L;
|
||||
|
||||
/**
|
||||
* @param message
|
||||
*/
|
||||
public MethodNotAllowedException(String message) {
|
||||
super(HttpStatus.METHOD_NOT_ALLOWED_405, ErrorData.builder().setTitle(message)
|
||||
.setStatus(Integer.toString(HttpStatus.METHOD_NOT_ALLOWED_405)).build());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.exception;
|
||||
|
||||
import io.crnk.core.engine.document.ErrorData;
|
||||
import io.crnk.core.engine.http.HttpStatus;
|
||||
import io.crnk.core.exception.CrnkMappableException;
|
||||
|
||||
/**
|
||||
* @Author john@grollerfamily.com
|
||||
* <p>
|
||||
* Custom NotImplementedException
|
||||
*/
|
||||
public final class NotImplementedException extends CrnkMappableException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 6988134119651781248L;
|
||||
|
||||
/**
|
||||
* @param message
|
||||
*/
|
||||
public NotImplementedException(String message) {
|
||||
super(HttpStatus.NOT_IMPLEMENTED_501, ErrorData.builder().setTitle(message)
|
||||
.setStatus(Integer.toString(HttpStatus.NOT_IMPLEMENTED_501)).build());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.exception;
|
||||
|
||||
import io.crnk.core.engine.document.ErrorData;
|
||||
import io.crnk.core.engine.http.HttpStatus;
|
||||
import io.crnk.core.exception.CrnkMappableException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @Author john@grollerfamily.com Custom ResourceNotFoundException, for when resource for a type
|
||||
* cannot be found.
|
||||
*/
|
||||
public final class ResourceNotFoundException extends CrnkMappableException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 5747890979256007262L;
|
||||
|
||||
public ResourceNotFoundException(String message) {
|
||||
super(HttpStatus.NOT_FOUND_404,
|
||||
ErrorData.builder().setTitle(message).setStatus(Integer.toString(HttpStatus.NOT_FOUND_404)).build());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.exception;
|
||||
|
||||
import io.crnk.core.engine.document.ErrorData;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author john@grollerfamily.com
|
||||
* <p>
|
||||
* Custom ValidationFailedException for the overall failure of
|
||||
* validations.
|
||||
*/
|
||||
public class ValidationFailedException extends RuntimeException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -7873709164154851376L;
|
||||
private List<ErrorData> errors;
|
||||
|
||||
/**
|
||||
* Constructs a new runtime exception with the specified detail message. The
|
||||
* cause is not initialized, and may subsequently be initialized by a call to
|
||||
* {@link #initCause}.
|
||||
*
|
||||
* @param message the detail message. The detail message is saved for later
|
||||
* retrieval by the {@link #getMessage()} method.
|
||||
*/
|
||||
public ValidationFailedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new runtime exception with {@code null} as its detail message.
|
||||
* The cause is not initialized, and may subsequently be initialized by a call
|
||||
* to {@link #initCause}.
|
||||
*/
|
||||
public ValidationFailedException(List<ErrorData> errors) {
|
||||
this.errors = errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public List<ErrorData> getErrors() {
|
||||
return errors;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.exception.mapper;
|
||||
|
||||
import com.jkgroller.validationauthority.exception.ValidationFailedException;
|
||||
import io.crnk.core.engine.document.ErrorData;
|
||||
import io.crnk.core.engine.error.ErrorResponse;
|
||||
import io.crnk.core.engine.error.ExceptionMapper;
|
||||
import io.crnk.core.engine.http.HttpStatus;
|
||||
import io.crnk.core.repository.response.JsonApiResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author john@grollerfamily.com
|
||||
* <p>
|
||||
* Overall validation failed exception which maps all the validation errors for the failure collection.
|
||||
*/
|
||||
@Component
|
||||
public class ValidationFailedExceptionMapper implements ExceptionMapper<ValidationFailedException> {
|
||||
|
||||
/**
|
||||
* Converts the given exception to an JSON API response. Used on the server-side.
|
||||
*
|
||||
* @param exception
|
||||
*/
|
||||
@Override
|
||||
public ErrorResponse toErrorResponse(ValidationFailedException exception) {
|
||||
return ErrorResponse.builder().setStatus(HttpStatus.BAD_REQUEST_400).setErrorData(exception.getErrors()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given error response to an exception. Used on the client-side.
|
||||
*
|
||||
* @param errorResponse error response
|
||||
* @return exception
|
||||
*/
|
||||
@Override
|
||||
public ValidationFailedException fromErrorResponse(ErrorResponse errorResponse) {
|
||||
JsonApiResponse response = errorResponse.getResponse();
|
||||
List<ErrorData> errors = response.getErrors();
|
||||
StringBuilder message = new StringBuilder();
|
||||
for (ErrorData error : errors) {
|
||||
String title = error.getDetail();
|
||||
message.append(title);
|
||||
}
|
||||
return new ValidationFailedException(message.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides whether the given errorResponse can be handled by this mapper.
|
||||
* If true is returned, {@link #fromErrorResponse(ErrorResponse)} will be called.
|
||||
* <p>
|
||||
* If multiple mappers accept a given error response, the most specific exception is chosen, meaning
|
||||
* the one with the most superTypes.
|
||||
*
|
||||
* @param errorResponse
|
||||
* @return true if it can be handled.
|
||||
*/
|
||||
@Override
|
||||
public boolean accepts(ErrorResponse errorResponse) {
|
||||
return HttpStatus.UNPROCESSABLE_ENTITY_422 == errorResponse.getHttpStatus();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.resource;
|
||||
|
||||
import io.crnk.core.resource.annotations.JsonApiId;
|
||||
|
||||
/**
|
||||
* @Author john@grollerfamily.com
|
||||
* <p>
|
||||
* All resources in this API follow JSON:API and are required to have an id attribute.
|
||||
*/
|
||||
public class BaseResource {
|
||||
|
||||
// Identifier required by the JSON:API spec
|
||||
@JsonApiId
|
||||
private String id;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.resource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author john@grollerfamily.com
|
||||
* <p>
|
||||
* All rules tend to have the same attributes.
|
||||
*/
|
||||
public class BaseRuleResource extends BaseResource {
|
||||
|
||||
private String code;
|
||||
|
||||
private String appliesTo;
|
||||
|
||||
private String description;
|
||||
|
||||
private String ariaDescription;
|
||||
|
||||
private List<String> modifiers;
|
||||
|
||||
private boolean explicit;
|
||||
|
||||
private String imageType;
|
||||
|
||||
private String image;
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param code
|
||||
*/
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public String getAppliesTo() {
|
||||
return appliesTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param appliesTo
|
||||
*/
|
||||
public void setAppliesTo(String appliesTo) {
|
||||
this.appliesTo = appliesTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param description
|
||||
*/
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public String getAriaDescription() {
|
||||
return ariaDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ariaDescription
|
||||
*/
|
||||
public void setAriaDescription(String ariaDescription) {
|
||||
this.ariaDescription = ariaDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public List<String> getModifiers() {
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param modifiers
|
||||
*/
|
||||
public void setModifiers(List<String> modifiers) {
|
||||
this.modifiers = modifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public boolean isExplicit() {
|
||||
return explicit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param explicit
|
||||
*/
|
||||
public void setExplicit(boolean explicit) {
|
||||
this.explicit = explicit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public String getImageType() {
|
||||
return imageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param imageType
|
||||
*/
|
||||
public void setImageType(String imageType) {
|
||||
this.imageType = imageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public String getImage() {
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param image
|
||||
*/
|
||||
public void setImage(String image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.resource;
|
||||
|
||||
import io.crnk.core.resource.annotations.JsonApiResource;
|
||||
|
||||
/**
|
||||
* @author john@grollerfamily.com
|
||||
* <p>
|
||||
* This class intentionally left blank.
|
||||
* <p>
|
||||
* It uses the attributes from the BaseRuleResource and represents them
|
||||
* as a /credentialRules resource.
|
||||
*/
|
||||
@JsonApiResource(type = "credentialRule", resourcePath = "credentialRules")
|
||||
public class CredentialRuleResource extends BaseRuleResource {
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.resource;
|
||||
|
||||
import io.crnk.core.resource.annotations.JsonApiField;
|
||||
import io.crnk.core.resource.annotations.JsonApiResource;
|
||||
|
||||
@JsonApiResource(type = "credentials", resourcePath = "credentials")
|
||||
public class CredentialsResource extends BaseResource {
|
||||
|
||||
public static final String SOURCE_POINTER_USERNAME = "/data/attributes/username";
|
||||
|
||||
public static final String SOURCE_POINTER_PASSWORD = "/data/attributes/password";
|
||||
|
||||
@JsonApiField(readable = false, postable = true, patchable = false, deletable = false)
|
||||
private char[] username;
|
||||
|
||||
@JsonApiField(readable = false, postable = true, patchable = false, deletable = false)
|
||||
private char[] password;
|
||||
|
||||
public char[] getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(char[] username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public char[] getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(char[] password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.resource.repository;
|
||||
|
||||
import com.jkgroller.validationauthority.exception.MethodNotAllowedException;
|
||||
import com.jkgroller.validationauthority.exception.NotImplementedException;
|
||||
import com.jkgroller.validationauthority.resource.CredentialRuleResource;
|
||||
import io.crnk.core.queryspec.QuerySpec;
|
||||
import io.crnk.core.repository.ResourceRepository;
|
||||
import io.crnk.core.resource.list.ResourceList;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author john@grollerfamily.com
|
||||
* <p>
|
||||
* Repository for handling GET/POST/PATCH/DELETE on the credential rule
|
||||
* resource.
|
||||
*/
|
||||
@Component
|
||||
public class CredentialRuleResourceRepository implements ResourceRepository<CredentialRuleResource, String> {
|
||||
|
||||
private final String METHOD_NOT_ALLOWED_MESSAGE = "Method is not allowed.";
|
||||
|
||||
private final String NOT_IMPLEMENTED_MESSAGE = "This request is currently unsupported.";
|
||||
|
||||
@Autowired
|
||||
private List<CredentialRuleResource> credentialRuleResources;
|
||||
|
||||
/**
|
||||
* Crnk will blow up without this and give you the most confusing exception
|
||||
* ever.
|
||||
*/
|
||||
@Override
|
||||
public Class<CredentialRuleResource> getResourceClass() {
|
||||
return CredentialRuleResource.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* GET requests for /v1/credentialRules/<id> will come here.
|
||||
*/
|
||||
@Override
|
||||
public CredentialRuleResource findOne(String id, QuerySpec querySpec) {
|
||||
return credentialRuleResources.stream()
|
||||
.filter(credentialRuleResource -> id.equals(credentialRuleResource.getId())).findAny().orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET requests for /v1/credentialRules will come here.
|
||||
*/
|
||||
@Override
|
||||
public ResourceList<CredentialRuleResource> findAll(QuerySpec querySpec) {
|
||||
return querySpec.apply(credentialRuleResources);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET requests for /v1/credentialRules/<id>,<id>,<id> will come here.
|
||||
*/
|
||||
@Override
|
||||
public ResourceList<CredentialRuleResource> findAll(Collection<String> ids, QuerySpec querySpec) {
|
||||
throw new NotImplementedException(NOT_IMPLEMENTED_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* PATCH requests for /v1/credentialRules/<id> will come here. Keep in mind,
|
||||
* however, that PATCH requests will first automatically perform a GET on
|
||||
* /v1/credentialRules/<id> to retrieve the resource.
|
||||
* <p>
|
||||
* Controll will then pass to this method to update the resource with the values
|
||||
* requested. Then, it's saved as you've specified.
|
||||
*/
|
||||
@Override
|
||||
public <S extends CredentialRuleResource> S save(S resource) {
|
||||
throw new MethodNotAllowedException(METHOD_NOT_ALLOWED_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST requests for /v1/credentialRules will come here.
|
||||
*/
|
||||
@Override
|
||||
public <S extends CredentialRuleResource> S create(S resource) {
|
||||
throw new MethodNotAllowedException(METHOD_NOT_ALLOWED_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE requests for /v1/credentialRules/<id> will come here.
|
||||
*/
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
throw new MethodNotAllowedException(METHOD_NOT_ALLOWED_MESSAGE);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.resource.repository;
|
||||
|
||||
import com.devskiller.friendly_id.FriendlyId;
|
||||
import com.jkgroller.validationauthority.converter.ResourceConverter;
|
||||
import com.jkgroller.validationauthority.exception.MethodNotAllowedException;
|
||||
import com.jkgroller.validationauthority.exception.ValidationFailedException;
|
||||
import com.jkgroller.validationauthority.resource.CredentialsResource;
|
||||
import com.jkgroller.validationauthority.service.ValidateCredentialService;
|
||||
import com.jkgroller.validationauthority.service.to.ValidateCredentialRequestTO;
|
||||
import com.jkgroller.validationauthority.service.to.ValidateCredentialResponseTO;
|
||||
import io.crnk.core.queryspec.QuerySpec;
|
||||
import io.crnk.core.repository.ResourceRepository;
|
||||
import io.crnk.core.resource.list.ResourceList;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author john@grollerfamily.com
|
||||
* <p>
|
||||
* Repository for handling GET/POST/PATCH/DELETE on the credential
|
||||
* resource.
|
||||
*/
|
||||
@Component
|
||||
public class CredentialsResourceRepository implements ResourceRepository<CredentialsResource, String> {
|
||||
|
||||
private final String METHOD_NOT_ALLOWED_MESSAGE = "Method is not allowed.";
|
||||
|
||||
@Autowired
|
||||
private ResourceConverter resourceConverter;
|
||||
|
||||
@Autowired
|
||||
private ValidateCredentialService validateCredentialService;
|
||||
|
||||
/**
|
||||
* Crnk will blow up without this and give you the most confusing exception
|
||||
* ever.
|
||||
*/
|
||||
@Override
|
||||
public Class<CredentialsResource> getResourceClass() {
|
||||
return CredentialsResource.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* GET requests for /v1/credentials/<id> will come here.
|
||||
*/
|
||||
@Override
|
||||
public CredentialsResource findOne(String id, QuerySpec querySpec) {
|
||||
throw new MethodNotAllowedException(METHOD_NOT_ALLOWED_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET requests for /v1/credentialRules will come here.
|
||||
*/
|
||||
@Override
|
||||
public ResourceList<CredentialsResource> findAll(QuerySpec querySpec) {
|
||||
throw new MethodNotAllowedException(METHOD_NOT_ALLOWED_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET requests for /v1/credentials/<id>,<id>,<id> will come here.
|
||||
*/
|
||||
@Override
|
||||
public ResourceList<CredentialsResource> findAll(Collection<String> ids, QuerySpec querySpec) {
|
||||
throw new MethodNotAllowedException(METHOD_NOT_ALLOWED_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* PATCH requests for /v1/credentials/<id> will come here. Keep in mind,
|
||||
* however, that PATCH requests will first automatically perform a GET on
|
||||
* /v1/credentials/<id> to retrieve the resource.
|
||||
* <p>
|
||||
* Controll will then pass to this method to update the resource with the values
|
||||
* requested. Then, it's saved as you've specified.
|
||||
*/
|
||||
@Override
|
||||
public <S extends CredentialsResource> S save(S resource) {
|
||||
throw new MethodNotAllowedException(METHOD_NOT_ALLOWED_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST requests for /v1/credentials will come here.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <S extends CredentialsResource> S create(S resource) {
|
||||
|
||||
ValidateCredentialRequestTO validateCredentialRequestTO = resourceConverter
|
||||
.credentialsResourceToValidateUsernameRequestTO(resource);
|
||||
ValidateCredentialResponseTO validateCredentialResponseTO = validateCredentialService
|
||||
.validateCredential(validateCredentialRequestTO);
|
||||
|
||||
if (validateCredentialResponseTO.isValid()) {
|
||||
resource.setId(FriendlyId.createFriendlyId());
|
||||
return resource;
|
||||
}
|
||||
|
||||
throw new ValidationFailedException(validateCredentialResponseTO.getErrorData());
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE requests for /v1/credentials/<id> will come here.
|
||||
*/
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
throw new MethodNotAllowedException(METHOD_NOT_ALLOWED_MESSAGE);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.service;
|
||||
|
||||
import com.jkgroller.validationauthority.service.to.ValidateCredentialRequestTO;
|
||||
import com.jkgroller.validationauthority.service.to.ValidateCredentialResponseTO;
|
||||
|
||||
/**
|
||||
* @Author john@grollerfamily.com
|
||||
* <p>
|
||||
* Service for validating the username.
|
||||
*/
|
||||
public interface ValidateCredentialService {
|
||||
|
||||
/**
|
||||
* Validate the credentials.
|
||||
*
|
||||
* @param validateCredentialRequestTO
|
||||
* @return
|
||||
*/
|
||||
ValidateCredentialResponseTO validateCredential(ValidateCredentialRequestTO validateCredentialRequestTO);
|
||||
|
||||
}
|
||||
@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.service;
|
||||
|
||||
import com.devskiller.friendly_id.FriendlyId;
|
||||
import com.jkgroller.validationauthority.constraints.password.*;
|
||||
import com.jkgroller.validationauthority.constraints.username.UsernameEmailSubstringConstraint;
|
||||
import com.jkgroller.validationauthority.constraints.username.UsernameEnglishAllowedCharacterConstraint;
|
||||
import com.jkgroller.validationauthority.constraints.username.UsernameLeadingTrailingSpaceConstraint;
|
||||
import com.jkgroller.validationauthority.constraints.username.UsernameLengthConstraint;
|
||||
import com.jkgroller.validationauthority.converter.ResourceConverter;
|
||||
import com.jkgroller.validationauthority.resource.CredentialsResource;
|
||||
import com.jkgroller.validationauthority.service.to.ValidateCredentialRequestTO;
|
||||
import com.jkgroller.validationauthority.service.to.ValidateCredentialResponseTO;
|
||||
import io.crnk.core.engine.document.ErrorData;
|
||||
import org.passay.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author john@grollerfamily.com
|
||||
* <p>
|
||||
* Validates the credentials and responds with the results.
|
||||
*/
|
||||
@Service
|
||||
public class ValidateCredentialServiceImpl implements ValidateCredentialService {
|
||||
|
||||
@Value("${regex.emailAddressSubstring}")
|
||||
private String emailAddressSubstringRegex;
|
||||
|
||||
@Value("${regex.leadingTrailingSpaces}")
|
||||
private String leadingTrailingSpacesRegex;
|
||||
|
||||
@Autowired
|
||||
private UsernameEmailSubstringConstraint usernameEmailSubstringConstraint;
|
||||
|
||||
@Autowired
|
||||
private UsernameEnglishAllowedCharacterConstraint usernameEnglishAllowedCharacterConstraint;
|
||||
|
||||
@Autowired
|
||||
private UsernameLeadingTrailingSpaceConstraint usernameLeadingTrailingSpaceConstraint;
|
||||
|
||||
@Autowired
|
||||
private UsernameLengthConstraint usernameLengthConstraint;
|
||||
|
||||
@Autowired
|
||||
private PasswordEmailSubstringConstraint passwordEmailSubstringConstraint;
|
||||
|
||||
@Autowired
|
||||
private PasswordEnglishAllowedCharacterConstraint passwordEnglishAllowedCharacterConstraint;
|
||||
|
||||
@Autowired
|
||||
private PasswordLengthConstraint passwordLengthConstraint;
|
||||
|
||||
@Autowired
|
||||
private PasswordSpecialCharacterConstraint passwordSpecialCharacterConstraint;
|
||||
|
||||
@Autowired
|
||||
private PasswordUsernameConstraint passwordUsernameConstraint;
|
||||
|
||||
@Autowired
|
||||
private ResourceConverter resourceConverter;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public ValidateCredentialResponseTO validateCredential(ValidateCredentialRequestTO validateCredentialRequestTO) {
|
||||
|
||||
ValidateCredentialResponseTO validationResponseTO = new ValidateCredentialResponseTO();
|
||||
List<ErrorData> masterErrorList = new ArrayList<ErrorData>();
|
||||
|
||||
masterErrorList.addAll(validateUsername(validateCredentialRequestTO));
|
||||
masterErrorList.addAll(validatePassword(validateCredentialRequestTO));
|
||||
|
||||
validationResponseTO.setErrorData(masterErrorList);
|
||||
|
||||
if (CollectionUtils.isEmpty(masterErrorList)) {
|
||||
validationResponseTO.setValid(true);
|
||||
}
|
||||
|
||||
return validationResponseTO;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param validateCredentialRequestTO
|
||||
* @return
|
||||
*/
|
||||
private List<ErrorData> validateUsername(ValidateCredentialRequestTO validateCredentialRequestTO) {
|
||||
|
||||
RuleResult ruleResult = applyRulesAndValidateUsername(validateCredentialRequestTO);
|
||||
return retrieveUsernameValidationErrorData(ruleResult);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param validateCredentialRequestTO
|
||||
* @return
|
||||
*/
|
||||
private List<ErrorData> validatePassword(ValidateCredentialRequestTO validateCredentialRequestTO) {
|
||||
|
||||
RuleResult ruleResult = applyRulesAndValidatePassword(validateCredentialRequestTO);
|
||||
List<ErrorData> errorData = new ArrayList<ErrorData>();
|
||||
|
||||
errorData.addAll(retrievePasswordValidationErrorData(ruleResult));
|
||||
|
||||
if (0 == validateCredentialRequestTO.getUsername().length) {
|
||||
errorData.add(usernameRequiredError());
|
||||
}
|
||||
|
||||
return errorData;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param validateCredentialRequestTO
|
||||
* @return
|
||||
*/
|
||||
private RuleResult applyRulesAndValidateUsername(ValidateCredentialRequestTO validateCredentialRequestTO) {
|
||||
|
||||
PasswordData usernameData = new PasswordData(new String(validateCredentialRequestTO.getUsername()));
|
||||
|
||||
List<Rule> validationRules = new ArrayList<Rule>();
|
||||
|
||||
validationRules.add(usernameEmailSubstringConstraint);
|
||||
validationRules.add(usernameEnglishAllowedCharacterConstraint);
|
||||
validationRules.add(usernameLengthConstraint);
|
||||
validationRules.add(usernameLeadingTrailingSpaceConstraint);
|
||||
|
||||
PasswordValidator usernameValidator = new PasswordValidator(validationRules);
|
||||
|
||||
return usernameValidator.validate(usernameData);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param validateCredentialRequestTO
|
||||
* @return
|
||||
*/
|
||||
private RuleResult applyRulesAndValidatePassword(ValidateCredentialRequestTO validateCredentialRequestTO) {
|
||||
|
||||
PasswordData passwordData = new PasswordData(new String(validateCredentialRequestTO.getPassword()));
|
||||
passwordData.setUsername(new String(validateCredentialRequestTO.getUsername()));
|
||||
|
||||
List<Rule> validationRules = new ArrayList<Rule>();
|
||||
|
||||
validationRules.add(passwordEmailSubstringConstraint);
|
||||
validationRules.add(passwordEnglishAllowedCharacterConstraint);
|
||||
validationRules.add(passwordLengthConstraint);
|
||||
validationRules.add(passwordSpecialCharacterConstraint);
|
||||
validationRules.add(passwordUsernameConstraint);
|
||||
|
||||
PasswordValidator passwordValidator = new PasswordValidator(validationRules);
|
||||
|
||||
return passwordValidator.validate(passwordData);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
private ErrorData usernameRequiredError() {
|
||||
|
||||
return ErrorData.builder().setId(FriendlyId.createFriendlyId()).setTitle("Missing required value.")
|
||||
.setDetail("Username is required to check validity of password.")
|
||||
.setSourcePointer(CredentialsResource.SOURCE_POINTER_USERNAME).setStatus("422").setCode("1213").build();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ruleResult
|
||||
* @return
|
||||
*/
|
||||
private List<ErrorData> retrieveUsernameValidationErrorData(RuleResult ruleResult) {
|
||||
|
||||
List<ErrorData> errors = new ArrayList<ErrorData>();
|
||||
|
||||
List<RuleResultDetail> ruleResultDetails = ruleResult.getDetails();
|
||||
|
||||
for (RuleResultDetail ruleResultDetail : ruleResultDetails) {
|
||||
|
||||
if (UsernameLengthConstraint.ERROR_CODE_MIN.equals(ruleResultDetail.getErrorCode())
|
||||
|| UsernameLengthConstraint.ERROR_CODE_MAX.equals(ruleResultDetail.getErrorCode())) {
|
||||
errors.add(usernameLengthConstraint.getErrorData());
|
||||
}
|
||||
|
||||
if (UsernameEnglishAllowedCharacterConstraint.ERROR_CODE.equals(ruleResultDetail.getErrorCode())) {
|
||||
errors.add(usernameEnglishAllowedCharacterConstraint.getErrorData());
|
||||
}
|
||||
|
||||
if (UsernameEmailSubstringConstraint.ERROR_CODE.equals(ruleResultDetail.getErrorCode())
|
||||
&& emailAddressSubstringRegex.equals(ruleResultDetail.getParameters().get("pattern").toString())) {
|
||||
errors.add(usernameEmailSubstringConstraint.getErrorData());
|
||||
}
|
||||
|
||||
if (UsernameLeadingTrailingSpaceConstraint.ERROR_CODE.equals(ruleResultDetail.getErrorCode())
|
||||
&& leadingTrailingSpacesRegex.equals(ruleResultDetail.getParameters().get("pattern").toString())) {
|
||||
errors.add(usernameLeadingTrailingSpaceConstraint.getErrorData());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return errors;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ruleResult
|
||||
* @return
|
||||
*/
|
||||
private List<ErrorData> retrievePasswordValidationErrorData(RuleResult ruleResult) {
|
||||
|
||||
List<ErrorData> errors = new ArrayList<ErrorData>();
|
||||
|
||||
List<RuleResultDetail> ruleResultDetails = ruleResult.getDetails();
|
||||
|
||||
for (RuleResultDetail ruleResultDetail : ruleResultDetails) {
|
||||
|
||||
if (PasswordLengthConstraint.ERROR_CODE_MIN.equals(ruleResultDetail.getErrorCode())
|
||||
|| PasswordLengthConstraint.ERROR_CODE_MAX.equals(ruleResultDetail.getErrorCode())) {
|
||||
errors.add(passwordLengthConstraint.getErrorData());
|
||||
}
|
||||
|
||||
if (PasswordEnglishAllowedCharacterConstraint.ERROR_CODE.equals(ruleResultDetail.getErrorCode())) {
|
||||
errors.add(passwordEnglishAllowedCharacterConstraint.getErrorData());
|
||||
}
|
||||
|
||||
if (PasswordEmailSubstringConstraint.ERROR_CODE.equals(ruleResultDetail.getErrorCode())
|
||||
&& emailAddressSubstringRegex.equals(ruleResultDetail.getParameters().get("pattern").toString())) {
|
||||
errors.add(passwordEmailSubstringConstraint.getErrorData());
|
||||
}
|
||||
|
||||
if (PasswordSpecialCharacterConstraint.ERROR_CODE.equals(ruleResultDetail.getErrorCode())) {
|
||||
errors.add(passwordSpecialCharacterConstraint.getErrorData());
|
||||
}
|
||||
|
||||
if (PasswordUsernameConstraint.ERROR_CODE.equals(ruleResultDetail.getErrorCode())) {
|
||||
errors.add(passwordUsernameConstraint.getErrorData());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return errors;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.service.to;
|
||||
|
||||
/**
|
||||
* @Author john@grollerfamily.com
|
||||
* <p>
|
||||
* Data required for performing validation on the username and password.
|
||||
*/
|
||||
public class ValidateCredentialRequestTO {
|
||||
|
||||
private char[] username = new char[0];
|
||||
|
||||
private char[] password = new char[0];
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public char[] getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param username
|
||||
*/
|
||||
public void setUsername(char[] username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public char[] getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(char[] password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.service.to;
|
||||
|
||||
import io.crnk.core.engine.document.ErrorData;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author john@grollerfamily.com
|
||||
* <p>
|
||||
* Response for validating the username and password.
|
||||
*/
|
||||
public class ValidateCredentialResponseTO extends ValidationResponseTO {
|
||||
|
||||
// List of actual CRNK ErrorData objects.
|
||||
List<ErrorData> errorData;
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public List<ErrorData> getErrorData() {
|
||||
return errorData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param errorData
|
||||
*/
|
||||
public void setErrorData(List<ErrorData> errorData) {
|
||||
this.errorData = errorData;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2021-2022 John Groller
|
||||
*/
|
||||
package com.jkgroller.validationauthority.service.to;
|
||||
|
||||
/**
|
||||
* @Auhor john@grollerfamily.com
|
||||
* <p>
|
||||
* Common response for validations.
|
||||
*/
|
||||
public class ValidationResponseTO {
|
||||
|
||||
private boolean valid;
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param valid
|
||||
*/
|
||||
public void setValid(boolean valid) {
|
||||
this.valid = valid;
|
||||
}
|
||||
}
|
||||
18
src/main/resources/application.yml
Normal file
18
src/main/resources/application.yml
Normal file
@ -0,0 +1,18 @@
|
||||
crnk:
|
||||
path-prefix: /v1
|
||||
return404-on-null: true
|
||||
|
||||
regex:
|
||||
emailAddressSubstring: '[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}'
|
||||
leadingTrailingSpaces: '^[ \t]+|[ \t]+$'
|
||||
|
||||
password:
|
||||
validation:
|
||||
minimumLength: 12
|
||||
maximumLength: 256
|
||||
numberOfSpecialCharacters: 1
|
||||
|
||||
username:
|
||||
validation:
|
||||
minimumLength: 5
|
||||
maximumLength: 64
|
||||
89
src/main/resources/errorCodesToChooseFrom.txt
Normal file
89
src/main/resources/errorCodesToChooseFrom.txt
Normal file
@ -0,0 +1,89 @@
|
||||
6664
|
||||
0217
|
||||
5339
|
||||
2055
|
||||
6387
|
||||
4255
|
||||
5451
|
||||
8133
|
||||
3275
|
||||
2462
|
||||
1003
|
||||
6263
|
||||
3493
|
||||
8646
|
||||
7229
|
||||
2352
|
||||
3705
|
||||
3729
|
||||
5965
|
||||
3486
|
||||
5014
|
||||
5456
|
||||
9135
|
||||
9880
|
||||
4294
|
||||
4252
|
||||
5037
|
||||
1754
|
||||
9963
|
||||
3317
|
||||
3106
|
||||
3955
|
||||
6935
|
||||
0231
|
||||
0495
|
||||
9995
|
||||
7180
|
||||
1578
|
||||
2308
|
||||
6281
|
||||
8023
|
||||
7540
|
||||
6971
|
||||
2577
|
||||
1699
|
||||
7036
|
||||
0016
|
||||
0907
|
||||
0825
|
||||
0405
|
||||
1160
|
||||
3837
|
||||
1258
|
||||
7419
|
||||
4697
|
||||
8219
|
||||
8512
|
||||
4962
|
||||
5680
|
||||
8187
|
||||
4462
|
||||
6483
|
||||
3707
|
||||
1381
|
||||
7737
|
||||
6606
|
||||
1585
|
||||
3752
|
||||
8911
|
||||
0542
|
||||
0221
|
||||
8833
|
||||
3600
|
||||
6968
|
||||
7222
|
||||
6436
|
||||
1314
|
||||
7990
|
||||
0512
|
||||
2864
|
||||
1382
|
||||
2495
|
||||
6345
|
||||
0492
|
||||
7081
|
||||
9321
|
||||
9198
|
||||
3796
|
||||
3395
|
||||
1
src/main/resources/licenseTemplate.txt
Normal file
1
src/main/resources/licenseTemplate.txt
Normal file
@ -0,0 +1 @@
|
||||
Copyright 2021-2022 John Groller
|
||||
@ -0,0 +1,68 @@
|
||||
{
|
||||
"info": {
|
||||
"_postman_id": "51726096-b531-47a9-bfed-39cca353f9e7",
|
||||
"name": "ValidationAuthority",
|
||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
||||
},
|
||||
"item": [
|
||||
{
|
||||
"name": "Create Credentials",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"type": "text",
|
||||
"value": "application/vnd.api+json"
|
||||
},
|
||||
{
|
||||
"key": "Crnk-Compact",
|
||||
"type": "text",
|
||||
"value": "true",
|
||||
"disabled": true
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\r\n \"data\": {\r\n \"type\": \"credentials\",\r\n \"attributes\": {\r\n \"username\": \"myUsername\",\r\n \"password\": \"ThePas$word1234\"\r\n }\r\n }\r\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "http://localhost:8080/v1/credentials",
|
||||
"protocol": "http",
|
||||
"host": [
|
||||
"localhost"
|
||||
],
|
||||
"port": "8080",
|
||||
"path": [
|
||||
"v1",
|
||||
"credentials"
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
}
|
||||
],
|
||||
"event": [
|
||||
{
|
||||
"listen": "prerequest",
|
||||
"script": {
|
||||
"id": "384fd387-7f33-41fe-a425-31b02810bf3b",
|
||||
"type": "text/javascript",
|
||||
"exec": [
|
||||
""
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"id": "95eb4ff6-cea3-4ba8-9fcc-287be3ee781f",
|
||||
"type": "text/javascript",
|
||||
"exec": [
|
||||
""
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"protocolProfileBehavior": {}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user