Skip to content

Commit

Permalink
Add support for User SSH signing keys
Browse files Browse the repository at this point in the history
  • Loading branch information
neersighted committed Sep 28, 2022
1 parent 0c3cd12 commit 188b59b
Show file tree
Hide file tree
Showing 4 changed files with 313 additions and 2 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Expand Up @@ -59,6 +59,7 @@ Beyang Liu <beyang.liu@gmail.com>
Billy Keyes <bluekeyes@gmail.com>
Billy Lynch <wlynch92@gmail.com>
Björn Häuser <b.haeuser@rebuy.de>
Bjorn Neergaard <bjorn@neersighted.com>
boljen <bol.christophe@gmail.com>
Brad Harris <bmharris@gmail.com>
Brad Moylan <moylan.brad@gmail.com>
Expand Down
4 changes: 2 additions & 2 deletions github/users_keys_test.go
Expand Up @@ -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"
Expand Down
108 changes: 108 additions & 0 deletions 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)
}
202 changes: 202 additions & 0 deletions 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)
}

0 comments on commit 188b59b

Please sign in to comment.