diff --git a/personal_access_tokens.go b/personal_access_tokens.go index ba32bd8fd..7c4adc471 100644 --- a/personal_access_tokens.go +++ b/personal_access_tokens.go @@ -129,12 +129,17 @@ type RotatePersonalAccessTokenOptions struct { ExpiresAt *ISOTime `url:"expires_at,omitempty" json:"expires_at,omitempty"` } -// RotatePersonalAccessToken revokes a token and returns a new token that +// RotatePersonalAccessToken is a backwards-compat shim for RotatePersonalAccessTokenByID. +func (s *PersonalAccessTokensService) RotatePersonalAccessToken(token int, opt *RotatePersonalAccessTokenOptions, options ...RequestOptionFunc) (*PersonalAccessToken, *Response, error) { + return s.RotatePersonalAccessTokenByID(token, opt, options...) +} + +// RotatePersonalAccessTokenByID revokes a token and returns a new token that // expires in one week per default. // // GitLab API docs: -// https://docs.gitlab.com/ee/api/personal_access_tokens.html#rotate-a-personal-access-token -func (s *PersonalAccessTokensService) RotatePersonalAccessToken(token int, opt *RotatePersonalAccessTokenOptions, options ...RequestOptionFunc) (*PersonalAccessToken, *Response, error) { +// https://docs.gitlab.com/ee/api/personal_access_tokens.html#use-a-personal-access-token-id +func (s *PersonalAccessTokensService) RotatePersonalAccessTokenByID(token int, opt *RotatePersonalAccessTokenOptions, options ...RequestOptionFunc) (*PersonalAccessToken, *Response, error) { u := fmt.Sprintf("personal_access_tokens/%d/rotate", token) req, err := s.client.NewRequest(http.MethodPost, u, opt, options) @@ -151,11 +156,38 @@ func (s *PersonalAccessTokensService) RotatePersonalAccessToken(token int, opt * return pat, resp, nil } -// RevokePersonalAccessToken revokes a personal access token. +// RotatePersonalAccessTokenSelf revokes the currently authenticated token +// and returns a new token that expires in one week per default. // // GitLab API docs: -// https://docs.gitlab.com/ee/api/personal_access_tokens.html#revoke-a-personal-access-token +// https://docs.gitlab.com/ee/api/personal_access_tokens.html#use-a-request-header +func (s *PersonalAccessTokensService) RotatePersonalAccessTokenSelf(opt *RotatePersonalAccessTokenOptions, options ...RequestOptionFunc) (*PersonalAccessToken, *Response, error) { + u := "personal_access_tokens/self/rotate" + + req, err := s.client.NewRequest(http.MethodPost, u, opt, options) + if err != nil { + return nil, nil, err + } + + pat := new(PersonalAccessToken) + resp, err := s.client.Do(req, pat) + if err != nil { + return nil, resp, err + } + + return pat, resp, nil +} + +// RevokePersonalAccessToken is a backwards-compat shim for RevokePersonalAccessTokenByID. func (s *PersonalAccessTokensService) RevokePersonalAccessToken(token int, options ...RequestOptionFunc) (*Response, error) { + return s.RevokePersonalAccessTokenByID(token, options...) +} + +// RevokePersonalAccessTokenByID revokes a personal access token by its ID. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/personal_access_tokens.html#using-a-personal-access-token-id-1 +func (s *PersonalAccessTokensService) RevokePersonalAccessTokenByID(token int, options ...RequestOptionFunc) (*Response, error) { u := fmt.Sprintf("personal_access_tokens/%d", token) req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) @@ -165,3 +197,19 @@ func (s *PersonalAccessTokensService) RevokePersonalAccessToken(token int, optio return s.client.Do(req, nil) } + +// RevokePersonalAccessTokenSelf revokes the currently authenticated +// personal access token. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/personal_access_tokens.html#using-a-request-header-1 +func (s *PersonalAccessTokensService) RevokePersonalAccessTokenSelf(options ...RequestOptionFunc) (*Response, error) { + u := "personal_access_tokens/self" + + req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/personal_access_tokens_test.go b/personal_access_tokens_test.go index 1236b7699..de9bcf6b7 100644 --- a/personal_access_tokens_test.go +++ b/personal_access_tokens_test.go @@ -31,7 +31,9 @@ func TestListPersonalAccessTokensWithUserFilter(t *testing.T) { mustWriteHTTPResponse(t, w, "testdata/list_personal_access_tokens_with_user_filter.json") }) - personalAccessTokens, _, err := client.PersonalAccessTokens.ListPersonalAccessTokens(&ListPersonalAccessTokensOptions{UserID: Ptr(1), ListOptions: ListOptions{Page: 1, PerPage: 10}}) + personalAccessTokens, _, err := client.PersonalAccessTokens.ListPersonalAccessTokens( + &ListPersonalAccessTokensOptions{UserID: Ptr(1), ListOptions: ListOptions{Page: 1, PerPage: 10}}, + ) if err != nil { t.Errorf("PersonalAccessTokens.ListPersonalAccessTokens returned error: %v", err) } @@ -81,7 +83,9 @@ func TestListPersonalAccessTokensWithUserFilter(t *testing.T) { } if !reflect.DeepEqual(want, personalAccessTokens) { - t.Errorf("PersonalAccessTokens.ListPersonalAccessTokens returned %+v, want %+v", personalAccessTokens, want) + t.Errorf( + "PersonalAccessTokens.ListPersonalAccessTokens returned %+v, want %+v", personalAccessTokens, want, + ) } } @@ -93,7 +97,9 @@ func TestListPersonalAccessTokensNoUserFilter(t *testing.T) { mustWriteHTTPResponse(t, w, "testdata/list_personal_access_tokens_without_user_filter.json") }) - personalAccessTokens, _, err := client.PersonalAccessTokens.ListPersonalAccessTokens(&ListPersonalAccessTokensOptions{ListOptions: ListOptions{Page: 1, PerPage: 10}}) + personalAccessTokens, _, err := client.PersonalAccessTokens.ListPersonalAccessTokens( + &ListPersonalAccessTokensOptions{ListOptions: ListOptions{Page: 1, PerPage: 10}}, + ) if err != nil { t.Errorf("PersonalAccessTokens.ListPersonalAccessTokens returned error: %v", err) } @@ -143,7 +149,9 @@ func TestListPersonalAccessTokensNoUserFilter(t *testing.T) { } if !reflect.DeepEqual(want, personalAccessTokens) { - t.Errorf("PersonalAccessTokens.ListPersonalAccessTokens returned %+v, want %+v", personalAccessTokens, want) + t.Errorf( + "PersonalAccessTokens.ListPersonalAccessTokens returned %+v, want %+v", personalAccessTokens, want, + ) } } @@ -253,7 +261,75 @@ func TestRotatePersonalAccessToken(t *testing.T) { } if !reflect.DeepEqual(want, rotatedToken) { - t.Errorf("PersonalAccessTokens.RotatePersonalAccessTokens returned %+v, want %+v", rotatedToken, want) + t.Errorf( + "PersonalAccessTokens.RotatePersonalAccessToken returned %+v, want %+v", rotatedToken, want, + ) + } +} + +func TestRotatePersonalAccessTokenByID(t *testing.T) { + mux, client := setup(t) + mux.HandleFunc("/api/v4/personal_access_tokens/42/rotate", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodPost) + mustWriteHTTPResponse(t, w, "testdata/rotate_personal_access_token.json") + }) + + createdAt, _ := time.Parse(time.RFC3339, "2023-08-01T15:00:00.000Z") + expiration := ISOTime(time.Date(2023, time.August, 15, 0, 0, 0, 0, time.UTC)) + opts := &RotatePersonalAccessTokenOptions{ExpiresAt: &expiration} + rotatedToken, _, err := client.PersonalAccessTokens.RotatePersonalAccessTokenByID(42, opts) + if err != nil { + t.Errorf("PersonalAccessTokens.RotatePersonalAccessTokenByID returned error: %v", err) + } + + want := &PersonalAccessToken{ + ID: 42, + UserID: 1337, + Name: "Rotated Token", + Scopes: []string{"api"}, + ExpiresAt: &expiration, + CreatedAt: &createdAt, + Active: true, + Revoked: false, + Token: "s3cr3t", + } + + if !reflect.DeepEqual(want, rotatedToken) { + t.Errorf( + "PersonalAccessTokens.RotatePersonalAccessTokenByID returned %+v, want %+v", rotatedToken, want, + ) + } +} + +func TestRotatePersonalAccessTokenSelf(t *testing.T) { + mux, client := setup(t) + mux.HandleFunc("/api/v4/personal_access_tokens/self/rotate", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodPost) + mustWriteHTTPResponse(t, w, "testdata/rotate_personal_access_token.json") + }) + + createdAt, _ := time.Parse(time.RFC3339, "2023-08-01T15:00:00.000Z") + expiration := ISOTime(time.Date(2023, time.August, 15, 0, 0, 0, 0, time.UTC)) + opts := &RotatePersonalAccessTokenOptions{ExpiresAt: &expiration} + rotatedToken, _, err := client.PersonalAccessTokens.RotatePersonalAccessTokenSelf(opts) + if err != nil { + t.Errorf("PersonalAccessTokens.RotatePersonalAccessTokenSelf returned error: %v", err) + } + + want := &PersonalAccessToken{ + ID: 42, + UserID: 1337, + Name: "Rotated Token", + Scopes: []string{"api"}, + ExpiresAt: &expiration, + CreatedAt: &createdAt, + Active: true, + Revoked: false, + Token: "s3cr3t", + } + + if !reflect.DeepEqual(want, rotatedToken) { + t.Errorf("PersonalAccessTokens.RotatePersonalAccessTokenSelf returned %+v, want %+v", rotatedToken, want) } } @@ -269,3 +345,29 @@ func TestRevokePersonalAccessToken(t *testing.T) { t.Errorf("PersonalAccessTokens.RevokePersonalAccessToken returned error: %v", err) } } + +func TestRevokePersonalAccessTokenByID(t *testing.T) { + mux, client := setup(t) + + mux.HandleFunc("/api/v4/personal_access_tokens/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodDelete) + }) + + _, err := client.PersonalAccessTokens.RevokePersonalAccessTokenByID(1) + if err != nil { + t.Errorf("PersonalAccessTokens.RevokePersonalAccessTokenByID returned error: %v", err) + } +} + +func TestRevokePersonalAccessTokenSelf(t *testing.T) { + mux, client := setup(t) + + mux.HandleFunc("/api/v4/personal_access_tokens/self", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodDelete) + }) + + _, err := client.PersonalAccessTokens.RevokePersonalAccessTokenSelf() + if err != nil { + t.Errorf("PersonalAccessTokens.RevokePersonalAccessTokenSelf returned error: %v", err) + } +}