Skip to content

Commit

Permalink
Add rate limit to authentication creation.
Browse files Browse the repository at this point in the history
  • Loading branch information
v0ctor committed Sep 3, 2023
1 parent 0f6533e commit 69d79f6
Show file tree
Hide file tree
Showing 16 changed files with 556 additions and 37 deletions.
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#DELVE_PORT=8001
#COCKROACHDB_CONSOLE_PORT=8080
#REDIS_PORT=6379

# Application
APP_ENV=development
Expand All @@ -18,6 +19,10 @@ COCKROACHDB_PASSWORD=
COCKROACHDB_DATABASE=defaultdb
COCKROACHDB_TLS_MODE=disable

REDIS_ADDRESS=redis:6379
#REDIS_PASSWORD=
#REDIS_DATABASE=0

HCAPTCHA_SECRET=0x0000000000000000000000000000000000000000

MAIL_SOURCE=
Expand Down
14 changes: 14 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ services:
- ${DELVE_PORT:-8001}:${DELVE_PORT:-8001}
depends_on:
- cockroachdb
- redis
tty: true

cockroachdb:
Expand All @@ -31,5 +32,18 @@ services:
timeout: 3s
retries: 3

redis:
image: redis:7.2.0-alpine
command:
- redis-server
- --appendonly
- 'yes'
user: redis
volumes:
- redis:/data
ports:
- ${REDIS_PORT:-6379}:6379

volumes:
cockroachdb:
redis:
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/brianvoe/gofakeit/v6 v6.23.1
github.com/caarlos0/env/v9 v9.0.0
github.com/criptalia/spanish_dni_validator v0.0.0-20230502125532-3278e5ffc050
github.com/go-redis/redis_rate/v10 v10.0.1
github.com/google/uuid v1.3.1
github.com/hashicorp/go-multierror v1.1.1
github.com/kataras/hcaptcha v0.0.2
Expand All @@ -24,6 +25,7 @@ require (
github.com/oklog/ulid/v2 v2.1.0
github.com/pariz/gountries v0.1.6
github.com/realclientip/realclientip-go v1.0.0
github.com/redis/go-redis/v9 v9.1.0
github.com/sarulabs/di/v2 v2.4.2
github.com/sarulabs/dingo/v4 v4.2.0
github.com/stretchr/testify v1.8.4
Expand All @@ -41,8 +43,10 @@ require (
github.com/andybalholm/cascadia v1.0.0 // indirect
github.com/aokoli/goutils v1.0.1 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/go-openapi/inflect v0.19.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.5.9 // indirect
Expand Down
12 changes: 12 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,30 @@ github.com/aws/aws-sdk-go v1.44.332 h1:Ze+98F41+LxoJUdsisAFThV+0yYYLYw17/Vt0++nF
github.com/aws/aws-sdk-go v1.44.332/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/brianvoe/gofakeit/v6 v6.23.1 h1:k2gX0hQpJStvixDbbw8oJOvPBg0XmHJWbSOF5JkiUHw=
github.com/brianvoe/gofakeit/v6 v6.23.1/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8=
github.com/bsm/ginkgo/v2 v2.9.5 h1:rtVBYPs3+TC5iLUVOis1B9tjLTup7Cj5IfzosKtvTJ0=
github.com/bsm/ginkgo/v2 v2.9.5/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/caarlos0/env/v9 v9.0.0 h1:SI6JNsOA+y5gj9njpgybykATIylrRMklbs5ch6wO6pc=
github.com/caarlos0/env/v9 v9.0.0/go.mod h1:ye5mlCVMYh6tZ+vCgrs/B95sj88cg5Tlnc0XIzgZ020=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/criptalia/spanish_dni_validator v0.0.0-20230502125532-3278e5ffc050 h1:r8iENXwfxhNFynMkd8oyLfOs6kDyyHG67FiOWDTrFS0=
github.com/criptalia/spanish_dni_validator v0.0.0-20230502125532-3278e5ffc050/go.mod h1:TThPXr8Af1AJCJ0u1h9GZHuPfN4+nhgQPkbvXZ1Lo6Q=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df/go.mod h1:GJr+FCSXshIwgHBtLglIg9M2l2kQSi6QjVAngtzI08Y=
github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
github.com/go-redis/redis_rate/v10 v10.0.1 h1:calPxi7tVlxojKunJwQ72kwfozdy25RjA0bCj1h0MUo=
github.com/go-redis/redis_rate/v10 v10.0.1/go.mod h1:EMiuO9+cjRkR7UvdvwMO7vbgqJkltQHtwbdIQvaBKIU=
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
Expand Down Expand Up @@ -127,6 +137,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/realclientip/realclientip-go v1.0.0 h1:+yPxeC0mEaJzq1BfCt2h4BxlyrvIIBzR6suDc3BEF1U=
github.com/realclientip/realclientip-go v1.0.0/go.mod h1:CXnUdVwFRcXFJIRb/dTYqbT7ud48+Pi2pFm80bxDmcI=
github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY=
github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
Expand Down
5 changes: 5 additions & 0 deletions internal/api/reporting/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ var ErrInternal = &gqlerror.Error{
Extensions: map[string]any{"code": "INTERNAL"},
}

var ErrRateLimit = &gqlerror.Error{
Message: "A limit of attempts per unit of time has been reached for this action.",
Extensions: map[string]any{"code": "RATE_LIMIT"},
}

var ErrNotFound = &gqlerror.Error{
Message: "The specified resource does not exist.",
Extensions: map[string]any{"code": "NOT_FOUND"},
Expand Down
31 changes: 30 additions & 1 deletion internal/api/resolvers/authentication.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 12 additions & 3 deletions internal/api/resolvers/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,29 @@ import (
"github.com/avptp/brain/internal/config"
"github.com/avptp/brain/internal/generated/data"
"github.com/avptp/brain/internal/messaging"
"github.com/go-redis/redis_rate/v10"
)

type Resolver struct {
cfg *config.Config
captcha auth.Captcha
cfg *config.Config
data *data.Client
limiter *redis_rate.Limiter
messenger messaging.Messenger
}

func NewResolver(cfg *config.Config, captcha auth.Captcha, data *data.Client, messenger messaging.Messenger) *Resolver {
func NewResolver(
captcha auth.Captcha,
cfg *config.Config,
data *data.Client,
limiter *redis_rate.Limiter,
messenger messaging.Messenger,
) *Resolver {
return &Resolver{
cfg,
captcha,
cfg,
data,
limiter,
messenger,
}
}
28 changes: 28 additions & 0 deletions internal/api/resolvers/resolvers_test/authentication_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package resolvers_test

import (
"encoding/base64"
"fmt"

"github.com/99designs/gqlgen/client"
"github.com/avptp/brain/internal/api/reporting"
"github.com/avptp/brain/internal/generated/data/authentication"
"github.com/go-redis/redis_rate/v10"
)

func (t *TestSuite) TestAuthentication() {
Expand Down Expand Up @@ -55,6 +57,32 @@ func (t *TestSuite) TestAuthentication() {
t.Equal(p.ID, atc.Edges.Person.ID)
})

t.Run("create_with_too_many_attempts", func() {
_, p, pf, _, _ := t.authenticate()

rlKey := fmt.Sprintf("createAuthentication:%s", p.Email)

res, err := t.limiter.AllowN(
t.allowCtx,
rlKey,
redis_rate.PerHour(t.cfg.AuthenticationRateLimit),
t.cfg.AuthenticationRateLimit,
)

t.NoError(err)
t.LessOrEqual(res.Remaining, 0)

var response create
err = t.api.Post(
createMutation,
&response,
client.Var("email", pf.Email),
client.Var("password", pf.Password),
)

t.ErrorContains(err, reporting.ErrRateLimit.Message)
})

t.Run("create_with_wrong_email", func() {
input := t.factory.Person().Fields

Expand Down
11 changes: 8 additions & 3 deletions internal/api/resolvers/resolvers_test/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"

"github.com/avptp/brain/internal/auth/auth_test"
"github.com/avptp/brain/internal/config"
"github.com/avptp/brain/internal/generated/container"
"github.com/avptp/brain/internal/generated/data"
"github.com/avptp/brain/internal/generated/data/factories"
Expand All @@ -15,6 +16,7 @@ import (
"github.com/avptp/brain/internal/messaging/messaging_test"
"github.com/avptp/brain/internal/services"
"github.com/avptp/brain/internal/transport"
"github.com/go-redis/redis_rate/v10"

"github.com/99designs/gqlgen/client"
"github.com/brianvoe/gofakeit/v6"
Expand Down Expand Up @@ -52,10 +54,11 @@ func init() {
type TestSuite struct {
suite.Suite

ctn *container.Container
data *data.Client

ctn *container.Container
captcha *auth_test.MockedCaptcha
cfg *config.Config
data *data.Client
limiter *redis_rate.Limiter
messenger *messaging_test.MockedMessenger

factory *factories.Factory
Expand Down Expand Up @@ -87,7 +90,9 @@ func (t *TestSuite) SetupSuite() {
ctn := builder.Build()

t.ctn = ctn
t.cfg = ctn.GetConfig()
t.data = ctn.GetData()
t.limiter = ctn.GetLimiter()

t.factory = factories.New(t.data)
t.api = client.New(transport.Mux(ctn))
Expand Down
7 changes: 6 additions & 1 deletion internal/config/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ type Config struct {
CockroachDBTLSMode string `env:"COCKROACHDB_TLS_MODE" envDefault:"require"`
CockroachDBTLSCA string `env:"COCKROACHDB_TLS_CA"`

RedisAddress string `env:"REDIS_ADDRESS"`
RedisPassword string `env:"REDIS_PASSWORD"`
RedisDatabase int `env:"REDIS_DATABASE"`

HcaptchaSecret string `env:"HCAPTCHA_SECRET"`

MailSource string `env:"MAIL_SOURCE"`
Expand All @@ -26,7 +30,8 @@ type Config struct {
AwsKeyId string `env:"AWS_KEY_ID"`
AwsKeySecret string `env:"AWS_KEY_SECRET"`

AuthorizationMaxAge time.Duration `env:"AUTHORIZATION_MAX_AGE"`
AuthenticationRateLimit int `env:"AUTHENTICATION_RATE_LIMIT" envDefault:"5"` // per person and hour
AuthorizationMaxAge time.Duration `env:"AUTHORIZATION_MAX_AGE"`

FrontUrl string `env:"FRONT_URL"`
FrontEmailAuthorizationPath string `env:"FRONT_EMAIL_AUTHORIZATION_PATH"`
Expand Down
Loading

0 comments on commit 69d79f6

Please sign in to comment.