diff --git a/README.md b/README.md index 81d80ba..1f33067 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,31 @@ # studio-sol-back-end-test +## Sumário +- [Introdução](#introdução) +- [Processo](#processo) +- [Tecnologias e padrões](#tecnologias-e-padrões) + - [Lista completa](#lista-completa) +- [Gerar projeto inicial](#gerar-projeto-inicial) +- [Criar schema da aplicação](#criar-schema-da-aplicação) +- [Configurar projeto para testes](#configurar-projeto-para-testes) +- [Implementação das validações](#implementação-das-validações) + - [Testes de integração como base](#testes-de-integração-como-base) + - [Arquitetura](#arquitetura) + - [MinSizeValidationStrategy](#minsizevalidationstrategy) + - [MinDigitValidationStrategy](#mindigitvalidationstrategy) + - [MinSpecialCharsValidationStrategy](#minspecialcharsvalidationstrategy) + - [NoRepetedValidationStrategy](#norepetedvalidationstrategy) + - [PasswordValidationService](#passwordvalidationservice) + - [Injetar serviço no *resolver*](#injetar-serviço-no-resolver) + - [Refatoração das regras com Regex](#refatoração-das-regras-com-regex) + - [MinUppercaseStrategy e MinLowercaseStrategy](#minuppercasestrategy-e-minlowercasestrategy) +- [Como testar a aplicação](#como-testar-a-aplicação) + - [Localmente](#localmente) + - [Dockerfile](#dockerfile) + - [Versão hospedada](#versão-hospedada) +- [Conclusão](#conclusão) +- [Alguns casos de teste](#alguns-casos-de-teste) + ## Introdução O problema consiste em validar uma senha com base nas regras fornecidas na requisição, depois, retornar se a senha é válida e, se não for, listar quais regras aquela senha fere. @@ -29,9 +55,9 @@ Meu primeiro passo foi criar o projeto usando o [gqlgen](https://github.com/99de Depois que o projeto foi gerado, parti para a criação da `Dockerfile`. -Por último, com o projeto criado e a `Dockerfile` pronta, criei o pipeline com *Github Actions* para automatizar o deploy da aplicação. +Por último, com o projeto criado e a [Dockerfile pronta](Dockerfile), criei o pipeline com *Github Actions* para automatizar o deploy da aplicação. -Quando a aplicação já estava funcionando no Heroku e o pipeline estava configurado, eu adicionei um domínio customizado usando o **Cloudflare**. +Quando a aplicação já estava funcionando no Heroku e o [pipeline estava configurado](.github/workflows/pipeline.yaml), eu adicionei um domínio customizado usando o **Cloudflare**. As rotas ficaram: - Playground: https://studio-sol-back-end-test.gabrielbrandao.net @@ -41,24 +67,9 @@ As rotas ficaram: O próximo passo foi editar o arquivo `schema.graphqls` para configurar a query `verify` e seus tipos. Depois, o script `generate` da **gqlgen** atualizou os códigos com base no novo esquema graphql. -O schema ficou assim: -```gql -type Verify { - verify: Boolean! - noMatch: [String!]! -} - -input Rule { - rule: String! - value: Int! -} - -type Query { - verify(password: String!, rules: [Rule!]!): Verify! -} -``` +Veja o schema [aqui](graph/schema.graphqls). -## Configurar ambiente para testes +## Configurar projeto para testes Eu resolvi utilizar o [ginkgo](https://github.com/onsi/ginkgo) e o [gomega](https://github.com/onsi/gomega) porque eu gosto da estrutura que eles fornecem para escrita de testes, acho que os testes ficam mais legíveis e organizados, além de mais fáceis de escrever. E, como eu desenvolvo com TDD, usar essas bibliotecas me traz mais produtividade para escrever muitos testes. @@ -72,7 +83,7 @@ Então, antes de começar o desenvolvimento, eu configurei o projeto e o pipelin Meu primeiro passo foi escrever testes de integração que testassem alguns casos da query `verify`, inclusive o caso fornecido na descrição da prova. Depois que eu tivesse os testes de integração falhando, eu seguiria o ciclo do TDD com testes de unidade até que a feature estivesse completa e os testes de integração também passassem. -Escrevi testes de integração parametrizados com as mesmas regras do exemplo da prova (minSize, minSpecialChars, noRepeted, minDigit). Coloquei alguns casos de testes apenas com essas regras para começar. Depois que elas estivessem implementadas eu acrescentaria mais casos de testes de integração para cobrir as outras regras. +Escrevi [testes de integração](src/integration_tests/verify_test.go) parametrizados com as mesmas regras do exemplo da prova (minSize, minSpecialChars, noRepeted, minDigit). Coloquei alguns casos de testes apenas com essas regras para começar. Depois que elas estivessem implementadas eu acrescentaria mais casos de testes de integração para cobrir as outras regras. ### Arquitetura @@ -88,6 +99,8 @@ Iniciei pela validação que me pareceu mais simples, a `minSize`. Essa validação consiste em retornar **inválido** para senhas *menores* que o valor fornecido para o `minSize` e retornar **válido** para senhas *maiores* ou de tamanho *igual* ao valor fornecido para `minSize`. +> [MinSizeValidationStrategy](src/strategies/validation/min_size.go) + ### MinDigitValidationStrategy As validações de `minDigit` e `minSpecialChars` são relativamente simples, também. As duas podem ser resolvidas facilmente usando **Regex**. Comecei pela de dígitos porque a expressão regular é mais simples. @@ -96,11 +109,15 @@ A validação consiste em encontrar todos as ocorrências de um dígito numéric A expressão regular é: `\d` -### MinSpecialChars +> [MinDigitValidationStrategy](src/strategies/validation/min_digit.go) + +### MinSpecialCharsValidationStrategy A lógica do `minSpecialChars` é a mesma do minDigit, mas a expressão regular é: `[!@#$%^&*()\-+\\\/{}\[\]]` -### NoRepeted +> [MinSpecialCharsValidationStrategy](src/strategies/validation/min_special_chars.go) + +### NoRepetedValidationStrategy A regra `noRepeted` foge mais da lógica das outras regras. A solução que eu pensei foi a seguinte, comprimir todos os caracteres repetidos consecutivos da senha em um só e comparar o tamanho da senha comprimida com a senha original. @@ -111,16 +128,172 @@ Exemplos: - Falha: A senha *"Opaaa73"*, depois de comprimida, vira *"Opa73"* (diferente da original). +> [NoRepetedValidationStrategy](src/strategies/validation/no_repeted.go) + ### PasswordValidationService O `PasswordValidationService` vai ser o serviço responsável por chamar as strategies em ordem e retornar a validação completa para o `resolver` da query **verify**. Ele recebe um map que atrela os nomes das regras de validação às suas respectivas *estratégias*. -### Resolver implementado com algumas regras +> [PasswordValidationService](src/services/password_validation/password_validation_service.go) -Com o serviço de validação implementado, eu fiz a injeção no resolver e coloquei tudo para funcionar. +### Injetar serviço no *resolver* -Nesse ponto, todos os testes estavam passando (de integração e de unidade) e eu testei alguns casos manualmente pelo playground da aplicação e tudo funcionou. +Com o serviço de validação implementado, eu criei uma [factory](src/factories/password_validation_service_factory.go) para a implementação padrão, fiz a injeção no [resolver](graph/resolver.go) e chamei o serviço no [resolver da query verify](graph/schema.resolvers.go). + +Nesse ponto, todos os testes estavam passando (de integração e de unidade). Eu testei alguns casos manualmente pelo playground da aplicação e tudo funcionou. Até o momento apenas as regras **minSize**, **minDigit**, **minSpecialChars** e **noRepeted** haviam sido implementadas, mas a estrutura já estava preparada para receber as regras restantes facilmente. + +### Refatoração das regras com Regex + +Olhando as regras que ainda não estavam implementadas e comparando com as lógicas de validação de `minDigit` e `minSpecialChars`, eu percebi que elas teriam certo nível de duplicação e uma refatoração podia ser feita para todas as validações que usassem Regex. + +Então, antes de escrever mais testes e implementar as regras novas, eu resolvi refatorar as estratégias de Regex existentes para usar o mesmo código, mudando apenas a expressão regular. + +Criei uma [struct base](src/strategies/validation/regex_validation.go) com a lógica de validação com base em uma expressão. Depois atualizei as estratégias `minSpecialChars` e `minDigit` para utilizar a implementação, cada uma passando sua própria expressão regular de validação. + +### MinUppercaseStrategy e MinLowercaseStrategy + +As duas estratégias restantes, *minUppercase* e *minLowerCase* se aproveitam da struct base de validação regex, porém cada uma com sua expressão: +- minUppercase: `[A-Z]` +- minLowercase: `[a-z]` + +Então, escrevi mais testes de integração que incluíssem essas regras, depois escrevi testes de unidade para implementar cada estratégia e finalizar as implementações das regras. + +- [MinUppercaseStrategy](src/strategies/validation/min_uppercase.go) +- [MinLowercaseStrategy](src/strategies/validation/min_lowercase.go) + +## Conclusão + +Nesse ponto, todas as regras estavam implementadas e todos os testes automatizados e manuais passando. + +Nas seções seguintes, você encontra as instruções para rodar a aplicação, assim como alguns casos de teste de exemplo. + +## Como testar a aplicação + +### Localmente + +Para executar localmente você precisa ter `go 1.19` instalado. + +```bash +# Navegue até pasta raiz do projeto +cd /studio-sol-back-end-test + +# Execute a aplicação +go run cmd/server/server.go +``` + +A aplicação decide a porta pela variável de ambiente `PORT`. Caso nenhuma seja fornecida, a porta padrão é a 8080. As rotas são as seguintes: + +Endpoint graphql: http://localhost:8080/graphql + +Playground GraphQL: http://localhost:8080 + +> Lembre-se de trocar a porta se tiver fornecido um valor para a variável de ambiente `PORT`. + +### Dockerfile + +Caso não tenha `go 1.19` instalado, pode ser mais simples utilizar um container para testar. + +```bash +# Navegue até pasta raiz do projeto +cd /studio-sol-back-end-test + +# Faça o build da imagem +docker build -t studio-sol-back-end-test . + +# Rode o container +docker run -d -p 8080:8080 --name studio-sol-back-end-test studio-sol-back-end-test +``` + +As rotas são as seguintes: + +Endpoint graphql: http://localhost:8080/graphql + +Playground GraphQL: http://localhost:8080 + +Você também pode passar outra porta ser usada pela aplicação: + +```bash +docker run -d -e PORT=8000 -p 8080:8000 --name studio-sol-back-end-test studio-sol-back-end-test +``` + +### Versão hospedada + +A aplicação também está hospedada, então você pode testá-la nesses links: + +Endpoint graphql: https://studio-sol-back-end-test.gabrielbrandao.net/graphql + +Playground GraphQL: https://studio-sol-back-end-test.gabrielbrandao.net + +## Alguns casos de teste + +Caso 1: + + - Entrada: + + ```gql + { + verify( + password: "ee123&" + rules: [{rule: "minSize", value: 8}, {rule: "minSpecialChars", value: 2}, {rule: "noRepeted", value: 0}, {rule: "minDigit", value: 4}, {rule: "minUppercase", value: 7}] + ) { + verify + noMatch + } + } + ``` + + - Saída: + + ```json + { + "data": { + "verify": { + "verify": false, + "noMatch": [ + "minSize", + "minSpecialChars", + "noRepeted", + "minDigit", + "minUppercase" + ] + } + } + } + ``` + +Caso 2: + + - Entrada: + + ```gql + query { + verify(password: "TesteSenhaForte!123&", rules: [ + {rule: "minSize",value: 8}, + {rule: "minSpecialChars",value: 2}, + {rule: "noRepeted",value: 0}, + {rule: "minDigit",value: 4} + ]) { + verify + noMatch + } + } + ``` + + - Saída: + + ```json + { + "data": { + "verify": { + "verify": false, + "noMatch": [ + "minDigit" + ] + } + } + } + ``` diff --git a/src/factories/password_validation_service_factory.go b/src/factories/password_validation_service_factory.go index eafa57b..aae0aa5 100644 --- a/src/factories/password_validation_service_factory.go +++ b/src/factories/password_validation_service_factory.go @@ -13,6 +13,8 @@ func GetDefaultPasswordValidationService() password_validation.PasswordValidatio string(validation.MIN_DIGIT): validation.NewMinDigitValidationStrategy(), string(validation.MIN_SPECIAL_CHARS): validation.NewMinSpecialCharsValidationStrategy(), string(validation.NO_REPETED): validation.NoRepetedStrategy{}, + string(validation.MIN_UPPERCASE): validation.NewMinUppercaseValidationStrategy(), + string(validation.MIN_LOWERCASE): validation.NewMinLowercaseValidationStrategy(), } return *password_validation.NewPasswordValidationService(validationStrategies) diff --git a/src/integration_tests/verify_test.go b/src/integration_tests/verify_test.go index 4d148cf..ce28812 100644 --- a/src/integration_tests/verify_test.go +++ b/src/integration_tests/verify_test.go @@ -51,23 +51,58 @@ var _ = Describe("Verify", func() { Entry("Test case 1", "Opa1@", 5, 1, 1), Entry("Test case 2", "SenhaForte!23", 5, 1, 1), ) + + DescribeTable("minUppercase, minLowercase, minDigit", + func (password string, minUppercase int, minLowercase int, minDigit int) { + var resp struct { + Verify model.Verify + } + + error := c.Post( + ` + query($password: String!, $minUppercase: Int!, $minLowercase: Int!, $minDigit: Int!) { + verify(password: $password, rules: [ + {rule: "minUppercase", value: $minUppercase}, + {rule: "minLowercase", value: $minLowercase}, + {rule: "minDigit", value: $minDigit}, + ]) { + verify + noMatch + } + }`, + &resp, + client.Var("password", password), + client.Var("minUppercase", minUppercase), + client.Var("minLowercase", minLowercase), + client.Var("minDigit", minDigit), + ) + + Expect(error).NotTo(HaveOccurred()) + Expect(resp.Verify.Verify).To(BeTrue()) + Expect(resp.Verify.NoMatch).To(BeEmpty()) + }, + Entry("Test case 1", "OOOOoPpppa1@", 3, 5, 1), + Entry("Test case 2", "S3nHaForTe!23", 4, 4, 3), + ) }) Context("Checking that password does not follow all the specified rules", func () { DescribeTable("The following rules fail:", - func (password string, minSize int, minSpecialChars int, minDigit int, noMatch []string) { + func (password string, minSize int, minSpecialChars int, minDigit int, minUppercase int, minLowercase int, noMatch []string) { var resp struct { Verify model.Verify } error := c.Post( ` - query($password: String!, $minSize: Int!, $minSpecialChars: Int!, $minDigit: Int!) { + query($password: String!, $minSize: Int!, $minSpecialChars: Int!, $minDigit: Int!,$minUppercase: Int!, $minLowercase: Int!) { verify(password: $password, rules: [ {rule: "minSize", value: $minSize}, {rule: "minSpecialChars", value: $minSpecialChars}, {rule: "noRepeted", value: 0}, {rule: "minDigit", value: $minDigit}, + {rule: "minUppercase", value: $minUppercase}, + {rule: "minLowercase", value: $minLowercase}, ]) { verify noMatch @@ -78,6 +113,8 @@ var _ = Describe("Verify", func() { client.Var("minSize", minSize), client.Var("minSpecialChars", minSpecialChars), client.Var("minDigit", minDigit), + client.Var("minUppercase", minUppercase), + client.Var("minLowercase", minLowercase), ) Expect(error).NotTo(HaveOccurred()) @@ -85,9 +122,10 @@ var _ = Describe("Verify", func() { Expect(resp.Verify.NoMatch).NotTo(BeEmpty()) Expect(resp.Verify.NoMatch).To(Equal(noMatch)) }, - Entry("minSize", "0p@", 5, 1, 1, []string{string(validation.MIN_SIZE)}), - Entry("minSize, minSpecialChars, minDigit", "SenhaForte!2", 20, 4, 2, []string{string(validation.MIN_SIZE), string(validation.MIN_SPECIAL_CHARS), string(validation.MIN_DIGIT)}), - Entry("noRepeted", "aaaaaaa!2", 3, 1, 1, []string{string(validation.NO_REPETED)}), + Entry("minSize", "O0p@", 5, 1, 1, 1, 1, []string{string(validation.MIN_SIZE)}), + Entry("minSize, minSpecialChars, minDigit", "SenhaForte!2", 20, 4, 2, 1, 1, []string{string(validation.MIN_SIZE), string(validation.MIN_SPECIAL_CHARS), string(validation.MIN_DIGIT)}), + Entry("noRepeted, minUppercase", "aaaaaaa!2", 3, 1, 1, 3, 1, []string{string(validation.NO_REPETED), string(validation.MIN_UPPERCASE)}), + Entry("minUppercase, minLowercase", "senha!2", 3, 1, 1, 3, 9, []string{string(validation.MIN_UPPERCASE), string(validation.MIN_LOWERCASE)}), ) }) }) diff --git a/src/strategies/validation/min_digit.go b/src/strategies/validation/min_digit.go index c71bd88..0d3e84f 100644 --- a/src/strategies/validation/min_digit.go +++ b/src/strategies/validation/min_digit.go @@ -4,14 +4,12 @@ import "regexp" // Checks that the password contains at least the minimum number of digits. type MinDigitValidationStrategy struct { - digitRegexp regexp.Regexp + RegexValidation } func NewMinDigitValidationStrategy() *MinDigitValidationStrategy { const DIGIT_REGEXP string = `\d` - return &MinDigitValidationStrategy{digitRegexp: *regexp.MustCompile(DIGIT_REGEXP)} -} - -func (md MinDigitValidationStrategy) IsValid(password string, value int) bool { - return len(md.digitRegexp.FindAllString(password, -1)) >= value + return &MinDigitValidationStrategy{RegexValidation: RegexValidation{ + validationExpression: *regexp.MustCompile(DIGIT_REGEXP), + }} } \ No newline at end of file diff --git a/src/strategies/validation/min_lowercase.go b/src/strategies/validation/min_lowercase.go new file mode 100644 index 0000000..43e7f46 --- /dev/null +++ b/src/strategies/validation/min_lowercase.go @@ -0,0 +1,15 @@ +package validation + +import "regexp" + +// Checks that the password has at least the minimum number of lower case characters. +type MinLowercaseValidationStrategy struct { + RegexValidation +} + +func NewMinLowercaseValidationStrategy() MinLowercaseValidationStrategy { + const MIN_LOWERCASE_REGEXP string = "[a-z]" + return MinLowercaseValidationStrategy{RegexValidation: RegexValidation{ + validationExpression: *regexp.MustCompile(MIN_LOWERCASE_REGEXP), + }} +} \ No newline at end of file diff --git a/src/strategies/validation/min_lowercase_test.go b/src/strategies/validation/min_lowercase_test.go new file mode 100644 index 0000000..ea3f6f7 --- /dev/null +++ b/src/strategies/validation/min_lowercase_test.go @@ -0,0 +1,49 @@ +package validation_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/brandaogabriel7/studio-sol-back-end-test/src/strategies/validation" +) + +var _ = Describe("MinLowercase", func() { + minLowercaseStrategy := validation.NewMinLowercaseValidationStrategy() + + Context("Check that the password follows minLowercase rule", func () { + DescribeTable("When password contains more lower case characters than minLowercase value", + func (password string, minLowercase int) { + isValid := minLowercaseStrategy.IsValid(password, minLowercase) + + Expect(isValid).To(BeTrue()) + }, + Entry("minLowercase 3", "senha123456", 3), + Entry("minLowercase 10", "minhasenhaaaa12", 10), + Entry("minLowercase 23", "eusouumasenhasuperforteesegura", 23), + ) + + DescribeTable("When password contains as many lower case characters as minLowercase value", + func (password string, minLowercase int) { + isValid := minLowercaseStrategy.IsValid(password, minLowercase) + + Expect(isValid).To(BeTrue()) + }, + Entry("minLowercase 5", "senha12345", 5), + Entry("minLowercase 10", "sseenhaaaa12345", 10), + Entry("minLowercase 18", "eusouumasupersenha", 18), + ) + }) + + Context("Check that the password does not follow minLowercase rule", func () { + DescribeTable("When password has less lower case characters than minLowercase value", + func (password string, minLowercase int) { + isValid := minLowercaseStrategy.IsValid(password, minLowercase) + + Expect(isValid).To(BeFalse()) + }, + Entry("minLowercase 5", "opa", 5), + Entry("minLowercase 10", "superSenha321", 10), + Entry("minLowercase 30", "SuperS3nh@Forte", 30), + ) + }) +}) diff --git a/src/strategies/validation/min_special_chars.go b/src/strategies/validation/min_special_chars.go index d2b31f0..93a134d 100644 --- a/src/strategies/validation/min_special_chars.go +++ b/src/strategies/validation/min_special_chars.go @@ -5,14 +5,10 @@ import "regexp" // Checks that the password contains at least the minimum number of special chars. // The special chars are these: !@#$%^&*()-+\/{}[] type MinSpecialCharsStrategy struct { - specialCharsRegexp regexp.Regexp + RegexValidation } func NewMinSpecialCharsValidationStrategy() *MinSpecialCharsStrategy { const SPECIAL_CHARS_REGEXP = `[!@#$%^&*()\-+\\\/{}\[\]]` - return &MinSpecialCharsStrategy{specialCharsRegexp: *regexp.MustCompile(SPECIAL_CHARS_REGEXP)} -} - -func (msc MinSpecialCharsStrategy) IsValid(password string, value int) bool { - return len(msc.specialCharsRegexp.FindAllString(password, -1)) >= value + return &MinSpecialCharsStrategy{RegexValidation: RegexValidation{validationExpression: *regexp.MustCompile(SPECIAL_CHARS_REGEXP)}} } \ No newline at end of file diff --git a/src/strategies/validation/min_uppercase.go b/src/strategies/validation/min_uppercase.go new file mode 100644 index 0000000..9a333fc --- /dev/null +++ b/src/strategies/validation/min_uppercase.go @@ -0,0 +1,15 @@ +package validation + +import "regexp" + +// Checks that password has at least the minimum number of lower case characters. +type MinUppercaseValidationStrategy struct{ + RegexValidation +} + +func NewMinUppercaseValidationStrategy() MinUppercaseValidationStrategy { + const MIN_UPPERCASE_REGEXP string = "[A-Z]" + return MinUppercaseValidationStrategy{RegexValidation: RegexValidation{ + validationExpression: *regexp.MustCompile(MIN_UPPERCASE_REGEXP), + }} +} \ No newline at end of file diff --git a/src/strategies/validation/min_uppercase_test.go b/src/strategies/validation/min_uppercase_test.go new file mode 100644 index 0000000..7dec85d --- /dev/null +++ b/src/strategies/validation/min_uppercase_test.go @@ -0,0 +1,49 @@ +package validation_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/brandaogabriel7/studio-sol-back-end-test/src/strategies/validation" +) + +var _ = Describe("MinUppercase", func() { + minUppercaseStrategy := validation.NewMinUppercaseValidationStrategy() + + Context("Check that the password follows minUppercase rule", func () { + DescribeTable("When password contains more upper case characters than minUppercase value", + func (password string, minUppercase int) { + isValid := minUppercaseStrategy.IsValid(password, minUppercase) + + Expect(isValid).To(BeTrue()) + }, + Entry("minUppercase 5", "SENHAA", 5), + Entry("minUppercase 10", "SUPERSENHA123", 10), + Entry("minUppercase 14", "123PARALELEPIPEDOSENHA", 14), + ) + + DescribeTable("When password contains as many upper case characters as minUppercase value", + func (password string, minUppercase int) { + isValid := minUppercaseStrategy.IsValid(password, minUppercase) + + Expect(isValid).To(BeTrue()) + }, + Entry("minUppercase 5", "SENHA", 5), + Entry("minUppercase 10", "SUPERSENHA123", 10), + Entry("minUppercase 23", "DISFARCANDOASEVIDENCIAS", 23), + ) + }) + + Context("Check that the password does not follow minUppercase rule", func () { + DescribeTable("When password has less upper case characters than minUppercase value", + func (password string, minUppercase int) { + isValid := minUppercaseStrategy.IsValid(password, minUppercase) + + Expect(isValid).To(BeFalse()) + }, + Entry("minUppercase 5", "opaaa", 5), + Entry("minUppercase 10", "superSenha321", 10), + Entry("minUppercase 15", "SSSSuperS3nh@Forte", 15), + ) + }) +}) diff --git a/src/strategies/validation/regex_validation.go b/src/strategies/validation/regex_validation.go new file mode 100644 index 0000000..03fadf2 --- /dev/null +++ b/src/strategies/validation/regex_validation.go @@ -0,0 +1,12 @@ +package validation + +import "regexp" + +// Checks that the password has at least the minimum number of whatever is specified in the validationExpression. +type RegexValidation struct { + validationExpression regexp.Regexp +} + +func (rv RegexValidation) IsValid(password string, value int) bool { + return len(rv.validationExpression.FindAllString(password, -1)) >= value +} \ No newline at end of file diff --git a/src/strategies/validation/validation_types.go b/src/strategies/validation/validation_types.go index 134ccb0..f244b5b 100644 --- a/src/strategies/validation/validation_types.go +++ b/src/strategies/validation/validation_types.go @@ -6,4 +6,6 @@ type ValidationType string const MIN_SIZE ValidationType = "minSize" const MIN_DIGIT ValidationType = "minDigit" const MIN_SPECIAL_CHARS ValidationType = "minSpecialChars" -const NO_REPETED ValidationType = "noRepeted" \ No newline at end of file +const NO_REPETED ValidationType = "noRepeted" +const MIN_UPPERCASE ValidationType = "minUppercase" +const MIN_LOWERCASE ValidationType = "minLowercase" \ No newline at end of file