diff --git a/github/github-accessors.go b/github/github-accessors.go index 35e61043ab8..4dd5529a3a3 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -4670,6 +4670,94 @@ func (c *CreateUserProjectOptions) GetBody() string { return *c.Body } +// GetAuthorizedCredentialExpiresAt returns the AuthorizedCredentialExpiresAt field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetAuthorizedCredentialExpiresAt() Timestamp { + if c == nil || c.AuthorizedCredentialExpiresAt == nil { + return Timestamp{} + } + return *c.AuthorizedCredentialExpiresAt +} + +// GetAuthorizedCredentialID returns the AuthorizedCredentialID field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetAuthorizedCredentialID() int64 { + if c == nil || c.AuthorizedCredentialID == nil { + return 0 + } + return *c.AuthorizedCredentialID +} + +// GetAuthorizedCredentialNote returns the AuthorizedCredentialNote field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetAuthorizedCredentialNote() string { + if c == nil || c.AuthorizedCredentialNote == nil { + return "" + } + return *c.AuthorizedCredentialNote +} + +// GetAuthorizedCredentialTitle returns the AuthorizedCredentialTitle field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetAuthorizedCredentialTitle() string { + if c == nil || c.AuthorizedCredentialTitle == nil { + return "" + } + return *c.AuthorizedCredentialTitle +} + +// GetCredentialAccessedAt returns the CredentialAccessedAt field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetCredentialAccessedAt() Timestamp { + if c == nil || c.CredentialAccessedAt == nil { + return Timestamp{} + } + return *c.CredentialAccessedAt +} + +// GetCredentialAuthorizedAt returns the CredentialAuthorizedAt field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetCredentialAuthorizedAt() Timestamp { + if c == nil || c.CredentialAuthorizedAt == nil { + return Timestamp{} + } + return *c.CredentialAuthorizedAt +} + +// GetCredentialID returns the CredentialID field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetCredentialID() int64 { + if c == nil || c.CredentialID == nil { + return 0 + } + return *c.CredentialID +} + +// GetCredentialType returns the CredentialType field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetCredentialType() string { + if c == nil || c.CredentialType == nil { + return "" + } + return *c.CredentialType +} + +// GetFingerprint returns the Fingerprint field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetFingerprint() string { + if c == nil || c.Fingerprint == nil { + return "" + } + return *c.Fingerprint +} + +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetLogin() string { + if c == nil || c.Login == nil { + return "" + } + return *c.Login +} + +// GetTokenLastEight returns the TokenLastEight field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetTokenLastEight() string { + if c == nil || c.TokenLastEight == nil { + return "" + } + return *c.TokenLastEight +} + // GetBaseRole returns the BaseRole field if it's non-nil, zero value otherwise. func (c *CustomRepoRoles) GetBaseRole() string { if c == nil || c.BaseRole == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 29d1fd4f1a5..75148227d3b 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -5508,6 +5508,116 @@ func TestCreateUserProjectOptions_GetBody(tt *testing.T) { c.GetBody() } +func TestCredentialAuthorization_GetAuthorizedCredentialExpiresAt(tt *testing.T) { + var zeroValue Timestamp + c := &CredentialAuthorization{AuthorizedCredentialExpiresAt: &zeroValue} + c.GetAuthorizedCredentialExpiresAt() + c = &CredentialAuthorization{} + c.GetAuthorizedCredentialExpiresAt() + c = nil + c.GetAuthorizedCredentialExpiresAt() +} + +func TestCredentialAuthorization_GetAuthorizedCredentialID(tt *testing.T) { + var zeroValue int64 + c := &CredentialAuthorization{AuthorizedCredentialID: &zeroValue} + c.GetAuthorizedCredentialID() + c = &CredentialAuthorization{} + c.GetAuthorizedCredentialID() + c = nil + c.GetAuthorizedCredentialID() +} + +func TestCredentialAuthorization_GetAuthorizedCredentialNote(tt *testing.T) { + var zeroValue string + c := &CredentialAuthorization{AuthorizedCredentialNote: &zeroValue} + c.GetAuthorizedCredentialNote() + c = &CredentialAuthorization{} + c.GetAuthorizedCredentialNote() + c = nil + c.GetAuthorizedCredentialNote() +} + +func TestCredentialAuthorization_GetAuthorizedCredentialTitle(tt *testing.T) { + var zeroValue string + c := &CredentialAuthorization{AuthorizedCredentialTitle: &zeroValue} + c.GetAuthorizedCredentialTitle() + c = &CredentialAuthorization{} + c.GetAuthorizedCredentialTitle() + c = nil + c.GetAuthorizedCredentialTitle() +} + +func TestCredentialAuthorization_GetCredentialAccessedAt(tt *testing.T) { + var zeroValue Timestamp + c := &CredentialAuthorization{CredentialAccessedAt: &zeroValue} + c.GetCredentialAccessedAt() + c = &CredentialAuthorization{} + c.GetCredentialAccessedAt() + c = nil + c.GetCredentialAccessedAt() +} + +func TestCredentialAuthorization_GetCredentialAuthorizedAt(tt *testing.T) { + var zeroValue Timestamp + c := &CredentialAuthorization{CredentialAuthorizedAt: &zeroValue} + c.GetCredentialAuthorizedAt() + c = &CredentialAuthorization{} + c.GetCredentialAuthorizedAt() + c = nil + c.GetCredentialAuthorizedAt() +} + +func TestCredentialAuthorization_GetCredentialID(tt *testing.T) { + var zeroValue int64 + c := &CredentialAuthorization{CredentialID: &zeroValue} + c.GetCredentialID() + c = &CredentialAuthorization{} + c.GetCredentialID() + c = nil + c.GetCredentialID() +} + +func TestCredentialAuthorization_GetCredentialType(tt *testing.T) { + var zeroValue string + c := &CredentialAuthorization{CredentialType: &zeroValue} + c.GetCredentialType() + c = &CredentialAuthorization{} + c.GetCredentialType() + c = nil + c.GetCredentialType() +} + +func TestCredentialAuthorization_GetFingerprint(tt *testing.T) { + var zeroValue string + c := &CredentialAuthorization{Fingerprint: &zeroValue} + c.GetFingerprint() + c = &CredentialAuthorization{} + c.GetFingerprint() + c = nil + c.GetFingerprint() +} + +func TestCredentialAuthorization_GetLogin(tt *testing.T) { + var zeroValue string + c := &CredentialAuthorization{Login: &zeroValue} + c.GetLogin() + c = &CredentialAuthorization{} + c.GetLogin() + c = nil + c.GetLogin() +} + +func TestCredentialAuthorization_GetTokenLastEight(tt *testing.T) { + var zeroValue string + c := &CredentialAuthorization{TokenLastEight: &zeroValue} + c.GetTokenLastEight() + c = &CredentialAuthorization{} + c.GetTokenLastEight() + c = nil + c.GetTokenLastEight() +} + func TestCustomRepoRoles_GetBaseRole(tt *testing.T) { var zeroValue string c := &CustomRepoRoles{BaseRole: &zeroValue} diff --git a/github/orgs_credential_authorizations.go b/github/orgs_credential_authorizations.go new file mode 100644 index 00000000000..88fccb8ad4d --- /dev/null +++ b/github/orgs_credential_authorizations.go @@ -0,0 +1,90 @@ +package github + +import ( + "context" + "fmt" + "net/http" +) + +// CredentialAuthorization represents a credential authorized through SAML SSO +type CredentialAuthorization struct { + // User login that owns the underlying credential. + Login *string `json:"login"` + + // Unique identifier for the credential. + CredentialID *int64 `json:"credential_id"` + + // Human-readable description of the credential type. + CredentialType *string `json:"credential_type"` + + // Last eight characters of the credential. + // Only included in responses with credential_type of personal access token. + TokenLastEight *string `json:"token_last_eight"` + + // Date when the credential was authorized for use. + CredentialAuthorizedAt *Timestamp `json:"credential_authorized_at"` + + // Date when the credential was last accessed. + // May be null if it was never accessed. + CredentialAccessedAt *Timestamp `json:"credential_accessed_at"` + + // List of oauth scopes the token has been granted. + Scopes []string `json:"scopes"` + + // Unique string to distinguish the credential. + // Only included in responses with credential_type of SSH Key. + Fingerprint *string `json:"fingerprint"` + + AuthorizedCredentialID *int64 `json:"authorized_credential_id"` + + // The title given to the ssh key. + // This will only be present when the credential is an ssh key. + AuthorizedCredentialTitle *string `json:"authorized_credential_title"` + + // The note given to the token. + // This will only be present when the credential is a token. + AuthorizedCredentialNote *string `json:"authorized_credential_note"` + + // The expiry for the token. + // This will only be present when the credential is a token. + AuthorizedCredentialExpiresAt *Timestamp `json:"authorized_credential_expires_at"` +} + +// ListCredentialAuthorizations lists credentials authorized through SAML SSO +// for a given organization. Only available with GitHub Enterprise Cloud. +// +// GitHub API docs: https://docs.github.com/en/enterprise-cloud@latest/rest/orgs/orgs?apiVersion=2022-11-28#list-saml-sso-authorizations-for-an-organization +func (s *OrganizationsService) ListCredentialAuthorizations(ctx context.Context, org string, opts *ListOptions) ([]*CredentialAuthorization, *Response, error) { + u := fmt.Sprintf("orgs/%v/credential-authorizations", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(http.MethodGet, u, nil) + if err != nil { + return nil, nil, err + } + + var creds []*CredentialAuthorization + resp, err := s.client.Do(ctx, req, &creds) + if err != nil { + return nil, resp, err + } + + return creds, resp, nil +} + +// RemoveCredentialAuthorization revokes the SAML SSO authorization for a given +// credential within an organization. Only available with GitHub Enterprise Cloud. +// +// GitHub API docs: https://docs.github.com/en/enterprise-cloud@latest/rest/orgs/orgs?apiVersion=2022-11-28#remove-a-saml-sso-authorization-for-an-organization +func (s *OrganizationsService) RemoveCredentialAuthorization(ctx context.Context, org string, credentialID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/credential-authorizations/%v", org, credentialID) + req, err := s.client.NewRequest(http.MethodDelete, u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/github/orgs_credential_authorizations_test.go b/github/orgs_credential_authorizations_test.go new file mode 100644 index 00000000000..5ed312d427c --- /dev/null +++ b/github/orgs_credential_authorizations_test.go @@ -0,0 +1,92 @@ +package github + +import ( + "context" + "fmt" + "net/http" + "testing" + "time" + + "github.com/google/go-cmp/cmp" +) + +func TestOrganizationsService_ListCredentialAuthorizations(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/credential-authorizations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Fprint(w, `[ + { + "login": "l", + "credential_id": 1, + "credential_type": "t", + "credential_authorized_at": "2017-01-21T00:00:00Z", + "credential_accessed_at": "2017-01-21T00:00:00Z", + "authorized_credential_id": 1 + } + ]`) + }) + + ctx := context.Background() + creds, _, err := client.Organizations.ListCredentialAuthorizations(ctx, "o", nil) + if err != nil { + t.Errorf("Organizations.ListCredentialAuthorizations returned error: %v", err) + } + + ts := time.Date(2017, time.January, 21, 0, 0, 0, 0, time.UTC) + want := []*CredentialAuthorization{ + { + Login: String("l"), + CredentialID: Int64(1), + CredentialType: String("t"), + CredentialAuthorizedAt: &Timestamp{ts}, + CredentialAccessedAt: &Timestamp{ts}, + AuthorizedCredentialID: Int64(1), + }, + } + if !cmp.Equal(creds, want) { + t.Errorf("Organizations.ListCredentialAuthorizations returned %+v, want %+v", creds, want) + } + + const methodName = "ListCredentialAuthorizations" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListCredentialAuthorizations(ctx, "\n", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.Organizations.ListCredentialAuthorizations(ctx, "o", nil) + return resp, err + }) +} + +func TestOrganizationsService_RemoveCredentialAuthorization(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/credential-authorizations/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodDelete) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := context.Background() + resp, err := client.Organizations.RemoveCredentialAuthorization(ctx, "o", 1) + if err != nil { + t.Errorf("Organizations.RemoveCredentialAuthorization returned error: %v", err) + } + + if resp.StatusCode != http.StatusNoContent { + t.Errorf("Organizations.RemoveCredentialAuthorization returned %v, want %v", resp.StatusCode, http.StatusNoContent) + } + + const methodName = "RemoveCredentialAuthorization" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.RemoveCredentialAuthorization(ctx, "\n", 0) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.RemoveCredentialAuthorization(ctx, "o", 1) + }) +}