From 8c47d05000859943ea05a5b26fae26f25eac8048 Mon Sep 17 00:00:00 2001 From: cparkins Date: Wed, 14 Aug 2024 13:04:49 -0600 Subject: [PATCH 1/2] #138: Add the /tokens endpoints to create/update/revoke/list Personal Access Tokens made available in API v7.2-preview1. --- azuredevops/v7/tokens/client.go | 189 ++++++++++++++++++++++++++++++++ azuredevops/v7/tokens/models.go | 173 +++++++++++++++++++++++++++++ 2 files changed, 362 insertions(+) create mode 100644 azuredevops/v7/tokens/client.go create mode 100644 azuredevops/v7/tokens/models.go diff --git a/azuredevops/v7/tokens/client.go b/azuredevops/v7/tokens/client.go new file mode 100644 index 00000000..43bb3d25 --- /dev/null +++ b/azuredevops/v7/tokens/client.go @@ -0,0 +1,189 @@ +// -------------------------------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// -------------------------------------------------------------------------------------------- +// Generated file, DO NOT EDIT +// Changes may cause incorrect behavior and will be lost if the code is regenerated. +// -------------------------------------------------------------------------------------------- + +package tokens + +import ( + "bytes" + "context" + "encoding/json" + "github.com/google/uuid" + "github.com/microsoft/azure-devops-go-api/azuredevops/v7" + "net/http" + "net/url" + "strconv" +) + +var ResourceAreaId, _ = uuid.Parse("55967393-20ef-45c6-a96c-b5d5d5986a9a") + +type Client interface { + // [Preview API] Creates a new personal access token (PAT) for the requesting user. + CreatePat(context.Context, CreatePatArgs) (*PatTokenResult, error) + // [Preview API] Gets a single personal access token (PAT). + GetPat(context.Context, GetPatArgs) (*PatTokenResult, error) + // [Preview API] Revokes a personal access token (PAT) by authorizationId. + Revoke(context.Context, RevokeArgs) error + // [Preview API] Updates an existing personal access token (PAT) with the new parameters. To update a token, it must be valid (has not been revoked). + UpdatePat(context.Context, UpdatePatArgs) (*PatTokenResult, error) + // [Preview API] Gets a paged list of personal access tokens (PATs) created in this organization. Subsequent calls to the API require the same filtering options to be supplied. + ListPats(context.Context, ListPatsArgs) (*PagedPatResults, error) +} + +type ClientImpl struct { + Client azuredevops.Client +} + +func NewClient(ctx context.Context, connection *azuredevops.Connection) (Client, error) { + client, err := connection.GetClientByResourceAreaId(ctx, ResourceAreaId) + if err != nil { + return nil, err + } + return &ClientImpl{ + Client: *client, + }, nil +} + +// [Preview API] Creates a new personal access token (PAT) for the requesting user. +func (client *ClientImpl) CreatePat(ctx context.Context, args CreatePatArgs) (*PatTokenResult, error) { + if args.Token == nil { + return nil, &azuredevops.ArgumentNilError{ArgumentName: "args.Token"} + } + + body, marshalErr := json.Marshal(*args.Token) + if marshalErr != nil { + return nil, marshalErr + } + locationId, _ := uuid.Parse("55967393-20ef-45c6-a96c-b5d5d5986a9a") + resp, err := client.Client.Send(ctx, http.MethodPost, locationId, "7.2-preview.1", nil, nil, bytes.NewReader(body), "application/json", "application/json", nil) + if err != nil { + return nil, err + } + + var responseValue PatTokenResult + err = client.Client.UnmarshalBody(resp, &responseValue) + return &responseValue, err +} + +type CreatePatArgs struct { + // (required) The request parameters for creating a personal access token (PAT) + Token *PatTokenCreateRequest +} + +// [Preview API] Gets a single personal access token (PAT). +func (client *ClientImpl) GetPat(ctx context.Context, args GetPatArgs) (*PatTokenResult, error) { + if args.AuthorizationId == nil { + return nil, &azuredevops.ArgumentNilError{ArgumentName: "args.AuthorizationId"} + } + + queryParams := url.Values{} + queryParams.Add("authorizationId", (*args.AuthorizationId).String()) + locationId, _ := uuid.Parse("55967393-20ef-45c6-a96c-b5d5d5986a9a") + resp, err := client.Client.Send(ctx, http.MethodGet, locationId, "7.2-preview.1", nil, queryParams, nil, "", "application/json", nil) + if err != nil { + return nil, err + } + + var responseValue PatTokenResult + err = client.Client.UnmarshalBody(resp, &responseValue) + return &responseValue, err +} + +type GetPatArgs struct { + // (required) The authorizationId identifying a single, unique personal access token (PAT). + AuthorizationId *uuid.UUID +} + +// [Preview API] Revokes a personal access token (PAT) by authorizationId. +func (client *ClientImpl) Revoke(ctx context.Context, args RevokeArgs) error { + if args.AuthorizationId == nil { + return &azuredevops.ArgumentNilError{ArgumentName: "args.AuthorizationId"} + } + + queryParams := url.Values{} + queryParams.Add("authorizationId", (*args.AuthorizationId).String()) + locationId, _ := uuid.Parse("55967393-20ef-45c6-a96c-b5d5d5986a9a") + _, err := client.Client.Send(ctx, http.MethodDelete, locationId, "7.2-preview.1", nil, queryParams, nil, "", "application/json", nil) + if err != nil { + return err + } + + return nil +} + +type RevokeArgs struct { + // (required) The authorizationId identifying a single, unique personal access token (PAT). + AuthorizationId *uuid.UUID +} + +func (client *ClientImpl) UpdatePat(ctx context.Context, args UpdatePatArgs) (*PatTokenResult, error) { + if args.Token == nil { + return nil, &azuredevops.ArgumentNilError{ArgumentName: "args.Body"} + } + + body, marshalErr := json.Marshal(*args.Token) + if marshalErr != nil { + return nil, marshalErr + } + locationId, _ := uuid.Parse("55967393-20ef-45c6-a96c-b5d5d5986a9a") + resp, err := client.Client.Send(ctx, http.MethodPut, locationId, "7.2-preview.1", nil, nil, bytes.NewReader(body), "application/json", "application/json", nil) + if err != nil { + return nil, err + } + + var responseValue PatTokenResult + err = client.Client.UnmarshalBody(resp, &responseValue) + return &responseValue, err +} + +type UpdatePatArgs struct { + // (required) The authorizationId identifying a single, unique personal access token (PAT). + Token *PatTokenUpdateRequest +} + +// [Preview API] Lists of all the session token details of the personal access tokens (PATs) for a particular user. +func (client *ClientImpl) ListPats(ctx context.Context, args ListPatsArgs) (*PagedPatResults, error) { + queryParams := url.Values{} + if args.DisplayFilterOption != nil && *args.DisplayFilterOption != "" { + queryParams.Add("displayFilterOption", string(*args.DisplayFilterOption)) + } + if args.SortByOption != nil && *args.SortByOption != "" { + queryParams.Add("sortByOption", string(*args.SortByOption)) + } + if args.IsSortAscending != nil { + queryParams.Add("isSortAscending", strconv.FormatBool(*args.IsSortAscending)) + } + if args.ContinuationToken != nil && *args.ContinuationToken != "" { + queryParams.Add("continuationToken", *args.ContinuationToken) + } + if args.Top != nil { + queryParams.Add("$top", strconv.Itoa(*args.Top)) + } + locationId, _ := uuid.Parse("55967393-20ef-45c6-a96c-b5d5d5986a9a") + resp, err := client.Client.Send(ctx, http.MethodGet, locationId, "7.1-preview.1", nil, queryParams, nil, "", "application/json", nil) + if err != nil { + return nil, err + } + + var responseValue PagedPatResults + err = client.Client.UnmarshalBody(resp, &responseValue) + return &responseValue, err +} + +// Arguments for the ListPersonalAccessTokens function +type ListPatsArgs struct { + // (Optional) Refers to the status of the personal access token (PAT) + DisplayFilterOption *DisplayFilterOption + // (Optional) Which field to sort by + SortByOption *SortByOption + // (Optional) Ascending or descending + IsSortAscending *bool + // (Optional) Where to start returning tokens from + ContinuationToken *string + // (Optional) How many tokens to return, limit of 100 + Top *int +} diff --git a/azuredevops/v7/tokens/models.go b/azuredevops/v7/tokens/models.go new file mode 100644 index 00000000..203467bf --- /dev/null +++ b/azuredevops/v7/tokens/models.go @@ -0,0 +1,173 @@ +package tokens + +import ( + "github.com/google/uuid" + "github.com/microsoft/azure-devops-go-api/azuredevops/v7" +) + +// PatTokenCreateRequest encapsulates the request parameters for creating a new personal access token (PAT) +type PatTokenCreateRequest struct { + // True, if this personal access token (PAT) is for all of the user's accessible organizations. False, if otherwise (e.g. if the token is for a specific organization) + AllOrgs *bool `json:"allOrgs,omitempty"` + // The token name + DisplayName *string `json:"displayName,omitempty"` + // The token scopes for accessing Azure DevOps resources + Scope *string `json:"scope,omitempty"` + // The token expiration date. If the "Enforce maximum personal access token lifespan" policy is enabled and the provided token expiration date is past the maximum allowed lifespan, it will return back a PAT with a validTo date equal to the current date + maximum allowed lifespan. + ValidTo *azuredevops.Time `json:"validTo,omitempty"` +} + +// PatTokenResult contains the resulting personal access token (PAT) and the error (if any) that occurred during the operation +type PatTokenResult struct { + // Represents a personal access token (PAT) used to access Azure DevOps resources + PatToken PatToken `json:"patToken,omitempty"` + // The error (if any) that occurred + PatTokenError *PatTokenError `json:"patTokenError,omitempty"` +} + +// PatToken represents a personal access token (PAT) used to access Azure DevOps resources +type PatToken struct { + // Unique guid identifier + AuthorizationId *uuid.UUID `json:"authorizationId,omitempty"` + // The token name + DisplayName *string `json:"displayName,omitempty"` + // The token scopes for accessing Azure DevOps resources + Scope *string `json:"scope,omitempty"` + // The organizations for which the token is valid; null if the token applies to all of the user's accessible organizations + TargetAccounts *[]uuid.UUID `json:"targetAccounts,omitempty"` + // The unique token string generated at creation + Token *string `json:"token,omitempty"` + // The token creation date + ValidFrom *azuredevops.Time `json:"validFrom,omitempty"` + // The token expiration date + ValidTo *azuredevops.Time `json:"validTo,omitempty"` +} + +// Enumeration of possible errors returned when creating a personal access token (PAT) +type PatTokenError string + +type buildPatTokenErrorType struct { + None PatTokenError + DisplayNameRequired PatTokenError + InvalideDisplayName PatTokenError + InvalidValidTo PatTokenError + InvalidScope PatTokenError + UserIdRequired PatTokenError + InvalidUserId PatTokenError + InvalidUserType PatTokenError + AccessDenied PatTokenError + FailedToIssueAccessToken PatTokenError + InvalidClient PatTokenError + InvalidClientType PatTokenError + InvalidClientId PatTokenError + InvalideTargetAccounts PatTokenError + HostAuthorizationNotFound PatTokenError + AuthorizationNotFound PatTokenError + FailedToUpdateAccessToken PatTokenError + SourceNotSupported PatTokenError + InvalidSourceIP PatTokenError + InvalideSource PatTokenError + DuplicateHash PatTokenError + SshPolicyDisabled PatTokenError + InvalidToken PatTokenError + TokenNotFound PatTokenError + InvalidAuthorizationId PatTokenError + FailedToReadTenantPolicy PatTokenError + GlobalPatPolicyViolation PatTokenError + FullScopePatPolicyViolation PatTokenError + PatLifespanPolicyViolation PatTokenError + InvalidTokenType PatTokenError + InvalidAudience PatTokenError + InvalidSubject PatTokenError + DeploymentHostNotSupported PatTokenError +} + +var PatTokeErrorValues = buildPatTokenErrorType{ + None: "none", + DisplayNameRequired: "displayNameRequired", + InvalideDisplayName: "invalidDisplayName", + InvalidValidTo: "invalidValidTo", + InvalidScope: "invalidScope", + UserIdRequired: "userIdRequired", + InvalidUserId: "invalidUserId", + InvalidUserType: "invalidUserType", + AccessDenied: "accessDenied", + FailedToIssueAccessToken: "failedToIssueAccessToken", + InvalidClient: "invalidClient", + InvalidClientType: "invalidClientType", + InvalidClientId: "invalidClientId", + InvalideTargetAccounts: "invalidTargetAccounts", + HostAuthorizationNotFound: "hostAuthorizationNotFound", + AuthorizationNotFound: "authorizationNotFound", + FailedToUpdateAccessToken: "failedToUpdateAccessToken", + SourceNotSupported: "sourceNotSupported", + InvalidSourceIP: "invalidSourceIP", + InvalideSource: "invalidSource", + DuplicateHash: "duplicateHash", + SshPolicyDisabled: "sshPolicyDisabled", + InvalidToken: "invalidToken", + TokenNotFound: "tokenNotFound", + InvalidAuthorizationId: "invalidAuthorizationId", + FailedToReadTenantPolicy: "failedToReadTenantPolicy", + GlobalPatPolicyViolation: "globalPatPolicyViolation", + FullScopePatPolicyViolation: "fullScopePatPolicyViolation", + PatLifespanPolicyViolation: "patLifespanPolicyViolation", + InvalidTokenType: "invalidTokenType", + InvalidAudience: "invalidAudience", + InvalidSubject: "invalidSubject", + DeploymentHostNotSupported: "deploymentHostNotSupported", +} + +// PatTokenUpdateRequest encapsulates the request parameters for updating a personal access token (PAT) +type PatTokenUpdateRequest struct { + // (Optional) True if this personal access token (PAT) is for all of the user's accessible organizations. False if otherwise (e.g. if the token is for a specific organization) + AllOrgs *bool `json:"allOrgs,omitempty"` + // The authorizationId identifying a single, unique personal access token (PAT) + AuthorizationId *uuid.UUID `json:"authorizationId,omitempty"` + // (Optional) The token name + DisplayName *string `json:"displayName,omitempty"` + // (Optional) The token scopes for accessing Azure DevOps resources + Scope *string `json:"scope,omitempty"` + // (Optional) The token expiration date. If the \"Enforce maximum personal access token lifespan\" policy is enabled and the provided token expiration date is past the maximum allowed lifespan, it will return back a PAT with a validTo date equal to the date when the PAT was intially created + maximum allowed lifespan. + ValidTo *azuredevops.Time `json:"validTo,omitempty"` +} + +// Enumerates display filter options for Personal Access Tokens (PATs) +type DisplayFilterOption string + +type buildDisplayFilterOptionType struct { + Active DisplayFilterOption + Revoked DisplayFilterOption + Expired DisplayFilterOption + All DisplayFilterOption +} + +var DisplayFilterOptionValues = buildDisplayFilterOptionType{ + Active: "active", + Revoked: "revoked", + Expired: "expired", + All: "all", +} + +// Enumerates sort by options for Personal Access Tokens (PATs) +type SortByOption string + +type buildSortByOptionType struct { + DisplayName SortByOption + DisplayDate SortByOption + Status SortByOption +} + +var SortByOptionValues = buildSortByOptionType{ + DisplayName: "displayName", + DisplayDate: "displayDate", + Status: "status", +} + +// PagedPatResults returned by the List method; contains a list of personal access tokens (PATs) and the continuation token to get the next page of results +type PagedPatResults struct { + // "Used to access the next page of results in successive API calls to list personal access tokens (PATs) + ContinuationToken *string `json:"continuationToken,omitempty"` + // The list of personal access tokens (PATs) + PatTokens *[]PatToken `json:"patTokens,omitempty"` +} From 1bf06ceec4b328314a3599e2bf967958835eb371 Mon Sep 17 00:00:00 2001 From: cparkins Date: Thu, 15 Aug 2024 12:57:52 -0600 Subject: [PATCH 2/2] Update to send the request with 7.1-preview.1 instead. These APIs have been available since 6.1-preview.1. --- azuredevops/v7/tokens/client.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/azuredevops/v7/tokens/client.go b/azuredevops/v7/tokens/client.go index 43bb3d25..ec8e9bb2 100644 --- a/azuredevops/v7/tokens/client.go +++ b/azuredevops/v7/tokens/client.go @@ -12,11 +12,12 @@ import ( "bytes" "context" "encoding/json" - "github.com/google/uuid" - "github.com/microsoft/azure-devops-go-api/azuredevops/v7" "net/http" "net/url" "strconv" + + "github.com/google/uuid" + "github.com/microsoft/azure-devops-go-api/azuredevops/v7" ) var ResourceAreaId, _ = uuid.Parse("55967393-20ef-45c6-a96c-b5d5d5986a9a") @@ -58,8 +59,9 @@ func (client *ClientImpl) CreatePat(ctx context.Context, args CreatePatArgs) (*P if marshalErr != nil { return nil, marshalErr } + locationId, _ := uuid.Parse("55967393-20ef-45c6-a96c-b5d5d5986a9a") - resp, err := client.Client.Send(ctx, http.MethodPost, locationId, "7.2-preview.1", nil, nil, bytes.NewReader(body), "application/json", "application/json", nil) + resp, err := client.Client.Send(ctx, http.MethodPost, locationId, "7.1-preview.1", nil, nil, bytes.NewReader(body), "application/json", "application/json", nil) if err != nil { return nil, err } @@ -83,7 +85,7 @@ func (client *ClientImpl) GetPat(ctx context.Context, args GetPatArgs) (*PatToke queryParams := url.Values{} queryParams.Add("authorizationId", (*args.AuthorizationId).String()) locationId, _ := uuid.Parse("55967393-20ef-45c6-a96c-b5d5d5986a9a") - resp, err := client.Client.Send(ctx, http.MethodGet, locationId, "7.2-preview.1", nil, queryParams, nil, "", "application/json", nil) + resp, err := client.Client.Send(ctx, http.MethodGet, locationId, "7.1-preview.1", nil, queryParams, nil, "", "application/json", nil) if err != nil { return nil, err } @@ -107,7 +109,7 @@ func (client *ClientImpl) Revoke(ctx context.Context, args RevokeArgs) error { queryParams := url.Values{} queryParams.Add("authorizationId", (*args.AuthorizationId).String()) locationId, _ := uuid.Parse("55967393-20ef-45c6-a96c-b5d5d5986a9a") - _, err := client.Client.Send(ctx, http.MethodDelete, locationId, "7.2-preview.1", nil, queryParams, nil, "", "application/json", nil) + _, err := client.Client.Send(ctx, http.MethodDelete, locationId, "7.1-preview.1", nil, queryParams, nil, "", "application/json", nil) if err != nil { return err } @@ -130,7 +132,7 @@ func (client *ClientImpl) UpdatePat(ctx context.Context, args UpdatePatArgs) (*P return nil, marshalErr } locationId, _ := uuid.Parse("55967393-20ef-45c6-a96c-b5d5d5986a9a") - resp, err := client.Client.Send(ctx, http.MethodPut, locationId, "7.2-preview.1", nil, nil, bytes.NewReader(body), "application/json", "application/json", nil) + resp, err := client.Client.Send(ctx, http.MethodPut, locationId, "7.1-preview.1", nil, nil, bytes.NewReader(body), "application/json", "application/json", nil) if err != nil { return nil, err } @@ -163,6 +165,7 @@ func (client *ClientImpl) ListPats(ctx context.Context, args ListPatsArgs) (*Pag if args.Top != nil { queryParams.Add("$top", strconv.Itoa(*args.Top)) } + locationId, _ := uuid.Parse("55967393-20ef-45c6-a96c-b5d5d5986a9a") resp, err := client.Client.Send(ctx, http.MethodGet, locationId, "7.1-preview.1", nil, queryParams, nil, "", "application/json", nil) if err != nil {