Skip to content
This repository has been archived by the owner on Dec 10, 2024. It is now read-only.

Add expires_at for token rotation operations #1841

Merged
merged 1 commit into from
Dec 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion examples/personal_access_tokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"encoding/json"
"fmt"
"log"
"time"

"github.com/xanzy/go-gitlab"
)
Expand Down Expand Up @@ -68,7 +69,11 @@ func patRotateExample() {
log.Fatal(err)
}

newPersonalAccessToken, _, err := git.PersonalAccessTokens.RotatePersonalAccessToken(12345)
expiry := gitlab.ISOTime(time.Date(2023, time.August, 15, 0, 0, 0, 0, time.UTC))
opts := &gitlab.RotatePersonalAccessTokenOptions{
ExpiresAt: &expiry,
}
newPersonalAccessToken, _, err := git.PersonalAccessTokens.RotatePersonalAccessToken(12345, opts)
if err != nil {
log.Fatal(err)
}
Expand Down
15 changes: 12 additions & 3 deletions group_access_tokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,18 +145,27 @@ func (s *GroupAccessTokensService) CreateGroupAccessToken(gid interface{}, opt *
return pat, resp, nil
}

// RotateGroupAccessTokenOptions represents the available RotateGroupAccessToken()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_access_tokens.html#rotate-a-group-access-token
type RotateGroupAccessTokenOptions struct {
ExpiresAt *ISOTime `url:"expires_at,omitempty" json:"expires_at,omitempty"`
}

// RotateGroupAccessToken revokes a group access token and returns a new group
// access token that expires in one week.
// access token that expires in one week per default.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_access_tokens.html#rotate-a-group-access-token
func (s *GroupAccessTokensService) RotateGroupAccessToken(gid interface{}, id int, options ...RequestOptionFunc) (*GroupAccessToken, *Response, error) {
func (s *GroupAccessTokensService) RotateGroupAccessToken(gid interface{}, id int, opt *RotateGroupAccessTokenOptions, options ...RequestOptionFunc) (*GroupAccessToken, *Response, error) {
groups, err := parseID(gid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("groups/%s/access_tokens/%d/rotate", PathEscape(groups), id)
req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
if err != nil {
return nil, nil, err
}
Expand Down
32 changes: 32 additions & 0 deletions group_access_tokens_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,38 @@ func TestCreateGroupAccessToken(t *testing.T) {
t.Errorf("GroupAccessTokens.CreateGroupAccessToken returned %+v, want %+v", groupAccessToken, want)
}
}
func TestRotateGroupAccessToken(t *testing.T) {
mux, client := setup(t)
mux.HandleFunc("/api/v4/groups/1/access_tokens/42/rotate", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodPost)
mustWriteHTTPResponse(t, w, "testdata/rotate_group_access_token.json")
})

createdAt, _ := time.Parse(time.RFC3339, "2023-08-01T15:00:00.00Z")
expiration := ISOTime(time.Date(2023, time.August, 15, 0, 0, 0, 0, time.UTC))
opts := &RotateGroupAccessTokenOptions{ExpiresAt: &expiration}
rotatedToken, _, err := client.GroupAccessTokens.RotateGroupAccessToken(1, 42, opts)
if err != nil {
t.Errorf("GroupAccessTokens.RotateGroupAccessToken returned error: %v", err)
}

want := &GroupAccessToken{
ID: 42,
UserID: 1337,
Name: "Rotated Token",
Scopes: []string{"api"},
ExpiresAt: &expiration,
CreatedAt: &createdAt,
Active: true,
Revoked: false,
Token: "s3cr3t",
AccessLevel: AccessLevelValue(30),
}

if !reflect.DeepEqual(want, rotatedToken) {
t.Errorf("GroupAccessTokens.RotateGroupAccessToken returned %+v, want %+v", rotatedToken, want)
}
}

func TestRevokeGroupAccessToken(t *testing.T) {
mux, client := setup(t)
Expand Down
16 changes: 13 additions & 3 deletions personal_access_tokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,24 @@ func (s *PersonalAccessTokensService) GetSinglePersonalAccessToken(options ...Re
return pat, resp, nil
}

// RotatePersonalAccessTokenOptions represents the available RotatePersonalAccessToken()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/personal_access_tokens.html#rotate-a-personal-access-token
type RotatePersonalAccessTokenOptions struct {
ExpiresAt *ISOTime `url:"expires_at,omitempty" json:"expires_at,omitempty"`
}

// RotatePersonalAccessToken revokes a token and returns a new token that
// expires in one week.
// 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, options ...RequestOptionFunc) (*PersonalAccessToken, *Response, error) {
func (s *PersonalAccessTokensService) RotatePersonalAccessToken(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, nil, options)

req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
if err != nil {
return nil, nil, err
}
Expand Down
31 changes: 31 additions & 0 deletions personal_access_tokens_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,37 @@ func TestGetSinglePersonalAccessToken(t *testing.T) {
}
}

func TestRotatePersonalAccessToken(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.RotatePersonalAccessToken(42, opts)
if err != nil {
t.Errorf("PersonalAccessTokens.RotatePersonalAccessToken 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.RotatePersonalAccessTokens returned %+v, want %+v", rotatedToken, want)
}
}
func TestRevokePersonalAccessToken(t *testing.T) {
mux, client := setup(t)

Expand Down
15 changes: 12 additions & 3 deletions project_access_tokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,18 +146,27 @@ func (s *ProjectAccessTokensService) CreateProjectAccessToken(pid interface{}, o
return pat, resp, nil
}

// RotateProjectAccessTokenOptions represents the available RotateProjectAccessToken()
// options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_access_tokens.html#rotate-a-project-access-token
type RotateProjectAccessTokenOptions struct {
ExpiresAt *ISOTime `url:"expires_at,omitempty" json:"expires_at,omitempty"`
}

// RotateProjectAccessToken revokes a project access token and returns a new
// project access token that expires in one week.
// project access token that expires in one week per default.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_access_tokens.html#rotate-a-project-access-token
func (s *ProjectAccessTokensService) RotateProjectAccessToken(pid interface{}, id int, options ...RequestOptionFunc) (*ProjectAccessToken, *Response, error) {
func (s *ProjectAccessTokensService) RotateProjectAccessToken(pid interface{}, id int, opt *RotateProjectAccessTokenOptions, options ...RequestOptionFunc) (*ProjectAccessToken, *Response, error) {
projects, err := parseID(pid)
if err != nil {
return nil, nil, err
}
u := fmt.Sprintf("projects/%s/access_tokens/%d/rotate", PathEscape(projects), id)
req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
if err != nil {
return nil, nil, err
}
Expand Down
33 changes: 33 additions & 0 deletions project_access_tokens_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,39 @@ func TestCreateProjectAccessToken(t *testing.T) {
}
}

func TestRotateProjectAccessToken(t *testing.T) {
mux, client := setup(t)
mux.HandleFunc("/api/v4/projects/1/access_tokens/42/rotate", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodPost)
mustWriteHTTPResponse(t, w, "testdata/rotate_project_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 := &RotateProjectAccessTokenOptions{ExpiresAt: &expiration}
rotatedToken, _, err := client.ProjectAccessTokens.RotateProjectAccessToken(1, 42, opts)
if err != nil {
t.Errorf("ProjectAccessTokens.RotateProjectAccessToken returned error: %v", err)
}

want := &ProjectAccessToken{
ID: 42,
UserID: 1337,
Name: "Rotated Token",
Scopes: []string{"api"},
ExpiresAt: &expiration,
CreatedAt: &createdAt,
Active: true,
Revoked: false,
AccessLevel: AccessLevelValue(30),
Token: "s3cr3t",
}

if !reflect.DeepEqual(want, rotatedToken) {
t.Errorf("ProjectAccessTokens.RotateProjectAccessTokens returned %+v, want %+v", rotatedToken, want)
}
}

func TestRevokeProjectAccessToken(t *testing.T) {
mux, client := setup(t)

Expand Down
13 changes: 13 additions & 0 deletions testdata/rotate_group_access_token.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"id": 42,
"name": "Rotated Token",
"revoked": false,
"created_at": "2023-08-01T15:00:00.000Z",
"scopes": ["api"],
"user_id": 1337,
"last_used_at": null,
"active": true,
"expires_at": "2023-08-15",
"access_level": 30,
"token": "s3cr3t"
}
12 changes: 12 additions & 0 deletions testdata/rotate_personal_access_token.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"id": 42,
"name": "Rotated Token",
"revoked": false,
"created_at": "2023-08-01T15:00:00.000Z",
"scopes": ["api"],
"user_id": 1337,
"last_used_at": null,
"active": true,
"expires_at": "2023-08-15",
"token": "s3cr3t"
}
13 changes: 13 additions & 0 deletions testdata/rotate_project_access_token.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"id": 42,
"name": "Rotated Token",
"revoked": false,
"created_at": "2023-08-01T15:00:00.000Z",
"scopes": ["api"],
"user_id": 1337,
"last_used_at": null,
"active": true,
"expires_at": "2023-08-15",
"access_level": 30,
"token": "s3cr3t"
}
Loading