From 092389fcc6495edd4abd216644384a66b81d14b0 Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Mon, 27 Apr 2026 06:22:35 +0200 Subject: [PATCH 01/17] Started working on the accounts service Signed-off-by: Nikolai Rodionov --- api/v1/accounts.go | 46 +++++++++++++++----------------- go.mod | 2 +- go.sum | 4 +-- helmfile.yaml.gotmpl | 32 +++++++++++----------- internal/controllers/accounts.go | 14 ++++++---- kind-config.yaml | 4 +-- main.go | 4 +-- 7 files changed, 52 insertions(+), 54 deletions(-) diff --git a/api/v1/accounts.go b/api/v1/accounts.go index 7dedad8..d2a7dc3 100644 --- a/api/v1/accounts.go +++ b/api/v1/accounts.go @@ -6,7 +6,7 @@ import ( "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/email" - "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1" + accounts "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1" "github.com/golang/protobuf/ptypes/empty" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -14,55 +14,51 @@ import ( func NewAccountRPCImpl(db *sql.DB, hashCost int16, email *email.EmailConf, devMode bool) *AccountsServer { return &AccountsServer{ - Params: &controllers.AccountParams{ - HashCost: hashCost, - }, + Params: &controllers.AccountParams{}, SQLDriver: db, emailConfig: *email, devMode: devMode, + hashCost: hashCost, } } type AccountsServer struct { - accounts.UnimplementedAccountsServer + accounts.UnimplementedAccountsServiceServer Params *controllers.AccountParams SQLDriver *sql.DB emailConfig email.EmailConf // When dev mode is enabled, email won't be sent, instead the code will be returned in metadata - devMode bool + devMode bool + hashCost int16 } -func (a *AccountsServer) SignUp(ctx context.Context, in *accounts.AccountWithPassword) (*accounts.AccountFullWithToken, error) { - accountCtrl := controllers.AccountController{ - Params: controllers.AccountParams{ - HashCost: a.Params.HashCost, - }, - DB: a.SQLDriver, - DevMode: a.devMode, +func (a *AccountsServer) SignUp(ctx context.Context, in *accounts.SignUpRequest) (*empty.Empty, error) { + ctrl := &controllers.AccountController{ + HashCost: a.hashCost, + DB: a.SQLDriver, + DevMode: a.devMode, } - if err := accountCtrl.Create(ctx, &controllers.AccountData{ + data := &controllers.AccountData{ Username: in.Data.GetName(), Password: in.Password.GetPassword(), Email: in.Data.GetEmail(), - }); err != nil { - return nil, status.Error(codes.Aborted, "Couldn't create a user") } - return &accounts.AccountFullWithToken{ - Id: &accounts.AccountId{}, - Data: &accounts.AccountData{}, - Token: "", - }, nil -} -func (a *AccountsServer) SignIn(ctx context.Context, in *accounts.AccountWithPassword) (*accounts.AccountFullWithToken, error) { + if err := ctrl.Create(ctx, data); err != nil { + return nil, status.Error(codes.Aborted, "User can't be created") + } return nil, status.Error(codes.Unimplemented, "Endpoint is not implemented") } -func (a *AccountsServer) ResetPassword(ctx context.Context, in *accounts.AccountData) (*empty.Empty, error) { +func (a *AccountsServer) SignIn(ctx context.Context, in *accounts.SignInRequest) (*empty.Empty, error) { return nil, status.Error(codes.Unimplemented, "Endpoint is not implemented") } -func (acc *AccountsServer) NewPassword(ctx context.Context, in *accounts.AccountWithPasswordAndCode) (*empty.Empty, error) { +func (a *AccountsServer) ResetPassword(ctx context.Context, in *accounts.ResetPasswordRequest) (*empty.Empty, error) { + return nil, status.Error(codes.Unimplemented, "Endpoint is not implemented") +} + +func (acc *AccountsServer) NewPassword(ctx context.Context, in *accounts.NewPasswordRequest) (*empty.Empty, error) { return nil, status.Error(codes.Unimplemented, "Endpoint is not implemented") } diff --git a/go.mod b/go.mod index 78e66c3..83c443f 100644 --- a/go.mod +++ b/go.mod @@ -133,7 +133,7 @@ require ( ) require ( - gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260423180238-53a8a976b540 + gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260427034527-ad8a1eb091c2 github.com/golang/protobuf v1.5.4 golang.org/x/net v0.49.0 // indirect golang.org/x/sys v0.40.0 // indirect diff --git a/go.sum b/go.sum index 02da231..6f18223 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260423180238-53a8a976b540 h1:DX/lMTetxHz4ezEyBI4bKaJwxhO0uXUyGv9pQTV9NYI= -gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260423180238-53a8a976b540/go.mod h1:zgX1KfGcHue8TjuXWN0onGpg3pQPek/lKMfdT6S7TQM= +gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260427034527-ad8a1eb091c2 h1:iOJrE/EiQ3JPgcwgp4oddMX2gil364TomWSUxeDgSvE= +gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260427034527-ad8a1eb091c2/go.mod h1:AgOh1lkPHyRgBf3/s1btKcAqke/33LbKYarTD13qeAg= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= diff --git a/helmfile.yaml.gotmpl b/helmfile.yaml.gotmpl index fa2ae55..5bea930 100644 --- a/helmfile.yaml.gotmpl +++ b/helmfile.yaml.gotmpl @@ -55,20 +55,18 @@ releases: readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 1000 - redis: - # -- Enable redis cluster with docker container. - enable: true - image: - repository: bitnamilegacy/redis - # -- Cluster domain. - clusterDomain: 'cluster.local' - auth: - # -- Enable password authentication. - enabled: true - # -- Redis password. - password: dragonfly - master: - service: - ports: - # -- Redis master service port. - redis: 6379 + service: + port: 30379 + type: NodePort + strategicMergePatches: + - apiVersion: v1 + kind: Service + metadata: + name: dragonfly + namespace: databases + spec: + ports: + - name: dragonfly + port: 30379 + protocol: TCP + nodePort: 30379 diff --git a/internal/controllers/accounts.go b/internal/controllers/accounts.go index 53c34db..7bbc60e 100644 --- a/internal/controllers/accounts.go +++ b/internal/controllers/accounts.go @@ -9,13 +9,17 @@ import ( ) type AccountController struct { - Params AccountParams - DB *sql.DB - DevMode bool + DB *sql.DB + DevMode bool + HashCost int16 +} + +type JWT struct { + RefreshToken string + AccessToken string } type AccountParams struct { - HashCost int16 } type AccountData struct { @@ -28,7 +32,7 @@ type AccountData struct { func (c *AccountController) Create(ctx context.Context, data *AccountData) error { data.UUID = uuid.New().String() - passwordHash, err := hash.HashPassword(data.Password, int(c.Params.HashCost)) + passwordHash, err := hash.HashPassword(data.Password, int(c.HashCost)) if err != nil { return nil } diff --git a/kind-config.yaml b/kind-config.yaml index c69d6d8..58a1f51 100644 --- a/kind-config.yaml +++ b/kind-config.yaml @@ -5,5 +5,5 @@ nodes: extraPortMappings: - containerPort: 30432 hostPort: 30432 - - containerPort: 30431 - hostPort: 30431 + - containerPort: 30379 + hostPort: 30379 diff --git a/main.go b/main.go index 1a24aec..6de2b45 100644 --- a/main.go +++ b/main.go @@ -11,7 +11,7 @@ import ( v1 "gitea.badhouseplants.net/softplayer/softplayer-backend/api/v1" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/email" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger" - "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1" + accounts "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1" applications_proto "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/applications/v1" email_proto "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/email/v1" "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/environments/v1" @@ -147,7 +147,7 @@ func server(ctx context.Context, params Serve) error { } environments.RegisterEnvironmentsServer(grpcServer, v1.NewapiGrpcImpl(controller, log)) - accounts.RegisterAccountsServer(grpcServer, v1.NewAccountRPCImpl(db, params.HashCost, &emailConfig, params.DevMode)) + accounts.RegisterAccountsServiceServer(grpcServer, v1.NewAccountRPCImpl(db, params.HashCost, &emailConfig, params.DevMode)) email_proto.RegisterEmailValidationServer(grpcServer, v1.InitEmailServer(controller, &emailConfig, params.DevMode)) applications_proto.RegisterApplicationsServer(grpcServer, v1.NewApplicationsGrpcImpl(controller, log)) From fdf0deb1d132c210e2f301dfa8784fa36d7538a1 Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Mon, 27 Apr 2026 12:56:19 +0200 Subject: [PATCH 02/17] Move to another ns Signed-off-by: Nikolai Rodionov --- helmfile.yaml.gotmpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/helmfile.yaml.gotmpl b/helmfile.yaml.gotmpl index 5bea930..a404f97 100644 --- a/helmfile.yaml.gotmpl +++ b/helmfile.yaml.gotmpl @@ -19,7 +19,7 @@ repositories: releases: - name: postgres-instance - namespace: databases + namespace: softplayer chart: cloudpirates/postgres version: 0.19.1 installed: true @@ -35,7 +35,7 @@ releases: enabled: true whenDeleted: Delete - name: dragonfly - namespace: databases + namespace: softplayer chart: dragonfly/dragonfly version: v1.38.0 installed: true @@ -63,7 +63,7 @@ releases: kind: Service metadata: name: dragonfly - namespace: databases + namespace: softplayer spec: ports: - name: dragonfly From 48ff7bb43934564400c87a45c78cf8bd822c8ed6 Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Mon, 27 Apr 2026 13:23:54 +0200 Subject: [PATCH 03/17] Add database and helm chart Signed-off-by: Nikolai Rodionov --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 6de2b45..d93ccfc 100644 --- a/main.go +++ b/main.go @@ -115,7 +115,7 @@ func server(ctx context.Context, params Serve) error { }() log.Info("Opening a database connection") - db, err := sql.Open("postgres", "postgres://softplayer:qwertyu9@localhost:30432/softplayer?sslmode=disable") + db, err := sql.Open("postgres", params.DBConnectionString) if err != nil { log.Error(err, "Couldn't start a database driver") return err From 346cdb7b37801eab23537ba1b1bfed811d3197f7 Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Mon, 27 Apr 2026 13:26:45 +0200 Subject: [PATCH 04/17] Add database and helm chart Signed-off-by: Nikolai Rodionov From a81679af305b5d4c48c3d0b871a30e81f9ee612d Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Mon, 27 Apr 2026 13:27:16 +0200 Subject: [PATCH 05/17] Add database and helm chart Signed-off-by: Nikolai Rodionov From 646e9895c1897a1497b736e60759002bbd5d3b67 Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Mon, 27 Apr 2026 13:38:15 +0200 Subject: [PATCH 06/17] Add migrations to the container Signed-off-by: Nikolai Rodionov --- Containerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Containerfile b/Containerfile index 303ad0d..49dcc94 100644 --- a/Containerfile +++ b/Containerfile @@ -10,4 +10,5 @@ RUN CGO_ENABLED=0 GOOS=linux go build -o backend FROM scratch COPY --from=0 /app/backend /app COPY --from=0 /etc/ssl /etc/ssl +COPY migrations /migrations ENTRYPOINT ["/app"] From 962b9bb6c513a4c1addfa192f04cdaaf2f914d1c Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Mon, 27 Apr 2026 13:39:55 +0200 Subject: [PATCH 07/17] Add database and helm chart Signed-off-by: Nikolai Rodionov From 43d008c19f8351352dd5817b655ae7c8187ab012 Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Mon, 27 Apr 2026 13:40:05 +0200 Subject: [PATCH 08/17] Add database and helm chart Signed-off-by: Nikolai Rodionov From a7494956333e9e2afab7cb817c2eef118aa72eef Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Mon, 27 Apr 2026 18:34:40 +0200 Subject: [PATCH 09/17] Someting Signed-off-by: Nikolai Rodionov --- api/v1/accounts.go | 1 + go.mod | 3 +++ go.sum | 14 ++++++++++++-- internal/controllers/accounts.go | 7 +++++-- main.go | 9 +++++++++ 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/api/v1/accounts.go b/api/v1/accounts.go index d2a7dc3..05f75e9 100644 --- a/api/v1/accounts.go +++ b/api/v1/accounts.go @@ -32,6 +32,7 @@ type AccountsServer struct { hashCost int16 } +// SignUp should create a new user and return JWT tokens so the sessions is active right after registration func (a *AccountsServer) SignUp(ctx context.Context, in *accounts.SignUpRequest) (*empty.Empty, error) { ctrl := &controllers.AccountController{ HashCost: a.hashCost, diff --git a/go.mod b/go.mod index 83c443f..e315daf 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/joho/godotenv v1.5.1 github.com/lib/pq v1.10.9 github.com/mattn/go-colorable v0.1.13 + github.com/redis/go-redis/v9 v9.18.0 github.com/sirupsen/logrus v1.9.3 go.uber.org/zap v1.27.0 golang.org/x/crypto v0.47.0 @@ -45,6 +46,7 @@ require ( github.com/containerd/platforms v0.2.1 // indirect github.com/cyphar/filepath-securejoin v0.6.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/emicklei/go-restful/v3 v3.12.2 // indirect github.com/evanphx/json-patch v5.9.11+incompatible // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect @@ -104,6 +106,7 @@ require ( github.com/spf13/pflag v1.0.10 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xlab/treeprint v1.2.0 // indirect + go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect diff --git a/go.sum b/go.sum index 6f18223..d398867 100644 --- a/go.sum +++ b/go.sum @@ -40,6 +40,10 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -205,6 +209,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -294,8 +300,8 @@ github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fO github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U= github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc= github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ= -github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= -github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= +github.com/redis/go-redis/v9 v9.18.0 h1:pMkxYPkEbMPwRdenAzUNyFNrDgHx9U+DrBabWNfSRQs= +github.com/redis/go-redis/v9 v9.18.0/go.mod h1:k3ufPphLU5YXwNTUcCRXGxUoF1fqxnhFQmscfkCoDA0= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rubenv/sql-migrate v1.8.1 h1:EPNwCvjAowHI3TnZ+4fQu3a915OpnQoPAjTXCGOy2U0= @@ -335,6 +341,8 @@ github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 h1:UW0+QyeyBVhn+COBec3nGhfnFe5lwB0ic1JBVjzhk0w= @@ -382,6 +390,8 @@ go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4Etq go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= diff --git a/internal/controllers/accounts.go b/internal/controllers/accounts.go index 7bbc60e..c93d7a9 100644 --- a/internal/controllers/accounts.go +++ b/internal/controllers/accounts.go @@ -6,10 +6,12 @@ import ( "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/hash" "github.com/google/uuid" + "github.com/redis/go-redis/v9" ) type AccountController struct { DB *sql.DB + Redis *redis.Client DevMode bool HashCost int16 } @@ -19,8 +21,7 @@ type JWT struct { AccessToken string } -type AccountParams struct { -} +type AccountParams struct{} type AccountData struct { Username string @@ -36,9 +37,11 @@ func (c *AccountController) Create(ctx context.Context, data *AccountData) error if err != nil { return nil } + query := "INSERT INTO users (uuid, username, email, password_hash) VALUES ($1, $2, $3, $4)" if _, err := c.DB.Query(query, data.UUID, data.Username, data.Email, passwordHash); err != nil { return err } + return nil } diff --git a/main.go b/main.go index d93ccfc..5bc45e5 100644 --- a/main.go +++ b/main.go @@ -21,6 +21,7 @@ import ( _ "github.com/golang-migrate/migrate/v4/source/file" grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" _ "github.com/lib/pq" + "github.com/redis/go-redis/v9" "google.golang.org/grpc" "google.golang.org/grpc/reflection" ctrl "sigs.k8s.io/controller-runtime" @@ -38,6 +39,7 @@ type Serve struct { SmtpPassword string `env:"SOFTPLAYER_SMTP_PASSWORD"` DownloadDir string `env:"SOFTPLAYER_DOWNLOAD_DIR" default:"/tmp/softplayer"` DBConnectionString string `env:"SOFTPLAYER_DB_CONNECTION_STRING"` + RedisHost string `env:"SOFTPLAYER_REDIS_HOST"` } var CLI struct { @@ -142,6 +144,13 @@ func server(ctx context.Context, params Serve) error { grpc.StreamInterceptor(grpc_zap.StreamServerInterceptor(logger.SetupLogger("info"))), ) + rdb := redis.NewClient(&redis.Options{ + Addr: params.RedisHost, + }) + err = rdb.Set(ctx, "key", "value", 0).Err() + if err != nil { + panic(err) + } if params.Reflection { reflection.Register(grpcServer) } From f82778cbd1b103e3fc28416c1fa22a6983210155 Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Mon, 27 Apr 2026 21:35:39 +0200 Subject: [PATCH 10/17] Trying to implement auth Signed-off-by: Nikolai Rodionov --- api/v1/accounts.go | 77 +++++++++++++++----------- go.mod | 3 +- go.sum | 6 ++- internal/controllers/accounts.go | 45 +++++++++++++--- main.go | 93 +++++++++++++++++++++++++++----- 5 files changed, 167 insertions(+), 57 deletions(-) diff --git a/api/v1/accounts.go b/api/v1/accounts.go index 05f75e9..3bb33b9 100644 --- a/api/v1/accounts.go +++ b/api/v1/accounts.go @@ -2,64 +2,77 @@ package v1 import ( "context" - "database/sql" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers" - "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/email" + "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger" accounts "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1" "github.com/golang/protobuf/ptypes/empty" + "google.golang.org/grpc" "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/emptypb" ) -func NewAccountRPCImpl(db *sql.DB, hashCost int16, email *email.EmailConf, devMode bool) *AccountsServer { - return &AccountsServer{ - Params: &controllers.AccountParams{}, - SQLDriver: db, - emailConfig: *email, - devMode: devMode, - hashCost: hashCost, +func NewAccountRPCImpl(ctrl *controllers.AccountController) *AccountsNoAuthServer { + return &AccountsNoAuthServer{ + ctrl: ctrl, } } -type AccountsServer struct { - accounts.UnimplementedAccountsServiceServer - Params *controllers.AccountParams - SQLDriver *sql.DB - emailConfig email.EmailConf - // When dev mode is enabled, email won't be sent, instead the code will be returned in metadata - devMode bool - hashCost int16 +type AccountsNoAuthServer struct { + accounts.UnimplementedAccountsNoAuthServiceServer + ctrl *controllers.AccountController } // SignUp should create a new user and return JWT tokens so the sessions is active right after registration -func (a *AccountsServer) SignUp(ctx context.Context, in *accounts.SignUpRequest) (*empty.Empty, error) { - ctrl := &controllers.AccountController{ - HashCost: a.hashCost, - DB: a.SQLDriver, - DevMode: a.devMode, - } - +func (a *AccountsNoAuthServer) SignUp(ctx context.Context, in *accounts.SignUpRequest) (*empty.Empty, error) { + log := logger.FromContext(ctx) data := &controllers.AccountData{ Username: in.Data.GetName(), Password: in.Password.GetPassword(), Email: in.Data.GetEmail(), } - if err := ctrl.Create(ctx, data); err != nil { - return nil, status.Error(codes.Aborted, "User can't be created") + uuid, err := a.ctrl.Create(ctx, data) + if err != nil { + log.Error(err, "Couldn't create a user") + return nil, status.Error(codes.Aborted, "user can't be created") } + + accessToken, err := a.ctrl.GenerateAccessToken(uuid) + if err != nil { + log.Error(err, "Couldn't generate an access token") + return nil, status.Error(codes.Aborted, "Couldn't generate Access Token") + } + + refreshToken, err := a.ctrl.GenerateRefreshToken(uuid) + if err != nil { + log.Error(err, "Couldn't generate a refresh token") + return nil, status.Error(codes.Aborted, "Couldn't generate Access Token") + } + + header := metadata.Pairs( + "access-token", accessToken, + "refreshToken", refreshToken, + ) + + if err := grpc.SetHeader(ctx, header); err != nil { + log.Error(err, "Couldn't set headers") + return nil, status.Error(codes.Unknown, "Couldn't set headers") + } + + return &emptypb.Empty{}, nil +} + +func (a *AccountsNoAuthServer) SignIn(ctx context.Context, in *accounts.SignInRequest) (*empty.Empty, error) { return nil, status.Error(codes.Unimplemented, "Endpoint is not implemented") } -func (a *AccountsServer) SignIn(ctx context.Context, in *accounts.SignInRequest) (*empty.Empty, error) { +func (a *AccountsNoAuthServer) ResetPassword(ctx context.Context, in *accounts.ResetPasswordRequest) (*empty.Empty, error) { return nil, status.Error(codes.Unimplemented, "Endpoint is not implemented") } -func (a *AccountsServer) ResetPassword(ctx context.Context, in *accounts.ResetPasswordRequest) (*empty.Empty, error) { - return nil, status.Error(codes.Unimplemented, "Endpoint is not implemented") -} - -func (acc *AccountsServer) NewPassword(ctx context.Context, in *accounts.NewPasswordRequest) (*empty.Empty, error) { +func (acc *AccountsNoAuthServer) NewPassword(ctx context.Context, in *accounts.NewPasswordRequest) (*empty.Empty, error) { return nil, status.Error(codes.Unimplemented, "Endpoint is not implemented") } diff --git a/go.mod b/go.mod index e315daf..2be84a6 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/alecthomas/kong v1.15.0 github.com/go-logr/logr v1.4.3 github.com/go-logr/zapr v1.3.0 + github.com/golang-jwt/jwt/v5 v5.2.2 github.com/golang-migrate/migrate/v4 v4.19.1 github.com/google/uuid v1.6.0 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 @@ -136,7 +137,7 @@ require ( ) require ( - gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260427034527-ad8a1eb091c2 + gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260427171725-b565d3fa2b01 github.com/golang/protobuf v1.5.4 golang.org/x/net v0.49.0 // indirect golang.org/x/sys v0.40.0 // indirect diff --git a/go.sum b/go.sum index d398867..a20b3ff 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260427034527-ad8a1eb091c2 h1:iOJrE/EiQ3JPgcwgp4oddMX2gil364TomWSUxeDgSvE= -gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260427034527-ad8a1eb091c2/go.mod h1:AgOh1lkPHyRgBf3/s1btKcAqke/33LbKYarTD13qeAg= +gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260427171725-b565d3fa2b01 h1:iaBLJRS0A6QykeBgIj170itknvQ4Zc5LSe+6hmsRL7g= +gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260427171725-b565d3fa2b01/go.mod h1:AgOh1lkPHyRgBf3/s1btKcAqke/33LbKYarTD13qeAg= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= @@ -147,6 +147,8 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/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.19.1 h1:OCyb44lFuQfYXYLx1SCxPZQGU7mcaZ7gH9yH4jSFbBA= github.com/golang-migrate/migrate/v4 v4.19.1/go.mod h1:CTcgfjxhaUtsLipnLoQRWCrjYXycRz/g5+RWDuYgPrE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= diff --git a/internal/controllers/accounts.go b/internal/controllers/accounts.go index c93d7a9..d4757bf 100644 --- a/internal/controllers/accounts.go +++ b/internal/controllers/accounts.go @@ -3,17 +3,22 @@ package controllers import ( "context" "database/sql" + "time" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/hash" + "github.com/golang-jwt/jwt/v5" "github.com/google/uuid" "github.com/redis/go-redis/v9" ) type AccountController struct { - DB *sql.DB - Redis *redis.Client - DevMode bool - HashCost int16 + DB *sql.DB + Redis *redis.Client + DevMode bool + HashCost int16 + AccessTokenTTL time.Duration + RefrestTokenTTL time.Duration + JWTSecret []byte } type JWT struct { @@ -30,18 +35,42 @@ type AccountData struct { UUID string } -func (c *AccountController) Create(ctx context.Context, data *AccountData) error { +func (c *AccountController) Create(ctx context.Context, data *AccountData) (string, error) { data.UUID = uuid.New().String() passwordHash, err := hash.HashPassword(data.Password, int(c.HashCost)) if err != nil { - return nil + return "", nil } query := "INSERT INTO users (uuid, username, email, password_hash) VALUES ($1, $2, $3, $4)" if _, err := c.DB.Query(query, data.UUID, data.Username, data.Email, passwordHash); err != nil { - return err + return "", err } - return nil + return data.UUID, nil +} + +func (c *AccountController) GenerateAccessToken(userID string) (string, error) { + claims := jwt.MapClaims{ + "user_id": userID, + "type": "access", + "exp": time.Now().Add(c.AccessTokenTTL).Unix(), + } + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + return token.SignedString(c.JWTSecret) +} + +func (c *AccountController) GenerateRefreshToken(userID string) (string, error) { + tokenID := uuid.New().String() + claims := jwt.MapClaims{ + "user_id": userID, + "token_id": tokenID, + "type": "refresh", + "exp": time.Now().Add(c.RefrestTokenTTL).Unix(), + } + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + return token.SignedString(c.JWTSecret) } diff --git a/main.go b/main.go index 5bc45e5..ea96cc8 100644 --- a/main.go +++ b/main.go @@ -7,8 +7,11 @@ import ( "fmt" "net" "os" + "strings" + "time" v1 "gitea.badhouseplants.net/softplayer/softplayer-backend/api/v1" + "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/email" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger" accounts "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1" @@ -16,6 +19,7 @@ import ( email_proto "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/email/v1" "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/environments/v1" "github.com/alecthomas/kong" + "github.com/golang-jwt/jwt/v5" "github.com/golang-migrate/migrate/v4" "github.com/golang-migrate/migrate/v4/database/postgres" _ "github.com/golang-migrate/migrate/v4/source/file" @@ -23,23 +27,27 @@ import ( _ "github.com/lib/pq" "github.com/redis/go-redis/v9" "google.golang.org/grpc" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/reflection" ctrl "sigs.k8s.io/controller-runtime" ) type Serve struct { - Port int64 `short:"p" env:"SOFTPLAYER_PORT" default:"4020"` - Host string `env:"SOFTPLAYER_HOST" default:"0.0.0.0"` - HashCost int16 `env:"SOFTPLAYER_HASH_COST" default:"1"` - Reflection bool `env:"SOFTPLAYER_REFLECTION" default:"false"` - DevMode bool `env:"SOFTPLAYER_DEV_MODE" default:"false"` - SmtpHost string `env:"SOFTPLAYER_SMTP_HOST"` - SmtpPort string `env:"SOFTPLAYER_SMTP_PORT" default:"587"` - SmtpFrom string `env:"SOFTPLAYER_SMTP_FROM" default:"overlord@badhouseplants.net"` - SmtpPassword string `env:"SOFTPLAYER_SMTP_PASSWORD"` - DownloadDir string `env:"SOFTPLAYER_DOWNLOAD_DIR" default:"/tmp/softplayer"` - DBConnectionString string `env:"SOFTPLAYER_DB_CONNECTION_STRING"` - RedisHost string `env:"SOFTPLAYER_REDIS_HOST"` + Port int64 `short:"p" env:"SOFTPLAYER_PORT" default:"4020"` + Host string `env:"SOFTPLAYER_HOST" default:"0.0.0.0"` + HashCost int16 `env:"SOFTPLAYER_HASH_COST" default:"1"` + Reflection bool `env:"SOFTPLAYER_REFLECTION" default:"false"` + DevMode bool `env:"SOFTPLAYER_DEV_MODE" default:"false"` + SmtpHost string `env:"SOFTPLAYER_SMTP_HOST"` + SmtpPort string `env:"SOFTPLAYER_SMTP_PORT" default:"587"` + SmtpFrom string `env:"SOFTPLAYER_SMTP_FROM" default:"overlord@badhouseplants.net"` + SmtpPassword string `env:"SOFTPLAYER_SMTP_PASSWORD"` + DownloadDir string `env:"SOFTPLAYER_DOWNLOAD_DIR" default:"/tmp/softplayer"` + DBConnectionString string `env:"SOFTPLAYER_DB_CONNECTION_STRING"` + RedisHost string `env:"SOFTPLAYER_REDIS_HOST"` + RefrestTokenTTL time.Duration `default:"8h"` + AccessTokenTTL time.Duration `default:"15m"` + JWTSecret string `default:"qwertyu9"` } var CLI struct { @@ -92,6 +100,52 @@ func migrateDB(ctx context.Context, db *sql.DB) error { return nil } +func extractToken(ctx context.Context) (string, error) { + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return "", fmt.Errorf("missing metadata") + } + + auth := md.Get("authorization") + if len(auth) == 0 { + return "", fmt.Errorf("missing auth token") + } + + token := strings.TrimPrefix(auth[0], "Bearer ") + return token, nil +} + +func AuthInterceptor( + ctx context.Context, + req interface{}, + info *grpc.UnaryServerInfo, + handler grpc.UnaryHandler, +) (interface{}, error) { + if !strings.Contains(info.FullMethod, "NoAuth") { + tokenString, err := extractToken(ctx) + if err != nil { + return nil, err + } + + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) { + // hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key") + return []byte("qwertyu9"), nil + }, jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()})) + if err != nil { + return nil, err + } + + if claims, ok := token.Claims.(jwt.MapClaims); ok { + fmt.Println(claims["foo"], claims["nbf"]) + } else { + fmt.Println(err) + } + + return handler(ctx, req) + } + return nil, nil +} + func server(ctx context.Context, params Serve) error { // Make sure the download dir exists log := logger.FromContext(ctx) @@ -140,7 +194,10 @@ func server(ctx context.Context, params Serve) error { return err } grpcServer := grpc.NewServer( - grpc.UnaryInterceptor(grpc_zap.UnaryServerInterceptor(logger.SetupLogger("info"))), + grpc.UnaryInterceptor( + grpc_zap.UnaryServerInterceptor(logger.SetupLogger("info")), + ), + grpc.UnaryInterceptor(AuthInterceptor), grpc.StreamInterceptor(grpc_zap.StreamServerInterceptor(logger.SetupLogger("info"))), ) @@ -155,8 +212,16 @@ func server(ctx context.Context, params Serve) error { reflection.Register(grpcServer) } + accountCtrl := &controllers.AccountController{ + HashCost: params.HashCost, + DB: db, + DevMode: params.DevMode, + RefrestTokenTTL: params.RefrestTokenTTL, + AccessTokenTTL: params.AccessTokenTTL, + JWTSecret: []byte(params.JWTSecret), + } environments.RegisterEnvironmentsServer(grpcServer, v1.NewapiGrpcImpl(controller, log)) - accounts.RegisterAccountsServiceServer(grpcServer, v1.NewAccountRPCImpl(db, params.HashCost, &emailConfig, params.DevMode)) + accounts.RegisterAccountsNoAuthServiceServer(grpcServer, v1.NewAccountRPCImpl(accountCtrl)) email_proto.RegisterEmailValidationServer(grpcServer, v1.InitEmailServer(controller, &emailConfig, params.DevMode)) applications_proto.RegisterApplicationsServer(grpcServer, v1.NewApplicationsGrpcImpl(controller, log)) From 874d762efa607b4e844a10ca4e9114379d463db8 Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Tue, 28 Apr 2026 10:33:20 +0200 Subject: [PATCH 11/17] A couple of updates Signed-off-by: Nikolai Rodionov --- .env.dev | 2 + Taskfile.yml | 40 +++++++ api/v1/accounts.go | 2 +- helmfile.yaml.gotmpl | 1 + internal/controllers/accounts.go | 11 +- main.go | 178 +++++++++++-------------------- 6 files changed, 117 insertions(+), 117 deletions(-) create mode 100644 .env.dev create mode 100644 Taskfile.yml diff --git a/.env.dev b/.env.dev new file mode 100644 index 0000000..ba6aa41 --- /dev/null +++ b/.env.dev @@ -0,0 +1,2 @@ +export SOFTPLAYER_DB_CONNECTION_STRING=postgres://softplayer:qwertyu9@localhost:30432/softplayer?sslmode=disable +export SOFYTPLAYER_REDIS_HOST=localhost:30379 diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 0000000..d9bfa02 --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,40 @@ +# yaml-language-server: $schema=https://taskfile.dev/schema.json + +version: "3" + +tasks: + build: + desc: Build go code + cmd: go build + silent: true + + run-migrations-dev: + desc: Execute database migrations + env: + SOFTPLAYER_DB_CONNECTION_STRING: postgres://softplayer:qwertyu9@localhost:30432/softplayer?sslmode=disable + cmd: go run main.go migrate --migrations-path=file://migrations + + run-server-dev: + desc: Run the local dev server + env: + SOFTPLAYER_DB_CONNECTION_STRING: postgres://softplayer:qwertyu9@localhost:30432/softplayer?sslmode=disable + SOFTPLAYER_REDIS_HOST: localhost:30379 + cmd: go run main.go serve --dev-mode --reflection + + deploy-local-env: + desc: Run a kind cluster and deploy deps + deps: + - kind-cluster + - helmfile-deploy + + kind-cluster: + desc: Run a kind cluster + cmd: kind create cluster --config ./kind-config.yaml + + kind-cluster-remove: + desc: Remove the kind cluster + cmd: kind delete cluster + + helmfile-deploy: + desc: Deploy the helmfile for the local dev + cmd: helmfile apply diff --git a/api/v1/accounts.go b/api/v1/accounts.go index 3bb33b9..be597e9 100644 --- a/api/v1/accounts.go +++ b/api/v1/accounts.go @@ -46,7 +46,7 @@ func (a *AccountsNoAuthServer) SignUp(ctx context.Context, in *accounts.SignUpRe return nil, status.Error(codes.Aborted, "Couldn't generate Access Token") } - refreshToken, err := a.ctrl.GenerateRefreshToken(uuid) + refreshToken, err := a.ctrl.GenerateRefreshToken(ctx, uuid) if err != nil { log.Error(err, "Couldn't generate a refresh token") return nil, status.Error(codes.Aborted, "Couldn't generate Access Token") diff --git a/helmfile.yaml.gotmpl b/helmfile.yaml.gotmpl index a404f97..fe66f6d 100644 --- a/helmfile.yaml.gotmpl +++ b/helmfile.yaml.gotmpl @@ -1,5 +1,6 @@ environments: default: + kubeContext: kind-kind values: - databases: postgres: diff --git a/internal/controllers/accounts.go b/internal/controllers/accounts.go index d4757bf..e11d151 100644 --- a/internal/controllers/accounts.go +++ b/internal/controllers/accounts.go @@ -3,6 +3,7 @@ package controllers import ( "context" "database/sql" + "fmt" "time" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/hash" @@ -17,7 +18,7 @@ type AccountController struct { DevMode bool HashCost int16 AccessTokenTTL time.Duration - RefrestTokenTTL time.Duration + RefreshTokenTTL time.Duration JWTSecret []byte } @@ -62,15 +63,19 @@ func (c *AccountController) GenerateAccessToken(userID string) (string, error) { return token.SignedString(c.JWTSecret) } -func (c *AccountController) GenerateRefreshToken(userID string) (string, error) { +func (c *AccountController) GenerateRefreshToken(ctx context.Context, userID string) (string, error) { tokenID := uuid.New().String() claims := jwt.MapClaims{ "user_id": userID, "token_id": tokenID, "type": "refresh", - "exp": time.Now().Add(c.RefrestTokenTTL).Unix(), + "exp": time.Now().Add(c.RefreshTokenTTL).Unix(), } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + redisKey := fmt.Sprintf("refresh_token:%s", tokenID) + if err := c.Redis.Set(ctx, redisKey, userID, c.RefreshTokenTTL).Err(); err != nil { + return "", err + } return token.SignedString(c.JWTSecret) } diff --git a/main.go b/main.go index ea96cc8..98d4f7d 100644 --- a/main.go +++ b/main.go @@ -6,52 +6,54 @@ import ( "errors" "fmt" "net" - "os" - "strings" "time" v1 "gitea.badhouseplants.net/softplayer/softplayer-backend/api/v1" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers" - "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/email" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger" accounts "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1" - applications_proto "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/applications/v1" - email_proto "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/email/v1" - "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/environments/v1" "github.com/alecthomas/kong" - "github.com/golang-jwt/jwt/v5" "github.com/golang-migrate/migrate/v4" "github.com/golang-migrate/migrate/v4/database/postgres" _ "github.com/golang-migrate/migrate/v4/source/file" grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" _ "github.com/lib/pq" - "github.com/redis/go-redis/v9" "google.golang.org/grpc" - "google.golang.org/grpc/metadata" "google.golang.org/grpc/reflection" - ctrl "sigs.k8s.io/controller-runtime" + + "github.com/redis/go-redis/v9" ) -type Serve struct { - Port int64 `short:"p" env:"SOFTPLAYER_PORT" default:"4020"` - Host string `env:"SOFTPLAYER_HOST" default:"0.0.0.0"` - HashCost int16 `env:"SOFTPLAYER_HASH_COST" default:"1"` - Reflection bool `env:"SOFTPLAYER_REFLECTION" default:"false"` - DevMode bool `env:"SOFTPLAYER_DEV_MODE" default:"false"` - SmtpHost string `env:"SOFTPLAYER_SMTP_HOST"` - SmtpPort string `env:"SOFTPLAYER_SMTP_PORT" default:"587"` - SmtpFrom string `env:"SOFTPLAYER_SMTP_FROM" default:"overlord@badhouseplants.net"` - SmtpPassword string `env:"SOFTPLAYER_SMTP_PASSWORD"` - DownloadDir string `env:"SOFTPLAYER_DOWNLOAD_DIR" default:"/tmp/softplayer"` - DBConnectionString string `env:"SOFTPLAYER_DB_CONNECTION_STRING"` - RedisHost string `env:"SOFTPLAYER_REDIS_HOST"` - RefrestTokenTTL time.Duration `default:"8h"` - AccessTokenTTL time.Duration `default:"15m"` - JWTSecret string `default:"qwertyu9"` +var CLI struct { + Serve Serve `cmd:"" help:"Start the grpc server"` + Migrate Migrate `cmd:"" help:"Run the database migrations"` } -var CLI struct { - Serve Serve `cmd:"" help:"Start the grpc server"` +type Serve struct { + // Service related + Port int64 `short:"p" env:"SOFTPLAYER_PORT" default:"4020"` + Host string `env:"SOFTPLAYER_HOST" default:"0.0.0.0"` + HashCost int16 `env:"SOFTPLAYER_HASH_COST" default:"1"` + // SMTP Config + SMTPHost string `env:"SOFTPLAYER_SMTP_HOST"` + SMTPPort string `env:"SOFTPLAYER_SMTP_PORT" default:"587"` + SMTPFrom string `env:"SOFTPLAYER_SMTP_FROM" default:"overlord@badhouseplants.net"` + SMTPPassword string `env:"SOFTPLAYER_SMTP_PASSWORD"` + // Database and redis + DBConnectionString string `env:"SOFTPLAYER_DB_CONNECTION_STRING"` + RedisHost string `env:"SOFTPLAYER_REDIS_HOST"` + // JWT parameters + RefrestTokenTTL time.Duration `default:"8h"` + AccessTokenTTL time.Duration `default:"15m"` + JWTSecret string `default:"qwertyu9"` + // Dev and logging + Reflection bool `env:"SOFTPLAYER_REFLECTION" default:"false"` + DevMode bool `env:"SOFTPLAYER_DEV_MODE" default:"false"` +} + +type Migrate struct { + DBConnectionString string `env:"SOFTPLAYER_DB_CONNECTION_STRING"` + MigrationsPath string `env:"SOFTPLAYER_DB_MIGRATIOON_PATH" default:"file://migrations"` } func main() { @@ -62,15 +64,25 @@ func main() { if err := server(ctx, CLI.Serve); err != nil { panic(err) } + case "migrate": + if err := migrateDB(ctx, CLI.Migrate); err != nil { + panic(err) + } default: panic(kongCtx.Command()) } } -func migrateDB(ctx context.Context, db *sql.DB) error { +// Migrate the database to the latest version +func migrateDB(ctx context.Context, params Migrate) error { log := logger.FromContext(ctx) - log.Info("Starting a database migration driver") + db, err := sql.Open("postgres", params.DBConnectionString) + if err != nil { + log.Error(err, "Couldn't start a database driver") + return err + } + driver, err := postgres.WithInstance(db, &postgres.Config{}) if err != nil { log.Error(err, "Couldn't start a database migration driver") @@ -79,7 +91,7 @@ func migrateDB(ctx context.Context, db *sql.DB) error { log.Info("Preparing database migrations") m, err := migrate.NewWithDatabaseInstance( - "file://migrations", + params.MigrationsPath, "postgres", driver) if err != nil { log.Error(err, "Couldn't perform database migrations") @@ -87,7 +99,7 @@ func migrateDB(ctx context.Context, db *sql.DB) error { } log.Info("Starting database migrations") - err = m.Up() // or m.Steps(2) if you want to explicitly set the number of migrations to r + err = m.Up() if err != nil { if errors.Is(err, migrate.ErrNoChange) { log.Info("Database is already up-to-date") @@ -100,93 +112,40 @@ func migrateDB(ctx context.Context, db *sql.DB) error { return nil } -func extractToken(ctx context.Context) (string, error) { - md, ok := metadata.FromIncomingContext(ctx) - if !ok { - return "", fmt.Errorf("missing metadata") - } - - auth := md.Get("authorization") - if len(auth) == 0 { - return "", fmt.Errorf("missing auth token") - } - - token := strings.TrimPrefix(auth[0], "Bearer ") - return token, nil -} - -func AuthInterceptor( - ctx context.Context, - req interface{}, - info *grpc.UnaryServerInfo, - handler grpc.UnaryHandler, -) (interface{}, error) { - if !strings.Contains(info.FullMethod, "NoAuth") { - tokenString, err := extractToken(ctx) - if err != nil { - return nil, err - } - - token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) { - // hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key") - return []byte("qwertyu9"), nil - }, jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()})) - if err != nil { - return nil, err - } - - if claims, ok := token.Claims.(jwt.MapClaims); ok { - fmt.Println(claims["foo"], claims["nbf"]) - } else { - fmt.Println(err) - } - - return handler(ctx, req) - } - return nil, nil -} - +// Run the grpc backend server func server(ctx context.Context, params Serve) error { // Make sure the download dir exists log := logger.FromContext(ctx) - if err := os.MkdirAll(params.DownloadDir, 0o777); err != nil { - log.Error(err, "Coulnd't create a download dir") - return err - } - // Init a logger - log.Info("Starting a kubernetes manager") - controller, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{}) - if err != nil { - log.Error(err, "Coulnd't start a kube manager") - return err - } + //log.Info("Starting a kubernetes manager") + //controller, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{}) + //if err != nil { + // log.Error(err, "Coulnd't start a kube manager") + // return err + //} // TODO: Handle the error - go func() { - if err := controller.Start(ctx); err != nil { - panic(err) - } - }() + //go func() { + // if err := controller.Start(ctx); err != nil { + // panic(err) + // } + //}() log.Info("Opening a database connection") + db, err := sql.Open("postgres", params.DBConnectionString) if err != nil { log.Error(err, "Couldn't start a database driver") return err } - if err := migrateDB(ctx, db); err != nil { - log.Error(err, "Error occured while executing migrations") - return err - } - emailConfig := email.EmailConf{ - From: params.SmtpFrom, - Password: params.SmtpPassword, - SmtpHost: params.SmtpHost, - SmtpPort: params.SmtpPort, - } + //emailConfig := email.EmailConf{ + // From: params.SmtpFrom, + // Password: params.SmtpPassword, + // SmtpHost: params.SmtpHost, + // SmtpPort: params.SmtpPort, + //} address := fmt.Sprintf("%s:%d", params.Host, params.Port) lis, err := net.Listen("tcp", address) @@ -197,17 +156,12 @@ func server(ctx context.Context, params Serve) error { grpc.UnaryInterceptor( grpc_zap.UnaryServerInterceptor(logger.SetupLogger("info")), ), - grpc.UnaryInterceptor(AuthInterceptor), grpc.StreamInterceptor(grpc_zap.StreamServerInterceptor(logger.SetupLogger("info"))), ) rdb := redis.NewClient(&redis.Options{ Addr: params.RedisHost, }) - err = rdb.Set(ctx, "key", "value", 0).Err() - if err != nil { - panic(err) - } if params.Reflection { reflection.Register(grpcServer) } @@ -216,14 +170,12 @@ func server(ctx context.Context, params Serve) error { HashCost: params.HashCost, DB: db, DevMode: params.DevMode, - RefrestTokenTTL: params.RefrestTokenTTL, + RefreshTokenTTL: params.RefrestTokenTTL, AccessTokenTTL: params.AccessTokenTTL, JWTSecret: []byte(params.JWTSecret), + Redis: rdb, } - environments.RegisterEnvironmentsServer(grpcServer, v1.NewapiGrpcImpl(controller, log)) accounts.RegisterAccountsNoAuthServiceServer(grpcServer, v1.NewAccountRPCImpl(accountCtrl)) - email_proto.RegisterEmailValidationServer(grpcServer, v1.InitEmailServer(controller, &emailConfig, params.DevMode)) - applications_proto.RegisterApplicationsServer(grpcServer, v1.NewApplicationsGrpcImpl(controller, log)) if err := grpcServer.Serve(lis); err != nil { return err From fa78e2a787b452ec19291e4476e6638a3509d0e7 Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Tue, 28 Apr 2026 10:33:24 +0200 Subject: [PATCH 12/17] A couple of updates Signed-off-by: Nikolai Rodionov --- .env.dev | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .env.dev diff --git a/.env.dev b/.env.dev deleted file mode 100644 index ba6aa41..0000000 --- a/.env.dev +++ /dev/null @@ -1,2 +0,0 @@ -export SOFTPLAYER_DB_CONNECTION_STRING=postgres://softplayer:qwertyu9@localhost:30432/softplayer?sslmode=disable -export SOFYTPLAYER_REDIS_HOST=localhost:30379 From 69cdecb5b6cef0eed52231b48b30f06906e6ad69 Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Tue, 28 Apr 2026 13:36:12 +0200 Subject: [PATCH 13/17] Enable auth endpoint Signed-off-by: Nikolai Rodionov --- api/v1/accounts_auth.go | 17 ++++++++ api/v1/{accounts.go => accounts_no_auth.go} | 2 +- go.mod | 2 +- go.sum | 4 +- internal/interceptors/authjwt.go | 43 +++++++++++++++++++++ main.go | 10 ++++- 6 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 api/v1/accounts_auth.go rename api/v1/{accounts.go => accounts_no_auth.go} (96%) create mode 100644 internal/interceptors/authjwt.go diff --git a/api/v1/accounts_auth.go b/api/v1/accounts_auth.go new file mode 100644 index 0000000..2c42abe --- /dev/null +++ b/api/v1/accounts_auth.go @@ -0,0 +1,17 @@ +package v1 + +import ( + "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers" + accounts "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1" +) + +func NewAccountAuthRPCImpl(ctrl *controllers.AccountController) *AccountsAuthServer { + return &AccountsAuthServer{ + ctrl: ctrl, + } +} + +type AccountsAuthServer struct { + accounts.UnimplementedAccountsAuthServiceServer + ctrl *controllers.AccountController +} diff --git a/api/v1/accounts.go b/api/v1/accounts_no_auth.go similarity index 96% rename from api/v1/accounts.go rename to api/v1/accounts_no_auth.go index be597e9..4331e9c 100644 --- a/api/v1/accounts.go +++ b/api/v1/accounts_no_auth.go @@ -14,7 +14,7 @@ import ( "google.golang.org/protobuf/types/known/emptypb" ) -func NewAccountRPCImpl(ctrl *controllers.AccountController) *AccountsNoAuthServer { +func NewAccountNoAuthRPCImpl(ctrl *controllers.AccountController) *AccountsNoAuthServer { return &AccountsNoAuthServer{ ctrl: ctrl, } diff --git a/go.mod b/go.mod index 2be84a6..1a052d0 100644 --- a/go.mod +++ b/go.mod @@ -137,7 +137,7 @@ require ( ) require ( - gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260427171725-b565d3fa2b01 + gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260428111006-efa5c57e6a14 github.com/golang/protobuf v1.5.4 golang.org/x/net v0.49.0 // indirect golang.org/x/sys v0.40.0 // indirect diff --git a/go.sum b/go.sum index a20b3ff..e3e8e4c 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260427171725-b565d3fa2b01 h1:iaBLJRS0A6QykeBgIj170itknvQ4Zc5LSe+6hmsRL7g= -gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260427171725-b565d3fa2b01/go.mod h1:AgOh1lkPHyRgBf3/s1btKcAqke/33LbKYarTD13qeAg= +gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260428111006-efa5c57e6a14 h1:PwOWag8dum67a1w/QIP7NlSGPL/Z7rZDHAwjRJjyk3U= +gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260428111006-efa5c57e6a14/go.mod h1:AgOh1lkPHyRgBf3/s1btKcAqke/33LbKYarTD13qeAg= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= diff --git a/internal/interceptors/authjwt.go b/internal/interceptors/authjwt.go new file mode 100644 index 0000000..ce778eb --- /dev/null +++ b/internal/interceptors/authjwt.go @@ -0,0 +1,43 @@ +package interceptors + +import ( + "context" + "strings" + + "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type JWTVerifier struct { + secret []byte + serverCtx context.Context +} + +func NewJWTVerifier(ctx context.Context, secret []byte) *JWTVerifier { + return &JWTVerifier{ + serverCtx: ctx, + secret: secret, + } +} + +// This is an interceptors that should verify that a user is authorized +func (v *JWTVerifier) JWTAuthInterceptor( + ctx context.Context, + req interface{}, + info *grpc.UnaryServerInfo, + handler grpc.UnaryHandler, +) (interface{}, error) { + log := logger.FromContext(v.serverCtx).WithValues("method", info.FullMethod) + if !strings.Contains(info.FullMethod, "NoAuth") { + log.Info("Checking the JWT token") + return nil, status.Error(codes.Unauthenticated, "Use is not authorized") + // Get the token from the metadata + // Validate the token + // Get the user id from the token + } else { + log.Info("Auth is not required for this request") + } + return handler(ctx, req) +} diff --git a/main.go b/main.go index 98d4f7d..e79dbeb 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,7 @@ import ( v1 "gitea.badhouseplants.net/softplayer/softplayer-backend/api/v1" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers" + "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/interceptors" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger" accounts "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1" "github.com/alecthomas/kong" @@ -152,9 +153,13 @@ func server(ctx context.Context, params Serve) error { if err != nil { return err } + + jwtVerifier := interceptors.NewJWTVerifier(ctx, []byte(params.JWTSecret)) + grpcServer := grpc.NewServer( - grpc.UnaryInterceptor( + grpc.ChainUnaryInterceptor( grpc_zap.UnaryServerInterceptor(logger.SetupLogger("info")), + jwtVerifier.JWTAuthInterceptor, ), grpc.StreamInterceptor(grpc_zap.StreamServerInterceptor(logger.SetupLogger("info"))), ) @@ -175,7 +180,8 @@ func server(ctx context.Context, params Serve) error { JWTSecret: []byte(params.JWTSecret), Redis: rdb, } - accounts.RegisterAccountsNoAuthServiceServer(grpcServer, v1.NewAccountRPCImpl(accountCtrl)) + accounts.RegisterAccountsNoAuthServiceServer(grpcServer, v1.NewAccountNoAuthRPCImpl(accountCtrl)) + accounts.RegisterAccountsAuthServiceServer(grpcServer, v1.NewAccountAuthRPCImpl(accountCtrl)) if err := grpcServer.Serve(lis); err != nil { return err From d2977ce4d9f6ecc3cf90aed5fc085c7426b13594 Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Tue, 28 Apr 2026 13:46:52 +0200 Subject: [PATCH 14/17] Access token auth implemented Signed-off-by: Nikolai Rodionov --- internal/interceptors/authjwt.go | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/internal/interceptors/authjwt.go b/internal/interceptors/authjwt.go index ce778eb..30eca99 100644 --- a/internal/interceptors/authjwt.go +++ b/internal/interceptors/authjwt.go @@ -2,11 +2,14 @@ package interceptors import ( "context" + "fmt" "strings" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger" + "github.com/golang-jwt/jwt/v5" "google.golang.org/grpc" "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) @@ -32,7 +35,25 @@ func (v *JWTVerifier) JWTAuthInterceptor( log := logger.FromContext(v.serverCtx).WithValues("method", info.FullMethod) if !strings.Contains(info.FullMethod, "NoAuth") { log.Info("Checking the JWT token") - return nil, status.Error(codes.Unauthenticated, "Use is not authorized") + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return nil, status.Error(codes.Unauthenticated, "User is not authorized") + } + + tokenString := md.Get("token")[0] + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) { + // hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key") + return v.secret, nil + }, jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()})) + if err != nil { + return nil, status.Error(codes.Unauthenticated, "User is not authorized") + } + + if claims, ok := token.Claims.(jwt.MapClaims); ok { + fmt.Println(claims["userID"]) + } else { + fmt.Println(err) + } // Get the token from the metadata // Validate the token // Get the user id from the token From bec92425a56cc7e55dc1459e32657f69e114de0a Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Tue, 28 Apr 2026 14:27:06 +0200 Subject: [PATCH 15/17] Access token auth implemented Signed-off-by: Nikolai Rodionov --- api/v1/accounts_no_auth.go | 38 ++++++++++++++++++++++++++++++++ internal/controllers/accounts.go | 32 +++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/api/v1/accounts_no_auth.go b/api/v1/accounts_no_auth.go index 4331e9c..e099fc1 100644 --- a/api/v1/accounts_no_auth.go +++ b/api/v1/accounts_no_auth.go @@ -76,3 +76,41 @@ func (a *AccountsNoAuthServer) ResetPassword(ctx context.Context, in *accounts.R func (acc *AccountsNoAuthServer) NewPassword(ctx context.Context, in *accounts.NewPasswordRequest) (*empty.Empty, error) { return nil, status.Error(codes.Unimplemented, "Endpoint is not implemented") } + +func (a *AccountsNoAuthServer) RefreshToken(ctx context.Context, in *empty.Empty) (*empty.Empty, error) { + log := logger.FromContext(ctx) + + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return nil, status.Error(codes.Unauthenticated, "User is not authorized") + } + + tokenString := md.Get("token")[0] + uuid, err := a.ctrl.ValidateRefreshToken(ctx, tokenString) + if err != nil { + return nil, status.Error(codes.Unauthenticated, "refresh token is invalid") + } + accessToken, err := a.ctrl.GenerateAccessToken(uuid) + if err != nil { + log.Error(err, "Couldn't generate an access token") + return nil, status.Error(codes.Aborted, "Couldn't generate Access Token") + } + + refreshToken, err := a.ctrl.GenerateRefreshToken(ctx, uuid) + if err != nil { + log.Error(err, "Couldn't generate a refresh token") + return nil, status.Error(codes.Aborted, "Couldn't generate Access Token") + } + + header := metadata.Pairs( + "access-token", accessToken, + "refreshToken", refreshToken, + ) + + if err := grpc.SetHeader(ctx, header); err != nil { + log.Error(err, "Couldn't set headers") + return nil, status.Error(codes.Unknown, "Couldn't set headers") + } + + return &emptypb.Empty{}, nil +} diff --git a/internal/controllers/accounts.go b/internal/controllers/accounts.go index e11d151..6495932 100644 --- a/internal/controllers/accounts.go +++ b/internal/controllers/accounts.go @@ -3,6 +3,7 @@ package controllers import ( "context" "database/sql" + "errors" "fmt" "time" @@ -79,3 +80,34 @@ func (c *AccountController) GenerateRefreshToken(ctx context.Context, userID str } return token.SignedString(c.JWTSecret) } + +// It must validate the refresh token +// Get it's id from the content +// Find a corresponding token in redis, and if it's found, remove it and create a new one +func (c *AccountController) ValidateRefreshToken(ctx context.Context, tokenString string) (string, error) { + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) { + // hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key") + return c.JWTSecret, nil + }, jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()})) + if err != nil { + return "", err + } + + var tokenID string + var userID string + if claims, ok := token.Claims.(jwt.MapClaims); ok { + tokenID = claims["token_id"].(string) + userID = claims["user_id"].(string) + } else { + return "", errors.New("token id is not set") + } + + userIDRedis := c.Redis.Get(ctx, tokenID).String() + if c.Redis.Del(ctx, tokenID).Err() != nil { + return "", err + } + if userID != userIDRedis { + return "", errors.New("user id doesn't match") + } + return userIDRedis, nil +} From 833834bef0a0e20267945111d39cf15ae3ee2dd2 Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Tue, 28 Apr 2026 22:54:51 +0200 Subject: [PATCH 16/17] Something about auth Signed-off-by: Nikolai Rodionov --- api/v1/accounts_auth.go | 42 ++++++++++++++++++++++++++++++++ api/v1/accounts_no_auth.go | 38 ----------------------------- go.mod | 3 ++- go.sum | 9 +++++-- internal/controllers/accounts.go | 35 ++++++++++---------------- main.go | 37 +++++++++++++++++++++++++--- 6 files changed, 98 insertions(+), 66 deletions(-) diff --git a/api/v1/accounts_auth.go b/api/v1/accounts_auth.go index 2c42abe..b3687c9 100644 --- a/api/v1/accounts_auth.go +++ b/api/v1/accounts_auth.go @@ -1,8 +1,17 @@ package v1 import ( + "context" + "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers" + "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger" accounts "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1" + "github.com/golang/protobuf/ptypes/empty" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/emptypb" ) func NewAccountAuthRPCImpl(ctrl *controllers.AccountController) *AccountsAuthServer { @@ -15,3 +24,36 @@ type AccountsAuthServer struct { accounts.UnimplementedAccountsAuthServiceServer ctrl *controllers.AccountController } + +func (a *AccountsAuthServer) RefreshToken(ctx context.Context, in *empty.Empty) (*empty.Empty, error) { + tokenID := ctx.Value("token_id").(string) + userID := ctx.Value("user_id").(string) + log := logger.FromContext(ctx) + uuid, err := a.ctrl.ValidateRefreshToken(ctx, tokenID, userID) + if err != nil { + return nil, status.Error(codes.Unauthenticated, "refresh token is invalid") + } + accessToken, err := a.ctrl.GenerateAccessToken(uuid) + if err != nil { + log.Error(err, "Couldn't generate an access token") + return nil, status.Error(codes.Aborted, "Couldn't generate Access Token") + } + + refreshToken, err := a.ctrl.GenerateRefreshToken(ctx, uuid) + if err != nil { + log.Error(err, "Couldn't generate a refresh token") + return nil, status.Error(codes.Aborted, "Couldn't generate Access Token") + } + + header := metadata.Pairs( + "access-token", accessToken, + "refreshToken", refreshToken, + ) + + if err := grpc.SetHeader(ctx, header); err != nil { + log.Error(err, "Couldn't set headers") + return nil, status.Error(codes.Unknown, "Couldn't set headers") + } + + return &emptypb.Empty{}, nil +} diff --git a/api/v1/accounts_no_auth.go b/api/v1/accounts_no_auth.go index e099fc1..4331e9c 100644 --- a/api/v1/accounts_no_auth.go +++ b/api/v1/accounts_no_auth.go @@ -76,41 +76,3 @@ func (a *AccountsNoAuthServer) ResetPassword(ctx context.Context, in *accounts.R func (acc *AccountsNoAuthServer) NewPassword(ctx context.Context, in *accounts.NewPasswordRequest) (*empty.Empty, error) { return nil, status.Error(codes.Unimplemented, "Endpoint is not implemented") } - -func (a *AccountsNoAuthServer) RefreshToken(ctx context.Context, in *empty.Empty) (*empty.Empty, error) { - log := logger.FromContext(ctx) - - md, ok := metadata.FromIncomingContext(ctx) - if !ok { - return nil, status.Error(codes.Unauthenticated, "User is not authorized") - } - - tokenString := md.Get("token")[0] - uuid, err := a.ctrl.ValidateRefreshToken(ctx, tokenString) - if err != nil { - return nil, status.Error(codes.Unauthenticated, "refresh token is invalid") - } - accessToken, err := a.ctrl.GenerateAccessToken(uuid) - if err != nil { - log.Error(err, "Couldn't generate an access token") - return nil, status.Error(codes.Aborted, "Couldn't generate Access Token") - } - - refreshToken, err := a.ctrl.GenerateRefreshToken(ctx, uuid) - if err != nil { - log.Error(err, "Couldn't generate a refresh token") - return nil, status.Error(codes.Aborted, "Couldn't generate Access Token") - } - - header := metadata.Pairs( - "access-token", accessToken, - "refreshToken", refreshToken, - ) - - if err := grpc.SetHeader(ctx, header); err != nil { - log.Error(err, "Couldn't set headers") - return nil, status.Error(codes.Unknown, "Couldn't set headers") - } - - return &emptypb.Empty{}, nil -} diff --git a/go.mod b/go.mod index 1a052d0..4f31774 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/golang-migrate/migrate/v4 v4.19.1 github.com/google/uuid v1.6.0 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 github.com/joho/godotenv v1.5.1 github.com/lib/pq v1.10.9 github.com/mattn/go-colorable v0.1.13 @@ -137,7 +138,7 @@ require ( ) require ( - gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260428111006-efa5c57e6a14 + gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260428191411-8c93fd05025a github.com/golang/protobuf v1.5.4 golang.org/x/net v0.49.0 // indirect golang.org/x/sys v0.40.0 // indirect diff --git a/go.sum b/go.sum index e3e8e4c..af99e68 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,13 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260428111006-efa5c57e6a14 h1:PwOWag8dum67a1w/QIP7NlSGPL/Z7rZDHAwjRJjyk3U= -gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260428111006-efa5c57e6a14/go.mod h1:AgOh1lkPHyRgBf3/s1btKcAqke/33LbKYarTD13qeAg= +gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260428191411-8c93fd05025a h1:8Mo14FqMkUcUNyyVfrTGZOe/BHn412olquEDqL+Fg6c= +gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260428191411-8c93fd05025a/go.mod h1:AgOh1lkPHyRgBf3/s1btKcAqke/33LbKYarTD13qeAg= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= @@ -182,6 +185,8 @@ github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJr github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 h1:FbSCl+KggFl+Ocym490i/EyXF4lPgLoUtcSWquBM0Rs= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= diff --git a/internal/controllers/accounts.go b/internal/controllers/accounts.go index 6495932..df3e21d 100644 --- a/internal/controllers/accounts.go +++ b/internal/controllers/accounts.go @@ -8,6 +8,7 @@ import ( "time" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/hash" + "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger" "github.com/golang-jwt/jwt/v5" "github.com/google/uuid" "github.com/redis/go-redis/v9" @@ -64,6 +65,10 @@ func (c *AccountController) GenerateAccessToken(userID string) (string, error) { return token.SignedString(c.JWTSecret) } +func redisKey(id string) string { + return fmt.Sprintf("refresh:%s", id) +} + func (c *AccountController) GenerateRefreshToken(ctx context.Context, userID string) (string, error) { tokenID := uuid.New().String() claims := jwt.MapClaims{ @@ -74,8 +79,7 @@ func (c *AccountController) GenerateRefreshToken(ctx context.Context, userID str } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - redisKey := fmt.Sprintf("refresh_token:%s", tokenID) - if err := c.Redis.Set(ctx, redisKey, userID, c.RefreshTokenTTL).Err(); err != nil { + if err := c.Redis.Set(ctx, redisKey(tokenID), userID, c.RefreshTokenTTL).Err(); err != nil { return "", err } return token.SignedString(c.JWTSecret) @@ -84,28 +88,15 @@ func (c *AccountController) GenerateRefreshToken(ctx context.Context, userID str // It must validate the refresh token // Get it's id from the content // Find a corresponding token in redis, and if it's found, remove it and create a new one -func (c *AccountController) ValidateRefreshToken(ctx context.Context, tokenString string) (string, error) { - token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) { - // hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key") - return c.JWTSecret, nil - }, jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()})) - if err != nil { - return "", err - } - - var tokenID string - var userID string - if claims, ok := token.Claims.(jwt.MapClaims); ok { - tokenID = claims["token_id"].(string) - userID = claims["user_id"].(string) - } else { - return "", errors.New("token id is not set") - } - - userIDRedis := c.Redis.Get(ctx, tokenID).String() - if c.Redis.Del(ctx, tokenID).Err() != nil { +func (c *AccountController) ValidateRefreshToken(ctx context.Context, tokenID, userID string) (string, error) { + log := logger.FromContext(ctx) + userIDRedis := c.Redis.Get(ctx, redisKey(tokenID)).Val() + if err := c.Redis.Del(ctx, redisKey(tokenID)).Err(); err != nil { + log.Error(err, "Couldn't delete redis entry") return "", err } + log.Info(userIDRedis) + log.Info(userID) if userID != userIDRedis { return "", errors.New("user id doesn't match") } diff --git a/main.go b/main.go index e79dbeb..f252886 100644 --- a/main.go +++ b/main.go @@ -6,21 +6,27 @@ import ( "errors" "fmt" "net" + "strings" "time" v1 "gitea.badhouseplants.net/softplayer/softplayer-backend/api/v1" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers" - "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/interceptors" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger" accounts "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1" "github.com/alecthomas/kong" + "github.com/golang-jwt/jwt/v5" "github.com/golang-migrate/migrate/v4" "github.com/golang-migrate/migrate/v4/database/postgres" _ "github.com/golang-migrate/migrate/v4/source/file" grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" + "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors" + "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth" + "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/selector" _ "github.com/lib/pq" "google.golang.org/grpc" + "google.golang.org/grpc/codes" "google.golang.org/grpc/reflection" + "google.golang.org/grpc/status" "github.com/redis/go-redis/v9" ) @@ -154,12 +160,37 @@ func server(ctx context.Context, params Serve) error { return err } - jwtVerifier := interceptors.NewJWTVerifier(ctx, []byte(params.JWTSecret)) + // jwtVerifier := interceptors.NewJWTVerifier(ctx, []byte(params.JWTSecret)) + authFn := func(ctx context.Context) (context.Context, error) { + tokenString, err := auth.AuthFromMD(ctx, "bearer") + if err != nil { + return nil, err + } + + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) { + return []byte(params.JWTSecret), nil + }, jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()})) + if err != nil { + return nil, status.Error(codes.Unauthenticated, "User is not authorized") + } + + if claims, ok := token.Claims.(jwt.MapClaims); ok { + ctx = context.WithValue(ctx, "token_id", claims["token_id"].(string)) + ctx = context.WithValue(ctx, "user_id", claims["user_id"].(string)) + } else { + return ctx, errors.New("claims are missing int the token") + } + return ctx, nil + } + authReqServices := func(ctx context.Context, callMeta interceptors.CallMeta) bool { + return !strings.Contains(callMeta.Service, "NoAuth") + } grpcServer := grpc.NewServer( grpc.ChainUnaryInterceptor( grpc_zap.UnaryServerInterceptor(logger.SetupLogger("info")), - jwtVerifier.JWTAuthInterceptor, + // jwtVerifier.JWTAuthInterceptor, + selector.UnaryServerInterceptor(auth.UnaryServerInterceptor(authFn), selector.MatchFunc(authReqServices)), ), grpc.StreamInterceptor(grpc_zap.StreamServerInterceptor(logger.SetupLogger("info"))), ) From ef009e29c643a480145dd7c47d2b53515bfbb111 Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Thu, 30 Apr 2026 17:34:09 +0200 Subject: [PATCH 17/17] Implement test services Signed-off-by: Nikolai Rodionov --- api/v1/accounts_no_auth.go | 79 ++++++----- api/v1/application.go | 84 ------------ api/v1/email.go | 86 ------------ api/v1/environments.go | 261 ------------------------------------- api/v1/test_auth.go | 22 ++++ api/v1/test_no_auth.go | 22 ++++ go.mod | 6 +- go.sum | 12 +- main.go | 4 +- 9 files changed, 96 insertions(+), 480 deletions(-) delete mode 100644 api/v1/application.go delete mode 100644 api/v1/email.go delete mode 100644 api/v1/environments.go create mode 100644 api/v1/test_auth.go create mode 100644 api/v1/test_no_auth.go diff --git a/api/v1/accounts_no_auth.go b/api/v1/accounts_no_auth.go index 4331e9c..d6aad9f 100644 --- a/api/v1/accounts_no_auth.go +++ b/api/v1/accounts_no_auth.go @@ -4,12 +4,11 @@ import ( "context" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers" - "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger" accounts "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1" + "github.com/coreos/go-oidc/v3/oidc" "github.com/golang/protobuf/ptypes/empty" - "google.golang.org/grpc" + "golang.org/x/oauth2" "google.golang.org/grpc/codes" - "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/emptypb" ) @@ -25,54 +24,50 @@ type AccountsNoAuthServer struct { ctrl *controllers.AccountController } -// SignUp should create a new user and return JWT tokens so the sessions is active right after registration -func (a *AccountsNoAuthServer) SignUp(ctx context.Context, in *accounts.SignUpRequest) (*empty.Empty, error) { - log := logger.FromContext(ctx) - data := &controllers.AccountData{ - Username: in.Data.GetName(), - Password: in.Password.GetPassword(), - Email: in.Data.GetEmail(), - } - - uuid, err := a.ctrl.Create(ctx, data) +func (a *AccountsNoAuthServer) SignIn(ctx context.Context, in *accounts.SignInRequest) (*empty.Empty, error) { + provider, err := oidc.NewProvider(ctx, "https://authentik.badhouseplants.net") if err != nil { - log.Error(err, "Couldn't create a user") - return nil, status.Error(codes.Aborted, "user can't be created") + return nil, err } - accessToken, err := a.ctrl.GenerateAccessToken(uuid) + // Configure an OpenID Connect aware OAuth2 client. + oauth2Config := oauth2.Config{ + ClientID: "softplayer-localhost", + ClientSecret: "pRpe3scGUE2jNH6t5rqI9R4OROeQHs4eO6ku957mYjDumKhQGX8QJcO0BMJ2FG4sUpvFrqccEqWgc3wKMp94tC8LyvTnkPF0Tg0CaldAEHuoQQdNKAzXVxwrHE6kNyBC", + RedirectURL: "http://localhost:8080/#/auth/callback", + + // Discovery returns the OAuth2 endpoints. + Endpoint: provider.Endpoint(), + + // "openid" is a required scope for OpenID Connect flows. + Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, + } + verifier := provider.Verifier(&oidc.Config{ClientID: "softplayer-localhost"}) + + oauth2Token, err := oauth2Config.Exchange(ctx, in.Code) if err != nil { - log.Error(err, "Couldn't generate an access token") - return nil, status.Error(codes.Aborted, "Couldn't generate Access Token") + return nil, err } - refreshToken, err := a.ctrl.GenerateRefreshToken(ctx, uuid) + // Extract the ID Token from OAuth2 token. + rawIDToken, ok := oauth2Token.Extra("id_token").(string) + if !ok { + return nil, status.Error(codes.Unauthenticated, "Couldn't parse oauth token") + } + + // Parse and verify ID Token payload. + idToken, err := verifier.Verify(ctx, rawIDToken) if err != nil { - log.Error(err, "Couldn't generate a refresh token") - return nil, status.Error(codes.Aborted, "Couldn't generate Access Token") + return nil, status.Error(codes.Unauthenticated, "Couldn't verify oauth token") } - header := metadata.Pairs( - "access-token", accessToken, - "refreshToken", refreshToken, - ) - - if err := grpc.SetHeader(ctx, header); err != nil { - log.Error(err, "Couldn't set headers") - return nil, status.Error(codes.Unknown, "Couldn't set headers") + // Extract custom claims + var claims struct { + Email string `json:"email"` + Verified bool `json:"email_verified"` + } + if err := idToken.Claims(&claims); err != nil { + // handle error } - return &emptypb.Empty{}, nil } - -func (a *AccountsNoAuthServer) SignIn(ctx context.Context, in *accounts.SignInRequest) (*empty.Empty, error) { - return nil, status.Error(codes.Unimplemented, "Endpoint is not implemented") -} - -func (a *AccountsNoAuthServer) ResetPassword(ctx context.Context, in *accounts.ResetPasswordRequest) (*empty.Empty, error) { - return nil, status.Error(codes.Unimplemented, "Endpoint is not implemented") -} - -func (acc *AccountsNoAuthServer) NewPassword(ctx context.Context, in *accounts.NewPasswordRequest) (*empty.Empty, error) { - return nil, status.Error(codes.Unimplemented, "Endpoint is not implemented") -} diff --git a/api/v1/application.go b/api/v1/application.go deleted file mode 100644 index 9d46de5..0000000 --- a/api/v1/application.go +++ /dev/null @@ -1,84 +0,0 @@ -package v1 - -import ( - "context" - - "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers" - proto "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/applications/v1" - "github.com/go-logr/logr" - "github.com/golang/protobuf/ptypes/empty" - "google.golang.org/protobuf/types/known/emptypb" - ctrl "sigs.k8s.io/controller-runtime" -) - -func NewApplicationsGrpcImpl(controller ctrl.Manager, log logr.Logger) *ApplicationServer { - return &ApplicationServer{ - controller: controller, - logInstance: log, - } -} - -type ApplicationServer struct { - proto.UnimplementedApplicationsServer - controller ctrl.Manager - logInstance logr.Logger -} - -// Create an environment -func (app *ApplicationServer) Create(ctx context.Context, in *proto.CreateOptions) (*proto.ApplicationFull, error) { - log := app.logInstance - log.WithValues("user_id", in.GetOwnerId().GetUuid(), "environment_id", in.GetSpec().GetEnvironemntId(), "app_name", in.GetSpec().GetApplication()) - ctx = logr.NewContext(ctx, log) - - data := &controllers.ApplicationData{ - Name: in.Metadata.Name, - Application: in.Spec.Application, - Version: in.Spec.Version, - Environemnt: in.Spec.EnvironemntId, - Config: in.Spec.Config, - RawConfig: "", - } - - application := &controllers.Application{ - UserID: in.GetOwnerId().GetUuid(), - Controller: app.controller, - Data: data, - Token: in.GetToken().GetToken(), - } - - err := application.Create(ctx) - if err != nil { - return nil, err - } - return &proto.ApplicationFull{ - Metadata: in.GetMetadata(), - Id: &proto.ApplicationId{ - Uuid: application.Data.UUID, - }, - Spec: in.GetSpec(), - }, nil -} - -func (app *ApplicationServer) Delete(ctx context.Context, in *proto.DeleteOptions) (*empty.Empty, error) { - log := app.logInstance - log.WithValues("user_id", in.GetOwnerId().GetUuid(), "app_id", in.GetId().GetUuid()) - ctx = logr.NewContext(ctx, log) - - data := &controllers.ApplicationData{ - Name: in.Metadata.Name, - UUID: in.GetId().GetUuid(), - } - - application := &controllers.Application{ - UserID: in.GetOwnerId().GetUuid(), - Controller: app.controller, - Data: data, - Token: in.GetToken().GetToken(), - } - - err := application.Delete(ctx) - if err != nil { - return nil, err - } - return &emptypb.Empty{}, nil -} diff --git a/api/v1/email.go b/api/v1/email.go deleted file mode 100644 index ff01de9..0000000 --- a/api/v1/email.go +++ /dev/null @@ -1,86 +0,0 @@ -package v1 - -import ( - "context" - "fmt" - - "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers" - "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/email" - proto_email "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/email/v1" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/types/known/emptypb" - ctrl "sigs.k8s.io/controller-runtime" -) - -type EmailServer struct { - proto_email.UnimplementedEmailValidationServer - emailConfig email.EmailConf - controller ctrl.Manager - // When dev mode is enabled, email won't be sent, instead the code will be returned in metadata - devMode bool -} - -func InitEmailServer(controller ctrl.Manager, emailConfig *email.EmailConf, devMode bool) *EmailServer { - return &EmailServer{ - controller: controller, - emailConfig: *emailConfig, - devMode: devMode, - } -} - -// Send the validation code to email -func (c *EmailServer) SendRequest(ctx context.Context, in *proto_email.RequestValidation) (*emptypb.Empty, error) { - // Validation - if len(in.GetUserId()) == 0 { - return nil, status.Error(codes.InvalidArgument, "user id must not be empty") - } - - // Body - emailSvc := controllers.EmailSvc{ - Data: controllers.EmailData{ - UserID: in.GetUserId(), - }, - EmailConfig: c.emailConfig, - Controller: c.controller, - DevMode: c.devMode, - } - err := emailSvc.SendVerification(ctx) - if err != nil { - return nil, err - } - if c.devMode { - header := metadata.Pairs("code", emailSvc.Data.Code) - if err := grpc.SendHeader(ctx, header); err != nil { - return nil, err - } - } - return &emptypb.Empty{}, nil -} - -func (c *EmailServer) ValidateEmail(ctx context.Context, in *proto_email.ConfirmValidation) (*emptypb.Empty, error) { - // Validation - if len(in.GetUserId()) == 0 { - return nil, status.Error(codes.InvalidArgument, "user id must not be empty") - } - if in.GetCode() == 0 { - return nil, status.Error(codes.InvalidArgument, "code must not be empty") - } - - // Body - emailSvc := controllers.EmailSvc{ - Data: controllers.EmailData{ - UserID: in.GetUserId(), - Code: fmt.Sprintf("%d", in.GetCode()), - }, - EmailConfig: c.emailConfig, - Controller: c.controller, - } - err := emailSvc.ConfirmVerification(ctx) - if err != nil { - return nil, err - } - return &emptypb.Empty{}, nil -} diff --git a/api/v1/environments.go b/api/v1/environments.go deleted file mode 100644 index b381ddf..0000000 --- a/api/v1/environments.go +++ /dev/null @@ -1,261 +0,0 @@ -package v1 - -import ( - "context" - - "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers" - "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/providers/infra" - "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/providers/kubernetes" - proto "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/environments/v1" - "github.com/go-logr/logr" - "github.com/golang/protobuf/ptypes/empty" - ctrl "sigs.k8s.io/controller-runtime" -) - -func NewapiGrpcImpl(controller ctrl.Manager, log logr.Logger) *EnvironmentsServer { - return &EnvironmentsServer{ - controller: controller, - logInstance: log, - } -} - -type EnvironmentsServer struct { - proto.UnimplementedEnvironmentsServer - controller ctrl.Manager - logInstance logr.Logger -} - -// Create an environment -func (e *EnvironmentsServer) Create(ctx context.Context, in *proto.CreateOptions) (*proto.EnvironmentFull, error) { - log := e.logInstance - log.WithValues("user_id", in.GetOwnerId().GetUuid()) - ctx = logr.NewContext(ctx, log) - - provider, err := infra.GetProvider(in.GetSpec().GetProvider().String()) - if err != nil { - return nil, err - } - - k8s, err := kubernetes.GetKubernetes(in.GetSpec().GetKubernetes().String()) - if err != nil { - return nil, err - } - - location, err := provider.GetServerLocation(in.GetSpec().GetServerLocation().String()) - if err != nil { - return nil, err - } - - serverType, err := provider.GetServerType(in.Spec.ServerType.String()) - if err != nil { - return nil, err - } - - data := &controllers.EnvironemntData{ - Name: in.GetMetadata().GetName(), - Description: in.GetMetadata().GetDescription(), - Provider: provider.GetProviderName(), - Kubernetes: k8s.GetKubernetesName(), - Location: location, - ServerType: serverType, - DiskSize: int(in.GetSpec().GetDiskSize()), - } - - environment := &controllers.Environemnt{ - UserID: in.GetOwnerId().GetUuid(), - Config: ctrl.GetConfigOrDie(), - Controller: e.controller, - Data: data, - Token: in.GetToken().GetToken(), - } - - err = environment.Create(ctx) - if err != nil { - return nil, err - } - return &proto.EnvironmentFull{ - Metadata: in.GetMetadata(), - Id: &proto.EnvironmentId{ - Uuid: environment.Data.UUID, - }, - Spec: in.GetSpec(), - }, nil -} - -func (e *EnvironmentsServer) Update(ctx context.Context, in *proto.UpdateOptions) (*proto.EnvironmentFull, error) { - log := e.logInstance - log.WithValues("user_id", in.GetOwnerId().GetUuid()) - ctx = logr.NewContext(ctx, log) - - provider, err := infra.GetProvider(in.GetSpec().GetProvider().String()) - if err != nil { - return nil, err - } - - k8s, err := kubernetes.GetKubernetes(in.GetSpec().GetKubernetes().String()) - if err != nil { - return nil, err - } - - location, err := provider.GetServerLocation(in.GetSpec().GetServerLocation().String()) - if err != nil { - return nil, err - } - - serverType, err := provider.GetServerType(in.Spec.ServerType.String()) - if err != nil { - return nil, err - } - - data := &controllers.EnvironemntData{ - Name: in.GetMetadata().GetName(), - UUID: in.GetId().GetUuid(), - Description: in.GetMetadata().GetDescription(), - Provider: provider.GetProviderName(), - Kubernetes: k8s.GetKubernetesName(), - Location: location, - ServerType: serverType, - DiskSize: int(in.GetSpec().GetDiskSize()), - } - - environment := &controllers.Environemnt{ - Config: ctrl.GetConfigOrDie(), - UserID: in.GetOwnerId().GetUuid(), - Controller: e.controller, - Data: data, - Token: in.GetToken().GetToken(), - } - - err = environment.Update(ctx) - if err != nil { - return nil, err - } - - return &proto.EnvironmentFull{ - Metadata: in.GetMetadata(), - Id: in.GetId(), - Spec: in.GetSpec(), - }, nil - -} - -func (e *EnvironmentsServer) Delete(ctx context.Context, in *proto.DeleteOptions) (*empty.Empty, error) { - log := e.logInstance - log.WithValues("user_id", in.GetOwnerId().GetUuid()) - ctx = logr.NewContext(ctx, log) - - data := &controllers.EnvironemntData{ - Name: in.GetMetadata().GetName(), - UUID: in.GetId().GetUuid(), - } - - environment := &controllers.Environemnt{ - Config: ctrl.GetConfigOrDie(), - UserID: in.GetOwnerId().GetUuid(), - Controller: e.controller, - Data: data, - Token: in.GetToken().GetToken(), - } - err := environment.Delete(ctx) - if err != nil { - return nil, err - } - return nil, nil -} - -func (e *EnvironmentsServer) Get(ctx context.Context, in *proto.GetOptions) (*proto.EnvironmentFull, error) { - log := e.logInstance - log.WithValues("user_id", in.GetOwnerId().GetUuid()) - ctx = logr.NewContext(ctx, log) - - data := &controllers.EnvironemntData{ - UUID: in.GetId().GetUuid(), - } - - environment := &controllers.Environemnt{ - Config: ctrl.GetConfigOrDie(), - UserID: in.GetOwnerId().GetUuid(), - Controller: e.controller, - Data: data, - Token: in.GetToken().GetToken(), - } - - if err := environment.Get(ctx); err != nil { - return nil, err - } - - provider, err := infra.GetProvider(environment.Data.Provider) - if err != nil { - return nil, err - } - - k8s, err := kubernetes.GetKubernetes(environment.Data.Kubernetes) - if err != nil { - return nil, err - } - - return &proto.EnvironmentFull{ - Spec: &proto.EnvironmentSpec{ - Provider: proto.Provider(proto.Provider_value[provider.RawProviderName()]), - Kubernetes: proto.Kubernetes(proto.Kubernetes_value[k8s.RawKubernetesName()]), - ServerLocation: proto.Location(proto.Location_value[provider.RawServerLocation(environment.Data.Location)]), - ServerType: proto.ServerType(proto.ServerType_value[provider.RawServerType(environment.Data.ServerType)]), - DiskSize: int32(environment.Data.DiskSize), - }, - Id: in.GetId(), - Metadata: &proto.EnvironmentMetadata{ - Name: environment.Data.Name, - Description: environment.Data.Description, - }, - }, nil -} - -func (e *EnvironmentsServer) List(in *proto.ListOptions, stream proto.Environments_ListServer) error { - logInstance := e.logInstance - log := logInstance.WithValues("user_id", in.GetOwnerId().GetUuid()) - ctx := logr.NewContext(stream.Context(), log) - - environment := &controllers.Environemnt{ - Config: ctrl.GetConfigOrDie(), - UserID: in.GetOwnerId().GetUuid(), - Controller: e.controller, - Token: in.GetToken().GetToken(), - } - - envs, err := environment.List(ctx, in.GetSearchString()) - if err != nil { - return err - } - - for _, env := range envs { - provider, err := infra.GetProvider(env.Data.Provider) - if err != nil { - return err - } - - k8s, err := kubernetes.GetKubernetes(env.Data.Kubernetes) - if err != nil { - return err - } - - if err := stream.Send(&proto.EnvironmentFull{ - Metadata: &proto.EnvironmentMetadata{ - Name: env.Data.Name, - Description: env.Data.Description, - }, - Id: &proto.EnvironmentId{ - Uuid: env.Data.UUID, - }, - Spec: &proto.EnvironmentSpec{ - Provider: proto.Provider(proto.Provider_value[provider.RawProviderName()]), - Kubernetes: proto.Kubernetes(proto.Kubernetes_value[k8s.RawKubernetesName()]), - ServerLocation: proto.Location(proto.Location_value[provider.RawServerLocation(env.Data.Location)]), - ServerType: proto.ServerType(proto.ServerType_value[provider.RawServerType(env.Data.ServerType)]), - DiskSize: int32(env.Data.DiskSize), - }, - }); err != nil { - return err - } - } - return nil -} diff --git a/api/v1/test_auth.go b/api/v1/test_auth.go new file mode 100644 index 0000000..692b3de --- /dev/null +++ b/api/v1/test_auth.go @@ -0,0 +1,22 @@ +package v1 + +import ( + "context" + + "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger" + test "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/test/v1" +) + +func NewTestAuthRPCImpl() *TestAuthServer { + return &TestAuthServer{} +} + +type TestAuthServer struct { + test.UnimplementedTestAuthServiceServer +} + +func (t *TestAuthServer) Pong(ctx context.Context, in *test.PongRequest) (*test.PongResponse, error) { + log := logger.FromContext(ctx) + log.Info("Pong") + return &test.PongResponse{}, nil +} diff --git a/api/v1/test_no_auth.go b/api/v1/test_no_auth.go new file mode 100644 index 0000000..40bbd09 --- /dev/null +++ b/api/v1/test_no_auth.go @@ -0,0 +1,22 @@ +package v1 + +import ( + "context" + + "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger" + test "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/test/v1" +) + +func NewTestNoAuthRPCImpl() *TestNoAuthServer { + return &TestNoAuthServer{} +} + +type TestNoAuthServer struct { + test.UnimplementedTestNoAuthServiceServer +} + +func (t *TestNoAuthServer) Ping(ctx context.Context, in *test.PingRequest) (*test.PingResponse, error) { + log := logger.FromContext(ctx) + log.Info("Ping") + return &test.PingResponse{}, nil +} diff --git a/go.mod b/go.mod index 4f31774..80ad433 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.25.9 require ( github.com/alecthomas/assert/v2 v2.11.0 github.com/alecthomas/kong v1.15.0 + github.com/coreos/go-oidc/v3 v3.18.0 github.com/go-logr/logr v1.4.3 github.com/go-logr/zapr v1.3.0 github.com/golang-jwt/jwt/v5 v5.2.2 @@ -19,6 +20,7 @@ require ( github.com/sirupsen/logrus v1.9.3 go.uber.org/zap v1.27.0 golang.org/x/crypto v0.47.0 + golang.org/x/oauth2 v0.36.0 gopkg.in/yaml.v2 v2.4.0 helm.sh/helm/v3 v3.20.2 k8s.io/api v0.35.1 @@ -58,6 +60,7 @@ require ( github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect + github.com/go-jose/go-jose/v4 v4.1.4 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect @@ -112,7 +115,6 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/oauth2 v0.34.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/term v0.39.0 // indirect golang.org/x/time v0.12.0 // indirect @@ -138,7 +140,7 @@ require ( ) require ( - gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260428191411-8c93fd05025a + gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260430152421-88c087f0cea0 github.com/golang/protobuf v1.5.4 golang.org/x/net v0.49.0 // indirect golang.org/x/sys v0.40.0 // indirect diff --git a/go.sum b/go.sum index af99e68..f9ec13b 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,8 @@ dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260428191411-8c93fd05025a h1:8Mo14FqMkUcUNyyVfrTGZOe/BHn412olquEDqL+Fg6c= -gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260428191411-8c93fd05025a/go.mod h1:AgOh1lkPHyRgBf3/s1btKcAqke/33LbKYarTD13qeAg= +gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260430152421-88c087f0cea0 h1:2UggBAWgOJ1MYgkk+RTaWhfTGtzAZ0B9MriZMoHqnq4= +gitea.badhouseplants.net/softplayer/softplayer-go-proto v0.0.0-20260430152421-88c087f0cea0/go.mod h1:AgOh1lkPHyRgBf3/s1btKcAqke/33LbKYarTD13qeAg= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= @@ -66,6 +66,8 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/coreos/go-oidc/v3 v3.18.0 h1:V9orjXynvu5wiC9SemFTWnG4F45v403aIcjWo0d41+A= +github.com/coreos/go-oidc/v3 v3.18.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -127,6 +129,8 @@ github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxI github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= +github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= +github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= @@ -435,8 +439,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= -golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/main.go b/main.go index f252886..cee7ddd 100644 --- a/main.go +++ b/main.go @@ -13,6 +13,7 @@ import ( "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers" "gitea.badhouseplants.net/softplayer/softplayer-backend/internal/tools/logger" accounts "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/accounts/v1" + test "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/test/v1" "github.com/alecthomas/kong" "github.com/golang-jwt/jwt/v5" "github.com/golang-migrate/migrate/v4" @@ -213,7 +214,8 @@ func server(ctx context.Context, params Serve) error { } accounts.RegisterAccountsNoAuthServiceServer(grpcServer, v1.NewAccountNoAuthRPCImpl(accountCtrl)) accounts.RegisterAccountsAuthServiceServer(grpcServer, v1.NewAccountAuthRPCImpl(accountCtrl)) - + test.RegisterTestAuthServiceServer(grpcServer, v1.NewTestAuthRPCImpl()) + test.RegisterTestNoAuthServiceServer(grpcServer, v1.NewTestNoAuthRPCImpl()) if err := grpcServer.Serve(lis); err != nil { return err }