Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion policy/common/actor.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package common
import (
"context"
"sort"
"strings"

"github.com/palantir/policy-bot/pull"
"github.com/pkg/errors"
Expand All @@ -27,6 +28,7 @@ import (
// all conditions in this structure.
type Actors struct {
Users []string `yaml:"users,omitempty" json:"users"`
UserTypes []string `yaml:"user_types,omitempty" json:"user_types"`
Teams []string `yaml:"teams,omitempty" json:"teams"`
Organizations []string `yaml:"organizations,omitempty" json:"organizations"`

Expand All @@ -37,11 +39,13 @@ type Actors struct {
// A list of GitHub collaborator permissions that are allowed. Values may
// be any of "admin", "maintain", "write", "triage", and "read".
Permissions []pull.Permission `yaml:"permissions,omitempty" json:"permissions"`

Not *Actors `yaml:"not,omitempty" json:"not,omitempty"`
}

// IsZero returns true if no conditions for actors are defined.
func (a *Actors) IsZero() bool {
return a == nil || (len(a.Users) == 0 && len(a.Teams) == 0 && len(a.Organizations) == 0 &&
return a == nil || (len(a.Users) == 0 && len(a.UserTypes) == 0 && len(a.Teams) == 0 && len(a.Organizations) == 0 &&
len(a.Permissions) == 0 && !a.Admins && !a.WriteCollaborators)
}

Expand Down Expand Up @@ -73,6 +77,29 @@ func (a *Actors) GetPermissions() []pull.Permission {
// IsActor returns true if the given user satisfies at least one of the
// conditions in this structure.
func (a *Actors) IsActor(ctx context.Context, prctx pull.Context, user string) (bool, error) {
if a.Not != nil {
isNotActor, err := a.Not.IsActor(ctx, prctx, user)
if err != nil {
return false, errors.Wrap(err, "failed to check not actor condition")
}
if isNotActor {
return false, nil
}
}

var userType string
if strings.HasSuffix(user, "[bot]") {
userType = "Bot"
} else {
userType = "User"
}

for _, t := range a.UserTypes {
if userType == t {
return true, nil
}
}

for _, u := range a.Users {
if user == u {
return true, nil
Expand Down
47 changes: 47 additions & 0 deletions policy/common/actor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,47 @@ func TestIsActor(t *testing.T) {
assertActor(t, a, "jstrawnickel")
assertNotActor(t, a, "ttest")
})

t.Run("user_types_bot", func(t *testing.T) {
a := &Actors{
UserTypes: []string{"Bot"},
}

assertActor(t, a, "dependabot[bot]")
assertNotActor(t, a, "mhaypenny")
})

t.Run("user_types_user", func(t *testing.T) {
a := &Actors{
UserTypes: []string{"User"},
}

assertActor(t, a, "mhaypenny")
assertNotActor(t, a, "dependabot[bot]")
})

t.Run("not_empty", func(t *testing.T) {
a := &Actors{
Not: &Actors{
Users: []string{"ttest"},
},
}

assertNotActor(t, a, "ttest")
assertNotActor(t, a, "mhaypenny")
})

t.Run("not_precedence", func(t *testing.T) {
a := &Actors{
Users: []string{"mhaypenny", "ttest"},
Not: &Actors{
Users: []string{"mhaypenny"},
},
}

assertActor(t, a, "ttest")
assertNotActor(t, a, "mhaypenny")
})
}

func TestIsEmpty(t *testing.T) {
Expand All @@ -122,12 +163,18 @@ func TestIsEmpty(t *testing.T) {
a = &Actors{Users: []string{"user"}}
assert.False(t, a.IsZero(), "Actors struct was empty")

a = &Actors{UserTypes: []string{"User"}}
assert.False(t, a.IsZero(), "Actors struct was empty")

a = &Actors{Teams: []string{"org/team"}}
assert.False(t, a.IsZero(), "Actors struct was empty")

a = &Actors{Organizations: []string{"org"}}
assert.False(t, a.IsZero(), "Actors struct was empty")

a = &Actors{Not: &Actors{Users: []string{"user"}}}
assert.True(t, a.IsZero(), "Actors struct with Not was not empty (specifying Not only should not make it non-empty)")

a = nil
assert.True(t, a.IsZero(), "nil struct was not empty")
}