From ee2aa77ceb91494ebec0bad2d9c9019ac020d065 Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Thu, 14 May 2026 15:09:21 +0200 Subject: [PATCH] Update the migration Signed-off-by: Nikolai Rodionov --- api/v1/tokens.go | 30 ++++++++- go.mod | 1 + go.sum | 1 + internal/controllers/tokens.go | 68 ++++++++++++++------ migrations/20260510174348_tokens_init.up.sql | 1 + 5 files changed, 79 insertions(+), 22 deletions(-) diff --git a/api/v1/tokens.go b/api/v1/tokens.go index d774961..b40c972 100644 --- a/api/v1/tokens.go +++ b/api/v1/tokens.go @@ -10,6 +10,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/emptypb" + "google.golang.org/protobuf/types/known/timestamppb" ) // var _ tokens.TokensServiceServer = (*TokensServer)(nil) @@ -77,7 +78,34 @@ func (t *TokensServer) GetToken(context.Context, *tokens.GetTokenRequest) (*toke } // ListTokens implements [v1.TokensServiceServer]. -func (t *TokensServer) ListTokens(*emptypb.Empty, grpc.ServerStreamingServer[tokens.ListTokensResponse]) error { +func (srv *TokensServer) ListTokens(in *emptypb.Empty, stream grpc.ServerStreamingServer[tokens.ListTokensResponse]) error { + claims, err := srv.authorizationCtrl.ClaimsFromContext(stream.Context()) + if err != nil { + return status.Error(codes.Aborted, "Context is invalid") + } + if claims.UserID == "" { + return status.Error(codes.Aborted, "Context is invalid") + } + + tokensRes, err := srv.tokenCtrl.List(stream.Context(), claims.UserID) + if err != nil { + if errors.Is(err, controllers.ErrServerError) { + return status.Error(codes.Internal, "Something is broken on our side") + } + return status.Error(codes.Aborted, "Couldn't create a token") + } + + for _, tokenRes := range tokensRes { + stream.Send(&tokens.ListTokensResponse{ + TokenUuid: &tokens.TokenUUID{ + Uuid: tokenRes.UUID, + }, + TokenMetadata: &tokens.TokenMetadata{ + Name: tokenRes.Name, + ExpiresAt: timestamppb.New(tokenRes.ExpiresAt), + }, + }) + } return status.Error(codes.Unimplemented, "Method is not implemented") } diff --git a/go.mod b/go.mod index c64afdb..b4b23a1 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 github.com/mattn/go-colorable v0.1.14 + github.com/opentracing/opentracing-go v1.1.0 github.com/redis/go-redis/v9 v9.18.0 github.com/stretchr/testify v1.11.1 go.uber.org/zap v1.27.0 diff --git a/go.sum b/go.sum index f986d32..507b266 100644 --- a/go.sum +++ b/go.sum @@ -117,6 +117,7 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/internal/controllers/tokens.go b/internal/controllers/tokens.go index 2548c6e..e191c0f 100644 --- a/internal/controllers/tokens.go +++ b/internal/controllers/tokens.go @@ -31,14 +31,15 @@ var ( ) type TokenData struct { - UUID string - Name string - UserID string - CreatedAt time.Time - LastUserAt time.Time - RevokedAt time.Time - ExpiredAt time.Time - Scopes map[string][]string + UUID string + Name string + UserID string + CreatedAt time.Time + LastUserAt time.Time + RevokedAt time.Time + ExpiresAt time.Time + GeneratedAt time.Time + Scopes map[string][]string } type Scopes struct{} @@ -93,7 +94,7 @@ func (ctrl *TokenController) Create(ctx context.Context, data *TokenData) (strin scopesJson, time.Now(), time.Now(), - data.ExpiredAt, + data.ExpiresAt, ); err != nil { log.Error(err, "Couldn't insert a token in the database") return "", ErrServerError @@ -161,7 +162,7 @@ func (ctrl *TokenController) Regenerate(ctx context.Context, id string) (string, expires_at = NOW() + (expires_at - generated_at), WHERE uuid = $3;` - if _, err := ctrl.DB.Query(query, tokenHash, time.Now(), data.UUID); err != nil { + if _, err := ctrl.DB.Query(query, tokenHash, time.Now(), id); err != nil { log.Error(err, "Couldn't insert a token in the database") return "", ErrServerError } @@ -175,22 +176,47 @@ func (ctrl *TokenController) Get(ctx context.Context, uuid string) error { } // List all available token -func (ctrl *TokenController) List(ctx context.Context, userID string) error { +func (ctrl *TokenController) List(ctx context.Context, userID string) ([]TokenData, error) { + log := logger.FromContext(ctx).WithValues("user_id", userID) + log.V(2).Info("Regenerating a token") + + result := []TokenData{} + query := ` - SELECT id, name, generated_at, expires_at + SELECT uuid, name, generated_at, expires_at FROM tokens WHERE user_id = $1` - err := ctrl.DB.QueryRowContext(ctx, query, userID).Scan( - &t.ID, - &t.UserID, - &t.Name, - &scopes, - &t.GeneratedAt, - &t.ExpiresAt, - ) + + rows, err := ctrl.DB.QueryContext(ctx, query, userID) if err != nil { - return nil, err + log.Error(err, "Couldn't list tokens") + return nil, ErrServerError } + + defer rows.Close() + + if err := rows.Err(); err != nil { + log.Error(err, "Couldn't list tokens") + return nil, ErrServerError + } + + for rows.Next() { + var t TokenData + + err := rows.Scan( + &t.UUID, + &t.Name, + &t.GeneratedAt, + &t.ExpiresAt, + ) + if err != nil { + return nil, err + } + + result = append(result, t) + } + + return result, nil } // Lis all available permissions diff --git a/migrations/20260510174348_tokens_init.up.sql b/migrations/20260510174348_tokens_init.up.sql index eb74e73..0765384 100644 --- a/migrations/20260510174348_tokens_init.up.sql +++ b/migrations/20260510174348_tokens_init.up.sql @@ -1,5 +1,6 @@ CREATE TABLE IF NOT EXISTS tokens ( uuid UUID PRIMARY KEY, + name TEXT NOT NULL, token_hash TEXT NOT NULL, user_id UUID NOT NULL, scopes JSONB NOT NULL DEFAULT '[]',