From c3c2c61acafe8bc58833d443af7da0182555e37b Mon Sep 17 00:00:00 2001 From: Bjorn Neergaard Date: Wed, 28 Sep 2022 14:53:13 -0600 Subject: [PATCH] Add support for User SSH signing keys --- AUTHORS | 1 + github/github-accessors.go | 32 ++++ github/github-accessors_test.go | 40 +++++ github/github-stringify_test.go | 13 ++ github/users_keys_test.go | 4 +- github/users_ssh_signing_keys.go | 108 ++++++++++++++ github/users_ssh_signing_keys_test.go | 202 ++++++++++++++++++++++++++ 7 files changed, 398 insertions(+), 2 deletions(-) create mode 100644 github/users_ssh_signing_keys.go create mode 100644 github/users_ssh_signing_keys_test.go diff --git a/AUTHORS b/AUTHORS index 80bd26dd4f5..1ee400fe206 100644 --- a/AUTHORS +++ b/AUTHORS @@ -59,6 +59,7 @@ Beyang Liu Billy Keyes Billy Lynch Björn Häuser +Bjorn Neergaard boljen Brad Harris Brad Moylan diff --git a/github/github-accessors.go b/github/github-accessors.go index 0c87a56eeb1..258942fa8c8 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -17230,6 +17230,38 @@ func (s *SourceImportAuthor) GetURL() string { return *s.URL } +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (s *SSHSigningKey) GetCreatedAt() Timestamp { + if s == nil || s.CreatedAt == nil { + return Timestamp{} + } + return *s.CreatedAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (s *SSHSigningKey) GetID() int64 { + if s == nil || s.ID == nil { + return 0 + } + return *s.ID +} + +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (s *SSHSigningKey) GetKey() string { + if s == nil || s.Key == nil { + return "" + } + return *s.Key +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (s *SSHSigningKey) GetTitle() string { + if s == nil || s.Title == nil { + return "" + } + return *s.Title +} + // GetAction returns the Action field if it's non-nil, zero value otherwise. func (s *StarEvent) GetAction() string { if s == nil || s.Action == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 086db726750..be0042e99ae 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -20116,6 +20116,46 @@ func TestSourceImportAuthor_GetURL(tt *testing.T) { s.GetURL() } +func TestSSHSigningKey_GetCreatedAt(tt *testing.T) { + var zeroValue Timestamp + s := &SSHSigningKey{CreatedAt: &zeroValue} + s.GetCreatedAt() + s = &SSHSigningKey{} + s.GetCreatedAt() + s = nil + s.GetCreatedAt() +} + +func TestSSHSigningKey_GetID(tt *testing.T) { + var zeroValue int64 + s := &SSHSigningKey{ID: &zeroValue} + s.GetID() + s = &SSHSigningKey{} + s.GetID() + s = nil + s.GetID() +} + +func TestSSHSigningKey_GetKey(tt *testing.T) { + var zeroValue string + s := &SSHSigningKey{Key: &zeroValue} + s.GetKey() + s = &SSHSigningKey{} + s.GetKey() + s = nil + s.GetKey() +} + +func TestSSHSigningKey_GetTitle(tt *testing.T) { + var zeroValue string + s := &SSHSigningKey{Title: &zeroValue} + s.GetTitle() + s = &SSHSigningKey{} + s.GetTitle() + s = nil + s.GetTitle() +} + func TestStarEvent_GetAction(tt *testing.T) { var zeroValue string s := &StarEvent{Action: &zeroValue} diff --git a/github/github-stringify_test.go b/github/github-stringify_test.go index 09a6888e1f3..b83ff2373d6 100644 --- a/github/github-stringify_test.go +++ b/github/github-stringify_test.go @@ -1714,6 +1714,19 @@ func TestRepositoryRelease_String(t *testing.T) { } } +func TestSSHSigningKey_String(t *testing.T) { + v := SSHSigningKey{ + ID: Int64(0), + Key: String(""), + Title: String(""), + CreatedAt: &Timestamp{}, + } + want := `github.SSHSigningKey{ID:0, Key:"", Title:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` + if got := v.String(); got != want { + t.Errorf("SSHSigningKey.String = %v, want %v", got, want) + } +} + func TestSecretScanning_String(t *testing.T) { v := SecretScanning{ Status: String(""), diff --git a/github/users_keys_test.go b/github/users_keys_test.go index b3e5fc9e7a3..ac5c10d0335 100644 --- a/github/users_keys_test.go +++ b/github/users_keys_test.go @@ -138,12 +138,12 @@ func TestUsersService_CreateKey(t *testing.T) { ctx := context.Background() key, _, err := client.Users.CreateKey(ctx, input) if err != nil { - t.Errorf("Users.GetKey returned error: %v", err) + t.Errorf("Users.CreateKey returned error: %v", err) } want := &Key{ID: Int64(1)} if !cmp.Equal(key, want) { - t.Errorf("Users.GetKey returned %+v, want %+v", key, want) + t.Errorf("Users.CreateKey returned %+v, want %+v", key, want) } const methodName = "CreateKey" diff --git a/github/users_ssh_signing_keys.go b/github/users_ssh_signing_keys.go new file mode 100644 index 00000000000..567623f8875 --- /dev/null +++ b/github/users_ssh_signing_keys.go @@ -0,0 +1,108 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// SSHSigningKey represents a public SSH key used to sign git commits. +type SSHSigningKey struct { + ID *int64 `json:"id,omitempty"` + Key *string `json:"key,omitempty"` + Title *string `json:"title,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` +} + +func (k SSHSigningKey) String() string { + return Stringify(k) +} + +// ListSSHSigningKeys lists the SSH signing keys for a user. Passing an empty +// username string will fetch SSH signing keys for the authenticated user. +// +// GitHub API docs: https://docs.github.com/en/rest/users/ssh-signing-keys#list-ssh-signing-keys-for-the-authenticated-user +// GitHub API docs: https://docs.github.com/en/rest/users/ssh-signing-keys#list-ssh-signing-keys-for-a-user +func (s *UsersService) ListSSHSigningKeys(ctx context.Context, user string, opts *ListOptions) ([]*SSHSigningKey, *Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("users/%v/ssh_signing_keys", user) + } else { + u = "user/ssh_signing_keys" + } + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var keys []*SSHSigningKey + resp, err := s.client.Do(ctx, req, &keys) + if err != nil { + return nil, resp, err + } + + return keys, resp, nil +} + +// GetSSHSigningKey fetches a single SSH signing key for the authenticated user. +// +// GitHub API docs: https://docs.github.com/en/rest/users/ssh-signing-keys#get-an-ssh-signing-key-for-the-authenticated-user +func (s *UsersService) GetSSHSigningKey(ctx context.Context, id int64) (*SSHSigningKey, *Response, error) { + u := fmt.Sprintf("user/ssh_signing_keys/%v", id) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + key := new(SSHSigningKey) + resp, err := s.client.Do(ctx, req, key) + if err != nil { + return nil, resp, err + } + + return key, resp, nil +} + +// CreateSSHSigningKey adds a SSH signing key for the authenticated user. +// +// GitHub API docs: https://docs.github.com/en/rest/users/ssh-signing-keys#create-a-ssh-signing-key-for-the-authenticated-user +func (s *UsersService) CreateSSHSigningKey(ctx context.Context, key *Key) (*SSHSigningKey, *Response, error) { + u := "user/ssh_signing_keys" + + req, err := s.client.NewRequest("POST", u, key) + if err != nil { + return nil, nil, err + } + + k := new(SSHSigningKey) + resp, err := s.client.Do(ctx, req, k) + if err != nil { + return nil, resp, err + } + + return k, resp, nil +} + +// DeleteKey deletes a SSH signing key for the authenticated user. +// +// GitHub API docs: https://docs.github.com/en/rest/users/ssh-signing-keys#delete-an-ssh-signing-key-for-the-authenticated-user +func (s *UsersService) DeleteSSHSigningKey(ctx context.Context, id int64) (*Response, error) { + u := fmt.Sprintf("user/ssh_signing_keys/%v", id) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/github/users_ssh_signing_keys_test.go b/github/users_ssh_signing_keys_test.go new file mode 100644 index 00000000000..3bf59e108db --- /dev/null +++ b/github/users_ssh_signing_keys_test.go @@ -0,0 +1,202 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestUsersService_ListSSHSigningKeys_authenticatedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/ssh_signing_keys", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 2} + ctx := context.Background() + keys, _, err := client.Users.ListSSHSigningKeys(ctx, "", opt) + if err != nil { + t.Errorf("Users.ListSSHSigningKeys returned error: %v", err) + } + + want := []*SSHSigningKey{{ID: Int64(1)}} + if !cmp.Equal(keys, want) { + t.Errorf("Users.ListSSHSigningKeys returned %+v, want %+v", keys, want) + } + + const methodName = "ListSSHSigningKeys" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.ListSSHSigningKeys(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListSSHSigningKeys(ctx, "", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestUsersService_ListSSHSigningKeys_specifiedUser(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/users/u/ssh_signing_keys", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"id":1}]`) + }) + + ctx := context.Background() + keys, _, err := client.Users.ListSSHSigningKeys(ctx, "u", nil) + if err != nil { + t.Errorf("Users.ListSSHSigningKeys returned error: %v", err) + } + + want := []*SSHSigningKey{{ID: Int64(1)}} + if !cmp.Equal(keys, want) { + t.Errorf("Users.ListSSHSigningKeys returned %+v, want %+v", keys, want) + } +} + +func TestUsersService_ListSSHSigningKeys_invalidUser(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + ctx := context.Background() + _, _, err := client.Users.ListSSHSigningKeys(ctx, "%", nil) + testURLParseError(t, err) +} + +func TestUsersService_GetSSHSigningKey(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/ssh_signing_keys/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1}`) + }) + + ctx := context.Background() + key, _, err := client.Users.GetSSHSigningKey(ctx, 1) + if err != nil { + t.Errorf("Users.GetSSHSigningKey returned error: %v", err) + } + + want := &SSHSigningKey{ID: Int64(1)} + if !cmp.Equal(key, want) { + t.Errorf("Users.GetSSHSigningKey returned %+v, want %+v", key, want) + } + + const methodName = "GetSSHSigningKey" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.GetSSHSigningKey(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.GetSSHSigningKey(ctx, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestUsersService_CreateSSHSigningKey(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &Key{Key: String("k"), Title: String("t")} + + mux.HandleFunc("/user/ssh_signing_keys", func(w http.ResponseWriter, r *http.Request) { + v := new(Key) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + if !cmp.Equal(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"id":1}`) + }) + + ctx := context.Background() + key, _, err := client.Users.CreateSSHSigningKey(ctx, input) + if err != nil { + t.Errorf("Users.CreateSSHSigningKey returned error: %v", err) + } + + want := &SSHSigningKey{ID: Int64(1)} + if !cmp.Equal(key, want) { + t.Errorf("Users.CreateSSHSigningKey returned %+v, want %+v", key, want) + } + + const methodName = "CreateKey" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.CreateKey(ctx, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestUsersService_DeleteSSHSigningKey(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/user/ssh_signing_keys/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := context.Background() + _, err := client.Users.DeleteSSHSigningKey(ctx, 1) + if err != nil { + t.Errorf("Users.DeleteSSHSigningKey returned error: %v", err) + } + + const methodName = "DeleteSSHSigningKey" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.DeleteSSHSigningKey(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.DeleteSSHSigningKey(ctx, 1) + }) +} + +func TestSSHSigningKey_Marshal(t *testing.T) { + testJSONMarshal(t, &SSHSigningKey{}, "{}") + + u := &Key{ + ID: Int64(1), + Key: String("abc"), + Title: String("title"), + CreatedAt: &Timestamp{referenceTime}, + } + + want := `{ + "id": 1, + "key": "abc", + "title": "title", + "created_at": ` + referenceTimeStr + ` + }` + + testJSONMarshal(t, u, want) +}