Skip to content
This repository has been archived by the owner on Dec 11, 2024. It is now read-only.

Commit

Permalink
feat: password validation service
Browse files Browse the repository at this point in the history
  • Loading branch information
brandaogabriel7 committed Dec 19, 2022
1 parent f465bc7 commit f4a9293
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 6 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ type Verify {

input Rule {
rule: String!
value: Int
value: Int!
}

type Query {
Expand Down Expand Up @@ -111,4 +111,8 @@ Exemplos:

- Falha: A senha *"Opaaa73"*, depois de comprimida, vira *"Opa73"* (diferente da original).

### 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*.
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,22 @@ require (
github.com/99designs/gqlgen v0.17.22
github.com/onsi/ginkgo/v2 v2.6.1
github.com/onsi/gomega v1.24.1
github.com/stretchr/testify v1.8.1
github.com/vektah/gqlparser/v2 v2.5.1
)

require (
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/urfave/cli/v2 v2.8.1 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/mod v0.7.0 // indirect
Expand Down
7 changes: 6 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,14 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/urfave/cli/v2 v2.8.1 h1:CGuYNZF9IKZY/rfBe3lJpccSoIY1ytfvmgQT90cNOl4=
github.com/urfave/cli/v2 v2.8.1/go.mod h1:Z41J9TPoffeoqP0Iza0YbAhGvymRdZAd2uPmZ5JxRdY=
github.com/vektah/gqlparser/v2 v2.5.1 h1:ZGu+bquAY23jsxDRcYpWjttRZrUz07LbiY77gUOHcr4=
Expand Down
29 changes: 29 additions & 0 deletions src/services/password_validation/password_validation_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package password_validation

import (
"github.com/brandaogabriel7/studio-sol-back-end-test/graph/model"
"github.com/brandaogabriel7/studio-sol-back-end-test/src/strategies/validation"
)

type PasswordValidationService struct {
strategies map[string]validation.ValidationStrategy
}

func NewPasswordValidationService(strategies map[string]validation.ValidationStrategy) *PasswordValidationService {
return &PasswordValidationService{strategies: strategies}
}

func (pvs *PasswordValidationService) Validate(password string, rules []*model.Rule) model.Verify {
verifyResponse := model.Verify{Verify: true, NoMatch: make([]string, 0)}

for _, rule := range rules {
if strategy, exists := pvs.strategies[rule.Rule]; exists {
if !strategy.IsValid(password, rule.Value) {
verifyResponse.NoMatch = append(verifyResponse.NoMatch, rule.Rule)
}
}
}
verifyResponse.Verify = len(verifyResponse.NoMatch) > 0

return verifyResponse
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package password_validation_test

import (
"github.com/brandaogabriel7/studio-sol-back-end-test/graph/model"
"github.com/brandaogabriel7/studio-sol-back-end-test/src/services/password_validation"
"github.com/brandaogabriel7/studio-sol-back-end-test/src/strategies/validation"
"github.com/brandaogabriel7/studio-sol-back-end-test/src/utils"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/stretchr/testify/mock"
)

// ValidationStrategy test double
type mockedValidationStrategy struct { mock.Mock }

func newMockedValidationStrategy() *mockedValidationStrategy { return &mockedValidationStrategy{} }

func (m mockedValidationStrategy) IsValid(password string, value int) bool {
args := m.Called(password, value)
return args.Bool(0)
}

// tests
var _ = Describe("PasswordValidationService", func() {
const FIRST_RULE string = "firstRule"
const SECOND_RULE string = "secondRule"
const THIRD_RULE string = "thirdRule"

firstRuleStrategy := mockedValidationStrategy{}
secondRuleStrategy := mockedValidationStrategy{}
thirdRuleStrategy := mockedValidationStrategy{}

mockedValidationStrategies := map[string]*mockedValidationStrategy{
FIRST_RULE: &firstRuleStrategy,
SECOND_RULE: &secondRuleStrategy,
THIRD_RULE: &thirdRuleStrategy,
}

validationStrategies := make(map[string]validation.ValidationStrategy)

for key, value := range mockedValidationStrategies {
validationStrategies[key] = value
}

pvs := password_validation.NewPasswordValidationService(validationStrategies)

DescribeTable("Validate password when following rules have not passed",
func (password string, rules []*model.Rule, noMatch []string) {
// overwrite stub for the noMatch strategies
for key, mockedStrategy := range mockedValidationStrategies {
passsedValidation := !utils.Contains(noMatch, key)
mockedStrategy.On("IsValid", password, mock.AnythingOfType("int")).Return(passsedValidation)
}

verifyResponse := pvs.Validate(password, rules)

isValid := len(noMatch) > 0

Expect(verifyResponse.NoMatch).To(Equal(noMatch))
Expect(verifyResponse.Verify).To(Equal(isValid))
},
Entry(
"third",
"opa",
[]model.Rule{
{Rule: FIRST_RULE, Value: 3},
{Rule: SECOND_RULE, Value: 2},
{Rule: THIRD_RULE, Value: 0},
},
[]string{THIRD_RULE},
),
Entry(
"first, second",
"senhaaa",
[]model.Rule{
{Rule: FIRST_RULE, Value: 5},
{Rule: SECOND_RULE, Value: 7},
{Rule: THIRD_RULE, Value: 2},
},
[]string{FIRST_RULE, SECOND_RULE},
),
Entry(
"-",
"senhaforte",
[]model.Rule{
{Rule: FIRST_RULE, Value: 0},
{Rule: SECOND_RULE, Value: 7},
{Rule: THIRD_RULE, Value: 2},
},
[]string{},
),
)
})
13 changes: 13 additions & 0 deletions src/services/password_validation/password_validation_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package password_validation_test

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func TestPasswordValidation(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "PasswordValidation Suite")
}
2 changes: 1 addition & 1 deletion src/strategies/validation/min_digit.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ func NewMinDigitValidationStrategy() *MinDigitValidationStrategy {
return &MinDigitValidationStrategy{digitRegexp: *regexp.MustCompile(DIGIT_REGEXP)}
}

func (md *MinDigitValidationStrategy) IsValid(password string, value int) bool {
func (md MinDigitValidationStrategy) IsValid(password string, value int) bool {
return len(md.digitRegexp.FindAllString(password, -1)) >= value
}
2 changes: 1 addition & 1 deletion src/strategies/validation/min_size.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ package validation

type MinSizeValidationStrategy struct {}

func (md *MinSizeValidationStrategy) IsValid(password string, value int) bool {
func (md MinSizeValidationStrategy) IsValid(password string, value int) bool {
return len(password) >= value
}
2 changes: 1 addition & 1 deletion src/strategies/validation/min_special_chars.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ func NewMinSpecialCharsValidationStrategy() *MinSpecialCharsStrategy {
return &MinSpecialCharsStrategy{specialCharsRegexp: *regexp.MustCompile(SPECIAL_CHARS_REGEXP)}
}

func (msc *MinSpecialCharsStrategy) IsValid(password string, value int) bool {
func (msc MinSpecialCharsStrategy) IsValid(password string, value int) bool {
return len(msc.specialCharsRegexp.FindAllString(password, -1)) >= value
}
2 changes: 1 addition & 1 deletion src/strategies/validation/no_repeted.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import "strings"

type NoRepetedStrategy struct {}

func (nr *NoRepetedStrategy) IsValid(password string, _ int) bool {
func (nr NoRepetedStrategy) IsValid(password string, _ int) bool {
compressedPasswordSb := &strings.Builder{}
var previous rune
for _, r := range password {
Expand Down
10 changes: 10 additions & 0 deletions src/utils/slice_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package utils

func Contains(s []string, str string) bool {
for _, v := range s {
if v == str {
return true
}
}
return false
}

0 comments on commit f4a9293

Please sign in to comment.