Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow to list trusted apps #1603

Merged
merged 1 commit into from
Apr 22, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ Trigger specifies a configuration for a single trigger.<br /><br />The configura
|---|---|---|---|
| `repos` | []string | No | Repos is either of the form org/repos or just org. |
| `trusted_org` | string | No | TrustedOrg is the org whose members' PRs will be automatically built<br />for PRs to the above repos. The default is the PR's org. |
| `trusted_apps` | []string | No | TrustedApps is the explicit list of GitHub apps whose PRs will be automatically<br />considered as trusted. The list should contain usernames of each GitHub App without [bot] suffix.<br/>By default, trigger will ignore this list. |
| `join_org_url` | string | No | JoinOrgURL is a link that redirects users to a location where they<br />should be able to read more about joining the organization in order<br />to become trusted members. Defaults to the Github link of TrustedOrg. |
| `only_org_members` | bool | No | OnlyOrgMembers requires PRs and/or /ok-to-test comments to come from org members.<br />By default, trigger also include repo collaborators. |
| `ignore_ok_to_test` | bool | No | IgnoreOkToTest makes trigger ignore /ok-to-test comments.<br />This is a security mitigation to only allow testing from trusted users. |
Expand Down
1 change: 1 addition & 0 deletions docs/plugins/Plugins config.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ Trigger specifies a configuration for a single trigger.<br /><br />The configura
|---|---|---|---|---|
| Repos | `repos` | []string | No | Repos is either of the form org/repos or just org. |
| TrustedOrg | `trusted_org` | string | No | TrustedOrg is the org whose members' PRs will be automatically built<br />for PRs to the above repos. The default is the PR's org. |
| TrustedApps | `trusted_apps` | []string | No | TrustedApps is the explicit list of GitHub apps whose PRs will be automatically<br />considered as trusted. The list should contain usernames of each GitHub App without [bot] suffix.<br/>By default, trigger will ignore this list. |
| JoinOrgURL | `join_org_url` | string | No | JoinOrgURL is a link that redirects users to a location where they<br />should be able to read more about joining the organization in order<br />to become trusted members. Defaults to the Github link of TrustedOrg. |
| OnlyOrgMembers | `only_org_members` | bool | No | OnlyOrgMembers requires PRs and/or /ok-to-test comments to come from org members.<br />By default, trigger also include repo collaborators. |
| IgnoreOkToTest | `ignore_ok_to_test` | bool | No | IgnoreOkToTest makes trigger ignore /ok-to-test comments.<br />This is a security mitigation to only allow testing from trusted users. |
Expand Down
4 changes: 4 additions & 0 deletions pkg/plugins/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,10 @@ type Trigger struct {
// TrustedOrg is the org whose members' PRs will be automatically built
// for PRs to the above repos. The default is the PR's org.
TrustedOrg string `json:"trusted_org,omitempty"`
// TrustedApps is the explicit list of GitHub apps whose PRs will be automatically
// considered as trusted. The list should contain usernames of each GitHub App without [bot] suffix.
// By default, trigger will ignore this list.
TrustedApps []string `json:"trusted_apps,omitempty"`
// JoinOrgURL is a link that redirects users to a location where they
// should be able to read more about joining the organization in order
// to become trusted members. Defaults to the Github link of TrustedOrg.
Expand Down
10 changes: 9 additions & 1 deletion pkg/plugins/trigger/trigger.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ func handlePush(pc plugins.Agent, pe scm.PushHook) error {

// TrustedUser returns true if user is trusted in repo.
//
// Trusted users are either repo collaborators, org members or trusted org members.
// Trusted users are either repo collaborators, org members, trusted org members or trusted Github Apps.
// Whether repo collaborators and/or a second org is trusted is configured by trigger.
func TrustedUser(spc trustedUserClient, trigger *plugins.Trigger, user, org, repo string) (bool, error) {
botUser, err := spc.BotName()
Expand Down Expand Up @@ -214,6 +214,14 @@ func TrustedUser(spc trustedUserClient, trigger *plugins.Trigger, user, org, rep
return true, nil
}

// Determine if user is on trusted_apps list.
// This allows automatic tests execution for GitHub automations that cannot be added as collaborators.
for _, trustedApp := range trigger.TrustedApps {
if tUser := strings.TrimSuffix(user, "[bot]"); tUser == trustedApp {
return true, nil
}
}

// Determine if there is a second org to check
if trigger.TrustedOrg == "" || trigger.TrustedOrg == org {
return false, nil // No trusted org and/or it is the same
Expand Down
125 changes: 125 additions & 0 deletions pkg/plugins/trigger/trigger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -515,3 +515,128 @@ func TestValidateContextOverlap(t *testing.T) {
})
}
}

func TestTrustedUser(t *testing.T) {
var testcases = []struct {
name string

onlyOrgMembers bool
trustedApps []string
trustedOrg string

user string
org string
repo string

expectedTrusted bool
}{
{
name: "user is member of trusted org",
onlyOrgMembers: false,
user: "test",
org: "kubernetes",
repo: "kubernetes",
expectedTrusted: true,
},
{
name: "user is member of trusted org (only org members enabled)",
onlyOrgMembers: true,
user: "test",
org: "kubernetes",
repo: "kubernetes",
expectedTrusted: true,
},
{
name: "user is collaborator",
onlyOrgMembers: false,
user: "test-collaborator",
org: "kubernetes",
repo: "kubernetes",
expectedTrusted: true,
},
{
name: "user is collaborator (only org members enabled)",
onlyOrgMembers: true,
user: "test-collaborator",
org: "kubernetes",
repo: "kubernetes",
expectedTrusted: false,
},
{
name: "user is trusted org member",
onlyOrgMembers: false,
trustedOrg: "kubernetes",
user: "test",
org: "kubernetes-sigs",
repo: "test",
expectedTrusted: true,
},
{
name: "user is not org member",
onlyOrgMembers: false,
user: "test-2",
org: "kubernetes",
repo: "kubernetes",
expectedTrusted: false,
},
{
name: "user is not org member or trusted org member",
onlyOrgMembers: false,
trustedOrg: "kubernetes-sigs",
user: "test-2",
org: "kubernetes",
repo: "kubernetes",
expectedTrusted: false,
},
{
name: "user is not org member or trusted org member, onlyOrgMembers true",
onlyOrgMembers: true,
trustedOrg: "kubernetes-sigs",
user: "test-2",
org: "kubernetes",
repo: "kubernetes",
expectedTrusted: false,
},
{
name: "Self as bot is trusted",
user: "k8s-ci-robot",
expectedTrusted: true,
},
{
name: "github-app[bot] is in trusted list",
user: "github-app[bot]",
trustedApps: []string{"github-app"},
expectedTrusted: true,
},
{
name: "github-app[bot] is not in trusted list",
user: "github-app[bot]",
trustedApps: []string{"other-app"},
expectedTrusted: false,
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
fakeSCMClient := fake2.SCMClient{}
fakeSCMClient.OrgMembers = map[string][]string{
"kubernetes": {"test"},
}
fakeSCMClient.Collaborators = []string{"test-collaborator"}

triggerPlugin := plugins.Trigger{
TrustedOrg: tc.trustedOrg,
TrustedApps: tc.trustedApps,
OnlyOrgMembers: tc.onlyOrgMembers,
}

trustedResponse, err := TrustedUser(&fakeSCMClient, &triggerPlugin, tc.user, tc.org, tc.repo)
if err != nil {
t.Errorf("For case %s, didn't expect error from TrustedUser: %v", tc.name, err)
}
if trustedResponse != tc.expectedTrusted {
t.Errorf("For case %s, expect trusted: %v, but got: %v", tc.name, tc.expectedTrusted, trustedResponse)
}
})
}
}
Loading