blob: a4640e8108299f9f92e42f87ba62651364bfaa54 [file] [log] [blame]
// Copyright 2018 The Goma Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package acl
import (
"context"
"testing"
"golang.org/x/oauth2"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"go.chromium.org/goma/server/auth"
"go.chromium.org/goma/server/auth/account"
pb "go.chromium.org/goma/server/proto/auth"
)
type fakePool struct{}
func (fakePool) New(name string) (account.Account, error) {
return fakeAccount{name}, nil
}
type fakeAccount struct {
name string
}
func (f fakeAccount) Equals(other account.Account) bool {
o, ok := other.(fakeAccount)
if !ok {
return false
}
return f.name != o.name
}
func (f fakeAccount) Token(ctx context.Context) (*oauth2.Token, error) {
return &oauth2.Token{AccessToken: "token"}, nil
}
func TestChecker(t *testing.T) {
config := &pb.ACL{
Groups: []*pb.Group{
{
Id: "service-account",
Emails: []string{"foo@project.iam.gserviceaccount.com"},
},
{
Id: "chrome-bot",
Emails: []string{"goma-client@chrome-infra-auth.iam.gserviceaccount.com"},
ServiceAccount: "chrome-bot-service-account",
},
{
Id: "googler",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
Domains: []string{"google.com"},
ServiceAccount: "googler-service-account",
},
{
Id: "contributor",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
Emails: []string{"foo@gmail.com"},
ServiceAccount: "contributor-service-account",
},
},
}
checker := &Checker{
Pool: fakePool{},
}
ctx := context.Background()
err := checker.Set(ctx, config)
if err != nil {
t.Errorf("checker.Set(ctx, config)=%v; want nil-error", err)
}
type testData struct {
tokenInfo *auth.TokenInfo
errCode codes.Code
}
testCases := map[string]*testData{
"service-account": {
tokenInfo: &auth.TokenInfo{
Email: "foo@project.iam.gserviceaccount.com",
Audience: "123456-xxxxxx.apps.googleusercontent.com",
},
},
"chrome-bot": {
tokenInfo: &auth.TokenInfo{
Email: "goma-client@chrome-infra-auth.iam.gserviceaccount.com",
Audience: "7890-xxxxxx.apps.googleusercontent.com",
},
},
"googler": {
tokenInfo: &auth.TokenInfo{
Email: "someone@google.com",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
},
},
"malicious-googler": {
tokenInfo: &auth.TokenInfo{
Email: "malicious@google.com",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
},
},
"contributor": {
tokenInfo: &auth.TokenInfo{
Email: "foo@gmail.com",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
},
},
"unknown service account": {
tokenInfo: &auth.TokenInfo{
Email: "unknown-service-account@chrome-infra-auth.iam.gserviceaccount.com",
Audience: "7890-xxxxxx.apps.googleusercontent.com",
},
errCode: codes.PermissionDenied,
},
"unknown audience": {
tokenInfo: &auth.TokenInfo{
Email: "someone@google.com",
Audience: "187410957766-f28toeml294bk5mm7nr2jn2rup1rvtjj.apps.googleusercontent.com",
},
errCode: codes.PermissionDenied,
},
"unknown user": {
tokenInfo: &auth.TokenInfo{
Email: "unknown.user@gmail.com",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
},
errCode: codes.PermissionDenied,
},
"contributor2": {
tokenInfo: &auth.TokenInfo{
Email: "contributor@gmail.com",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
},
errCode: codes.PermissionDenied,
},
}
testCheck := func() {
for desc, tc := range testCases {
t.Logf("check %s %s", desc, tc.tokenInfo.Email)
_, _, err := checker.CheckToken(ctx, &oauth2.Token{AccessToken: "token"}, tc.tokenInfo)
if st, _ := status.FromError(err); st.Code() != tc.errCode {
t.Errorf("checker.CheckToken(ctx, token, tokenInfo %s)=_, %v, want err code %d", tc.tokenInfo.Email, err, tc.errCode)
}
}
}
testCheck()
t.Logf("allow new contributor")
g := config.Groups[len(config.Groups)-1]
g.Emails = append(g.Emails, "contributor@gmail.com")
err = checker.Set(ctx, config)
if err != nil {
t.Errorf("checker.Set(ctx, config)=%v; want nil-error", err)
}
testCases["contributor2"].errCode = codes.OK
testCheck()
t.Logf("remove contributor")
config.Groups = config.Groups[:len(config.Groups)-1]
err = checker.Set(ctx, config)
if err != nil {
t.Errorf("checker.Set(ctx, config)=%v; want nil-error", err)
}
testCases["contributor"].errCode = codes.PermissionDenied
testCases["contributor2"].errCode = codes.PermissionDenied
testCheck()
t.Logf("reject bad googler")
config.Groups = append([]*pb.Group{
{
Id: "bad-googler",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
Emails: []string{"malicious@google.com"},
Reject: true,
},
}, config.Groups...)
err = checker.Set(ctx, config)
if err != nil {
t.Errorf("checker.Set(ctx, config)=%v; want nil-error", err)
}
testCases["malicious-googler"].errCode = codes.PermissionDenied
testCheck()
}
type fakeAuthDB struct {
db map[string]bool
}
func (f fakeAuthDB) IsMember(ctx context.Context, email, group string) bool {
return f.db[email+":"+group]
}
func TestCheckGroup(t *testing.T) {
ctx := context.Background()
authDB := fakeAuthDB{
db: map[string]bool{
"someone@google.com:googler": true,
},
}
for _, tc := range []struct {
desc string
tokenInfo *auth.TokenInfo
g *pb.Group
want bool
}{
{
desc: "empty group",
tokenInfo: &auth.TokenInfo{
Email: "someone@google.com",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
},
g: &pb.Group{
Id: "empty",
},
},
{
desc: "email match",
tokenInfo: &auth.TokenInfo{
Email: "someone@google.com",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
},
g: &pb.Group{
Id: "someone-in-google",
Emails: []string{
"someone@google.com",
},
},
want: true,
},
{
desc: "domain match",
tokenInfo: &auth.TokenInfo{
Email: "someone@google.com",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
},
g: &pb.Group{
Id: "googler",
Domains: []string{
"google.com",
},
},
want: true,
},
{
desc: "authdb match",
tokenInfo: &auth.TokenInfo{
Email: "someone@google.com",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
},
g: &pb.Group{
Id: "googler",
},
want: true,
},
{
desc: "email match with audience check",
tokenInfo: &auth.TokenInfo{
Email: "someone@google.com",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
},
g: &pb.Group{
Id: "someone-in-google",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
Emails: []string{
"someone@google.com",
},
},
want: true,
},
{
desc: "domain match with audience check",
tokenInfo: &auth.TokenInfo{
Email: "someone@google.com",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
},
g: &pb.Group{
Id: "googler",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
Domains: []string{
"google.com",
},
},
want: true,
},
{
desc: "authdb match with audience check",
tokenInfo: &auth.TokenInfo{
Email: "someone@google.com",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
},
g: &pb.Group{
Id: "googler",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
},
want: true,
},
{
desc: "email match but audience mismatch",
tokenInfo: &auth.TokenInfo{
Email: "someone@google.com",
Audience: "123456-xxxxxx.apps.googleusercontent.com",
},
g: &pb.Group{
Id: "someone-in-google",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
Emails: []string{
"someone@google.com",
},
},
},
{
desc: "domain match but audience mismatch",
tokenInfo: &auth.TokenInfo{
Email: "someone@google.com",
Audience: "123456-xxxxxx.apps.googleusercontent.com",
},
g: &pb.Group{
Id: "googler",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
Domains: []string{
"google.com",
},
},
},
{
desc: "authdb match but audience mismatch",
tokenInfo: &auth.TokenInfo{
Email: "someone@google.com",
Audience: "123456-xxxxxx.apps.googleusercontent.com",
},
g: &pb.Group{
Id: "googler",
Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
},
},
} {
t.Run(tc.desc, func(t *testing.T) {
got := checkGroup(ctx, tc.tokenInfo, tc.g, authDB)
if got != tc.want {
t.Errorf("checkGroup(ctx, tokenInfo, group, authDB)=%t; want=%t", got, tc.want)
}
})
}
}