Skip to content

Commit 69cc216

Browse files
authored
Run Access check only with a Token (#43)
1 parent ef9437f commit 69cc216

9 files changed

Lines changed: 346 additions & 72 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
.DS_Store
2+
profile.cov

checkers/access.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ type accessValidator struct {
3131

3232
// ValidateLine runs this NoOwner's check against each line
3333
func (v accessValidator) ValidateLine(lineNo int, line string) []codeowners.CheckResult {
34+
if len(v.options.GithubToken) == 0 {
35+
return nil
36+
}
37+
3438
results := []codeowners.CheckResult{}
3539

3640
_, owners := codeowners.ParseLine(line)

checkers/access_test.go

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ func TestAccessCheck(t *testing.T) {
3535
validator := checker.NewValidator(codeowners.ValidatorOptions{
3636
Directory: ".",
3737
CodeownersFileLocation: "CODEOWNERS",
38+
GithubToken: "token",
3839
})
3940
got := validator.ValidateLine(input.lineNo, input.line)
4041
if !reflect.DeepEqual(got, want) {
@@ -69,6 +70,7 @@ func TestAccessCheckError(t *testing.T) {
6970
validator := checker.NewValidator(codeowners.ValidatorOptions{
7071
Directory: ".",
7172
CodeownersFileLocation: "CODEOWNERS",
73+
GithubToken: "token",
7274
})
7375
got := validator.ValidateLine(input.lineNo, input.line)
7476
if !reflect.DeepEqual(got, want) {
@@ -88,6 +90,7 @@ func TestAccessCheckPass(t *testing.T) {
8890
validator := checker.NewValidator(codeowners.ValidatorOptions{
8991
Directory: ".",
9092
CodeownersFileLocation: "CODEOWNERS",
93+
GithubToken: "token",
9194
})
9295
got := validator.ValidateLine(input.lineNo, input.line)
9396
if got != nil {
@@ -107,6 +110,7 @@ func TestAccessCheckPassInvalidOwners(t *testing.T) {
107110
validator := checker.NewValidator(codeowners.ValidatorOptions{
108111
Directory: ".",
109112
CodeownersFileLocation: "CODEOWNERS",
113+
GithubToken: "token",
110114
})
111115
got := validator.ValidateLine(input.lineNo, input.line)
112116
if got != nil {
@@ -126,6 +130,26 @@ func TestAccessCheckPassNoOwners(t *testing.T) {
126130
validator := checker.NewValidator(codeowners.ValidatorOptions{
127131
Directory: ".",
128132
CodeownersFileLocation: "CODEOWNERS",
133+
GithubToken: "token",
134+
})
135+
got := validator.ValidateLine(input.lineNo, input.line)
136+
if got != nil {
137+
t.Errorf("Input: %v, Want: %v, Got: %v", input, nil, got)
138+
}
139+
}
140+
141+
func TestAccessCheckNoTokenPass(t *testing.T) {
142+
input := struct {
143+
lineNo int
144+
line string
145+
}{
146+
lineNo: 1,
147+
line: "filepattern @ownerWithAccess",
148+
}
149+
checker := checkers.Access{}
150+
validator := checker.NewValidator(codeowners.ValidatorOptions{
151+
Directory: "bad",
152+
CodeownersFileLocation: "bad",
129153
})
130154
got := validator.ValidateLine(input.lineNo, input.line)
131155
if got != nil {
@@ -159,6 +183,7 @@ func TestAccessCheckInvalidDir(t *testing.T) {
159183
validator := checker.NewValidator(codeowners.ValidatorOptions{
160184
Directory: "bad",
161185
CodeownersFileLocation: "bad",
186+
GithubToken: "token",
162187
})
163188
got := validator.ValidateLine(input.lineNo, input.line)
164189
if !reflect.DeepEqual(got, want) {
@@ -192,6 +217,7 @@ func TestAccessCheckNoCollaborator(t *testing.T) {
192217
validator := checker.NewValidator(codeowners.ValidatorOptions{
193218
Directory: ".",
194219
CodeownersFileLocation: "CODEOWNERS",
220+
GithubToken: "token",
195221
})
196222
got := validator.ValidateLine(input.lineNo, input.line)
197223
if !reflect.DeepEqual(got, want) {
@@ -225,6 +251,7 @@ func TestAccessCheckIsCollaboratorError(t *testing.T) {
225251
validator := checker.NewValidator(codeowners.ValidatorOptions{
226252
Directory: ".",
227253
CodeownersFileLocation: "CODEOWNERS",
254+
GithubToken: "token",
228255
})
229256
got := validator.ValidateLine(input.lineNo, input.line)
230257
if !reflect.DeepEqual(got, want) {
@@ -244,6 +271,7 @@ func TestAccessCheckTeamPass(t *testing.T) {
244271
validator := checker.NewValidator(codeowners.ValidatorOptions{
245272
Directory: ".",
246273
CodeownersFileLocation: "CODEOWNERS",
274+
GithubToken: "token",
247275
})
248276
got := validator.ValidateLine(input.lineNo, input.line)
249277
if got != nil {
@@ -257,7 +285,7 @@ func TestAccessCheckTeamDeny(t *testing.T) {
257285
line string
258286
}{
259287
lineNo: 1,
260-
line: "filepattern @org/team",
288+
line: "filepattern @org/denyTeam",
261289
}
262290
want := []codeowners.CheckResult{
263291
{
@@ -266,9 +294,9 @@ func TestAccessCheckTeamDeny(t *testing.T) {
266294
StartLine: 1,
267295
StartColumn: 13,
268296
EndLine: 1,
269-
EndColumn: 22,
297+
EndColumn: 26,
270298
},
271-
Message: "Owner '@org/team' has no write access",
299+
Message: "Owner '@org/denyTeam' has no write access",
272300
Severity: codeowners.Error,
273301
CheckName: "Access",
274302
},
@@ -277,6 +305,7 @@ func TestAccessCheckTeamDeny(t *testing.T) {
277305
validator := checker.NewValidator(codeowners.ValidatorOptions{
278306
Directory: ".",
279307
CodeownersFileLocation: "CODEOWNERS",
308+
GithubToken: "token",
280309
})
281310
got := validator.ValidateLine(input.lineNo, input.line)
282311
if !reflect.DeepEqual(got, want) {
@@ -296,6 +325,7 @@ func TestAccessCheckEmailPass(t *testing.T) {
296325
validator := checker.NewValidator(codeowners.ValidatorOptions{
297326
Directory: ".",
298327
CodeownersFileLocation: "CODEOWNERS",
328+
GithubToken: "token",
299329
})
300330
got := validator.ValidateLine(input.lineNo, input.line)
301331
if got != nil {
@@ -328,6 +358,7 @@ func TestAccessCheckEmailDeny(t *testing.T) {
328358
validator := checker.NewValidator(codeowners.ValidatorOptions{
329359
Directory: ".",
330360
CodeownersFileLocation: "CODEOWNERS",
361+
GithubToken: "token",
331362
})
332363
got := validator.ValidateLine(input.lineNo, input.line)
333364
if !reflect.DeepEqual(got, want) {
@@ -342,11 +373,11 @@ func TestAccessCheckDenyMemo(t *testing.T) {
342373
}{
343374
{
344375
lineNo: 1,
345-
line: "filepattern @org/team",
376+
line: "filepattern @org/denyTeam",
346377
},
347378
{
348379
lineNo: 2,
349-
line: "filepattern2 @org/team",
380+
line: "filepattern2 @org/denyTeam",
350381
},
351382
}
352383
want := []codeowners.CheckResult{
@@ -356,9 +387,9 @@ func TestAccessCheckDenyMemo(t *testing.T) {
356387
StartLine: 1,
357388
StartColumn: 13,
358389
EndLine: 1,
359-
EndColumn: 22,
390+
EndColumn: 26,
360391
},
361-
Message: "Owner '@org/team' has no write access",
392+
Message: "Owner '@org/denyTeam' has no write access",
362393
Severity: codeowners.Error,
363394
CheckName: "Access",
364395
},
@@ -368,9 +399,9 @@ func TestAccessCheckDenyMemo(t *testing.T) {
368399
StartLine: 2,
369400
StartColumn: 14,
370401
EndLine: 2,
371-
EndColumn: 23,
402+
EndColumn: 27,
372403
},
373-
Message: "Owner '@org/team' has no write access",
404+
Message: "Owner '@org/denyTeam' has no write access",
374405
Severity: codeowners.Error,
375406
CheckName: "Access",
376407
},
@@ -379,6 +410,7 @@ func TestAccessCheckDenyMemo(t *testing.T) {
379410
validator := checker.NewValidator(codeowners.ValidatorOptions{
380411
Directory: ".",
381412
CodeownersFileLocation: "CODEOWNERS",
413+
GithubToken: "token",
382414
})
383415
got := []codeowners.CheckResult{}
384416
for _, inputLine := range input {

checkers/github.go

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import (
77
"strings"
88

99
"github.com/fmenezes/codeowners"
10-
"github.com/google/go-github/github"
10+
"github.com/google/go-github/v32/github"
1111
)
1212

13-
type accessApi struct {
13+
type accessAPI struct {
1414
directory string
1515
tokenType string
1616
token string
@@ -23,35 +23,40 @@ type accessApi struct {
2323
ctx context.Context
2424
}
2525

26-
func (a *accessApi) extractRepoData() {
26+
func (a *accessAPI) extractRepoData() {
2727
r := regexp.MustCompile(`github.com[\:\/]([A-Za-z0-9-]+)\/([A-Za-z0-9-]+)\.git`)
2828
data := r.FindStringSubmatch(a.repoURL)
2929
a.repoOwner = data[1]
3030
a.repoName = data[2]
3131
}
3232

33-
func (a accessApi) fetchTeamAccess(org, team string) (string, error) {
34-
teams, _, err := a.client.Repositories.ListTeams(a.ctx, a.repoOwner, a.repoName, nil)
33+
func (a accessAPI) fetchTeamAccess(org, team string) (string, error) {
34+
repo, _, err := a.client.Teams.IsTeamRepoBySlug(a.ctx, org, team, a.repoOwner, a.repoName)
3535
if err != nil {
3636
return "", err
3737
}
38-
for _, t := range teams {
39-
if t.GetOrganization().GetLogin() == org && t.GetSlug() == team {
40-
return t.GetPermission(), nil
41-
}
38+
data := repo.GetPermissions()
39+
if data["admin"] {
40+
return "admin", nil
41+
}
42+
if data["maintain"] {
43+
return "maintain", nil
44+
}
45+
if data["push"] {
46+
return "push", nil
4247
}
4348
return "none", nil
4449
}
4550

46-
func (a accessApi) hasWriteAccess() bool {
51+
func (a accessAPI) hasWriteAccess() bool {
4752
switch a.accessLevel {
4853
case "admin", "push", "maintain", "write", "email": // allowing emails to pass
4954
return true
5055
}
5156
return false
5257
}
5358

54-
func (a accessApi) fetchUserAccess(user string) (string, error) {
59+
func (a accessAPI) fetchUserAccess(user string) (string, error) {
5560
isCollaborator, _, err := a.client.Repositories.IsCollaborator(a.ctx, a.repoOwner, a.repoName, user)
5661
if err != nil {
5762
return "", err
@@ -66,7 +71,7 @@ func (a accessApi) fetchUserAccess(user string) (string, error) {
6671
return permissionLevel.GetPermission(), nil
6772
}
6873

69-
func (a accessApi) findUserFromEmail(email string) (string, error) {
74+
func (a accessAPI) findUserFromEmail(email string) (string, error) {
7075
res, _, err := a.client.Search.Users(a.ctx, fmt.Sprintf("%s in:email type:user", email), nil)
7176
if err != nil {
7277
return "", err
@@ -77,7 +82,7 @@ func (a accessApi) findUserFromEmail(email string) (string, error) {
7782
return "", nil
7883
}
7984

80-
func (a *accessApi) fetchAccess(user string) error {
85+
func (a *accessAPI) fetchAccess(user string) error {
8186
var err error
8287
login := ""
8388
if string(user[0]) == "@" {
@@ -113,7 +118,7 @@ func (a *accessApi) fetchAccess(user string) error {
113118
}
114119

115120
func ownerHasWriteAccess(options codeowners.ValidatorOptions, user string) (bool, error) {
116-
a := accessApi{
121+
a := accessAPI{
117122
ctx: context.Background(),
118123
directory: options.Directory,
119124
token: options.GithubToken,

checkers/github_api.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ import (
66
"os/exec"
77
"strings"
88

9-
"github.com/google/go-github/github"
9+
"github.com/google/go-github/v32/github"
1010
"golang.org/x/oauth2"
1111
)
1212

13-
func (a *accessApi) extractRepoURL() error {
13+
func (a *accessAPI) extractRepoURL() error {
1414
cmd := exec.Command("git", "config", "--get", "remote.origin.url")
1515
cmd.Dir = a.directory
1616
out, err := cmd.Output()
@@ -21,12 +21,12 @@ func (a *accessApi) extractRepoURL() error {
2121
return nil
2222
}
2323

24-
func (a *accessApi) initiateClient() {
24+
func (a *accessAPI) initiateClient() {
2525
tokenSource := oauth2.StaticTokenSource(
2626
&oauth2.Token{AccessToken: a.token, TokenType: a.tokenType},
2727
)
2828
oauthClient := oauth2.NewClient(a.ctx, tokenSource)
2929
a.client = github.NewClient(oauthClient)
3030
}
3131

32-
func (a *accessApi) terminateClient() {}
32+
func (a *accessAPI) terminateClient() {}

checkers/github_mock.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import (
1111
"regexp"
1212
"strings"
1313

14-
"github.com/google/go-github/github"
14+
"github.com/google/go-github/v32/github"
1515
)
1616

17-
func (a *accessApi) extractRepoURL() error {
17+
func (a *accessAPI) extractRepoURL() error {
1818
if a.directory == "bad" {
1919
return errors.New("Mocked error")
2020
}
@@ -71,9 +71,14 @@ func mockedServer(w http.ResponseWriter, r *http.Request) {
7171
}
7272
}
7373

74-
teamUrlRegex := regexp.MustCompile(`/repos/([^/]+)/([^/]+)/teams$`)
74+
teamUrlRegex := regexp.MustCompile(`/orgs/([^/]+)/teams/([^/]+)/repos/([^/]+)/([^/]+)$`)
7575
parts = teamUrlRegex.FindStringSubmatch(r.URL.String())
76+
7677
if len(parts) > 0 {
78+
if parts[2] == "denyTeam" {
79+
w.WriteHeader(404)
80+
return
81+
}
7782
h.Add("Content-Type", "application/json")
7883
w.WriteHeader(200)
7984
c, _ := ioutil.ReadFile(filepath.Join("..", "test", "fixtures", "team", "pass.json"))
@@ -94,12 +99,12 @@ func mockedServer(w http.ResponseWriter, r *http.Request) {
9499
}
95100
}
96101

97-
func (a *accessApi) initiateClient() {
102+
func (a *accessAPI) initiateClient() {
98103
server = httptest.NewServer(http.HandlerFunc(mockedServer))
99104
client, _ := github.NewEnterpriseClient(server.URL, server.URL, nil)
100105
a.client = client
101106
}
102107

103-
func (a *accessApi) terminateClient() {
108+
func (a *accessAPI) terminateClient() {
104109
server.Close()
105110
}

go.mod

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ module github.com/fmenezes/codeowners
33
go 1.14
44

55
require (
6-
github.com/google/go-github v17.0.0+incompatible
7-
github.com/google/go-querystring v1.0.0 // indirect
8-
golang.org/x/net v0.0.0-20150619012842-d375fa34084f // indirect
9-
golang.org/x/oauth2 v0.0.0-20160428204544-9ef2eddcc677
6+
github.com/google/go-github/v32 v32.1.0
7+
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
108
)

go.sum

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1-
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
2-
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
1+
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
2+
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
3+
github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II=
4+
github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
35
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
46
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
5-
golang.org/x/net v0.0.0-20150619012842-d375fa34084f h1:SCcQHcQHdI84d6MaqitXLnqMCHCY3JRWg9vpeKF9S4U=
6-
golang.org/x/net v0.0.0-20150619012842-d375fa34084f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
7-
golang.org/x/oauth2 v0.0.0-20160428204544-9ef2eddcc677 h1:zl0Z/AlDr4BNyCx+hax5ViLj7eiwDqE3YhKJ1MfVV34=
8-
golang.org/x/oauth2 v0.0.0-20160428204544-9ef2eddcc677/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
7+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
8+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
9+
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
10+
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
11+
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
12+
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
13+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
14+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
15+
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
16+
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=

0 commit comments

Comments
 (0)