refactor:
This commit is contained in:
parent
ba26d05516
commit
93eaf89d78
34 changed files with 1270 additions and 742 deletions
|
@ -1,4 +1,4 @@
|
||||||
package lib
|
package config
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Env string `env:"ENV" env-default:"prod"`
|
Env string `env:"ENV" env-default:"prod"`
|
60
go.mod
60
go.mod
|
@ -3,25 +3,71 @@ module git.sch9.ru/new_gate/ms-auth
|
||||||
go 1.21.3
|
go 1.21.3
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/ilyakaznacheev/cleanenv v1.5.0
|
github.com/ilyakaznacheev/cleanenv v1.5.0
|
||||||
github.com/valkey-io/valkey-go v1.0.38
|
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438
|
||||||
golang.org/x/crypto v0.24.0
|
github.com/stretchr/testify v1.9.0
|
||||||
google.golang.org/grpc v1.64.0
|
github.com/testcontainers/testcontainers-go/modules/valkey v0.33.0
|
||||||
|
github.com/valkey-io/valkey-go v1.0.41
|
||||||
|
go.uber.org/zap v1.27.0
|
||||||
|
golang.org/x/crypto v0.25.0
|
||||||
|
google.golang.org/grpc v1.64.1
|
||||||
google.golang.org/protobuf v1.34.1
|
google.golang.org/protobuf v1.34.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 // indirect
|
dario.cat/mergo v1.0.0 // indirect
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||||
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
|
github.com/containerd/containerd v1.7.19 // indirect
|
||||||
|
github.com/containerd/log v0.1.0 // indirect
|
||||||
|
github.com/containerd/platforms v0.2.1 // indirect
|
||||||
|
github.com/cpuguy83/dockercfg v0.3.1 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/distribution/reference v0.6.0 // indirect
|
||||||
|
github.com/docker/docker v27.1.1+incompatible // indirect
|
||||||
|
github.com/docker/go-connections v0.5.0 // indirect
|
||||||
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||||
|
github.com/klauspost/compress v1.17.9 // indirect
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae // indirect
|
||||||
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
|
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||||
|
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||||
|
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||||
|
github.com/moby/sys/user v0.1.0 // indirect
|
||||||
|
github.com/moby/term v0.5.0 // indirect
|
||||||
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
|
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||||
|
github.com/shirou/gopsutil/v3 v3.24.5 // indirect
|
||||||
|
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
|
github.com/testcontainers/testcontainers-go v0.33.0 // indirect
|
||||||
|
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
||||||
|
github.com/tklauser/numcpus v0.8.0 // indirect
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.28.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/zap v1.27.0 // indirect
|
golang.org/x/net v0.26.0 // indirect
|
||||||
golang.org/x/net v0.24.0 // indirect
|
|
||||||
golang.org/x/sync v0.7.0 // indirect
|
golang.org/x/sync v0.7.0 // indirect
|
||||||
golang.org/x/sys v0.21.0 // indirect
|
golang.org/x/sys v0.22.0 // indirect
|
||||||
golang.org/x/text v0.16.0 // indirect
|
golang.org/x/text v0.16.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
|
||||||
)
|
)
|
||||||
|
|
178
go.sum
178
go.sum
|
@ -1,18 +1,62 @@
|
||||||
|
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||||
|
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
|
||||||
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||||
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
|
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
|
||||||
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
|
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
||||||
|
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||||
|
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
|
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
|
github.com/containerd/containerd v1.7.19 h1:/xQ4XRJ0tamDkdzrrBAUy/LE5nCcxFKdBm4EcPrSMEE=
|
||||||
|
github.com/containerd/containerd v1.7.19/go.mod h1:h4FtNYUUMB4Phr6v+xG89RYKj9XccvbNSCKjdufCrkc=
|
||||||
|
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||||
|
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||||
|
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
||||||
|
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||||
|
github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E=
|
||||||
|
github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
|
||||||
|
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||||
|
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||||
|
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||||
|
github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
|
||||||
|
github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
|
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||||
|
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||||
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
|
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||||
|
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
|
||||||
github.com/ilyakaznacheev/cleanenv v1.5.0 h1:0VNZXggJE2OYdXE87bfSSwGxeiGt9moSR2lOrsHHvr4=
|
github.com/ilyakaznacheev/cleanenv v1.5.0 h1:0VNZXggJE2OYdXE87bfSSwGxeiGt9moSR2lOrsHHvr4=
|
||||||
github.com/ilyakaznacheev/cleanenv v1.5.0/go.mod h1:a5aDzaJrLCQZsazHol1w8InnDcOX0OColm64SlIi6gk=
|
github.com/ilyakaznacheev/cleanenv v1.5.0/go.mod h1:a5aDzaJrLCQZsazHol1w8InnDcOX0OColm64SlIi6gk=
|
||||||
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0=
|
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0=
|
||||||
|
@ -29,45 +73,149 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
|
||||||
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
|
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
||||||
|
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||||
|
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae h1:dIZY4ULFcto4tAFlj1FYZl8ztUZ13bdq+PLY+NOfbyI=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
||||||
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||||
|
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||||
|
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||||
|
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||||
|
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
||||||
|
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||||
|
github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg=
|
||||||
|
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=
|
||||||
|
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||||
|
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||||
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
|
github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
|
||||||
github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0=
|
github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0=
|
||||||
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
|
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||||
|
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||||
|
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||||
|
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
|
||||||
|
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||||
|
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||||
|
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||||
|
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/valkey-io/valkey-go v1.0.38 h1:0g+ozx+WUGmu18h8SVGoIyxdWp820utVLlFkGnQ3h0c=
|
github.com/testcontainers/testcontainers-go v0.33.0 h1:zJS9PfXYT5O0ZFXM2xxXfk4J5UMw/kRiISng037Gxdw=
|
||||||
github.com/valkey-io/valkey-go v1.0.38/go.mod h1:LXqAbjygRuA1YRocojTslAGx2dQB4p8feaseGviWka4=
|
github.com/testcontainers/testcontainers-go v0.33.0/go.mod h1:W80YpTa8D5C3Yy16icheD01UTDu+LmXIA2Keo+jWtT8=
|
||||||
|
github.com/testcontainers/testcontainers-go/modules/valkey v0.33.0 h1:yLD+3pvvTQiU0h9Aj6/2D+iaoDDl+IaMm0rGEUxQlZs=
|
||||||
|
github.com/testcontainers/testcontainers-go/modules/valkey v0.33.0/go.mod h1:q4939OS+DBYCAbwALicXW77GPt5TGrBnVLTlWzZBxOY=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
|
||||||
|
github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
|
||||||
|
github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
|
||||||
|
github.com/valkey-io/valkey-go v1.0.41 h1:pWgh9MP24Vl0ANZ0KxEMwB/LHvTUKwlm2SPuWIrSlFw=
|
||||||
|
github.com/valkey-io/valkey-go v1.0.41/go.mod h1:LXqAbjygRuA1YRocojTslAGx2dQB4p8feaseGviWka4=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
|
||||||
|
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
||||||
|
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
|
||||||
|
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
||||||
|
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
|
||||||
|
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
|
||||||
|
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
||||||
|
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
|
||||||
|
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
|
||||||
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||||
|
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||||
|
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||||
|
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
||||||
|
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
|
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
|
||||||
|
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13 h1:vlzZttNJGVqTsRFU9AmdnrcO1Znh8Ew9kCD//yjigk0=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||||
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
|
google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA=
|
||||||
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
|
google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0=
|
||||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
@ -76,5 +224,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||||
|
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||||
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 h1:slmdOY3vp8a7KQbHkL+FLbvbkgMqmXojpFUO/jENuqQ=
|
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 h1:slmdOY3vp8a7KQbHkL+FLbvbkgMqmXojpFUO/jENuqQ=
|
||||||
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3/go.mod h1:oVgVk4OWVDi43qWBEyGhXgYxt7+ED4iYNpTngSLX2Iw=
|
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3/go.mod h1:oVgVk4OWVDi43qWBEyGhXgYxt7+ED4iYNpTngSLX2Iw=
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import "git.sch9.ru/new_gate/ms-auth/internal/lib"
|
import (
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
type Role int32
|
type Role int32
|
||||||
|
|
||||||
|
@ -40,5 +42,5 @@ func (role Role) Valid() error {
|
||||||
case RoleSpectator, RoleParticipant, RoleModerator, RoleAdmin:
|
case RoleSpectator, RoleParticipant, RoleModerator, RoleAdmin:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return lib.ErrBadRole
|
return utils.ErrBadRole
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.sch9.ru/new_gate/ms-auth/internal/lib"
|
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
|
||||||
"github.com/golang-jwt/jwt"
|
"github.com/golang-jwt/jwt"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
@ -13,17 +13,17 @@ type Session struct {
|
||||||
|
|
||||||
func NewSession(userId int32) *Session {
|
func NewSession(userId int32) *Session {
|
||||||
return &Session{
|
return &Session{
|
||||||
Id: lib.AsStringP(uuid.NewString()),
|
Id: utils.AsStringP(uuid.NewString()),
|
||||||
UserId: &userId,
|
UserId: &userId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Session) Valid() error {
|
func (s Session) Valid() error {
|
||||||
if s.Id == nil {
|
if s.Id == nil {
|
||||||
return lib.ErrBadSession
|
return utils.ErrBadSession
|
||||||
}
|
}
|
||||||
if s.UserId == nil {
|
if s.UserId == nil {
|
||||||
return lib.ErrBadSession
|
return utils.ErrBadSession
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ func (s Session) Token(secret string) (string, error) {
|
||||||
refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, s)
|
refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, s)
|
||||||
str, err := refreshToken.SignedString([]byte(secret))
|
str, err := refreshToken.SignedString([]byte(secret))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", lib.ErrBadSession
|
return "", utils.ErrBadSession
|
||||||
}
|
}
|
||||||
return str, nil
|
return str, nil
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ func Parse(tkn string, secret string) (*Session, error) {
|
||||||
return []byte(secret), nil
|
return []byte(secret), nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, lib.ErrBadSession
|
return nil, utils.ErrBadSession
|
||||||
}
|
}
|
||||||
session := parsedToken.Claims.(*Session)
|
session := parsedToken.Claims.(*Session)
|
||||||
if err = session.Valid(); err != nil {
|
if err = session.Valid(); err != nil {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.sch9.ru/new_gate/ms-auth/internal/lib"
|
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -19,10 +19,10 @@ type User struct {
|
||||||
|
|
||||||
func (user *User) ValidUsername() error {
|
func (user *User) ValidUsername() error {
|
||||||
if user.Username == nil {
|
if user.Username == nil {
|
||||||
return lib.ErrBadUsername
|
return utils.ErrBadUsername
|
||||||
}
|
}
|
||||||
|
|
||||||
err := lib.ValidUsername(*user.Username)
|
err := utils.ValidUsername(*user.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -32,10 +32,10 @@ func (user *User) ValidUsername() error {
|
||||||
|
|
||||||
func (user *User) ValidPassword() error {
|
func (user *User) ValidPassword() error {
|
||||||
if user.Password == nil {
|
if user.Password == nil {
|
||||||
return lib.ErrBadHandleOrPassword
|
return utils.ErrBadHandleOrPassword
|
||||||
}
|
}
|
||||||
|
|
||||||
err := lib.ValidPassword(*user.Password)
|
err := utils.ValidPassword(*user.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -45,37 +45,37 @@ func (user *User) ValidPassword() error {
|
||||||
|
|
||||||
func (user *User) ValidEmail() error {
|
func (user *User) ValidEmail() error {
|
||||||
if user.Email == nil {
|
if user.Email == nil {
|
||||||
return lib.ErrBadEmail
|
return utils.ErrBadEmail
|
||||||
}
|
}
|
||||||
return lib.ValidEmail(*user.Email)
|
return utils.ValidEmail(*user.Email)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) ValidRole() error {
|
func (user *User) ValidRole() error {
|
||||||
if user.Role == nil {
|
if user.Role == nil {
|
||||||
return lib.ErrBadRole
|
return utils.ErrBadRole
|
||||||
}
|
}
|
||||||
return user.Role.Valid()
|
return user.Role.Valid()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) HashPassword() error {
|
func (user *User) HashPassword() error {
|
||||||
if user.Password == nil {
|
if user.Password == nil {
|
||||||
return lib.ErrBadHandleOrPassword
|
return utils.ErrBadHandleOrPassword
|
||||||
}
|
}
|
||||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(*user.Password), bcrypt.DefaultCost)
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(*user.Password), bcrypt.DefaultCost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return lib.ErrInternal
|
return utils.ErrInternal
|
||||||
}
|
}
|
||||||
user.Password = lib.AsStringP(string(hashedPassword))
|
user.Password = utils.AsStringP(string(hashedPassword))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) ComparePassword(password string) error {
|
func (user *User) ComparePassword(password string) error {
|
||||||
if user.Password == nil {
|
if user.Password == nil {
|
||||||
return lib.ErrBadHandleOrPassword
|
return utils.ErrBadHandleOrPassword
|
||||||
}
|
}
|
||||||
err := bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(password))
|
err := bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(password))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return lib.ErrBadHandleOrPassword
|
return utils.ErrBadHandleOrPassword
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,103 +0,0 @@
|
||||||
package services
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"git.sch9.ru/new_gate/ms-auth/internal/lib"
|
|
||||||
"git.sch9.ru/new_gate/ms-auth/internal/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SessionProvider interface {
|
|
||||||
CreateSession(ctx context.Context, userId int32) error
|
|
||||||
ReadSessionByToken(ctx context.Context, token string) (*models.Session, error)
|
|
||||||
ReadSessionByUserId(ctx context.Context, userId int32) (*models.Session, error)
|
|
||||||
UpdateSession(ctx context.Context, session *models.Session) error
|
|
||||||
DeleteSessionByToken(ctx context.Context, token string) error
|
|
||||||
DeleteSessionByUserId(ctx context.Context, userId int32) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type SessionService struct {
|
|
||||||
sessionProvider SessionProvider
|
|
||||||
userProvider UserStorage
|
|
||||||
cfg lib.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSessionService(sessionProvider SessionProvider, userProvider UserStorage, cfg lib.Config) *SessionService {
|
|
||||||
return &SessionService{
|
|
||||||
sessionProvider: sessionProvider,
|
|
||||||
userProvider: userProvider,
|
|
||||||
cfg: cfg,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SessionService) Create(ctx context.Context, handle, password string) (*string, error) {
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
user *models.User
|
|
||||||
)
|
|
||||||
|
|
||||||
if lib.ValidUsername(handle) == nil {
|
|
||||||
user, err = s.userProvider.ReadUserByUsername(ctx, handle)
|
|
||||||
} else if lib.ValidEmail(handle) == nil {
|
|
||||||
user, err = s.userProvider.ReadUserByEmail(ctx, handle)
|
|
||||||
} else {
|
|
||||||
return nil, lib.ErrBadHandleOrPassword
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = user.ComparePassword(password)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
//err = s.sessionProvider.CreateSession(ctx, *user.Id)
|
|
||||||
//if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
//}
|
|
||||||
s.sessionProvider.CreateSession(ctx, *user.Id) // FIXME
|
|
||||||
|
|
||||||
session, err := s.sessionProvider.ReadSessionByUserId(ctx, *user.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
token, err := session.Token(s.cfg.JWTSecret)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &token, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SessionService) Read(ctx context.Context, token string) (*int32, error) {
|
|
||||||
session, err := s.sessionProvider.ReadSessionByToken(ctx, token)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return session.UserId, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SessionService) Update(ctx context.Context, token string) error {
|
|
||||||
session, err := s.sessionProvider.ReadSessionByToken(ctx, token)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = s.sessionProvider.UpdateSession(ctx, session)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SessionService) Delete(ctx context.Context, token string) error {
|
|
||||||
session, err := s.sessionProvider.ReadSessionByToken(ctx, token)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = s.sessionProvider.DeleteSessionByUserId(ctx, *session.UserId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,131 +0,0 @@
|
||||||
package services
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"git.sch9.ru/new_gate/ms-auth/internal/lib"
|
|
||||||
"git.sch9.ru/new_gate/ms-auth/internal/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
type UserStorage interface {
|
|
||||||
CreateUser(ctx context.Context, user *models.User) (int32, error)
|
|
||||||
ReadUserByEmail(ctx context.Context, email string) (*models.User, error)
|
|
||||||
ReadUserByUsername(ctx context.Context, username string) (*models.User, error)
|
|
||||||
ReadUserById(ctx context.Context, id int32) (*models.User, error)
|
|
||||||
UpdateUser(ctx context.Context, user *models.User) error
|
|
||||||
DeleteUser(ctx context.Context, id int32) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserService struct {
|
|
||||||
userProvider UserStorage
|
|
||||||
sessionProvider SessionProvider
|
|
||||||
cfg lib.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewUserService(
|
|
||||||
userProvider UserStorage,
|
|
||||||
sessionProvider SessionProvider,
|
|
||||||
cfg lib.Config,
|
|
||||||
) *UserService {
|
|
||||||
return &UserService{
|
|
||||||
userProvider: userProvider,
|
|
||||||
sessionProvider: sessionProvider,
|
|
||||||
cfg: cfg,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserService) CreateUser(ctx context.Context, user *models.User) (int32, error) {
|
|
||||||
me := ctx.Value("user").(*models.User)
|
|
||||||
|
|
||||||
switch *me.Role {
|
|
||||||
case models.RoleAdmin:
|
|
||||||
break
|
|
||||||
case models.RoleModerator:
|
|
||||||
if !user.Role.AtMost(models.RoleParticipant) {
|
|
||||||
return 0, lib.ErrNoPermission
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return 0, lib.ErrNoPermission
|
|
||||||
}
|
|
||||||
|
|
||||||
return u.userProvider.CreateUser(ctx, user)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserService) ReadUserBySessionToken(ctx context.Context, token string) (*models.User, error) {
|
|
||||||
session, err := u.sessionProvider.ReadSessionByToken(ctx, token)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return u.userProvider.ReadUserById(ctx, *session.UserId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserService) ReadUser(ctx context.Context, id int32) (*models.User, error) {
|
|
||||||
return u.userProvider.ReadUserById(ctx, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserService) ReadUserByEmail(ctx context.Context, email string) (*models.User, error) {
|
|
||||||
return u.userProvider.ReadUserByEmail(ctx, email)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserService) ReadUserByUsername(ctx context.Context, username string) (*models.User, error) {
|
|
||||||
return u.userProvider.ReadUserByUsername(ctx, username)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserService) UpdateUser(ctx context.Context, modifiedUser *models.User) error {
|
|
||||||
me := ctx.Value("user").(*models.User)
|
|
||||||
|
|
||||||
user, err := u.userProvider.ReadUserById(ctx, *modifiedUser.Id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
hasAccess := func() bool {
|
|
||||||
if me.Role.IsAdmin() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if me.Role.IsModerator() {
|
|
||||||
if !user.Role.AtMost(models.RoleParticipant) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if me.Role.IsParticipant() {
|
|
||||||
if me.Id != user.Id {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if modifiedUser.Username != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if modifiedUser.Email != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if modifiedUser.ExpiresAt != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if modifiedUser.Role != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if me.Role.IsSpectator() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}()
|
|
||||||
|
|
||||||
if !hasAccess {
|
|
||||||
return lib.ErrNoPermission
|
|
||||||
}
|
|
||||||
|
|
||||||
return u.userProvider.UpdateUser(ctx, user)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserService) DeleteUser(ctx context.Context, id int32) error {
|
|
||||||
me := ctx.Value("user").(*models.User)
|
|
||||||
|
|
||||||
if *me.Id == id || !me.Role.IsAdmin() {
|
|
||||||
return lib.ErrNoPermission
|
|
||||||
}
|
|
||||||
|
|
||||||
return u.userProvider.DeleteUser(ctx, id)
|
|
||||||
}
|
|
14
internal/sessions/delivery.go
Normal file
14
internal/sessions/delivery.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package sessions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
sessionv1 "git.sch9.ru/new_gate/ms-auth/pkg/go/gen/proto/session/v1"
|
||||||
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SessionHandlers interface {
|
||||||
|
Create(ctx context.Context, req *sessionv1.CreateSessionRequest) (*sessionv1.CreateSessionResponse, error)
|
||||||
|
Read(ctx context.Context, req *sessionv1.ReadSessionRequest) (*sessionv1.ReadSessionResponse, error)
|
||||||
|
Update(ctx context.Context, req *sessionv1.UpdateSessionRequest) (*emptypb.Empty, error)
|
||||||
|
Delete(ctx context.Context, req *sessionv1.DeleteSessionRequest) (*emptypb.Empty, error)
|
||||||
|
}
|
89
internal/sessions/delivery/grpc/handlers.go
Normal file
89
internal/sessions/delivery/grpc/handlers.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/internal/models"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/internal/sessions"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/internal/users"
|
||||||
|
sessionv1 "git.sch9.ru/new_gate/ms-auth/pkg/go/gen/proto/session/v1"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sessionHandlers struct {
|
||||||
|
sessionv1.UnimplementedSessionServiceServer
|
||||||
|
sessionUC sessions.UseCase
|
||||||
|
userUC users.UseCase
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSessionHandlers(gserver *grpc.Server, sessionUC sessions.UseCase, userUC users.UseCase) {
|
||||||
|
handlers := &sessionHandlers{
|
||||||
|
sessionUC: sessionUC,
|
||||||
|
userUC: userUC,
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionv1.RegisterSessionServiceServer(gserver, handlers)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sessionHandlers) Create(ctx context.Context, req *sessionv1.CreateSessionRequest) (*sessionv1.CreateSessionResponse, error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
user *models.User
|
||||||
|
)
|
||||||
|
|
||||||
|
handle := req.GetHandle()
|
||||||
|
password := req.GetPassword()
|
||||||
|
|
||||||
|
if utils.ValidUsername(handle) == nil {
|
||||||
|
user, err = s.userUC.ReadUserByUsername(ctx, req.GetHandle())
|
||||||
|
} else if utils.ValidEmail(handle) == nil {
|
||||||
|
user, err = s.userUC.ReadUserByEmail(ctx, handle)
|
||||||
|
} else {
|
||||||
|
return nil, utils.ErrBadHandleOrPassword
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = user.ComparePassword(password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := s.sessionUC.Create(ctx, *user.Id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
|
||||||
|
}
|
||||||
|
return &sessionv1.CreateSessionResponse{
|
||||||
|
Token: *token,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sessionHandlers) Read(ctx context.Context, req *sessionv1.ReadSessionRequest) (*sessionv1.ReadSessionResponse, error) {
|
||||||
|
id, err := s.sessionUC.Read(ctx, req.GetToken())
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
|
||||||
|
}
|
||||||
|
return &sessionv1.ReadSessionResponse{
|
||||||
|
UserId: *id,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sessionHandlers) Update(ctx context.Context, req *sessionv1.UpdateSessionRequest) (*emptypb.Empty, error) {
|
||||||
|
err := s.sessionUC.Update(ctx, req.GetToken())
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
|
||||||
|
}
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sessionHandlers) Delete(ctx context.Context, req *sessionv1.DeleteSessionRequest) (*emptypb.Empty, error) {
|
||||||
|
err := s.sessionUC.Delete(ctx, req.GetToken())
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
|
||||||
|
}
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
126
internal/sessions/repository/valkey_repository.go
Normal file
126
internal/sessions/repository/valkey_repository.go
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/config"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/internal/models"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
|
||||||
|
"github.com/valkey-io/valkey-go"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type valkeyRepository struct {
|
||||||
|
db valkey.Client
|
||||||
|
cfg config.Config
|
||||||
|
logger *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewValkeyRepository(db valkey.Client, cfg config.Config, logger *zap.Logger) *valkeyRepository {
|
||||||
|
return &valkeyRepository{
|
||||||
|
db: db,
|
||||||
|
cfg: cfg,
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sessionLifetime = time.Minute * 40
|
||||||
|
|
||||||
|
func (r *valkeyRepository) CreateSession(ctx context.Context, userId int32) error {
|
||||||
|
session := models.NewSession(userId)
|
||||||
|
|
||||||
|
resp := r.db.Do(ctx, r.db.
|
||||||
|
B().Set().
|
||||||
|
Key(string(*session.UserId)).
|
||||||
|
Value(*session.Id).
|
||||||
|
Nx().
|
||||||
|
Exat(time.Now().Add(sessionLifetime)).
|
||||||
|
Build(),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := resp.Error(); err != nil {
|
||||||
|
return utils.ErrInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *valkeyRepository) ReadSessionByToken(ctx context.Context, token string) (*models.Session, error) {
|
||||||
|
session, err := models.Parse(token, r.cfg.JWTSecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionRecord, err := r.ReadSessionByUserId(ctx, *session.UserId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if *session.Id != *sessionRecord.Id {
|
||||||
|
return nil, utils.ErrInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
return session, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *valkeyRepository) ReadSessionByUserId(ctx context.Context, userId int32) (*models.Session, error) {
|
||||||
|
resp := r.db.Do(ctx, r.db.B().Get().Key(string(userId)).Build())
|
||||||
|
if err := resp.Error(); err != nil {
|
||||||
|
return nil, utils.ErrInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := resp.ToString()
|
||||||
|
if err != nil {
|
||||||
|
return nil, utils.ErrInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
return &models.Session{
|
||||||
|
Id: &id,
|
||||||
|
UserId: &userId,
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *valkeyRepository) UpdateSession(ctx context.Context, session *models.Session) error {
|
||||||
|
resp := r.db.Do(ctx, r.db.
|
||||||
|
B().Set().
|
||||||
|
Key(string(*session.UserId)).
|
||||||
|
Value(*session.Id).
|
||||||
|
Xx().
|
||||||
|
Exat(time.Now().Add(sessionLifetime)).
|
||||||
|
Build(),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := resp.Error(); err != nil {
|
||||||
|
return utils.ErrInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *valkeyRepository) DeleteSessionByToken(ctx context.Context, token string) error {
|
||||||
|
session, err := models.Parse(token, r.cfg.JWTSecret)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.DeleteSessionByUserId(ctx, *session.UserId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *valkeyRepository) DeleteSessionByUserId(ctx context.Context, userId int32) error {
|
||||||
|
resp := r.db.Do(ctx, r.db.
|
||||||
|
B().Del().
|
||||||
|
Key(string(userId)).
|
||||||
|
Build(),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := resp.Error(); err != nil {
|
||||||
|
return utils.ErrInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
64
internal/sessions/repository/valkey_repository_test.go
Normal file
64
internal/sessions/repository/valkey_repository_test.go
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/config"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/internal/sessions"
|
||||||
|
vk "git.sch9.ru/new_gate/ms-auth/pkg/external/valkey"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/testcontainers/testcontainers-go/modules/valkey"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"log"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetupValkey() (sessions.ValkeyRepository, func()) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
valkeyContainer, err := valkey.Run(ctx,
|
||||||
|
"docker.io/valkey/valkey:7.2.5",
|
||||||
|
//valkey.WithSnapshotting(10, 1),
|
||||||
|
//valkey.WithLogLevel(valkey.LogLevelVerbose),
|
||||||
|
//valkey.WithConfigFile(filepath.Join("testdata", "valkey7.conf")),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dsn, err := valkeyContainer.ConnectionString(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := vk.NewValkeyClient(dsn)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := config.Config{
|
||||||
|
JWTSecret: "secret",
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionRepo := NewValkeyRepository(client, cfg, zap.NewNop())
|
||||||
|
|
||||||
|
closeVC := func() {
|
||||||
|
if err := valkeyContainer.Stop(ctx, nil); err != nil {
|
||||||
|
log.Printf("failed to terminate container: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sessionRepo, closeVC
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValkeyRepository_CreateSession(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
sessionRepo, closeVC := SetupValkey()
|
||||||
|
defer closeVC()
|
||||||
|
|
||||||
|
t.Run("valid session creation", func(t *testing.T) {
|
||||||
|
err := sessionRepo.CreateSession(context.Background(), 1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
}
|
10
internal/sessions/usecase.go
Normal file
10
internal/sessions/usecase.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package sessions
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
type UseCase interface {
|
||||||
|
Create(ctx context.Context, userId int32) (*string, error)
|
||||||
|
Read(ctx context.Context, token string) (*int32, error)
|
||||||
|
Update(ctx context.Context, token string) error
|
||||||
|
Delete(ctx context.Context, token string) error
|
||||||
|
}
|
71
internal/sessions/usecase/usecase.go
Normal file
71
internal/sessions/usecase/usecase.go
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
package usecase
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/config"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/internal/sessions"
|
||||||
|
)
|
||||||
|
|
||||||
|
type useCase struct {
|
||||||
|
sessionRepo sessions.ValkeyRepository
|
||||||
|
cfg config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUseCase(sessionRepo sessions.ValkeyRepository, cfg config.Config) *useCase {
|
||||||
|
return &useCase{
|
||||||
|
sessionRepo: sessionRepo,
|
||||||
|
cfg: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *useCase) Create(ctx context.Context, userId int32) (*string, error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
s.sessionRepo.CreateSession(ctx, userId) // FIXME
|
||||||
|
|
||||||
|
session, err := s.sessionRepo.ReadSessionByUserId(ctx, userId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := session.Token(s.cfg.JWTSecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *useCase) Read(ctx context.Context, token string) (*int32, error) {
|
||||||
|
session, err := s.sessionRepo.ReadSessionByToken(ctx, token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return session.UserId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *useCase) Update(ctx context.Context, token string) error {
|
||||||
|
session, err := s.sessionRepo.ReadSessionByToken(ctx, token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = s.sessionRepo.UpdateSession(ctx, session)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *useCase) Delete(ctx context.Context, token string) error {
|
||||||
|
session, err := s.sessionRepo.ReadSessionByToken(ctx, token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = s.sessionRepo.DeleteSessionByUserId(ctx, *session.UserId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
15
internal/sessions/valkey_repository.go
Normal file
15
internal/sessions/valkey_repository.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package sessions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/internal/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ValkeyRepository interface {
|
||||||
|
CreateSession(ctx context.Context, userId int32) error
|
||||||
|
ReadSessionByToken(ctx context.Context, token string) (*models.Session, error)
|
||||||
|
ReadSessionByUserId(ctx context.Context, userId int32) (*models.Session, error)
|
||||||
|
UpdateSession(ctx context.Context, session *models.Session) error
|
||||||
|
DeleteSessionByToken(ctx context.Context, token string) error
|
||||||
|
DeleteSessionByUserId(ctx context.Context, userId int32) error
|
||||||
|
}
|
|
@ -1,125 +0,0 @@
|
||||||
package storage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"git.sch9.ru/new_gate/ms-auth/internal/lib"
|
|
||||||
"git.sch9.ru/new_gate/ms-auth/internal/models"
|
|
||||||
"github.com/valkey-io/valkey-go"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SessionStorage struct {
|
|
||||||
db valkey.Client
|
|
||||||
cfg lib.Config
|
|
||||||
logger *zap.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSessionStorage(db valkey.Client, cfg lib.Config, logger *zap.Logger) *SessionStorage {
|
|
||||||
return &SessionStorage{
|
|
||||||
db: db,
|
|
||||||
cfg: cfg,
|
|
||||||
logger: logger,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const sessionLifetime = time.Minute * 40
|
|
||||||
|
|
||||||
func (storage *SessionStorage) CreateSession(ctx context.Context, userId int32) error {
|
|
||||||
session := models.NewSession(userId)
|
|
||||||
|
|
||||||
resp := storage.db.Do(ctx, storage.db.
|
|
||||||
B().Set().
|
|
||||||
Key(string(*session.UserId)).
|
|
||||||
Value(*session.Id).
|
|
||||||
Nx().
|
|
||||||
Exat(time.Now().Add(sessionLifetime)).
|
|
||||||
Build(),
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := resp.Error(); err != nil {
|
|
||||||
return lib.ErrInternal
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (storage *SessionStorage) ReadSessionByToken(ctx context.Context, token string) (*models.Session, error) {
|
|
||||||
session, err := models.Parse(token, storage.cfg.JWTSecret)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionRecord, err := storage.ReadSessionByUserId(ctx, *session.UserId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if *session.Id != *sessionRecord.Id {
|
|
||||||
return nil, lib.ErrInternal
|
|
||||||
}
|
|
||||||
|
|
||||||
return session, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (storage *SessionStorage) ReadSessionByUserId(ctx context.Context, userId int32) (*models.Session, error) {
|
|
||||||
resp := storage.db.Do(ctx, storage.db.B().Get().Key(string(userId)).Build())
|
|
||||||
if err := resp.Error(); err != nil {
|
|
||||||
return nil, lib.ErrInternal
|
|
||||||
}
|
|
||||||
|
|
||||||
id, err := resp.ToString()
|
|
||||||
if err != nil {
|
|
||||||
return nil, lib.ErrInternal
|
|
||||||
}
|
|
||||||
|
|
||||||
return &models.Session{
|
|
||||||
Id: &id,
|
|
||||||
UserId: &userId,
|
|
||||||
}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (storage *SessionStorage) UpdateSession(ctx context.Context, session *models.Session) error {
|
|
||||||
resp := storage.db.Do(ctx, storage.db.
|
|
||||||
B().Set().
|
|
||||||
Key(string(*session.UserId)).
|
|
||||||
Value(*session.Id).
|
|
||||||
Xx().
|
|
||||||
Exat(time.Now().Add(sessionLifetime)).
|
|
||||||
Build(),
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := resp.Error(); err != nil {
|
|
||||||
return lib.ErrInternal
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (storage *SessionStorage) DeleteSessionByToken(ctx context.Context, token string) error {
|
|
||||||
session, err := models.Parse(token, storage.cfg.JWTSecret)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = storage.DeleteSessionByUserId(ctx, *session.UserId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (storage *SessionStorage) DeleteSessionByUserId(ctx context.Context, userId int32) error {
|
|
||||||
resp := storage.db.Do(ctx, storage.db.
|
|
||||||
B().Del().
|
|
||||||
Key(string(userId)).
|
|
||||||
Build(),
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := resp.Error(); err != nil {
|
|
||||||
return lib.ErrInternal
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
package transport
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ReqWithToken interface {
|
|
||||||
GetToken() string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AuthServer) AuthInterceptor() grpc.UnaryServerInterceptor {
|
|
||||||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
|
||||||
reqWithToken, ok := req.(ReqWithToken)
|
|
||||||
if !ok {
|
|
||||||
return handler(ctx, req)
|
|
||||||
}
|
|
||||||
token := reqWithToken.GetToken()
|
|
||||||
|
|
||||||
userId, err := s.sessionService.Read(ctx, token)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.Unauthenticated, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
user, err := s.userService.ReadUser(ctx, *userId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.Unauthenticated, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, "user", user)
|
|
||||||
|
|
||||||
return handler(ctx, req)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,93 +0,0 @@
|
||||||
package transport
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"git.sch9.ru/new_gate/ms-auth/internal/models"
|
|
||||||
sessionv1 "git.sch9.ru/new_gate/ms-auth/pkg/go/gen/proto/session/v1"
|
|
||||||
userv1 "git.sch9.ru/new_gate/ms-auth/pkg/go/gen/proto/user/v1"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SessionService interface {
|
|
||||||
Create(ctx context.Context, handle, password string) (*string, error)
|
|
||||||
Read(ctx context.Context, token string) (*int32, error)
|
|
||||||
Update(ctx context.Context, token string) error
|
|
||||||
Delete(ctx context.Context, token string) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserService interface {
|
|
||||||
CreateUser(ctx context.Context, user *models.User) (int32, error)
|
|
||||||
ReadUser(ctx context.Context, id int32) (*models.User, error)
|
|
||||||
UpdateUser(ctx context.Context, user *models.User) error
|
|
||||||
DeleteUser(ctx context.Context, id int32) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type AuthServer struct {
|
|
||||||
sessionv1.UnimplementedSessionServiceServer
|
|
||||||
sessionService SessionService
|
|
||||||
|
|
||||||
userv1.UnimplementedUserServiceServer
|
|
||||||
userService UserService
|
|
||||||
|
|
||||||
GRPCServer *grpc.Server
|
|
||||||
logger *zap.Logger
|
|
||||||
|
|
||||||
ln net.Listener
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAuthServer(
|
|
||||||
sessionService SessionService,
|
|
||||||
userService UserService,
|
|
||||||
logger *zap.Logger,
|
|
||||||
ln net.Listener,
|
|
||||||
) *AuthServer {
|
|
||||||
server := &AuthServer{
|
|
||||||
sessionService: sessionService,
|
|
||||||
userService: userService,
|
|
||||||
logger: logger,
|
|
||||||
ln: ln,
|
|
||||||
}
|
|
||||||
|
|
||||||
grpcServer := grpc.NewServer(
|
|
||||||
grpc.UnaryInterceptor(server.AuthInterceptor()),
|
|
||||||
)
|
|
||||||
|
|
||||||
server.GRPCServer = grpcServer
|
|
||||||
|
|
||||||
sessionv1.RegisterSessionServiceServer(server.GRPCServer, server)
|
|
||||||
|
|
||||||
return server
|
|
||||||
}
|
|
||||||
|
|
||||||
func AsTimeP(t *timestamppb.Timestamp) *time.Time {
|
|
||||||
if t == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
tt := t.AsTime()
|
|
||||||
return &tt
|
|
||||||
}
|
|
||||||
|
|
||||||
func AsMRoleP(v userv1.Role) *models.Role {
|
|
||||||
vv := models.Role(v.Number())
|
|
||||||
return &vv
|
|
||||||
}
|
|
||||||
|
|
||||||
func AsTimestampP(t *time.Time) *timestamppb.Timestamp {
|
|
||||||
if t == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return timestamppb.New(*t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func AsRoleP(r *models.Role) *userv1.Role {
|
|
||||||
if r == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
rr := userv1.Role(*r)
|
|
||||||
return &rr
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
package transport
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
sessionv1 "git.sch9.ru/new_gate/ms-auth/pkg/go/gen/proto/session/v1"
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
"google.golang.org/protobuf/types/known/emptypb"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *AuthServer) Create(ctx context.Context, req *sessionv1.CreateSessionRequest) (*sessionv1.CreateSessionResponse, error) {
|
|
||||||
token, err := s.sessionService.Create(ctx, req.GetHandle(), req.GetPassword())
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
|
|
||||||
}
|
|
||||||
return &sessionv1.CreateSessionResponse{
|
|
||||||
Token: *token,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AuthServer) Read(ctx context.Context, req *sessionv1.ReadSessionRequest) (*sessionv1.ReadSessionResponse, error) {
|
|
||||||
id, err := s.sessionService.Read(ctx, req.GetToken())
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
|
|
||||||
}
|
|
||||||
return &sessionv1.ReadSessionResponse{
|
|
||||||
UserId: *id,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AuthServer) Update(ctx context.Context, req *sessionv1.UpdateSessionRequest) (*emptypb.Empty, error) {
|
|
||||||
err := s.sessionService.Update(ctx, req.GetToken())
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
|
|
||||||
}
|
|
||||||
return &emptypb.Empty{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AuthServer) Delete(ctx context.Context, req *sessionv1.DeleteSessionRequest) (*emptypb.Empty, error) {
|
|
||||||
err := s.sessionService.Delete(ctx, req.GetToken())
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
|
|
||||||
}
|
|
||||||
return &emptypb.Empty{}, nil
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
package transport
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"git.sch9.ru/new_gate/ms-auth/internal/lib"
|
|
||||||
"git.sch9.ru/new_gate/ms-auth/internal/models"
|
|
||||||
userv1 "git.sch9.ru/new_gate/ms-auth/pkg/go/gen/proto/user/v1"
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
"google.golang.org/protobuf/types/known/emptypb"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *AuthServer) CreateUser(ctx context.Context, req *userv1.CreateUserRequest) (*userv1.CreateUserResponse, error) {
|
|
||||||
user := req.GetUser()
|
|
||||||
if user == nil {
|
|
||||||
return nil, status.Errorf(codes.Unknown, "") // FIXME
|
|
||||||
}
|
|
||||||
id, err := s.userService.CreateUser(
|
|
||||||
ctx,
|
|
||||||
&models.User{
|
|
||||||
Username: lib.AsStringP(user.GetUsername()),
|
|
||||||
Password: lib.AsStringP(user.GetPassword()),
|
|
||||||
Email: nil,
|
|
||||||
ExpiresAt: AsTimeP(user.ExpiresAt),
|
|
||||||
Role: AsMRoleP(user.GetRole()),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
|
|
||||||
}
|
|
||||||
|
|
||||||
return &userv1.CreateUserResponse{
|
|
||||||
Id: id,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AuthServer) ReadUser(ctx context.Context, req *userv1.ReadUserRequest) (*userv1.ReadUserResponse, error) {
|
|
||||||
user, err := s.userService.ReadUser(
|
|
||||||
ctx,
|
|
||||||
req.GetId(),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
|
|
||||||
}
|
|
||||||
|
|
||||||
return &userv1.ReadUserResponse{
|
|
||||||
User: &userv1.ReadUserResponse_User{
|
|
||||||
Id: *user.Id,
|
|
||||||
Username: *user.Username,
|
|
||||||
ExpiresAt: AsTimestampP(user.ExpiresAt),
|
|
||||||
CreatedAt: AsTimestampP(user.CreatedAt),
|
|
||||||
Role: *AsRoleP(user.Role),
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AuthServer) UpdateUser(ctx context.Context, req *userv1.UpdateUserRequest) (*emptypb.Empty, error) {
|
|
||||||
user := req.GetUser()
|
|
||||||
if user == nil {
|
|
||||||
return nil, status.Errorf(codes.Unknown, "") // FIXME
|
|
||||||
}
|
|
||||||
err := s.userService.UpdateUser(
|
|
||||||
ctx,
|
|
||||||
&models.User{
|
|
||||||
Id: lib.AsInt32P(user.GetId()),
|
|
||||||
Username: lib.AsStringP(user.GetUsername()),
|
|
||||||
Password: lib.AsStringP(user.GetPassword()),
|
|
||||||
Email: nil,
|
|
||||||
ExpiresAt: AsTimeP(user.ExpiresAt),
|
|
||||||
Role: AsMRoleP(user.GetRole()),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
|
|
||||||
}
|
|
||||||
return &emptypb.Empty{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AuthServer) DeleteUser(ctx context.Context, req *userv1.DeleteUserRequest) (*emptypb.Empty, error) {
|
|
||||||
err := s.userService.DeleteUser(
|
|
||||||
ctx,
|
|
||||||
req.GetId(),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
|
|
||||||
}
|
|
||||||
return &emptypb.Empty{}, nil
|
|
||||||
}
|
|
14
internal/users/delivery.go
Normal file
14
internal/users/delivery.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package users
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
userv1 "git.sch9.ru/new_gate/ms-auth/pkg/go/gen/proto/user/v1"
|
||||||
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserHandlers interface {
|
||||||
|
CreateUser(ctx context.Context, req *userv1.CreateUserRequest) (*userv1.CreateUserResponse, error)
|
||||||
|
ReadUser(ctx context.Context, req *userv1.ReadUserRequest) (*userv1.ReadUserResponse, error)
|
||||||
|
UpdateUser(ctx context.Context, req *userv1.UpdateUserRequest) (*emptypb.Empty, error)
|
||||||
|
DeleteUser(ctx context.Context, req *userv1.DeleteUserRequest) (*emptypb.Empty, error)
|
||||||
|
}
|
116
internal/users/delivery/grpc/handlers.go
Normal file
116
internal/users/delivery/grpc/handlers.go
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
package grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/internal/models"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/internal/users"
|
||||||
|
userv1 "git.sch9.ru/new_gate/ms-auth/pkg/go/gen/proto/user/v1"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type userHandlers struct {
|
||||||
|
userv1.UnimplementedUserServiceServer
|
||||||
|
userUC users.UseCase
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserHandlers(gserver *grpc.Server, userUC users.UseCase) {
|
||||||
|
handlers := &userHandlers{
|
||||||
|
userUC: userUC,
|
||||||
|
}
|
||||||
|
|
||||||
|
userv1.RegisterUserServiceServer(gserver, handlers)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *userHandlers) CreateUser(ctx context.Context, req *userv1.CreateUserRequest) (*userv1.CreateUserResponse, error) {
|
||||||
|
user := req.GetUser()
|
||||||
|
if user == nil {
|
||||||
|
return nil, status.Errorf(codes.Unknown, "") // FIXME
|
||||||
|
}
|
||||||
|
id, err := h.userUC.CreateUser(
|
||||||
|
ctx,
|
||||||
|
&models.User{
|
||||||
|
Username: utils.AsStringP(user.GetUsername()),
|
||||||
|
Password: utils.AsStringP(user.GetPassword()),
|
||||||
|
Email: nil,
|
||||||
|
ExpiresAt: utils.TimeP(user.ExpiresAt),
|
||||||
|
Role: AsMRoleP(user.GetRole()),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
|
||||||
|
}
|
||||||
|
|
||||||
|
return &userv1.CreateUserResponse{
|
||||||
|
Id: id,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *userHandlers) ReadUser(ctx context.Context, req *userv1.ReadUserRequest) (*userv1.ReadUserResponse, error) {
|
||||||
|
user, err := h.userUC.ReadUser(
|
||||||
|
ctx,
|
||||||
|
req.GetId(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
|
||||||
|
}
|
||||||
|
|
||||||
|
return &userv1.ReadUserResponse{
|
||||||
|
User: &userv1.ReadUserResponse_User{
|
||||||
|
Id: *user.Id,
|
||||||
|
Username: *user.Username,
|
||||||
|
ExpiresAt: utils.TimestampP(user.ExpiresAt),
|
||||||
|
CreatedAt: utils.TimestampP(user.CreatedAt),
|
||||||
|
Role: *AsRoleP(user.Role),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *userHandlers) UpdateUser(ctx context.Context, req *userv1.UpdateUserRequest) (*emptypb.Empty, error) {
|
||||||
|
user := req.GetUser()
|
||||||
|
if user == nil {
|
||||||
|
return nil, status.Errorf(codes.Unknown, "") // FIXME
|
||||||
|
}
|
||||||
|
err := h.userUC.UpdateUser(
|
||||||
|
ctx,
|
||||||
|
&models.User{
|
||||||
|
Id: utils.AsInt32P(user.GetId()),
|
||||||
|
Username: utils.AsStringP(user.GetUsername()),
|
||||||
|
Password: utils.AsStringP(user.GetPassword()),
|
||||||
|
Email: nil,
|
||||||
|
ExpiresAt: utils.TimeP(user.ExpiresAt),
|
||||||
|
Role: AsMRoleP(user.GetRole()),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
|
||||||
|
}
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *userHandlers) DeleteUser(ctx context.Context, req *userv1.DeleteUserRequest) (*emptypb.Empty, error) {
|
||||||
|
err := h.userUC.DeleteUser(
|
||||||
|
ctx,
|
||||||
|
req.GetId(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Unknown, err.Error()) // FIXME
|
||||||
|
}
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func AsMRoleP(v userv1.Role) *models.Role {
|
||||||
|
vv := models.Role(v.Number())
|
||||||
|
return &vv
|
||||||
|
}
|
||||||
|
|
||||||
|
func AsRoleP(r *models.Role) *userv1.Role {
|
||||||
|
if r == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rr := userv1.Role(*r)
|
||||||
|
return &rr
|
||||||
|
}
|
30
internal/users/delivery/grpc/token_interceptor.go
Normal file
30
internal/users/delivery/grpc/token_interceptor.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/internal/sessions"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TokenInterceptor(s sessions.UseCase) grpc.UnaryServerInterceptor {
|
||||||
|
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||||
|
md, ok := metadata.FromIncomingContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return handler(ctx, req)
|
||||||
|
}
|
||||||
|
auth := md.Get("authorization")
|
||||||
|
if len(auth) == 0 {
|
||||||
|
return handler(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
userId, err := s.Read(ctx, auth[0])
|
||||||
|
if err != nil {
|
||||||
|
return handler(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = context.WithValue(ctx, "userId", *userId)
|
||||||
|
|
||||||
|
return handler(ctx, req)
|
||||||
|
}
|
||||||
|
}
|
15
internal/users/pg_repository.go
Normal file
15
internal/users/pg_repository.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package users
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/internal/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PgRepository interface {
|
||||||
|
CreateUser(ctx context.Context, user *models.User) (int32, error)
|
||||||
|
ReadUserByEmail(ctx context.Context, email string) (*models.User, error)
|
||||||
|
ReadUserByUsername(ctx context.Context, username string) (*models.User, error)
|
||||||
|
ReadUserById(ctx context.Context, id int32) (*models.User, error)
|
||||||
|
UpdateUser(ctx context.Context, user *models.User) error
|
||||||
|
DeleteUser(ctx context.Context, id int32) error
|
||||||
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
package storage
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"git.sch9.ru/new_gate/ms-auth/internal/lib"
|
|
||||||
"git.sch9.ru/new_gate/ms-auth/internal/models"
|
"git.sch9.ru/new_gate/ms-auth/internal/models"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
|
||||||
"github.com/jackc/pgerrcode"
|
"github.com/jackc/pgerrcode"
|
||||||
"github.com/jackc/pgx/v5/pgconn"
|
"github.com/jackc/pgx/v5/pgconn"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
|
@ -12,13 +12,13 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserStorage struct {
|
type UsersRepository struct {
|
||||||
db *sqlx.DB
|
db *sqlx.DB
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUserStorage(db *sqlx.DB, logger *zap.Logger) *UserStorage {
|
func NewUserRepository(db *sqlx.DB, logger *zap.Logger) *UsersRepository {
|
||||||
return &UserStorage{
|
return &UsersRepository{
|
||||||
db: db,
|
db: db,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,14 @@ func NewUserStorage(db *sqlx.DB, logger *zap.Logger) *UserStorage {
|
||||||
|
|
||||||
const year = time.Hour * 24 * 365
|
const year = time.Hour * 24 * 365
|
||||||
|
|
||||||
func (storage *UserStorage) CreateUser(ctx context.Context, user *models.User) (int32, error) {
|
const createUser = `
|
||||||
|
INSERT INTO users
|
||||||
|
(username, hashed_pwd, email, expires_at, role)
|
||||||
|
VALUES (?, ?, ?, ?, ?)
|
||||||
|
RETURNING id
|
||||||
|
`
|
||||||
|
|
||||||
|
func (storage *UsersRepository) CreateUser(ctx context.Context, user *models.User) (int32, error) {
|
||||||
if err := user.ValidUsername(); err != nil {
|
if err := user.ValidUsername(); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -39,16 +46,13 @@ func (storage *UserStorage) CreateUser(ctx context.Context, user *models.User) (
|
||||||
if err := user.ValidRole(); err != nil {
|
if err := user.ValidRole(); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if err := user.HashPassword(); err != nil {
|
if err := user.HashPassword(); err != nil { // FIXME: get rid of mutation
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
query := storage.db.Rebind(`
|
user.ExpiresAt = utils.AsTimeP(time.Now().Add(100 * year)) // FIXME: get rid of mutation
|
||||||
INSERT INTO users
|
|
||||||
(username, hashed_pwd, email, expires_at, role)
|
query := storage.db.Rebind(createUser)
|
||||||
VALUES (?, ?, ?, ?, ?)
|
|
||||||
RETURNING id
|
|
||||||
`)
|
|
||||||
|
|
||||||
rows, err := storage.db.QueryxContext(
|
rows, err := storage.db.QueryxContext(
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -56,7 +60,7 @@ RETURNING id
|
||||||
user.Username,
|
user.Username,
|
||||||
user.Password,
|
user.Password,
|
||||||
user.Email,
|
user.Email,
|
||||||
time.Now().Add(100*year),
|
user.ExpiresAt,
|
||||||
user.Role,
|
user.Role,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -65,7 +69,8 @@ RETURNING id
|
||||||
|
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
var id int32
|
var id int32
|
||||||
err = rows.StructScan(&id)
|
rows.Next()
|
||||||
|
err = rows.Scan(&id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, storage.handlePgErr(err)
|
return 0, storage.handlePgErr(err)
|
||||||
}
|
}
|
||||||
|
@ -73,9 +78,11 @@ RETURNING id
|
||||||
return id, nil
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (storage *UserStorage) ReadUserByEmail(ctx context.Context, email string) (*models.User, error) {
|
const readUserByEmail = "SELECT * from users WHERE email=? LIMIT 1"
|
||||||
|
|
||||||
|
func (storage *UsersRepository) ReadUserByEmail(ctx context.Context, email string) (*models.User, error) {
|
||||||
var user models.User
|
var user models.User
|
||||||
query := storage.db.Rebind("SELECT * from users WHERE email=? LIMIT 1")
|
query := storage.db.Rebind(readUserByEmail)
|
||||||
err := storage.db.GetContext(ctx, &user, query, email)
|
err := storage.db.GetContext(ctx, &user, query, email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, storage.handlePgErr(err)
|
return nil, storage.handlePgErr(err)
|
||||||
|
@ -83,9 +90,11 @@ func (storage *UserStorage) ReadUserByEmail(ctx context.Context, email string) (
|
||||||
return &user, nil
|
return &user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (storage *UserStorage) ReadUserByUsername(ctx context.Context, username string) (*models.User, error) {
|
const readUserByUsername = "SELECT * from users WHERE username=? LIMIT 1"
|
||||||
|
|
||||||
|
func (storage *UsersRepository) ReadUserByUsername(ctx context.Context, username string) (*models.User, error) {
|
||||||
var user models.User
|
var user models.User
|
||||||
query := storage.db.Rebind("SELECT * from users WHERE username=? LIMIT 1")
|
query := storage.db.Rebind(readUserByUsername)
|
||||||
err := storage.db.GetContext(ctx, &user, query, username)
|
err := storage.db.GetContext(ctx, &user, query, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, storage.handlePgErr(err)
|
return nil, storage.handlePgErr(err)
|
||||||
|
@ -93,9 +102,11 @@ func (storage *UserStorage) ReadUserByUsername(ctx context.Context, username str
|
||||||
return &user, nil
|
return &user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (storage *UserStorage) ReadUserById(ctx context.Context, id int32) (*models.User, error) {
|
const readUserById = "SELECT * from users WHERE id=? LIMIT 1"
|
||||||
|
|
||||||
|
func (storage *UsersRepository) ReadUserById(ctx context.Context, id int32) (*models.User, error) {
|
||||||
var user models.User
|
var user models.User
|
||||||
query := storage.db.Rebind("SELECT * from users WHERE id=? LIMIT 1")
|
query := storage.db.Rebind(readUserById)
|
||||||
err := storage.db.GetContext(ctx, &user, query, id)
|
err := storage.db.GetContext(ctx, &user, query, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, storage.handlePgErr(err)
|
return nil, storage.handlePgErr(err)
|
||||||
|
@ -103,7 +114,17 @@ func (storage *UserStorage) ReadUserById(ctx context.Context, id int32) (*models
|
||||||
return &user, nil
|
return &user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (storage *UserStorage) UpdateUser(ctx context.Context, user *models.User) error {
|
const updateUser = `
|
||||||
|
UPDATE users
|
||||||
|
SET username = COALESCE(?, username),
|
||||||
|
hashed_pwd = COALESCE(?, hashed_pwd),
|
||||||
|
email = COALESCE(?, email),
|
||||||
|
expires_at = COALESCE(?, expires_at),
|
||||||
|
role = COALESCE(?, role)
|
||||||
|
WHERE id = ?
|
||||||
|
`
|
||||||
|
|
||||||
|
func (storage *UsersRepository) UpdateUser(ctx context.Context, user *models.User) error {
|
||||||
var err error
|
var err error
|
||||||
if user.Username != nil {
|
if user.Username != nil {
|
||||||
if err = user.ValidUsername(); err != nil {
|
if err = user.ValidUsername(); err != nil {
|
||||||
|
@ -119,7 +140,7 @@ func (storage *UserStorage) UpdateUser(ctx context.Context, user *models.User) e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if user.Email != nil {
|
if user.Email != nil {
|
||||||
if err = lib.ValidEmail(*user.Email); err != nil {
|
if err = utils.ValidEmail(*user.Email); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,14 +150,7 @@ func (storage *UserStorage) UpdateUser(ctx context.Context, user *models.User) e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query := storage.db.Rebind(`
|
query := storage.db.Rebind(updateUser)
|
||||||
UPDATE users
|
|
||||||
SET username = COALESCE(?, username),
|
|
||||||
hashed_pwd = COALESCE(?, hashed_pwd),
|
|
||||||
email = COALESCE(?, email),
|
|
||||||
expires_at = COALESCE(?, expires_at),
|
|
||||||
role = COALESCE(?, role)
|
|
||||||
WHERE id = ?`)
|
|
||||||
|
|
||||||
_, err = storage.db.ExecContext(
|
_, err = storage.db.ExecContext(
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -154,8 +168,11 @@ WHERE id = ?`)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (storage *UserStorage) DeleteUser(ctx context.Context, id int32) error {
|
|
||||||
query := storage.db.Rebind("UPDATE users SET expired_at=now() WHERE id = ?")
|
const deleteUser = "UPDATE users SET expired_at=now() WHERE id = ?"
|
||||||
|
|
||||||
|
func (storage *UsersRepository) DeleteUser(ctx context.Context, id int32) error {
|
||||||
|
query := storage.db.Rebind(deleteUser)
|
||||||
_, err := storage.db.ExecContext(ctx, query, id)
|
_, err := storage.db.ExecContext(ctx, query, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return storage.handlePgErr(err)
|
return storage.handlePgErr(err)
|
||||||
|
@ -164,15 +181,15 @@ func (storage *UserStorage) DeleteUser(ctx context.Context, id int32) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (storage *UserStorage) handlePgErr(err error) error {
|
func (storage *UsersRepository) handlePgErr(err error) error {
|
||||||
var pgErr *pgconn.PgError
|
var pgErr *pgconn.PgError
|
||||||
if !errors.As(err, &pgErr) {
|
if !errors.As(err, &pgErr) {
|
||||||
storage.logger.DPanic("unexpected error from postgres", zap.String("err", err.Error()))
|
storage.logger.DPanic("unexpected error from postgres", zap.String("err", err.Error()))
|
||||||
return lib.ErrUnexpected
|
return utils.ErrUnexpected
|
||||||
}
|
}
|
||||||
if pgerrcode.IsIntegrityConstraintViolation(pgErr.Code) {
|
if pgerrcode.IsIntegrityConstraintViolation(pgErr.Code) {
|
||||||
return errors.New("unique key violation") // FIXME
|
return errors.New("unique key violation") // FIXME
|
||||||
}
|
}
|
||||||
storage.logger.DPanic("unexpected internal error from postgres", zap.String("err", err.Error()))
|
storage.logger.DPanic("unexpected internal error from postgres", zap.String("err", err.Error()))
|
||||||
return lib.ErrInternal
|
return utils.ErrInternal
|
||||||
}
|
}
|
146
internal/users/repository/pg_repository_test.go
Normal file
146
internal/users/repository/pg_repository_test.go
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql/driver"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
|
||||||
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/internal/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUsersRepository_CreateUser(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
sqlxDB := sqlx.NewDb(db, "sqlmock")
|
||||||
|
defer sqlxDB.Close()
|
||||||
|
|
||||||
|
userRepo := NewUserRepository(sqlxDB, zap.NewNop())
|
||||||
|
|
||||||
|
t.Run("valid user creation", func(t *testing.T) {
|
||||||
|
rows := sqlmock.NewRows([]string{"id"}).AddRow(1)
|
||||||
|
|
||||||
|
user := &models.User{
|
||||||
|
Username: utils.AsStringP("testuser"),
|
||||||
|
Password: utils.AsStringP("testpassword"),
|
||||||
|
Email: utils.AsStringP("test@example.com"),
|
||||||
|
Role: AsRoleP(models.RoleAdmin),
|
||||||
|
}
|
||||||
|
|
||||||
|
mock.ExpectQuery(sqlxDB.Rebind(createUser)).WithArgs(
|
||||||
|
user.Username,
|
||||||
|
AnyString{},
|
||||||
|
user.Email,
|
||||||
|
AnyTime{},
|
||||||
|
user.Role,
|
||||||
|
).WillReturnRows(rows)
|
||||||
|
|
||||||
|
_, err = userRepo.CreateUser(context.Background(), user)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: add more tests
|
||||||
|
// invalid username
|
||||||
|
// invalid password
|
||||||
|
// invalid email
|
||||||
|
// invalid role
|
||||||
|
// password hashing error
|
||||||
|
// database query error
|
||||||
|
// database scan error
|
||||||
|
// etc
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUsersRepository_UpdateUser(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
sqlxDB := sqlx.NewDb(db, "sqlmock")
|
||||||
|
defer sqlxDB.Close()
|
||||||
|
|
||||||
|
userRepo := NewUserRepository(sqlxDB, zap.NewNop())
|
||||||
|
|
||||||
|
t.Run("valid user update", func(t *testing.T) {
|
||||||
|
user := &models.User{
|
||||||
|
Id: utils.AsInt32P(1),
|
||||||
|
Username: utils.AsStringP("testuser"),
|
||||||
|
Password: utils.AsStringP("testpassword"),
|
||||||
|
Email: utils.AsStringP("test@example.com"),
|
||||||
|
ExpiresAt: utils.AsTimeP(time.Now().Add(1 * time.Hour)),
|
||||||
|
Role: AsRoleP(models.RoleAdmin),
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mock.ExpectExec(sqlxDB.Rebind(updateUser)).
|
||||||
|
WithArgs(
|
||||||
|
*user.Username,
|
||||||
|
AnyString{},
|
||||||
|
*user.Email,
|
||||||
|
*user.ExpiresAt,
|
||||||
|
*user.Role,
|
||||||
|
*user.Id,
|
||||||
|
).WillReturnResult(sqlmock.NewResult(1, 1))
|
||||||
|
|
||||||
|
err = userRepo.UpdateUser(context.Background(), user)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: add more tests
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUsersRepository_DeleteUser(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
sqlxDB := sqlx.NewDb(db, "sqlmock")
|
||||||
|
defer sqlxDB.Close()
|
||||||
|
|
||||||
|
userRepo := NewUserRepository(sqlxDB, zap.NewNop())
|
||||||
|
|
||||||
|
t.Run("valid user deletion", func(t *testing.T) {
|
||||||
|
user := &models.User{
|
||||||
|
Id: utils.AsInt32P(1),
|
||||||
|
}
|
||||||
|
|
||||||
|
mock.ExpectExec(sqlxDB.Rebind(deleteUser)).WithArgs(*user.Id).WillReturnResult(sqlmock.NewResult(1, 1))
|
||||||
|
|
||||||
|
err = userRepo.DeleteUser(context.Background(), *user.Id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: add more tests
|
||||||
|
}
|
||||||
|
|
||||||
|
func AsRoleP(r models.Role) *models.Role {
|
||||||
|
return &r
|
||||||
|
}
|
||||||
|
|
||||||
|
type AnyTime struct{}
|
||||||
|
|
||||||
|
// Match satisfies sqlmock.Argument interface
|
||||||
|
func (a AnyTime) Match(v driver.Value) bool {
|
||||||
|
_, ok := v.(time.Time)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
type AnyString struct{}
|
||||||
|
|
||||||
|
func (a AnyString) Match(v driver.Value) bool {
|
||||||
|
_, ok := v.(string)
|
||||||
|
return ok
|
||||||
|
}
|
16
internal/users/usecase.go
Normal file
16
internal/users/usecase.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package users
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/internal/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UseCase interface {
|
||||||
|
CreateUser(ctx context.Context, user *models.User) (int32, error)
|
||||||
|
ReadUserBySessionToken(ctx context.Context, token string) (*models.User, error)
|
||||||
|
ReadUser(ctx context.Context, id int32) (*models.User, error)
|
||||||
|
ReadUserByEmail(ctx context.Context, email string) (*models.User, error)
|
||||||
|
ReadUserByUsername(ctx context.Context, username string) (*models.User, error)
|
||||||
|
UpdateUser(ctx context.Context, modifiedUser *models.User) error
|
||||||
|
DeleteUser(ctx context.Context, id int32) error
|
||||||
|
}
|
149
internal/users/usecase/usecase.go
Normal file
149
internal/users/usecase/usecase.go
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
package usecase
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/config"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/internal/models"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/internal/sessions"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/internal/users"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type useCase struct {
|
||||||
|
userRepo users.PgRepository
|
||||||
|
sessionProvider sessions.ValkeyRepository
|
||||||
|
cfg config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUseCase(
|
||||||
|
userRepo users.PgRepository,
|
||||||
|
sessionRepo sessions.ValkeyRepository,
|
||||||
|
cfg config.Config,
|
||||||
|
) *useCase {
|
||||||
|
return &useCase{
|
||||||
|
userRepo: userRepo,
|
||||||
|
sessionProvider: sessionRepo,
|
||||||
|
cfg: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *useCase) CreateUser(ctx context.Context, user *models.User) (int32, error) {
|
||||||
|
meId, ok := ctx.Value("userId").(*int32)
|
||||||
|
if !ok {
|
||||||
|
return 0, utils.ErrNoPermission
|
||||||
|
}
|
||||||
|
|
||||||
|
me, err := u.ReadUser(ctx, *meId)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch *me.Role {
|
||||||
|
case models.RoleAdmin:
|
||||||
|
break
|
||||||
|
case models.RoleModerator:
|
||||||
|
if !user.Role.AtMost(models.RoleParticipant) {
|
||||||
|
return 0, utils.ErrNoPermission
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return 0, utils.ErrNoPermission
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.userRepo.CreateUser(ctx, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *useCase) ReadUserBySessionToken(ctx context.Context, token string) (*models.User, error) {
|
||||||
|
session, err := u.sessionProvider.ReadSessionByToken(ctx, token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.userRepo.ReadUserById(ctx, *session.UserId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *useCase) ReadUser(ctx context.Context, id int32) (*models.User, error) {
|
||||||
|
return u.userRepo.ReadUserById(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *useCase) ReadUserByEmail(ctx context.Context, email string) (*models.User, error) {
|
||||||
|
return u.userRepo.ReadUserByEmail(ctx, email)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *useCase) ReadUserByUsername(ctx context.Context, username string) (*models.User, error) {
|
||||||
|
return u.userRepo.ReadUserByUsername(ctx, username)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *useCase) UpdateUser(ctx context.Context, modifiedUser *models.User) error {
|
||||||
|
meId, ok := ctx.Value("userId").(*int32)
|
||||||
|
if !ok {
|
||||||
|
return utils.ErrNoPermission
|
||||||
|
}
|
||||||
|
|
||||||
|
me, err := u.ReadUser(ctx, *meId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := u.userRepo.ReadUserById(ctx, *modifiedUser.Id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAccess := func() bool {
|
||||||
|
if me.Role.IsAdmin() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if me.Role.IsModerator() {
|
||||||
|
if !user.Role.AtMost(models.RoleParticipant) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if me.Role.IsParticipant() {
|
||||||
|
if me.Id != user.Id {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if modifiedUser.Username != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if modifiedUser.Email != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if modifiedUser.ExpiresAt != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if modifiedUser.Role != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if me.Role.IsSpectator() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}()
|
||||||
|
|
||||||
|
if !hasAccess {
|
||||||
|
return utils.ErrNoPermission
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.userRepo.UpdateUser(ctx, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *useCase) DeleteUser(ctx context.Context, id int32) error {
|
||||||
|
userId, ok := ctx.Value("userId").(*int32)
|
||||||
|
if !ok {
|
||||||
|
return utils.ErrNoPermission
|
||||||
|
}
|
||||||
|
|
||||||
|
me, err := u.ReadUser(ctx, *userId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if *me.Id == id || !me.Role.IsAdmin() {
|
||||||
|
return utils.ErrNoPermission
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.userRepo.DeleteUser(ctx, id)
|
||||||
|
}
|
47
main.go
47
main.go
|
@ -2,15 +2,20 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.sch9.ru/new_gate/ms-auth/internal/lib"
|
"git.sch9.ru/new_gate/ms-auth/config"
|
||||||
"git.sch9.ru/new_gate/ms-auth/internal/services"
|
sessionsDelivery "git.sch9.ru/new_gate/ms-auth/internal/sessions/delivery/grpc"
|
||||||
"git.sch9.ru/new_gate/ms-auth/internal/storage"
|
sessionsRepository "git.sch9.ru/new_gate/ms-auth/internal/sessions/repository"
|
||||||
"git.sch9.ru/new_gate/ms-auth/internal/transport"
|
sessionsUseCase "git.sch9.ru/new_gate/ms-auth/internal/sessions/usecase"
|
||||||
|
usersDelivery "git.sch9.ru/new_gate/ms-auth/internal/users/delivery/grpc"
|
||||||
|
usersRepository "git.sch9.ru/new_gate/ms-auth/internal/users/repository"
|
||||||
|
usersUseCase "git.sch9.ru/new_gate/ms-auth/internal/users/usecase"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/pkg/external/postgres"
|
||||||
|
"git.sch9.ru/new_gate/ms-auth/pkg/external/valkey"
|
||||||
"github.com/ilyakaznacheev/cleanenv"
|
"github.com/ilyakaznacheev/cleanenv"
|
||||||
_ "github.com/jackc/pgx/v5/stdlib"
|
_ "github.com/jackc/pgx/v5/stdlib"
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
"github.com/valkey-io/valkey-go"
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/reflection"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
@ -18,7 +23,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var cfg lib.Config
|
var cfg config.Config
|
||||||
err := cleanenv.ReadConfig(".env", &cfg)
|
err := cleanenv.ReadConfig(".env", &cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("error reading config: %s", err.Error()))
|
panic(fmt.Sprintf("error reading config: %s", err.Error()))
|
||||||
|
@ -33,39 +38,33 @@ func main() {
|
||||||
panic(fmt.Sprintf(`error reading config: env expected "prod" or "dev", got "%s"`, cfg.Env))
|
panic(fmt.Sprintf(`error reading config: env expected "prod" or "dev", got "%s"`, cfg.Env))
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := sqlx.Connect("pgx", cfg.PostgresDSN)
|
db, err := postgres.NewPostgresDB(cfg.PostgresDSN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
userStorage := storage.NewUserStorage(db, logger)
|
vk, err := valkey.NewValkeyClient(cfg.RedisDSN)
|
||||||
|
|
||||||
opts, err := valkey.ParseURL(cfg.RedisDSN)
|
userRepo := usersRepository.NewUserRepository(db, logger)
|
||||||
if err != nil {
|
userUC := usersUseCase.NewUseCase(userRepo, nil, cfg)
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
vk, err := valkey.NewClient(opts)
|
sessionRepo := sessionsRepository.NewValkeyRepository(vk, cfg, logger)
|
||||||
if err != nil {
|
sessionUC := sessionsUseCase.NewUseCase(sessionRepo, cfg)
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer vk.Close()
|
|
||||||
|
|
||||||
sessionStorage := storage.NewSessionStorage(vk, cfg, logger)
|
gserver := grpc.NewServer(grpc.UnaryInterceptor(usersDelivery.TokenInterceptor(sessionUC)))
|
||||||
|
|
||||||
sessionService := services.NewSessionService(sessionStorage, userStorage, cfg)
|
usersDelivery.NewUserHandlers(gserver, userUC)
|
||||||
userService := services.NewUserService(userStorage, sessionStorage, cfg)
|
sessionsDelivery.NewSessionHandlers(gserver, sessionUC, userUC)
|
||||||
|
reflection.Register(gserver)
|
||||||
|
|
||||||
ln, err := net.Listen("tcp", cfg.Address)
|
ln, err := net.Listen("tcp", cfg.Address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
server := transport.NewAuthServer(sessionService, userService, logger, ln)
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err = server.GRPCServer.Serve(ln); err != nil {
|
if err = gserver.Serve(ln); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
30
pkg/external/postgres/postgres.go
vendored
Normal file
30
pkg/external/postgres/postgres.go
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxOpenConns = 60
|
||||||
|
connMaxLifetime = 120
|
||||||
|
maxIdleConns = 30
|
||||||
|
connMaxIdleTime = 20
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewPostgresDB(dsn string) (*sqlx.DB, error) {
|
||||||
|
db, err := sqlx.Open("pgx", dsn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
db.SetMaxOpenConns(maxOpenConns)
|
||||||
|
db.SetConnMaxLifetime(connMaxLifetime * time.Second)
|
||||||
|
db.SetMaxIdleConns(maxIdleConns)
|
||||||
|
db.SetConnMaxIdleTime(connMaxIdleTime * time.Second)
|
||||||
|
if err = db.Ping(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return db, nil
|
||||||
|
}
|
12
pkg/external/valkey/valkey.go
vendored
Normal file
12
pkg/external/valkey/valkey.go
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package valkey
|
||||||
|
|
||||||
|
import "github.com/valkey-io/valkey-go"
|
||||||
|
|
||||||
|
func NewValkeyClient(dsn string) (valkey.Client, error) {
|
||||||
|
opts, err := valkey.ParseURL(dsn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return valkey.NewClient(opts)
|
||||||
|
}
|
33
pkg/utils/converter.go
Normal file
33
pkg/utils/converter.go
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TimeP(t *timestamppb.Timestamp) *time.Time {
|
||||||
|
if t == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
tt := t.AsTime()
|
||||||
|
return &tt
|
||||||
|
}
|
||||||
|
|
||||||
|
func TimestampP(t *time.Time) *timestamppb.Timestamp {
|
||||||
|
if t == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return timestamppb.New(*t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AsTimeP(t time.Time) *time.Time {
|
||||||
|
return &t
|
||||||
|
}
|
||||||
|
|
||||||
|
func AsInt32P(v int32) *int32 {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
func AsStringP(str string) *string {
|
||||||
|
return &str
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package lib
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
|
@ -1,21 +1,6 @@
|
||||||
package lib
|
package utils
|
||||||
|
|
||||||
import (
|
import "net/mail"
|
||||||
"net/mail"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func AsTimeP(t time.Time) *time.Time {
|
|
||||||
return &t
|
|
||||||
}
|
|
||||||
|
|
||||||
func AsInt32P(v int32) *int32 {
|
|
||||||
return &v
|
|
||||||
}
|
|
||||||
|
|
||||||
func AsStringP(str string) *string {
|
|
||||||
return &str
|
|
||||||
}
|
|
||||||
|
|
||||||
func ValidPassword(str string) error {
|
func ValidPassword(str string) error {
|
||||||
if len(str) < 5 {
|
if len(str) < 5 {
|
Loading…
Reference in a new issue