Добавлен основные классы для сервиса авторизаци
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -22,4 +22,4 @@ go.work
|
|||||||
go.work.sum
|
go.work.sum
|
||||||
|
|
||||||
# env file
|
# env file
|
||||||
.env
|
*.env
|
||||||
55
cmd/main.go
55
cmd/main.go
@@ -2,15 +2,62 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"authorization/internal"
|
"authorization/internal"
|
||||||
|
"authorization/internal/config"
|
||||||
"authorization/internal/handler"
|
"authorization/internal/handler"
|
||||||
"log"
|
"authorization/internal/repository"
|
||||||
|
"authorization/internal/service"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/golang-migrate/migrate/v4"
|
||||||
|
_ "github.com/golang-migrate/migrate/v4/database/postgres" // Импорт драйвера PostgreSQL
|
||||||
|
_ "github.com/golang-migrate/migrate/v4/source/file" // Импорт файлового драйвера
|
||||||
|
"github.com/joho/godotenv"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
handlers := new(handler.Handler)
|
cfg, err := initConfigs()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatalf("Ошибка считывания конфига: %s", err.Error())
|
||||||
|
}
|
||||||
|
if err := godotenv.Load(); err != nil {
|
||||||
|
logrus.Fatalf("Ошибка загрузки конфига %s", err.Error())
|
||||||
|
}
|
||||||
|
print(cfg.Token.AccessToken.SecretWord)
|
||||||
|
print(cfg.Token.RefreshToken.SecretWord)
|
||||||
|
db, err := repository.NewPostgresDB(repository.Config{
|
||||||
|
Host: cfg.DB.Host,
|
||||||
|
Port: cfg.DB.Port,
|
||||||
|
Username: cfg.DB.Username,
|
||||||
|
Password: os.Getenv("DB_PASSWORD"),
|
||||||
|
DBName: cfg.DB.DBname,
|
||||||
|
SSLMode: cfg.DB.Sslmode,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatalf("Ошибка подключения к базе данных %s", err.Error())
|
||||||
|
}
|
||||||
|
m, err := migrate.New(
|
||||||
|
"file://schema",
|
||||||
|
"postgres://postgres:postgres@localhost:5432/authorization?sslmode=disable")
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatalf("Ошибка инициализации миграций: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Применение миграций
|
||||||
|
if err := m.Up(); err != nil && err != migrate.ErrNoChange {
|
||||||
|
logrus.Fatalf("Ошибка применения миграций: %s", err.Error())
|
||||||
|
}
|
||||||
|
repository := repository.NewRepository(db)
|
||||||
|
services := service.NewServices(repository)
|
||||||
|
handlers := handler.NewHandler(services)
|
||||||
|
|
||||||
srv := new(internal.Server)
|
srv := new(internal.Server)
|
||||||
if err := srv.Run("8080", handlers.InitRoutes()); err != nil {
|
if err := srv.Run(cfg.Server.Port, handlers.InitRoutes()); err != nil {
|
||||||
log.Fatalf("Ошибка запуска сервера: %s", err.Error())
|
logrus.Fatalf("Ошибка запуска сервера: %s", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initConfigs() (*config.Config, error) {
|
||||||
|
return config.LoadConfig("configs/application.yaml")
|
||||||
|
}
|
||||||
|
|||||||
15
configs/application.yaml
Normal file
15
configs/application.yaml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
server:
|
||||||
|
port: 8081
|
||||||
|
db:
|
||||||
|
username: postgres
|
||||||
|
host: localhost
|
||||||
|
port: 5432
|
||||||
|
sslmode: disable
|
||||||
|
dbname: authorization
|
||||||
|
token:
|
||||||
|
accessToken:
|
||||||
|
TTL-in-min: 15
|
||||||
|
secretWord: kdfmklsdlmk;asdmkl;ds
|
||||||
|
refreshToken:
|
||||||
|
TTL-in-min: 90
|
||||||
|
secretWord: asdflmkasdfklmsdafklm
|
||||||
11
docker-compose.yaml
Normal file
11
docker-compose.yaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:15.0
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: authorization
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
|
||||||
|
|
||||||
40
go.mod
40
go.mod
@@ -4,31 +4,61 @@ go 1.23.3
|
|||||||
|
|
||||||
require github.com/gin-gonic/gin v1.10.0
|
require github.com/gin-gonic/gin v1.10.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bytedance/sonic v1.11.6 // indirect
|
github.com/bytedance/sonic v1.11.6 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
|
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||||
|
github.com/golang-migrate/migrate/v4 v4.18.1 // indirect
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
|
github.com/joho/godotenv v1.5.1 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
|
github.com/lib/pq v1.10.9 // indirect
|
||||||
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
|
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||||
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/spf13/viper v1.19.0 // indirect
|
||||||
|
github.com/stretchr/testify v1.11.1
|
||||||
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
golang.org/x/arch v0.8.0 // indirect
|
golang.org/x/arch v0.8.0 // indirect
|
||||||
golang.org/x/crypto v0.23.0 // indirect
|
golang.org/x/crypto v0.27.0 // indirect
|
||||||
golang.org/x/net v0.25.0 // indirect
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||||
golang.org/x/sys v0.20.0 // indirect
|
golang.org/x/net v0.29.0 // indirect
|
||||||
golang.org/x/text v0.15.0 // indirect
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
google.golang.org/protobuf v1.34.1 // indirect
|
golang.org/x/text v0.18.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.34.2 // indirect
|
||||||
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
68
go.sum
68
go.sum
@@ -8,6 +8,10 @@ github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
|||||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||||
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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
@@ -22,7 +26,22 @@ github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBEx
|
|||||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
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/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
|
github.com/golang-migrate/migrate/v4 v4.18.1 h1:JML/k+t4tpHCpQTCAD62Nu43NUFzHY4CV3uAuvHGC+Y=
|
||||||
|
github.com/golang-migrate/migrate/v4 v4.18.1/go.mod h1:HAX6m3sQgcdO81tdjn5exv20+3Kb13cmGli1hrD6hks=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
@@ -31,8 +50,14 @@ github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZY
|
|||||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
@@ -41,6 +66,24 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
|
|||||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||||
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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||||
|
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||||
|
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||||
|
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
|
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||||
|
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||||
|
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||||
|
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||||
|
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||||
|
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||||
|
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||||
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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
@@ -52,26 +95,51 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
|||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
|
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||||
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||||
|
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
|
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||||
|
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||||
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||||
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
|
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||||
|
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||||
|
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||||
|
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
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=
|
||||||
|
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||||
|
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||||
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=
|
||||||
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
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=
|
||||||
|
|||||||
@@ -1,37 +1,49 @@
|
|||||||
package internal
|
package internal
|
||||||
|
|
||||||
type RegistrationUser struct {
|
import (
|
||||||
Username string `json:"username"`
|
"errors"
|
||||||
Password string `json:"password"`
|
"fmt"
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
Student UserRole = iota
|
|
||||||
Teacher
|
|
||||||
Admin
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Username string
|
Id int `json:"-" db:"id"`
|
||||||
Password string
|
Name string `json:"name" binding:"required"`
|
||||||
UserRole UserRole
|
Username string `json:"username" binding:"required"`
|
||||||
|
Password string `json:"password" binding:"required"`
|
||||||
|
UserRole string `json:"-" db:"role"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) ChangeRole(userRole UserRole) {
|
type AuthUser struct {
|
||||||
user.UserRole = userRole
|
Username string `json:"username" binding:"required"`
|
||||||
|
Password string `json:"password" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserRole int
|
type RefreshTokenRequest struct {
|
||||||
|
RefreshToken string `json:"refreshToken" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
func (role UserRole) String() string {
|
type UserRole string
|
||||||
switch role {
|
|
||||||
case Student:
|
const (
|
||||||
return "student"
|
Student UserRole = "student"
|
||||||
case Teacher:
|
Teacher UserRole = "teacher"
|
||||||
return "teacher"
|
Admin UserRole = "admin"
|
||||||
case Admin:
|
)
|
||||||
return "admin"
|
|
||||||
default:
|
func (userrole UserRole) ToString() string {
|
||||||
return "unknown"
|
return string(userrole)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrInvalidRole = errors.New("invalid role")
|
||||||
|
|
||||||
|
func FromString(in string) (UserRole, error) {
|
||||||
|
switch in {
|
||||||
|
case Student.ToString():
|
||||||
|
return Student, nil
|
||||||
|
case Teacher.ToString():
|
||||||
|
return Teacher, nil
|
||||||
|
case Admin.ToString():
|
||||||
|
return Admin, nil
|
||||||
}
|
}
|
||||||
|
return Student, fmt.Errorf("%q is not a valid role: %w", in, ErrInvalidRole)
|
||||||
}
|
}
|
||||||
|
|||||||
42
internal/config/config.go
Normal file
42
internal/config/config.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Token TokenConfig `yaml:"token" json:"token"`
|
||||||
|
Server ServerConfig `yaml:"server" json:"server"`
|
||||||
|
DB DatabaseConfig `yaml:"db" json:"db"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfig struct {
|
||||||
|
Port string `yaml:"port" json:"port"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DatabaseConfig struct {
|
||||||
|
Username string `yaml:"username" json:"username"`
|
||||||
|
Host string `yaml:"host" json:"host"`
|
||||||
|
Port string `yaml:"port" json:"port"`
|
||||||
|
Sslmode string `yaml:"sslmode" json:"sslmode"`
|
||||||
|
DBname string `yaml:"dbname" json:"dbname"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadConfig(absolutePath string) (*Config, error) {
|
||||||
|
config := &Config{}
|
||||||
|
|
||||||
|
file, err := os.Open(absolutePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
decoder := yaml.NewDecoder(file)
|
||||||
|
if err := decoder.Decode(config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
22
internal/config/token.go
Normal file
22
internal/config/token.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type TokenConfig struct {
|
||||||
|
AccessToken TokenSettings `yaml:"accessToken" json:"accessToken"`
|
||||||
|
RefreshToken TokenSettings `yaml:"refreshToken" json:"refreshToken"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TokenSettings struct {
|
||||||
|
TTLInMinutes int `yaml:"TTL-in-min" json:"TTL-in-min"`
|
||||||
|
SecretWord string `yaml:"secretWord" json:"secretWord"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Методы для удобства
|
||||||
|
func (t *TokenSettings) GetTTL() time.Duration {
|
||||||
|
return time.Duration(t.TTLInMinutes) * time.Minute
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TokenSettings) GetSecretBytes() []byte {
|
||||||
|
return []byte(t.SecretWord)
|
||||||
|
}
|
||||||
@@ -1,13 +1,65 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"authorization/internal"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *Handler) signUp(c *gin.Context) {
|
func (h *Handler) signUp(c *gin.Context) {
|
||||||
|
var input internal.User
|
||||||
|
|
||||||
|
if err := c.BindJSON(&input); err != nil {
|
||||||
|
newErrorResponse(c, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := h.services.Authorization.CreateUser(input)
|
||||||
|
if err != nil {
|
||||||
|
newErrorResponse(c, http.StatusInternalServerError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, map[string]interface{}{
|
||||||
|
"id": id,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) signIn(c *gin.Context) {
|
func (h *Handler) signIn(c *gin.Context) {
|
||||||
|
var input internal.AuthUser
|
||||||
|
|
||||||
|
if err := c.BindJSON(&input); err != nil {
|
||||||
|
newErrorResponse(c, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
accesstoken, refreshToken, err := h.services.Authorization.GenerateToken(input.Username, input.Password)
|
||||||
|
if err != nil {
|
||||||
|
newErrorResponse(c, http.StatusInternalServerError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, map[string]interface{}{
|
||||||
|
"accessToken": accesstoken,
|
||||||
|
"refreshToken": refreshToken,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) refresh(c *gin.Context) {
|
||||||
|
var input internal.RefreshTokenRequest
|
||||||
|
|
||||||
|
if err := c.BindJSON(&input); err != nil {
|
||||||
|
newErrorResponse(c, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
accessToken, refreshToken, err := h.services.Authorization.RefreshToken(input.RefreshToken)
|
||||||
|
if err != nil {
|
||||||
|
newErrorResponse(c, http.StatusUnauthorized, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, map[string]interface{}{
|
||||||
|
"accessToken": accessToken,
|
||||||
|
"refreshToken": refreshToken,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,40 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import "github.com/gin-gonic/gin"
|
import (
|
||||||
|
"authorization/internal/service"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
|
services *service.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandler(services *service.Service) *Handler {
|
||||||
|
return &Handler{
|
||||||
|
services: services,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) InitRoutes() *gin.Engine {
|
func (h *Handler) InitRoutes() *gin.Engine {
|
||||||
router := gin.New()
|
router := gin.New()
|
||||||
|
serviceRouter := router.Group("/auth-service")
|
||||||
auth := router.Group("/auth")
|
|
||||||
{
|
{
|
||||||
auth.POST("/sign-up", h.signUp)
|
auth := serviceRouter.Group("/auth")
|
||||||
auth.POST("/sign-in", h.signIn)
|
{
|
||||||
|
auth.POST("/sign-up", h.signUp)
|
||||||
|
auth.POST("/sign-in", h.signIn)
|
||||||
|
auth.POST("/refresh", h.refresh)
|
||||||
|
}
|
||||||
|
api := router.Group("/api")
|
||||||
|
{
|
||||||
|
users := api.Group("/users", h.checkAdminIdentity)
|
||||||
|
{
|
||||||
|
users.POST("/:username", h.changeUserRole)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
|
||||||
42
internal/handler/middleware.go
Normal file
42
internal/handler/middleware.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"authorization/internal"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
authorizationHeader = "Authorization"
|
||||||
|
roleKey = "user_role"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *Handler) checkAdminIdentity(c *gin.Context) {
|
||||||
|
header := c.GetHeader(authorizationHeader)
|
||||||
|
if header == "" {
|
||||||
|
newErrorResponse(c, http.StatusUnauthorized, "Пустой header авторизации")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
headerParts := strings.Split(header, " ")
|
||||||
|
if len(headerParts) != 2 {
|
||||||
|
newErrorResponse(c, http.StatusUnauthorized, "Невалидный токен JWT")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userRole, err := h.services.ParseToken(headerParts[1])
|
||||||
|
|
||||||
|
if userRole != string(internal.Admin) {
|
||||||
|
newErrorResponse(c, http.StatusUnauthorized, "Недостаточно прав для выполнения запроса")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
newErrorResponse(c, http.StatusUnauthorized, "Ошибка при извлечении claims")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set(roleKey, userRole)
|
||||||
|
}
|
||||||
15
internal/handler/response.go
Normal file
15
internal/handler/response.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type error struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func newErrorResponse(c *gin.Context, statusCode int, message string) {
|
||||||
|
logrus.Error(message)
|
||||||
|
c.AbortWithStatusJSON(statusCode, error{Message: message})
|
||||||
|
}
|
||||||
36
internal/handler/users.go
Normal file
36
internal/handler/users.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChangeUserRoleRequest struct {
|
||||||
|
Role string `json:"role"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) changeUserRole(c *gin.Context) {
|
||||||
|
var input ChangeUserRoleRequest
|
||||||
|
|
||||||
|
if err := c.BindJSON(&input); err != nil {
|
||||||
|
newErrorResponse(c, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
username := c.Param("username")
|
||||||
|
if username == "" {
|
||||||
|
newErrorResponse(c, http.StatusBadRequest, "Ошибка в строке запроса")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
role, err := h.services.ChangeUserRole(username, input.Role)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
newErrorResponse(c, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, map[string]interface{}{
|
||||||
|
"newRole": role,
|
||||||
|
})
|
||||||
|
}
|
||||||
35
internal/repository/postgres.go
Normal file
35
internal/repository/postgres.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
_ "github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
usersTable = "users"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Host string
|
||||||
|
Port string
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
DBName string
|
||||||
|
SSLMode string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPostgresDB(cfg Config) (*sql.DB, error) {
|
||||||
|
db, err := sql.Open("postgres",
|
||||||
|
fmt.Sprintf("user=%s password=%s host=%s dbname=%s sslmode=%s",
|
||||||
|
cfg.Username, cfg.Password, cfg.Host, cfg.DBName, cfg.SSLMode))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = db.Ping()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
22
internal/repository/repository.go
Normal file
22
internal/repository/repository.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"authorization/internal"
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserResository interface {
|
||||||
|
CreateUser(user internal.User) (int, error)
|
||||||
|
GetUser(username, password string) (internal.User, error)
|
||||||
|
UpdateUserRole(username string, userrole internal.UserRole) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Repository struct {
|
||||||
|
UserResository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRepository(db *sql.DB) *Repository {
|
||||||
|
return &Repository{
|
||||||
|
UserResository: NewUserPostgres(db),
|
||||||
|
}
|
||||||
|
}
|
||||||
44
internal/repository/user_postgres.go
Normal file
44
internal/repository/user_postgres.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"authorization/internal"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserPostgres struct {
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserPostgres(db *sql.DB) *UserPostgres {
|
||||||
|
return &UserPostgres{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *UserPostgres) CreateUser(user internal.User) (int, error) {
|
||||||
|
var id int
|
||||||
|
query := fmt.Sprintf("INSERT INTO %s(name,username,password_hash,role) values ($1,$2,$3,$4) RETURNING id", usersTable)
|
||||||
|
row := r.db.QueryRow(query, user.Name, user.Username, user.Password, internal.Student)
|
||||||
|
if err := row.Scan(&id); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *UserPostgres) GetUser(username, password string) (internal.User, error) {
|
||||||
|
var user internal.User
|
||||||
|
query := fmt.Sprintf("SELECT * from %s where username = $1 AND password_hash=$2", usersTable)
|
||||||
|
row := r.db.QueryRow(query, username, password)
|
||||||
|
err := row.Scan(&user.Id, &user.Name, &user.Username, &user.Password, &user.UserRole)
|
||||||
|
return user, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *UserPostgres) UpdateUserRole(username string, userrole internal.UserRole) (string, error) {
|
||||||
|
query := fmt.Sprintf("UPDATE %s SET role = $1 WHERE username = $2 RETURNING role", usersTable)
|
||||||
|
var newRole string
|
||||||
|
err := r.db.QueryRow(query, userrole, username).Scan(&newRole)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to update user role: %v", err)
|
||||||
|
}
|
||||||
|
return newRole, nil
|
||||||
|
}
|
||||||
142
internal/service/auth.go
Normal file
142
internal/service/auth.go
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"authorization/internal"
|
||||||
|
"authorization/internal/config"
|
||||||
|
"crypto/sha1"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang-jwt/jwt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
refresh = "refresh"
|
||||||
|
access = "access"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tokenClaims struct {
|
||||||
|
jwt.StandardClaims
|
||||||
|
UserId int `json:"user_id"`
|
||||||
|
UserRole string `json:"user_role"`
|
||||||
|
TokenType string `json:"token_type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthService struct {
|
||||||
|
userService UserService
|
||||||
|
tokenConfigs config.TokenConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAuthService(userService UserService) *AuthService {
|
||||||
|
return &AuthService{userService: userService}
|
||||||
|
}
|
||||||
|
func (s *AuthService) CreateUser(user internal.User) (int, error) {
|
||||||
|
user.Password = s.generatePasswordHash(user.Password)
|
||||||
|
return s.userService.CreateUser(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AuthService) GenerateToken(username string, password string) (string, string, error) {
|
||||||
|
user, err := s.userService.GetUser(username, s.generatePasswordHash(password))
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
accessTokenClaims := s.generateClaims(user.Id, user.UserRole, access)
|
||||||
|
|
||||||
|
refreshTokenClaims := s.generateClaims(user.Id, user.UserRole, refresh)
|
||||||
|
|
||||||
|
accessToken, err := accessTokenClaims.SignedString([]byte(s.tokenConfigs.AccessToken.GetSecretBytes()))
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
refreshToken, err := refreshTokenClaims.SignedString([]byte(s.tokenConfigs.RefreshToken.GetSecretBytes()))
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
return accessToken, refreshToken, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AuthService) ChangeUserRole(username string, userrole string) (string, error) {
|
||||||
|
user, err := s.userService.ChangeUserRole(username, userrole)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AuthService) generateClaims(userId int, userRole string, tokenType string) *jwt.Token {
|
||||||
|
tokenTTL := s.tokenConfigs.RefreshToken.GetTTL()
|
||||||
|
if tokenType == access {
|
||||||
|
tokenTTL = s.tokenConfigs.AccessToken.GetTTL()
|
||||||
|
}
|
||||||
|
return jwt.NewWithClaims(jwt.SigningMethodHS256, &tokenClaims{
|
||||||
|
jwt.StandardClaims{
|
||||||
|
ExpiresAt: time.Now().Add(tokenTTL).Unix(),
|
||||||
|
IssuedAt: time.Now().Unix(),
|
||||||
|
},
|
||||||
|
userId,
|
||||||
|
userRole,
|
||||||
|
tokenType,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AuthService) generatePasswordHash(password string) string {
|
||||||
|
hash := sha1.New()
|
||||||
|
hash.Write([]byte(password))
|
||||||
|
return fmt.Sprintf("%x", hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AuthService) ParseToken(accessToken string) (string, error) {
|
||||||
|
claims, err := s.parseTokenWithSecret(accessToken, s.tokenConfigs.AccessToken.GetSecretBytes())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return claims.UserRole, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AuthService) RefreshToken(refreshToken string) (string, string, error) {
|
||||||
|
// Парсим refresh token
|
||||||
|
claims, err := s.parseTokenWithSecret(refreshToken, s.tokenConfigs.RefreshToken.GetSecretBytes())
|
||||||
|
if err != nil {
|
||||||
|
return "", "", errors.New("invalid refresh token")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, что это именно refresh token
|
||||||
|
if claims.TokenType != refresh {
|
||||||
|
return "", "", errors.New("token is not a refresh token")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Генерируем новую пару токенов
|
||||||
|
newAccessTokenClaims := s.generateClaims(claims.UserId, claims.UserRole, access)
|
||||||
|
newRefreshTokenClaims := s.generateClaims(claims.UserId, claims.UserRole, refresh)
|
||||||
|
|
||||||
|
newAccessToken, err := newAccessTokenClaims.SignedString([]byte(s.tokenConfigs.AccessToken.GetSecretBytes()))
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
newRefreshToken, err := newRefreshTokenClaims.SignedString([]byte(s.tokenConfigs.RefreshToken.GetSecretBytes()))
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return newAccessToken, newRefreshToken, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseTokenWithSecret - общий метод для парсинга токена с заданным секретным ключом
|
||||||
|
func (s *AuthService) parseTokenWithSecret(tokenString string, secret []byte) (*tokenClaims, error) {
|
||||||
|
token, err := jwt.ParseWithClaims(tokenString, &tokenClaims{}, func(t *jwt.Token) (interface{}, error) {
|
||||||
|
return secret, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
claims, ok := token.Claims.(*tokenClaims)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("invalid token claims")
|
||||||
|
}
|
||||||
|
|
||||||
|
return claims, nil
|
||||||
|
}
|
||||||
117
internal/service/auth_test.go
Normal file
117
internal/service/auth_test.go
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"authorization/internal/config"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang-jwt/jwt"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAuthService_ParseToken(t *testing.T) {
|
||||||
|
// Настройка тестового сервиса
|
||||||
|
service := &AuthService{
|
||||||
|
tokenConfigs: config.TokenConfig{
|
||||||
|
AccessToken: config.TokenSettings{
|
||||||
|
TTLInMinutes: 15,
|
||||||
|
SecretWord: "test-secret-key",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("Valid token", func(t *testing.T) {
|
||||||
|
// Создаем валидный токен
|
||||||
|
claims := &tokenClaims{
|
||||||
|
StandardClaims: jwt.StandardClaims{
|
||||||
|
ExpiresAt: time.Now().Add(15 * time.Minute).Unix(),
|
||||||
|
IssuedAt: time.Now().Unix(),
|
||||||
|
},
|
||||||
|
UserId: 1,
|
||||||
|
UserRole: "admin",
|
||||||
|
TokenType: "access",
|
||||||
|
}
|
||||||
|
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
|
tokenString, err := token.SignedString([]byte("test-secret-key"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Парсим токен
|
||||||
|
role, err := service.ParseToken(tokenString)
|
||||||
|
|
||||||
|
// Проверяем результат
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "admin", role)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Expired token", func(t *testing.T) {
|
||||||
|
// Создаем истекший токен (истек 1 час назад)
|
||||||
|
claims := &tokenClaims{
|
||||||
|
StandardClaims: jwt.StandardClaims{
|
||||||
|
ExpiresAt: time.Now().Add(-1 * time.Hour).Unix(),
|
||||||
|
IssuedAt: time.Now().Add(-2 * time.Hour).Unix(),
|
||||||
|
},
|
||||||
|
UserId: 1,
|
||||||
|
UserRole: "admin",
|
||||||
|
TokenType: "access",
|
||||||
|
}
|
||||||
|
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
|
tokenString, err := token.SignedString([]byte("test-secret-key"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Парсим истекший токен
|
||||||
|
role, err := service.ParseToken(tokenString)
|
||||||
|
|
||||||
|
// Проверяем, что получили ошибку
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Empty(t, role)
|
||||||
|
|
||||||
|
// Проверяем, что это именно ошибка истечения срока
|
||||||
|
if ve, ok := err.(*jwt.ValidationError); ok {
|
||||||
|
assert.True(t, ve.Errors&jwt.ValidationErrorExpired != 0, "Expected token expired error")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Invalid signature", func(t *testing.T) {
|
||||||
|
// Создаем токен с другим секретным ключом
|
||||||
|
claims := &tokenClaims{
|
||||||
|
StandardClaims: jwt.StandardClaims{
|
||||||
|
ExpiresAt: time.Now().Add(15 * time.Minute).Unix(),
|
||||||
|
IssuedAt: time.Now().Unix(),
|
||||||
|
},
|
||||||
|
UserId: 1,
|
||||||
|
UserRole: "admin",
|
||||||
|
TokenType: "access",
|
||||||
|
}
|
||||||
|
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
|
tokenString, err := token.SignedString([]byte("wrong-secret-key"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Пытаемся парсить токен с неправильной подписью
|
||||||
|
role, err := service.ParseToken(tokenString)
|
||||||
|
|
||||||
|
// Проверяем, что получили ошибку
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Empty(t, role)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Malformed token", func(t *testing.T) {
|
||||||
|
// Пытаемся парсить невалидный токен
|
||||||
|
role, err := service.ParseToken("invalid.token.string")
|
||||||
|
|
||||||
|
// Проверяем, что получили ошибку
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Empty(t, role)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Empty token", func(t *testing.T) {
|
||||||
|
// Пытаемся парсить пустой токен
|
||||||
|
role, err := service.ParseToken("")
|
||||||
|
|
||||||
|
// Проверяем, что получили ошибку
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Empty(t, role)
|
||||||
|
})
|
||||||
|
}
|
||||||
29
internal/service/service.go
Normal file
29
internal/service/service.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"authorization/internal"
|
||||||
|
"authorization/internal/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Authorization interface {
|
||||||
|
CreateUser(internal.User) (int, error)
|
||||||
|
GenerateToken(username string, password string) (accessToken string, refreshToken string, err error)
|
||||||
|
ParseToken(token string) (string, error)
|
||||||
|
RefreshToken(refreshToken string) (accessToken string, newRefreshToken string, err error)
|
||||||
|
ChangeUserRole(username string, newRole string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
Authorization
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServices(repository *repository.Repository) *Service {
|
||||||
|
|
||||||
|
userService := newUserService(repository)
|
||||||
|
|
||||||
|
authService := newAuthService(userService)
|
||||||
|
|
||||||
|
return &Service{
|
||||||
|
Authorization: authService,
|
||||||
|
}
|
||||||
|
}
|
||||||
44
internal/service/user.go
Normal file
44
internal/service/user.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"authorization/internal"
|
||||||
|
"authorization/internal/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserService interface {
|
||||||
|
CreateUser(internal.User) (int, error)
|
||||||
|
ChangeUserRole(username string, Role string) (string, error)
|
||||||
|
GetUser(username string, hashedPassword string) (*internal.User, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserServiceImpl struct {
|
||||||
|
repo repository.UserResository
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUserService(repo repository.UserResository) *UserServiceImpl {
|
||||||
|
return &UserServiceImpl{repo: repo}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *UserServiceImpl) CreateUser(user internal.User) (int, error) {
|
||||||
|
return s.repo.CreateUser(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *UserServiceImpl) ChangeUserRole(username string, userRole string) (string, error) {
|
||||||
|
newRole, err := internal.FromString(userRole)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
user, err := s.repo.UpdateUserRole(username, newRole)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *UserServiceImpl) GetUser(username string, hashedPassword string) (*internal.User, error) {
|
||||||
|
user, err := s.repo.GetUser(username, hashedPassword)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
5
run.ps1
Normal file
5
run.ps1
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
docker compose down
|
||||||
|
docker compose up -d
|
||||||
|
go clean
|
||||||
|
go build cmd/main.go
|
||||||
|
./main.exe
|
||||||
1
schema/000001_init.down.sql
Normal file
1
schema/000001_init.down.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
drop table if exists USERS
|
||||||
6
schema/000001_init.up.sql
Normal file
6
schema/000001_init.up.sql
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
CREATE TABLE USERS(
|
||||||
|
id serial not null unique,
|
||||||
|
name varchar(255) not null,
|
||||||
|
username varchar(255) unique not null,
|
||||||
|
password_hash varchar(255) not null
|
||||||
|
)
|
||||||
2
schema/000002_add_user_role.down.sql
Normal file
2
schema/000002_add_user_role.down.sql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE USERS
|
||||||
|
drop COLUMN role
|
||||||
2
schema/000002_add_user_role.up.sql
Normal file
2
schema/000002_add_user_role.up.sql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE USERS
|
||||||
|
add role varchar(30)
|
||||||
Reference in New Issue
Block a user