From b477a5f9846304720497bdda50be9db38de58004 Mon Sep 17 00:00:00 2001 From: john Date: Mon, 24 Nov 2025 17:25:45 -0700 Subject: [PATCH] chore: restore from backup restore the project to new gitea instance --- .gitignore | 10 + README.md | 96 +++++++ pom.xml | 140 ++++++++++ .../ValidationAuthority.java | 27 ++ .../config/CredentialRulesConfig.java | 259 ++++++++++++++++++ .../PasswordEmailSubstringConstraint.java | 47 ++++ ...wordEnglishAllowedCharacterConstraint.java | 51 ++++ .../password/PasswordLengthConstraint.java | 53 ++++ .../PasswordSpecialCharacterConstraint.java | 52 ++++ .../password/PasswordUsernameConstraint.java | 47 ++++ .../UsernameEmailSubstringConstraint.java | 48 ++++ ...nameEnglishAllowedCharacterConstraint.java | 49 ++++ ...sernameLeadingTrailingSpaceConstraint.java | 51 ++++ .../username/UsernameLengthConstraint.java | 56 ++++ .../converter/ResourceConverter.java | 28 ++ ...nQwertySpecialCharactersCharacterData.java | 47 ++++ .../exception/MethodNotAllowedException.java | 30 ++ .../exception/NotImplementedException.java | 30 ++ .../exception/ResourceNotFoundException.java | 27 ++ .../exception/ValidationFailedException.java | 52 ++++ .../ValidationFailedExceptionMapper.java | 68 +++++ .../resource/BaseResource.java | 34 +++ .../resource/BaseRuleResource.java | 143 ++++++++++ .../resource/CredentialRuleResource.java | 18 ++ .../resource/CredentialsResource.java | 37 +++ .../CredentialRuleResourceRepository.java | 97 +++++++ .../CredentialsResourceRepository.java | 113 ++++++++ .../service/ValidateCredentialService.java | 24 ++ .../ValidateCredentialServiceImpl.java | 254 +++++++++++++++++ .../to/ValidateCredentialRequestTO.java | 38 +++ .../to/ValidateCredentialResponseTO.java | 34 +++ .../service/to/ValidationResponseTO.java | 28 ++ src/main/resources/application.yml | 18 ++ src/main/resources/errorCodesToChooseFrom.txt | 89 ++++++ src/main/resources/licenseTemplate.txt | 1 + ...alidationAuthority.postman_collection.json | 68 +++++ 36 files changed, 2264 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 pom.xml create mode 100644 src/main/java/com/jkgroller/validationauthority/ValidationAuthority.java create mode 100644 src/main/java/com/jkgroller/validationauthority/config/CredentialRulesConfig.java create mode 100644 src/main/java/com/jkgroller/validationauthority/constraints/password/PasswordEmailSubstringConstraint.java create mode 100644 src/main/java/com/jkgroller/validationauthority/constraints/password/PasswordEnglishAllowedCharacterConstraint.java create mode 100644 src/main/java/com/jkgroller/validationauthority/constraints/password/PasswordLengthConstraint.java create mode 100644 src/main/java/com/jkgroller/validationauthority/constraints/password/PasswordSpecialCharacterConstraint.java create mode 100644 src/main/java/com/jkgroller/validationauthority/constraints/password/PasswordUsernameConstraint.java create mode 100644 src/main/java/com/jkgroller/validationauthority/constraints/username/UsernameEmailSubstringConstraint.java create mode 100644 src/main/java/com/jkgroller/validationauthority/constraints/username/UsernameEnglishAllowedCharacterConstraint.java create mode 100644 src/main/java/com/jkgroller/validationauthority/constraints/username/UsernameLeadingTrailingSpaceConstraint.java create mode 100644 src/main/java/com/jkgroller/validationauthority/constraints/username/UsernameLengthConstraint.java create mode 100644 src/main/java/com/jkgroller/validationauthority/converter/ResourceConverter.java create mode 100644 src/main/java/com/jkgroller/validationauthority/enums/CommonQwertySpecialCharactersCharacterData.java create mode 100644 src/main/java/com/jkgroller/validationauthority/exception/MethodNotAllowedException.java create mode 100644 src/main/java/com/jkgroller/validationauthority/exception/NotImplementedException.java create mode 100644 src/main/java/com/jkgroller/validationauthority/exception/ResourceNotFoundException.java create mode 100644 src/main/java/com/jkgroller/validationauthority/exception/ValidationFailedException.java create mode 100644 src/main/java/com/jkgroller/validationauthority/exception/mapper/ValidationFailedExceptionMapper.java create mode 100644 src/main/java/com/jkgroller/validationauthority/resource/BaseResource.java create mode 100644 src/main/java/com/jkgroller/validationauthority/resource/BaseRuleResource.java create mode 100644 src/main/java/com/jkgroller/validationauthority/resource/CredentialRuleResource.java create mode 100644 src/main/java/com/jkgroller/validationauthority/resource/CredentialsResource.java create mode 100644 src/main/java/com/jkgroller/validationauthority/resource/repository/CredentialRuleResourceRepository.java create mode 100644 src/main/java/com/jkgroller/validationauthority/resource/repository/CredentialsResourceRepository.java create mode 100644 src/main/java/com/jkgroller/validationauthority/service/ValidateCredentialService.java create mode 100644 src/main/java/com/jkgroller/validationauthority/service/ValidateCredentialServiceImpl.java create mode 100644 src/main/java/com/jkgroller/validationauthority/service/to/ValidateCredentialRequestTO.java create mode 100644 src/main/java/com/jkgroller/validationauthority/service/to/ValidateCredentialResponseTO.java create mode 100644 src/main/java/com/jkgroller/validationauthority/service/to/ValidationResponseTO.java create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/errorCodesToChooseFrom.txt create mode 100644 src/main/resources/licenseTemplate.txt create mode 100644 src/test/resources/ValidationAuthority.postman_collection.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..93c7b81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +/target/ +/build/ +/.classpath +/.project +/.factorypath +/password-authority.iml +/.idea/ +/.settings/ + +/validation-authority.iml diff --git a/README.md b/README.md new file mode 100644 index 0000000..f2d0b6f --- /dev/null +++ b/README.md @@ -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` | + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..b8377b1 --- /dev/null +++ b/pom.xml @@ -0,0 +1,140 @@ + + + + 4.0.0 + + com.jkgroller + validation-authority + 1.0.0-SNAPSHOT + jar + + Validation Authority API + Provides resources used for rule-based validation and generation. + + + org.springframework.boot + spring-boot-starter-parent + 2.3.2.RELEASE + + + + UTF-8 + UTF-8 + 8 + 3.5 + 3.2.20200419165537 + 2.5 + 1.3.1.Final + 1.6.0 + 4.0.rc1 + 1.1.0 + + + + + JCenter + https://jcenter.bintray.com/ + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-devtools + + + commons-io + commons-io + ${commons.io.version} + + + org.apache.commons + commons-lang3 + ${commons.lang3.version} + + + io.crnk + crnk-setup-spring-boot2 + ${crnk.version} + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + com.devskiller.friendly-id + friendly-id + ${friendly.id.version} + + + org.passay + passay + ${passay.version} + + + commons-codec + commons-codec + + + + + ValidationAuthority + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + + + + com.mycila + license-maven-plugin + ${mycila.license.version} + + + +
src/main/resources/licenseTemplate.txt
+ + **/README + src/test/resources/** + src/main/resources/** + +
+
+ + John Groller + +
+ + + + check + + + +
+
+
+ +
diff --git a/src/main/java/com/jkgroller/validationauthority/ValidationAuthority.java b/src/main/java/com/jkgroller/validationauthority/ValidationAuthority.java new file mode 100644 index 0000000..65916ca --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/ValidationAuthority.java @@ -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 + *

+ * API providing validation resources. + */ +@SpringBootApplication +public class ValidationAuthority { + + /** + * Start it up. + * + * @param args + */ + public static void main(String[] args) { + SpringApplication.run(ValidationAuthority.class, args); + } + +} + diff --git a/src/main/java/com/jkgroller/validationauthority/config/CredentialRulesConfig.java b/src/main/java/com/jkgroller/validationauthority/config/CredentialRulesConfig.java new file mode 100644 index 0000000..9b2a906 --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/config/CredentialRulesConfig.java @@ -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 createCredentialRuleResources() { + + List CredentialRuleResources = new ArrayList(); + + 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() { + { + 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 modifiers = new ArrayList() { + { + 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; + } + +} diff --git a/src/main/java/com/jkgroller/validationauthority/constraints/password/PasswordEmailSubstringConstraint.java b/src/main/java/com/jkgroller/validationauthority/constraints/password/PasswordEmailSubstringConstraint.java new file mode 100644 index 0000000..21a4ca5 --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/constraints/password/PasswordEmailSubstringConstraint.java @@ -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 + *

+ * 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(); + + } + +} diff --git a/src/main/java/com/jkgroller/validationauthority/constraints/password/PasswordEnglishAllowedCharacterConstraint.java b/src/main/java/com/jkgroller/validationauthority/constraints/password/PasswordEnglishAllowedCharacterConstraint.java new file mode 100644 index 0000000..dd5ae52 --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/constraints/password/PasswordEnglishAllowedCharacterConstraint.java @@ -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 + *

+ * 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(); + + } + +} diff --git a/src/main/java/com/jkgroller/validationauthority/constraints/password/PasswordLengthConstraint.java b/src/main/java/com/jkgroller/validationauthority/constraints/password/PasswordLengthConstraint.java new file mode 100644 index 0000000..5b94992 --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/constraints/password/PasswordLengthConstraint.java @@ -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 + *

+ * 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(); + + } + +} diff --git a/src/main/java/com/jkgroller/validationauthority/constraints/password/PasswordSpecialCharacterConstraint.java b/src/main/java/com/jkgroller/validationauthority/constraints/password/PasswordSpecialCharacterConstraint.java new file mode 100644 index 0000000..8a7eba4 --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/constraints/password/PasswordSpecialCharacterConstraint.java @@ -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 + *

+ * 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(); + + } +} diff --git a/src/main/java/com/jkgroller/validationauthority/constraints/password/PasswordUsernameConstraint.java b/src/main/java/com/jkgroller/validationauthority/constraints/password/PasswordUsernameConstraint.java new file mode 100644 index 0000000..f3651ae --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/constraints/password/PasswordUsernameConstraint.java @@ -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 + *

+ * 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(); + + } + +} diff --git a/src/main/java/com/jkgroller/validationauthority/constraints/username/UsernameEmailSubstringConstraint.java b/src/main/java/com/jkgroller/validationauthority/constraints/username/UsernameEmailSubstringConstraint.java new file mode 100644 index 0000000..43e5a30 --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/constraints/username/UsernameEmailSubstringConstraint.java @@ -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 + *

+ * 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(); + + } + +} diff --git a/src/main/java/com/jkgroller/validationauthority/constraints/username/UsernameEnglishAllowedCharacterConstraint.java b/src/main/java/com/jkgroller/validationauthority/constraints/username/UsernameEnglishAllowedCharacterConstraint.java new file mode 100644 index 0000000..3dddbad --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/constraints/username/UsernameEnglishAllowedCharacterConstraint.java @@ -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 + *

+ * 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(); + } + +} diff --git a/src/main/java/com/jkgroller/validationauthority/constraints/username/UsernameLeadingTrailingSpaceConstraint.java b/src/main/java/com/jkgroller/validationauthority/constraints/username/UsernameLeadingTrailingSpaceConstraint.java new file mode 100644 index 0000000..4bff317 --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/constraints/username/UsernameLeadingTrailingSpaceConstraint.java @@ -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 + *

+ * 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(); + + } + +} diff --git a/src/main/java/com/jkgroller/validationauthority/constraints/username/UsernameLengthConstraint.java b/src/main/java/com/jkgroller/validationauthority/constraints/username/UsernameLengthConstraint.java new file mode 100644 index 0000000..abdbc8d --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/constraints/username/UsernameLengthConstraint.java @@ -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 + *

+ * 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(); + + } + +} diff --git a/src/main/java/com/jkgroller/validationauthority/converter/ResourceConverter.java b/src/main/java/com/jkgroller/validationauthority/converter/ResourceConverter.java new file mode 100644 index 0000000..a19e29c --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/converter/ResourceConverter.java @@ -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 + *

+ * 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); + +} diff --git a/src/main/java/com/jkgroller/validationauthority/enums/CommonQwertySpecialCharactersCharacterData.java b/src/main/java/com/jkgroller/validationauthority/enums/CommonQwertySpecialCharactersCharacterData.java new file mode 100644 index 0000000..02cc3a2 --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/enums/CommonQwertySpecialCharactersCharacterData.java @@ -0,0 +1,47 @@ +/* + * Copyright 2021-2022 John Groller + */ +package com.jkgroller.validationauthority.enums; + +import org.passay.CharacterData; + +/** + * @Author john@grollerfamily.com + *

+ * 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; + } +} diff --git a/src/main/java/com/jkgroller/validationauthority/exception/MethodNotAllowedException.java b/src/main/java/com/jkgroller/validationauthority/exception/MethodNotAllowedException.java new file mode 100644 index 0000000..0d7f21c --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/exception/MethodNotAllowedException.java @@ -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 + *

+ * 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()); + } + +} diff --git a/src/main/java/com/jkgroller/validationauthority/exception/NotImplementedException.java b/src/main/java/com/jkgroller/validationauthority/exception/NotImplementedException.java new file mode 100644 index 0000000..d84c7b6 --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/exception/NotImplementedException.java @@ -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 + *

+ * 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()); + } + +} diff --git a/src/main/java/com/jkgroller/validationauthority/exception/ResourceNotFoundException.java b/src/main/java/com/jkgroller/validationauthority/exception/ResourceNotFoundException.java new file mode 100644 index 0000000..2d9cab8 --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/exception/ResourceNotFoundException.java @@ -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()); + } + +} diff --git a/src/main/java/com/jkgroller/validationauthority/exception/ValidationFailedException.java b/src/main/java/com/jkgroller/validationauthority/exception/ValidationFailedException.java new file mode 100644 index 0000000..23f6697 --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/exception/ValidationFailedException.java @@ -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 + *

+ * Custom ValidationFailedException for the overall failure of + * validations. + */ +public class ValidationFailedException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = -7873709164154851376L; + private List 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 errors) { + this.errors = errors; + } + + /** + * @return + */ + public List getErrors() { + return errors; + } + +} diff --git a/src/main/java/com/jkgroller/validationauthority/exception/mapper/ValidationFailedExceptionMapper.java b/src/main/java/com/jkgroller/validationauthority/exception/mapper/ValidationFailedExceptionMapper.java new file mode 100644 index 0000000..62e66ef --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/exception/mapper/ValidationFailedExceptionMapper.java @@ -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 + *

+ * Overall validation failed exception which maps all the validation errors for the failure collection. + */ +@Component +public class ValidationFailedExceptionMapper implements ExceptionMapper { + + /** + * 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 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. + *

+ * 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(); + } + + +} diff --git a/src/main/java/com/jkgroller/validationauthority/resource/BaseResource.java b/src/main/java/com/jkgroller/validationauthority/resource/BaseResource.java new file mode 100644 index 0000000..dd4166f --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/resource/BaseResource.java @@ -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 + *

+ * 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; + } +} diff --git a/src/main/java/com/jkgroller/validationauthority/resource/BaseRuleResource.java b/src/main/java/com/jkgroller/validationauthority/resource/BaseRuleResource.java new file mode 100644 index 0000000..6bbe2bd --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/resource/BaseRuleResource.java @@ -0,0 +1,143 @@ +/* + * Copyright 2021-2022 John Groller + */ +package com.jkgroller.validationauthority.resource; + +import java.util.List; + +/** + * @Author john@grollerfamily.com + *

+ * 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 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 getModifiers() { + return modifiers; + } + + /** + * @param modifiers + */ + public void setModifiers(List 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; + } + +} diff --git a/src/main/java/com/jkgroller/validationauthority/resource/CredentialRuleResource.java b/src/main/java/com/jkgroller/validationauthority/resource/CredentialRuleResource.java new file mode 100644 index 0000000..53df43d --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/resource/CredentialRuleResource.java @@ -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 + *

+ * This class intentionally left blank. + *

+ * It uses the attributes from the BaseRuleResource and represents them + * as a /credentialRules resource. + */ +@JsonApiResource(type = "credentialRule", resourcePath = "credentialRules") +public class CredentialRuleResource extends BaseRuleResource { +} diff --git a/src/main/java/com/jkgroller/validationauthority/resource/CredentialsResource.java b/src/main/java/com/jkgroller/validationauthority/resource/CredentialsResource.java new file mode 100644 index 0000000..fe0b5b0 --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/resource/CredentialsResource.java @@ -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; + } +} diff --git a/src/main/java/com/jkgroller/validationauthority/resource/repository/CredentialRuleResourceRepository.java b/src/main/java/com/jkgroller/validationauthority/resource/repository/CredentialRuleResourceRepository.java new file mode 100644 index 0000000..25b7b24 --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/resource/repository/CredentialRuleResourceRepository.java @@ -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 + *

+ * Repository for handling GET/POST/PATCH/DELETE on the credential rule + * resource. + */ +@Component +public class CredentialRuleResourceRepository implements ResourceRepository { + + 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 credentialRuleResources; + + /** + * Crnk will blow up without this and give you the most confusing exception + * ever. + */ + @Override + public Class getResourceClass() { + return CredentialRuleResource.class; + } + + /** + * GET requests for /v1/credentialRules/ 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 findAll(QuerySpec querySpec) { + return querySpec.apply(credentialRuleResources); + } + + /** + * GET requests for /v1/credentialRules/,, will come here. + */ + @Override + public ResourceList findAll(Collection ids, QuerySpec querySpec) { + throw new NotImplementedException(NOT_IMPLEMENTED_MESSAGE); + } + + /** + * PATCH requests for /v1/credentialRules/ will come here. Keep in mind, + * however, that PATCH requests will first automatically perform a GET on + * /v1/credentialRules/ to retrieve the resource. + *

+ * 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 save(S resource) { + throw new MethodNotAllowedException(METHOD_NOT_ALLOWED_MESSAGE); + } + + /** + * POST requests for /v1/credentialRules will come here. + */ + @Override + public S create(S resource) { + throw new MethodNotAllowedException(METHOD_NOT_ALLOWED_MESSAGE); + } + + /** + * DELETE requests for /v1/credentialRules/ will come here. + */ + @Override + public void delete(String id) { + throw new MethodNotAllowedException(METHOD_NOT_ALLOWED_MESSAGE); + } + +} diff --git a/src/main/java/com/jkgroller/validationauthority/resource/repository/CredentialsResourceRepository.java b/src/main/java/com/jkgroller/validationauthority/resource/repository/CredentialsResourceRepository.java new file mode 100644 index 0000000..6b639a3 --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/resource/repository/CredentialsResourceRepository.java @@ -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 + *

+ * Repository for handling GET/POST/PATCH/DELETE on the credential + * resource. + */ +@Component +public class CredentialsResourceRepository implements ResourceRepository { + + 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 getResourceClass() { + return CredentialsResource.class; + } + + /** + * GET requests for /v1/credentials/ 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 findAll(QuerySpec querySpec) { + throw new MethodNotAllowedException(METHOD_NOT_ALLOWED_MESSAGE); + } + + /** + * GET requests for /v1/credentials/,, will come here. + */ + @Override + public ResourceList findAll(Collection ids, QuerySpec querySpec) { + throw new MethodNotAllowedException(METHOD_NOT_ALLOWED_MESSAGE); + } + + /** + * PATCH requests for /v1/credentials/ will come here. Keep in mind, + * however, that PATCH requests will first automatically perform a GET on + * /v1/credentials/ to retrieve the resource. + *

+ * 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 save(S resource) { + throw new MethodNotAllowedException(METHOD_NOT_ALLOWED_MESSAGE); + } + + /** + * POST requests for /v1/credentials will come here. + */ + @SuppressWarnings("unchecked") + @Override + public 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/ will come here. + */ + @Override + public void delete(String id) { + throw new MethodNotAllowedException(METHOD_NOT_ALLOWED_MESSAGE); + } + +} diff --git a/src/main/java/com/jkgroller/validationauthority/service/ValidateCredentialService.java b/src/main/java/com/jkgroller/validationauthority/service/ValidateCredentialService.java new file mode 100644 index 0000000..1a6a647 --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/service/ValidateCredentialService.java @@ -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 + *

+ * Service for validating the username. + */ +public interface ValidateCredentialService { + + /** + * Validate the credentials. + * + * @param validateCredentialRequestTO + * @return + */ + ValidateCredentialResponseTO validateCredential(ValidateCredentialRequestTO validateCredentialRequestTO); + +} diff --git a/src/main/java/com/jkgroller/validationauthority/service/ValidateCredentialServiceImpl.java b/src/main/java/com/jkgroller/validationauthority/service/ValidateCredentialServiceImpl.java new file mode 100644 index 0000000..e08a1eb --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/service/ValidateCredentialServiceImpl.java @@ -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 + *

+ * 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 masterErrorList = new ArrayList(); + + 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 validateUsername(ValidateCredentialRequestTO validateCredentialRequestTO) { + + RuleResult ruleResult = applyRulesAndValidateUsername(validateCredentialRequestTO); + return retrieveUsernameValidationErrorData(ruleResult); + + } + + /** + * @param validateCredentialRequestTO + * @return + */ + private List validatePassword(ValidateCredentialRequestTO validateCredentialRequestTO) { + + RuleResult ruleResult = applyRulesAndValidatePassword(validateCredentialRequestTO); + List errorData = new ArrayList(); + + 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 validationRules = new ArrayList(); + + 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 validationRules = new ArrayList(); + + 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 retrieveUsernameValidationErrorData(RuleResult ruleResult) { + + List errors = new ArrayList(); + + List 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 retrievePasswordValidationErrorData(RuleResult ruleResult) { + + List errors = new ArrayList(); + + List 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; + + } + +} \ No newline at end of file diff --git a/src/main/java/com/jkgroller/validationauthority/service/to/ValidateCredentialRequestTO.java b/src/main/java/com/jkgroller/validationauthority/service/to/ValidateCredentialRequestTO.java new file mode 100644 index 0000000..612add5 --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/service/to/ValidateCredentialRequestTO.java @@ -0,0 +1,38 @@ +/* + * Copyright 2021-2022 John Groller + */ +package com.jkgroller.validationauthority.service.to; + +/** + * @Author john@grollerfamily.com + *

+ * 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; + } +} diff --git a/src/main/java/com/jkgroller/validationauthority/service/to/ValidateCredentialResponseTO.java b/src/main/java/com/jkgroller/validationauthority/service/to/ValidateCredentialResponseTO.java new file mode 100644 index 0000000..d718599 --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/service/to/ValidateCredentialResponseTO.java @@ -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 + *

+ * Response for validating the username and password. + */ +public class ValidateCredentialResponseTO extends ValidationResponseTO { + + // List of actual CRNK ErrorData objects. + List errorData; + + /** + * @return + */ + public List getErrorData() { + return errorData; + } + + /** + * @param errorData + */ + public void setErrorData(List errorData) { + this.errorData = errorData; + } + +} diff --git a/src/main/java/com/jkgroller/validationauthority/service/to/ValidationResponseTO.java b/src/main/java/com/jkgroller/validationauthority/service/to/ValidationResponseTO.java new file mode 100644 index 0000000..c40ebf0 --- /dev/null +++ b/src/main/java/com/jkgroller/validationauthority/service/to/ValidationResponseTO.java @@ -0,0 +1,28 @@ +/* + * Copyright 2021-2022 John Groller + */ +package com.jkgroller.validationauthority.service.to; + +/** + * @Auhor john@grollerfamily.com + *

+ * 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; + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..16093b2 --- /dev/null +++ b/src/main/resources/application.yml @@ -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 diff --git a/src/main/resources/errorCodesToChooseFrom.txt b/src/main/resources/errorCodesToChooseFrom.txt new file mode 100644 index 0000000..5910111 --- /dev/null +++ b/src/main/resources/errorCodesToChooseFrom.txt @@ -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 \ No newline at end of file diff --git a/src/main/resources/licenseTemplate.txt b/src/main/resources/licenseTemplate.txt new file mode 100644 index 0000000..20586c2 --- /dev/null +++ b/src/main/resources/licenseTemplate.txt @@ -0,0 +1 @@ +Copyright 2021-2022 John Groller \ No newline at end of file diff --git a/src/test/resources/ValidationAuthority.postman_collection.json b/src/test/resources/ValidationAuthority.postman_collection.json new file mode 100644 index 0000000..014f684 --- /dev/null +++ b/src/test/resources/ValidationAuthority.postman_collection.json @@ -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": {} +} \ No newline at end of file