Skip to content

Commit

Permalink
Merge pull request #1841 from buehlmann/master
Browse files Browse the repository at this point in the history
Add expires_at for token rotation operations
  • Loading branch information
svanharmelen committed Dec 9, 2023
2 parents db87505 + 16a46db commit bc91377
Show file tree
Hide file tree
Showing 10 changed files with 177 additions and 10 deletions.
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"
}

0 comments on commit bc91377

Please sign in to comment.