All checks were successful
ci/woodpecker/push/build Pipeline was successful
Signed-off-by: Nikolai Rodionov <iam@allanger.xyz>
279 lines
9.7 KiB
Go
279 lines
9.7 KiB
Go
package v1
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/controllers"
|
|
tokens "gitea.badhouseplants.net/softplayer/softplayer-go-proto/pkg/tokens/v1"
|
|
"google.golang.org/grpc"
|
|
"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)
|
|
type TokensServer struct {
|
|
tokens.UnimplementedTokensServiceServer
|
|
tokenCtrl *controllers.TokenController
|
|
authorizationCtrl *controllers.AuthController
|
|
}
|
|
|
|
func NewTokensServer(
|
|
tokenCtrl *controllers.TokenController,
|
|
authorizationCtrl *controllers.AuthController,
|
|
) *TokensServer {
|
|
return &TokensServer{
|
|
tokenCtrl: tokenCtrl,
|
|
authorizationCtrl: authorizationCtrl,
|
|
}
|
|
}
|
|
|
|
// CreateToken implements [v1.TokensServiceServer].
|
|
func (srv *TokensServer) CreateToken(ctx context.Context, in *tokens.CreateTokenRequest) (*tokens.CreateTokenResponse, error) {
|
|
claims, err := srv.authorizationCtrl.ClaimsFromContext(ctx)
|
|
if err != nil {
|
|
return nil, status.Error(codes.Aborted, "Context is invalid")
|
|
}
|
|
if claims.UserID == "" {
|
|
return nil, status.Error(codes.Aborted, "Context is invalid")
|
|
}
|
|
|
|
if in.TokenPermissions == nil {
|
|
return nil, status.Error(codes.InvalidArgument, "Permissions must be set")
|
|
}
|
|
permissions := map[string][]string{}
|
|
for service, methods := range in.TokenPermissions.Permissions {
|
|
permissions[service] = methods.GetMethods()
|
|
}
|
|
|
|
tokenData := &controllers.TokenData{
|
|
Name: in.TokenMetadata.GetName(),
|
|
UserID: claims.UserID,
|
|
ExpiresAt: in.TokenMetadata.ExpiresAt.AsTime(),
|
|
Scopes: permissions,
|
|
}
|
|
|
|
token, err := srv.tokenCtrl.Create(ctx, tokenData)
|
|
if err != nil {
|
|
if errors.Is(err, controllers.ErrServerError) {
|
|
return nil, status.Error(codes.Internal, "Something is broken on our side")
|
|
}
|
|
return nil, status.Error(codes.Aborted, "Couldn't create a token")
|
|
}
|
|
|
|
return &tokens.CreateTokenResponse{
|
|
TokenValue: &tokens.TokenValue{Token: token},
|
|
}, nil
|
|
}
|
|
|
|
// ForceTokenExpiration implements [v1.TokensServiceServer].
|
|
func (srv *TokensServer) ForceTokenExpiration(ctx context.Context, in *tokens.ForceTokenExpirationRequest) (*emptypb.Empty, error) {
|
|
claims, err := srv.authorizationCtrl.ClaimsFromContext(ctx)
|
|
if err != nil {
|
|
return nil, status.Error(codes.Aborted, "Context is invalid")
|
|
}
|
|
if claims.UserID == "" {
|
|
return nil, status.Error(codes.Aborted, "Context is invalid")
|
|
}
|
|
|
|
if err := srv.tokenCtrl.VerifyTokenOwner(ctx, claims.UserID, in.TokenUuid.Uuid); err != nil {
|
|
if errors.Is(err, controllers.ErrServerError) {
|
|
return nil, status.Error(codes.Internal, "Something is broken on our side")
|
|
}
|
|
return nil, status.Error(codes.Aborted, "User is now allowed to manipulate this token")
|
|
}
|
|
|
|
if err := srv.tokenCtrl.ForceExpiration(ctx, in.TokenUuid.GetUuid()); err != nil {
|
|
if errors.Is(err, controllers.ErrServerError) {
|
|
return nil, status.Error(codes.Internal, "Something is broken on our side")
|
|
}
|
|
return nil, status.Error(codes.Aborted, "Couldn't create a token")
|
|
}
|
|
return &emptypb.Empty{}, nil
|
|
}
|
|
|
|
// GetToken implements [v1.TokensServiceServer].
|
|
func (srv *TokensServer) GetToken(ctx context.Context, in *tokens.GetTokenRequest) (*tokens.GetTokenResponse, error) {
|
|
claims, err := srv.authorizationCtrl.ClaimsFromContext(ctx)
|
|
if err != nil {
|
|
return nil, status.Error(codes.Aborted, "Context is invalid")
|
|
}
|
|
if claims.UserID == "" {
|
|
return nil, status.Error(codes.Aborted, "Context is invalid")
|
|
}
|
|
if err := srv.tokenCtrl.VerifyTokenOwner(ctx, claims.UserID, in.TokenUuid.Uuid); err != nil {
|
|
if errors.Is(err, controllers.ErrServerError) {
|
|
return nil, status.Error(codes.Internal, "Something is broken on our side")
|
|
}
|
|
return nil, status.Error(codes.Aborted, "User is now allowed to manipulate this token")
|
|
}
|
|
|
|
token, err := srv.tokenCtrl.Get(ctx, in.TokenUuid.Uuid, claims.UserID)
|
|
if err != nil {
|
|
if errors.Is(err, controllers.ErrServerError) {
|
|
return nil, status.Error(codes.Internal, "Something is broken on our side")
|
|
}
|
|
return nil, status.Error(codes.Aborted, "Couldn't list tokens")
|
|
}
|
|
|
|
return &tokens.GetTokenResponse{
|
|
TokenUuid: &tokens.TokenUUID{
|
|
Uuid: token.UUID,
|
|
},
|
|
TokenMetadata: &tokens.TokenMetadata{
|
|
Name: token.Name,
|
|
ExpiresAt: timestamppb.New(token.ExpiresAt),
|
|
LastUsedAt: timestamppb.New(token.LastUsedAt),
|
|
GeneratedAt: timestamppb.New(token.GeneratedAt),
|
|
CreatedAt: timestamppb.New(token.CreatedAt),
|
|
RevokedAt: timestamppb.New(token.RevokedAt),
|
|
},
|
|
TokenPermissions: &tokens.TokenPermissions{},
|
|
}, nil
|
|
}
|
|
|
|
// ListTokens implements [v1.TokensServiceServer].
|
|
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 list tokens")
|
|
}
|
|
|
|
for _, tokenRes := range tokensRes {
|
|
if err := stream.Send(&tokens.ListTokensResponse{
|
|
TokenUuid: &tokens.TokenUUID{
|
|
Uuid: tokenRes.UUID,
|
|
},
|
|
TokenMetadata: &tokens.TokenMetadata{
|
|
Name: tokenRes.Name,
|
|
ExpiresAt: timestamppb.New(tokenRes.ExpiresAt),
|
|
LastUsedAt: timestamppb.New(tokenRes.LastUsedAt),
|
|
GeneratedAt: timestamppb.New(tokenRes.GeneratedAt),
|
|
CreatedAt: timestamppb.New(tokenRes.CreatedAt),
|
|
RevokedAt: timestamppb.New(tokenRes.RevokedAt),
|
|
},
|
|
}); err != nil {
|
|
return status.Error(codes.Aborted, "Couldn't send data")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// RegenerateToken implements [v1.TokensServiceServer].
|
|
func (srv *TokensServer) RegenerateToken(ctx context.Context, in *tokens.RegenerateTokenRequest) (*tokens.RegenerateTokenResponse, error) {
|
|
claims, err := srv.authorizationCtrl.ClaimsFromContext(ctx)
|
|
if err != nil {
|
|
return nil, status.Error(codes.Aborted, "Context is invalid")
|
|
}
|
|
if claims.UserID == "" {
|
|
return nil, status.Error(codes.Aborted, "Context is invalid")
|
|
}
|
|
if err := srv.tokenCtrl.VerifyTokenOwner(ctx, claims.UserID, in.TokenUuid.Uuid); err != nil {
|
|
if errors.Is(err, controllers.ErrServerError) {
|
|
return nil, status.Error(codes.Internal, "Something is broken on our side")
|
|
}
|
|
return nil, status.Error(codes.Aborted, "User is now allowed to manipulate this token")
|
|
}
|
|
|
|
tokenVal, err := srv.tokenCtrl.Regenerate(ctx, in.TokenUuid.GetUuid())
|
|
if err != nil {
|
|
if errors.Is(err, controllers.ErrServerError) {
|
|
return nil, status.Error(codes.Internal, "Something is broken on our side")
|
|
}
|
|
return nil, status.Error(codes.Aborted, "Couldn't list tokens")
|
|
}
|
|
return &tokens.RegenerateTokenResponse{
|
|
TokenValue: &tokens.TokenValue{
|
|
Token: tokenVal,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// UpdateToken implements [v1.TokensServiceServer].
|
|
func (srv *TokensServer) UpdateToken(ctx context.Context, in *tokens.UpdateTokenRequest) (*tokens.UpdateTokenResponse, error) {
|
|
claims, err := srv.authorizationCtrl.ClaimsFromContext(ctx)
|
|
if err != nil {
|
|
return nil, status.Error(codes.Aborted, "Context is invalid")
|
|
}
|
|
if claims.UserID == "" {
|
|
return nil, status.Error(codes.Aborted, "Context is invalid")
|
|
}
|
|
|
|
if err := srv.tokenCtrl.VerifyTokenOwner(ctx, claims.UserID, in.TokenUuid.Uuid); err != nil {
|
|
if errors.Is(err, controllers.ErrServerError) {
|
|
return nil, status.Error(codes.Internal, "Something is broken on our side")
|
|
}
|
|
return nil, status.Error(codes.Aborted, "User is now allowed to manipulate this token")
|
|
}
|
|
if in.TokenPermissions == nil {
|
|
return nil, status.Error(codes.InvalidArgument, "Permissions must be set")
|
|
}
|
|
|
|
permissions := map[string][]string{}
|
|
for service, methods := range in.TokenPermissions.Permissions {
|
|
permissions[service] = methods.GetMethods()
|
|
}
|
|
tokenData := &controllers.TokenData{
|
|
Name: in.TokenMetadata.Name,
|
|
Scopes: permissions,
|
|
}
|
|
if err := srv.tokenCtrl.Update(ctx, tokenData); err != nil {
|
|
if errors.Is(err, controllers.ErrServerError) {
|
|
return nil, status.Error(codes.Internal, "Something is broken on our side")
|
|
}
|
|
return nil, status.Error(codes.Aborted, "Couldn't list tokens")
|
|
}
|
|
return &tokens.UpdateTokenResponse{
|
|
TokenUuid: &tokens.TokenUUID{},
|
|
TokenMetadata: &tokens.TokenMetadata{},
|
|
TokenPermissions: &tokens.TokenPermissions{},
|
|
}, nil
|
|
}
|
|
|
|
// ListPermissions implements [v1.TokensServiceServer].
|
|
func (srv *TokensServer) ListPermissions(in *emptypb.Empty, stream grpc.ServerStreamingServer[tokens.ListPermissionsResponse]) error {
|
|
data := srv.tokenCtrl.ListPermissions(stream.Context())
|
|
for key, data := range data {
|
|
result := &tokens.ListPermissionsResponse{
|
|
Permissions: &tokens.TokenPermissions{
|
|
Permissions: map[string]*tokens.MethodList{
|
|
key: {Methods: data},
|
|
},
|
|
},
|
|
}
|
|
if err := stream.Send(result); err != nil {
|
|
return status.Error(codes.Aborted, "Couldn't send data to the client")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (srv *TokensServer) AuthenticateWithToken(ctx context.Context, in *tokens.AuthenticateWithTokenRequest) (*emptypb.Empty, error) {
|
|
scopes, err := srv.tokenCtrl.AuthenticateWithToken(ctx, in.TokenValue.Token)
|
|
if err != nil {
|
|
if errors.Is(err, controllers.ErrBadToken) {
|
|
return nil, status.Error(codes.Unauthenticated, "Token is not valid")
|
|
}
|
|
if errors.Is(err, controllers.ErrServerError) {
|
|
return nil, status.Error(codes.Internal, "Something is broken on our side")
|
|
}
|
|
return nil, status.Error(codes.Aborted, "Couldn't list tokens")
|
|
}
|
|
fmt.Println(scopes)
|
|
return nil, nil
|
|
}
|