Skip to content

Commit

Permalink
chore: constants, cleanup, permissions helper
Browse files Browse the repository at this point in the history
  • Loading branch information
plyr4 committed Oct 29, 2024
1 parent 03c4232 commit 836d5cf
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 24 deletions.
3 changes: 2 additions & 1 deletion api/auth/get_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/sirupsen/logrus"

"github.com/go-vela/server/api/types"
"github.com/go-vela/server/constants"
"github.com/go-vela/server/database"
"github.com/go-vela/server/internal/token"
"github.com/go-vela/server/scm"
Expand Down Expand Up @@ -80,7 +81,7 @@ func GetAuthToken(c *gin.Context) {

// handle scm setup events
// setup_action==install represents the GitHub App installation callback redirect
if c.Request.FormValue("setup_action") == "install" {
if c.Request.FormValue("setup_action") == constants.AppInstallSetupActionInstall {
installID, err := strconv.ParseInt(c.Request.FormValue("installation_id"), 10, 0)
if err != nil {
retErr := fmt.Errorf("unable to parse installation_id: %w", err)
Expand Down
2 changes: 1 addition & 1 deletion compiler/native/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (c *client) Compile(ctx context.Context, v interface{}) (*pipeline.Build, *
}

// get the netrc password from the scm
netrc, err := c.scm.GetNetrcPassword(context.Background(), c.repo, c.user, p.Git.Repositories, p.Git.Permissions)
netrc, err := c.scm.GetNetrcPassword(ctx, c.repo, c.user, p.Git.Repositories, p.Git.Permissions)
if err != nil {
return nil, nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/types/pipeline/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type Token struct {

// Empty returns true if the provided struct is empty.
func (g *Git) Empty() bool {
// return true if every field is empty
// return false if any of the fields are provided
if g.Token != nil {
if g.Token.Repositories != nil {
return false
Expand All @@ -30,6 +30,6 @@ func (g *Git) Empty() bool {
}
}

// return false if any of the fields are provided
// return true if all fields are empty
return true
}
2 changes: 1 addition & 1 deletion compiler/types/yaml/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ func (b *Build) ToPipelineAPI() *api.Pipeline {
func (b *Build) UnmarshalYAML(unmarshal func(interface{}) error) error {
// build we try unmarshalling to
build := new(struct {
Git Git
Version string
Metadata Metadata
Environment raw.StringSliceMap
Expand All @@ -75,6 +74,7 @@ func (b *Build) UnmarshalYAML(unmarshal func(interface{}) error) error {
Stages StageSlice
Steps StepSlice
Templates TemplateSlice
Git Git
})

// attempt to unmarshal as a build type
Expand Down
26 changes: 22 additions & 4 deletions constants/app_install.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,33 @@ package constants

// see: https://docs.github.com/en/rest/authentication/permissions-required-for-github-apps?apiVersion=2022-11-28
const (
// The string value for GitHub App install read permissions.
// GitHub App install permission 'none'.
AppInstallPermissionNone = "none"
// GitHub App install permission 'read'.
AppInstallPermissionRead = "read"
// The string value for GitHub App install write permissions.
// GitHub App install permission 'write'.
AppInstallPermissionWrite = "write"
)

const (
// The string value for GitHub App install contents resource.
// GitHub App install contents resource.
AppInstallResourceContents = "contents"
// The string value for GitHub App install checks resource.
// GitHub App install checks resource.
AppInstallResourceChecks = "checks"
)

const (
// GitHub App install repositories selection when "all" repositories are selected.
AppInstallRepositoriesSelectionAll = "all"
// GitHub App install repositories selection when a subset of repositories are selected.
AppInstallRepositoriesSelectionSelected = "selected"
)

const (
// GitHub App install setup_action type 'install'.
AppInstallSetupActionInstall = "install"
// GitHub App install event type 'created'.
AppInstallCreated = "created"
// GitHub App install event type 'deleted'.
AppInstallDeleted = "deleted"
)
64 changes: 57 additions & 7 deletions scm/github/app_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,22 +54,72 @@ func (c *client) ValidateGitHubApp(ctx context.Context) error {
}

perms := app.GetPermissions()
if len(perms.GetContents()) == 0 ||
(perms.GetContents() != constants.AppInstallPermissionRead && perms.GetContents() != constants.AppInstallPermissionWrite) {
return fmt.Errorf("github app requires contents:read permissions, found: %s", perms.GetContents())

type perm struct {
resource string
requiredPermission string
actualPermission string
}

// GitHub App installation requires the following permissions
// - contents:read
// - checks:write
requiredPermissions := []perm{
{
resource: constants.AppInstallResourceContents,
requiredPermission: constants.AppInstallPermissionRead,
actualPermission: perms.GetContents(),
},
{
resource: constants.AppInstallResourceChecks,
requiredPermission: constants.AppInstallPermissionWrite,
actualPermission: perms.GetChecks(),
},
}

for _, p := range requiredPermissions {
err := hasPermission(p.resource, p.requiredPermission, p.actualPermission)
if err != nil {
return err
}
}

return nil
}

// hasPermission takes a resource:perm pair and checks if the actual permission matches the expected permission or is supersceded by a higher permission.
func hasPermission(resource, requiredPerm, actualPerm string) error {
if len(actualPerm) == 0 {
return fmt.Errorf("github app missing permission %s:%s", resource, requiredPerm)
}

permitted := false

switch requiredPerm {
case constants.AppInstallPermissionNone:
permitted = true
case constants.AppInstallPermissionRead:
if actualPerm == constants.AppInstallPermissionRead ||
actualPerm == constants.AppInstallPermissionWrite {
permitted = true
}
case constants.AppInstallPermissionWrite:
if actualPerm == constants.AppInstallPermissionWrite {
permitted = true
}
default:
return fmt.Errorf("invalid required permission type: %s", requiredPerm)
}

if len(perms.GetChecks()) == 0 ||
perms.GetChecks() != constants.AppInstallPermissionWrite {
return fmt.Errorf("github app requires checks:write permissions, found: %s", perms.GetChecks())
if !permitted {
return fmt.Errorf("github app requires permission %s:%s, found: %s", constants.AppInstallResourceContents, constants.AppInstallPermissionRead, actualPerm)
}

return nil
}

// newGithubAppClient returns the GitHub App client for authenticating as the GitHub App itself using the RoundTripper.
func (c *client) newGithubAppClient() (*github.Client, error) {
// todo: create transport using context to apply tracing
// create a github client based off the existing GitHub App configuration
client, err := github.NewClient(
&http.Client{
Expand Down
12 changes: 6 additions & 6 deletions scm/github/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -784,7 +784,7 @@ func (c *client) SyncRepoWithInstallation(ctx context.Context, r *api.Repo) (*ap

installationCanReadRepo := false

if installation.GetRepositorySelection() != "all" {
if installation.GetRepositorySelection() != constants.AppInstallRepositoriesSelectionAll {
client, err := c.newGithubAppClient()
if err != nil {
return r, err
Expand Down Expand Up @@ -821,19 +821,19 @@ func (c *client) SyncRepoWithInstallation(ctx context.Context, r *api.Repo) (*ap
func WithGitHubInstallationPermission(perms *github.InstallationPermissions, resource, perm string) (*github.InstallationPermissions, error) {
// convert permissions from yaml string
switch strings.ToLower(perm) {
case "read":
case "write":
case "none":
case constants.AppInstallPermissionNone:
case constants.AppInstallPermissionRead:
case constants.AppInstallPermissionWrite:
break
default:
return perms, fmt.Errorf("invalid permission value given for %s: %s", resource, perm)
}

// convert resource from yaml string
switch strings.ToLower(resource) {
case "contents":
case constants.AppInstallResourceContents:
perms.Contents = github.String(perm)
case "checks":
case constants.AppInstallResourceChecks:
perms.Checks = github.String(perm)
default:
return perms, fmt.Errorf("invalid permission key given: %s", perm)
Expand Down
4 changes: 2 additions & 2 deletions scm/github/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -555,11 +555,11 @@ func (c *client) processInstallationEvent(_ context.Context, h *api.Hook, payloa
install.Org = payload.GetInstallation().GetAccount().GetLogin()

switch payload.GetAction() {
case "created":
case constants.AppInstallCreated:
for _, repo := range payload.Repositories {
install.RepositoriesAdded = append(install.RepositoriesAdded, repo.GetName())
}
case "deleted":
case constants.AppInstallDeleted:
for _, repo := range payload.Repositories {
install.RepositoriesRemoved = append(install.RepositoriesRemoved, repo.GetName())
}
Expand Down

0 comments on commit 836d5cf

Please sign in to comment.