|
| 1 | +package sams |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "time" |
| 6 | + |
| 7 | + "connectrpc.com/connect" |
| 8 | + "google.golang.org/protobuf/types/known/timestamppb" |
| 9 | + |
| 10 | + clientsv1 "github.com/sourcegraph/sourcegraph-accounts-sdk-go/clients/v1" |
| 11 | + "github.com/sourcegraph/sourcegraph-accounts-sdk-go/clients/v1/clientsv1connect" |
| 12 | + "github.com/sourcegraph/sourcegraph-accounts-sdk-go/scopes" |
| 13 | + "github.com/sourcegraph/sourcegraph-accounts-sdk-go/services" |
| 14 | + "github.com/sourcegraph/sourcegraph/lib/errors" |
| 15 | + "golang.org/x/oauth2" |
| 16 | +) |
| 17 | + |
| 18 | +// ServiceAccessTokensServiceV1 provides client methods to interact with the |
| 19 | +// ServiceAccessTokensService API v1. |
| 20 | +type ServiceAccessTokensServiceV1 struct { |
| 21 | + client *ClientV1 |
| 22 | +} |
| 23 | + |
| 24 | +func (s *ServiceAccessTokensServiceV1) newClient(ctx context.Context) clientsv1connect.ServiceAccessTokensServiceClient { |
| 25 | + return clientsv1connect.NewServiceAccessTokensServiceClient( |
| 26 | + oauth2.NewClient(ctx, s.client.tokenSource), |
| 27 | + s.client.gRPCURL(), |
| 28 | + connect.WithInterceptors(s.client.defaultInterceptors...), |
| 29 | + ) |
| 30 | +} |
| 31 | + |
| 32 | +// CreateServiceAccessTokenOptions represents the optional parameters for creating a service access token. |
| 33 | +type CreateServiceAccessTokenOptions struct { |
| 34 | + // The human-friendly name of the token (optional). |
| 35 | + DisplayName string |
| 36 | + // The time the token will expire (optional, defaults to never expire). |
| 37 | + ExpiresAt *time.Time |
| 38 | +} |
| 39 | + |
| 40 | +// CreateServiceAccessTokenResponse represents the response from creating a service access token. |
| 41 | +type CreateServiceAccessTokenResponse struct { |
| 42 | + Token *clientsv1.ServiceAccessToken |
| 43 | + Secret string |
| 44 | +} |
| 45 | + |
| 46 | +// CreateServiceAccessToken creates a new service access token. |
| 47 | +// |
| 48 | +// Required scope: sams::service_access_tokens::write |
| 49 | +func (s *ServiceAccessTokensServiceV1) CreateServiceAccessToken(ctx context.Context, service services.Service, tokenScopes []scopes.Scope, userID string, opts CreateServiceAccessTokenOptions) (*CreateServiceAccessTokenResponse, error) { |
| 50 | + if service == "" { |
| 51 | + return nil, errors.New("service cannot be empty") |
| 52 | + } |
| 53 | + if len(tokenScopes) == 0 { |
| 54 | + return nil, errors.New("scopes cannot be empty") |
| 55 | + } |
| 56 | + if userID == "" { |
| 57 | + return nil, errors.New("user ID cannot be empty") |
| 58 | + } |
| 59 | + |
| 60 | + token := &clientsv1.ServiceAccessToken{ |
| 61 | + Service: string(service), |
| 62 | + Scopes: scopes.ToStrings(tokenScopes), |
| 63 | + UserId: userID, |
| 64 | + DisplayName: opts.DisplayName, |
| 65 | + } |
| 66 | + |
| 67 | + if opts.ExpiresAt != nil { |
| 68 | + token.ExpireTime = timestamppb.New(*opts.ExpiresAt) |
| 69 | + } |
| 70 | + |
| 71 | + req := &clientsv1.CreateServiceAccessTokenRequest{Token: token} |
| 72 | + client := s.newClient(ctx) |
| 73 | + resp, err := parseResponseAndError(client.CreateServiceAccessToken(ctx, connect.NewRequest(req))) |
| 74 | + if err != nil { |
| 75 | + return nil, err |
| 76 | + } |
| 77 | + |
| 78 | + return &CreateServiceAccessTokenResponse{ |
| 79 | + Token: resp.Msg.Token, |
| 80 | + Secret: resp.Msg.Secret, |
| 81 | + }, nil |
| 82 | +} |
| 83 | + |
| 84 | +// ListServiceAccessTokensOptions represents the options for listing service access tokens. |
| 85 | +type ListServiceAccessTokensOptions struct { |
| 86 | + // Maximum number of results to return (optional). |
| 87 | + PageSize int32 |
| 88 | + // Page token for pagination (optional). |
| 89 | + PageToken string |
| 90 | + // Service filter (optional). |
| 91 | + Service string |
| 92 | + // User ID filter (optional). |
| 93 | + UserID string |
| 94 | + // Whether to include expired tokens (optional). |
| 95 | + ShowExpired bool |
| 96 | +} |
| 97 | + |
| 98 | +// ListServiceAccessTokens returns a list of service access tokens in reverse chronological |
| 99 | +// order by creation time. |
| 100 | +// |
| 101 | +// Required scope: sams::service_access_tokens::read |
| 102 | +func (s *ServiceAccessTokensServiceV1) ListServiceAccessTokens(ctx context.Context, opts ListServiceAccessTokensOptions) ([]*clientsv1.ServiceAccessToken, error) { |
| 103 | + req := &clientsv1.ListServiceAccessTokensRequest{ |
| 104 | + PageSize: opts.PageSize, |
| 105 | + PageToken: opts.PageToken, |
| 106 | + } |
| 107 | + |
| 108 | + // Build filters |
| 109 | + var filters []*clientsv1.ListServiceAccessTokensFilter |
| 110 | + if opts.Service != "" { |
| 111 | + filters = append(filters, &clientsv1.ListServiceAccessTokensFilter{ |
| 112 | + Filter: &clientsv1.ListServiceAccessTokensFilter_Service{Service: opts.Service}, |
| 113 | + }) |
| 114 | + } |
| 115 | + if opts.UserID != "" { |
| 116 | + filters = append(filters, &clientsv1.ListServiceAccessTokensFilter{ |
| 117 | + Filter: &clientsv1.ListServiceAccessTokensFilter_UserId{UserId: opts.UserID}, |
| 118 | + }) |
| 119 | + } |
| 120 | + if opts.ShowExpired { |
| 121 | + filters = append(filters, &clientsv1.ListServiceAccessTokensFilter{ |
| 122 | + Filter: &clientsv1.ListServiceAccessTokensFilter_ShowExpired{ShowExpired: opts.ShowExpired}, |
| 123 | + }) |
| 124 | + } |
| 125 | + req.Filters = filters |
| 126 | + |
| 127 | + client := s.newClient(ctx) |
| 128 | + resp, err := parseResponseAndError(client.ListServiceAccessTokens(ctx, connect.NewRequest(req))) |
| 129 | + if err != nil { |
| 130 | + return nil, err |
| 131 | + } |
| 132 | + return resp.Msg.GetTokens(), nil |
| 133 | +} |
| 134 | + |
| 135 | +// RevokeServiceAccessToken revokes the specified service access token. |
| 136 | +// |
| 137 | +// Required scope: sams::service_access_tokens::delete |
| 138 | +func (s *ServiceAccessTokensServiceV1) RevokeServiceAccessToken(ctx context.Context, tokenID string) error { |
| 139 | + if tokenID == "" { |
| 140 | + return errors.New("token ID cannot be empty") |
| 141 | + } |
| 142 | + |
| 143 | + req := &clientsv1.RevokeServiceAccessTokenRequest{Id: tokenID} |
| 144 | + client := s.newClient(ctx) |
| 145 | + _, err := parseResponseAndError(client.RevokeServiceAccessToken(ctx, connect.NewRequest(req))) |
| 146 | + return err |
| 147 | +} |
0 commit comments