From d323cafdd538f67fda88e60a138b578827c7a400 Mon Sep 17 00:00:00 2001 From: Yiannis Date: Thu, 24 Sep 2020 17:34:43 +0100 Subject: [PATCH] Implement HasTokenPermission for GitHub --- github/client.go | 29 +++++++++++++++++++++++++++++ gitprovider/client.go | 4 ++++ gitprovider/enums.go | 6 ++++++ gitprovider/errors.go | 2 ++ 4 files changed, 41 insertions(+) diff --git a/github/client.go b/github/client.go index 068775d0..279ba5d5 100644 --- a/github/client.go +++ b/github/client.go @@ -17,6 +17,9 @@ limitations under the License. package github import ( + "context" + "strings" + "github.com/google/go-github/v32/github" "github.com/fluxcd/go-git-providers/gitprovider" @@ -94,3 +97,29 @@ func (c *Client) OrgRepositories() gitprovider.OrgRepositoriesClient { func (c *Client) UserRepositories() gitprovider.UserRepositoriesClient { return c.userRepos } + +//nolint:gochecknoglobals +var permissionsToScopes = map[gitprovider.TokenPermission]string{ + gitprovider.TokenPermissionFullRepo: "repo", +} + +func (c *Client) HasTokenPermission(ctx context.Context, permission gitprovider.TokenPermission) (bool, error) { + // The X-OAuth-Scopes header is returned for any API calls, using Meta here to keep things simple. + _, res, err := c.c.Client().APIMeta(ctx) + if err != nil { + return false, err + } + + scopes := res.Header.Get("X-OAuth-Scopes") + if scopes == "" { + return false, gitprovider.ErrMissingHeader + } + + for _, scope := range strings.Split(scopes, ",") { + if permissionsToScopes[permission] == scope { + return true, nil + } + } + + return false, nil +} diff --git a/gitprovider/client.go b/gitprovider/client.go index 37d15d6d..ff596153 100644 --- a/gitprovider/client.go +++ b/gitprovider/client.go @@ -33,6 +33,10 @@ type Client interface { // This field is set at client creation time, and can't be changed. ProviderID() ProviderID + // HasTokenPermission returns a boolean indicating whether the supplied token has the requested + // permission. Permissions should be coarse-grained and applicable to *all* providers. + HasTokenPermission(ctx context.Context, permission TokenPermission) (bool, error) + // Raw returns the Go client used under the hood to access the Git provider. Raw() interface{} } diff --git a/gitprovider/enums.go b/gitprovider/enums.go index 639ac29c..1be9da49 100644 --- a/gitprovider/enums.go +++ b/gitprovider/enums.go @@ -161,3 +161,9 @@ func ValidateLicenseTemplate(t LicenseTemplate) error { func LicenseTemplateVar(t LicenseTemplate) *LicenseTemplate { return &t } + +type TokenPermission string + +const ( + TokenPermissionFullRepo = TokenPermission("full_repo") +) diff --git a/gitprovider/errors.go b/gitprovider/errors.go index b4332276..0233d84d 100644 --- a/gitprovider/errors.go +++ b/gitprovider/errors.go @@ -62,6 +62,8 @@ var ( ErrDestructiveCallDisallowed = errors.New("destructive call was blocked, disallowed by client") // ErrInvalidTransportChainReturn is returned if a ChainableRoundTripperFunc returns nil, which is invalid. ErrInvalidTransportChainReturn = errors.New("the return value of a ChainableRoundTripperFunc must not be nil") + // ErrMissingHeader is returned when an expected header is missing from the HTTP response. + ErrMissingHeader = errors.New("header is missing") ) // HTTPError is an error that contains context about the HTTP request/response that failed.