diff --git a/.gitignore b/.gitignore index 64ae7f3..3d79520 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,4 @@ go.work go.work.sum # env file -.env \ No newline at end of file +*.env \ No newline at end of file diff --git a/cmd/main.go b/cmd/main.go index 963211c..29c705e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -2,15 +2,62 @@ package main import ( "authorization/internal" + "authorization/internal/config" "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() { - 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) - if err := srv.Run("8080", handlers.InitRoutes()); err != nil { - log.Fatalf("Ошибка запуска сервера: %s", err.Error()) + if err := srv.Run(cfg.Server.Port, handlers.InitRoutes()); err != nil { + logrus.Fatalf("Ошибка запуска сервера: %s", err.Error()) } } + +func initConfigs() (*config.Config, error) { + return config.LoadConfig("configs/application.yaml") +} diff --git a/configs/application.yaml b/configs/application.yaml new file mode 100644 index 0000000..4116811 --- /dev/null +++ b/configs/application.yaml @@ -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 diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..0ade066 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,11 @@ +services: + postgres: + image: postgres:15.0 + environment: + POSTGRES_DB: authorization + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + ports: + - 5432:5432 + + \ No newline at end of file diff --git a/go.mod b/go.mod index 5246e50..cf001ea 100644 --- a/go.mod +++ b/go.mod @@ -4,31 +4,61 @@ go 1.23.3 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 ( github.com/bytedance/sonic v1.11.6 // indirect github.com/bytedance/sonic/loader v0.1.1 // indirect github.com/cloudwego/base64x v0.1.4 // 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/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.20.0 // 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/klauspost/cpuid/v2 v2.2.7 // 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/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.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/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/crypto v0.23.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect - google.golang.org/protobuf v1.34.1 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/sys v0.33.0 // 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 ) diff --git a/go.sum b/go.sum index b657a9f..061bc01 100644 --- a/go.sum +++ b/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/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.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/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= 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/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 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/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/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 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/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/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/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-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 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/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.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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 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.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.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/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/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.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= 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/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/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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.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/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/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/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/Dtos.go b/internal/Dtos.go index 827235d..88f37aa 100644 --- a/internal/Dtos.go +++ b/internal/Dtos.go @@ -1,37 +1,49 @@ package internal -type RegistrationUser struct { - Username string `json:"username"` - Password string `json:"password"` -} - -const ( - Student UserRole = iota - Teacher - Admin +import ( + "errors" + "fmt" ) type User struct { - Username string - Password string - UserRole UserRole + Id int `json:"-" db:"id"` + Name string `json:"name" binding:"required"` + Username string `json:"username" binding:"required"` + Password string `json:"password" binding:"required"` + UserRole string `json:"-" db:"role"` } -func (user *User) ChangeRole(userRole UserRole) { - user.UserRole = userRole +type AuthUser struct { + 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 { - switch role { - case Student: - return "student" - case Teacher: - return "teacher" - case Admin: - return "admin" - default: - return "unknown" +type UserRole string + +const ( + Student UserRole = "student" + Teacher UserRole = "teacher" + Admin UserRole = "admin" +) + +func (userrole UserRole) ToString() string { + 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) } diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..4ebc896 --- /dev/null +++ b/internal/config/config.go @@ -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 +} diff --git a/internal/config/token.go b/internal/config/token.go new file mode 100644 index 0000000..ab47bd2 --- /dev/null +++ b/internal/config/token.go @@ -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) +} diff --git a/internal/handler/auth.go b/internal/handler/auth.go index 3d11006..a2585a3 100644 --- a/internal/handler/auth.go +++ b/internal/handler/auth.go @@ -1,13 +1,65 @@ package handler import ( + "authorization/internal" + "net/http" + "github.com/gin-gonic/gin" ) 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) { + 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, + }) } diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 3c270b6..53df14f 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -1,17 +1,40 @@ package handler -import "github.com/gin-gonic/gin" +import ( + "authorization/internal/service" + + "github.com/gin-gonic/gin" +) type Handler struct { + services *service.Service +} + +func NewHandler(services *service.Service) *Handler { + return &Handler{ + services: services, + } } func (h *Handler) InitRoutes() *gin.Engine { router := gin.New() - - auth := router.Group("/auth") + serviceRouter := router.Group("/auth-service") { - auth.POST("/sign-up", h.signUp) - auth.POST("/sign-in", h.signIn) + auth := serviceRouter.Group("/auth") + { + 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 } + \ No newline at end of file diff --git a/internal/handler/middleware.go b/internal/handler/middleware.go new file mode 100644 index 0000000..0083c71 --- /dev/null +++ b/internal/handler/middleware.go @@ -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) +} diff --git a/internal/handler/response.go b/internal/handler/response.go new file mode 100644 index 0000000..780677e --- /dev/null +++ b/internal/handler/response.go @@ -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}) +} diff --git a/internal/handler/users.go b/internal/handler/users.go new file mode 100644 index 0000000..475e6f6 --- /dev/null +++ b/internal/handler/users.go @@ -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, + }) +} diff --git a/internal/repository/postgres.go b/internal/repository/postgres.go new file mode 100644 index 0000000..05fbd45 --- /dev/null +++ b/internal/repository/postgres.go @@ -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 +} diff --git a/internal/repository/repository.go b/internal/repository/repository.go new file mode 100644 index 0000000..1eefc22 --- /dev/null +++ b/internal/repository/repository.go @@ -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), + } +} diff --git a/internal/repository/user_postgres.go b/internal/repository/user_postgres.go new file mode 100644 index 0000000..e66ed79 --- /dev/null +++ b/internal/repository/user_postgres.go @@ -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 +} diff --git a/internal/service/auth.go b/internal/service/auth.go new file mode 100644 index 0000000..ad62a53 --- /dev/null +++ b/internal/service/auth.go @@ -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 +} diff --git a/internal/service/auth_test.go b/internal/service/auth_test.go new file mode 100644 index 0000000..6697fc5 --- /dev/null +++ b/internal/service/auth_test.go @@ -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) + }) +} diff --git a/internal/service/service.go b/internal/service/service.go new file mode 100644 index 0000000..a947d54 --- /dev/null +++ b/internal/service/service.go @@ -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, + } +} diff --git a/internal/service/user.go b/internal/service/user.go new file mode 100644 index 0000000..2045968 --- /dev/null +++ b/internal/service/user.go @@ -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 +} diff --git a/run.ps1 b/run.ps1 new file mode 100644 index 0000000..ecad1e1 --- /dev/null +++ b/run.ps1 @@ -0,0 +1,5 @@ +docker compose down +docker compose up -d +go clean +go build cmd/main.go +./main.exe \ No newline at end of file diff --git a/schema/000001_init.down.sql b/schema/000001_init.down.sql new file mode 100644 index 0000000..7058768 --- /dev/null +++ b/schema/000001_init.down.sql @@ -0,0 +1 @@ +drop table if exists USERS \ No newline at end of file diff --git a/schema/000001_init.up.sql b/schema/000001_init.up.sql new file mode 100644 index 0000000..ccb7643 --- /dev/null +++ b/schema/000001_init.up.sql @@ -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 +) \ No newline at end of file diff --git a/schema/000002_add_user_role.down.sql b/schema/000002_add_user_role.down.sql new file mode 100644 index 0000000..2b2acb7 --- /dev/null +++ b/schema/000002_add_user_role.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE USERS +drop COLUMN role \ No newline at end of file diff --git a/schema/000002_add_user_role.up.sql b/schema/000002_add_user_role.up.sql new file mode 100644 index 0000000..c8a905a --- /dev/null +++ b/schema/000002_add_user_role.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE USERS +add role varchar(30) \ No newline at end of file