Skip to content

Commit

Permalink
[github-auth] Support teams and emails
Browse files Browse the repository at this point in the history
  • Loading branch information
csweichel committed Apr 9, 2022
1 parent 19e24b4 commit b7929ec
Show file tree
Hide file tree
Showing 14 changed files with 206 additions and 194 deletions.
3 changes: 3 additions & 0 deletions cmd/client/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ func prettyPrint(obj proto.Message, defaultTpl string) error {
}

func getMetadataFilter(md *v1.JobMetadata) ([]*v1.FilterExpression, error) {
if md == nil {
return nil, nil
}
return []*v1.FilterExpression{
{Terms: []*v1.FilterTerm{{Field: "repo.owner", Value: md.Repository.Owner}}},
{Terms: []*v1.FilterTerm{{Field: "repo.repo", Value: md.Repository.Repo}}},
Expand Down
13 changes: 9 additions & 4 deletions cmd/client/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,15 @@ func dial() (res closableGrpcClientConnInterface) {
func getRequestContext(md *v1.JobMetadata) (ctx context.Context, cancel context.CancelFunc, err error) {
reqMD := make(metadata.MD)
if rootCmdOpts.CredentialHelper != "" {
var m jsonpb.Marshaler
mdJSON, err := m.MarshalToString(md)
if err != nil {
return nil, nil, err
var (
m jsonpb.Marshaler
mdJSON string
)
if md != nil {
mdJSON, err = m.MarshalToString(md)
if err != nil {
return nil, nil, err
}
}

cmd := exec.Command(rootCmdOpts.CredentialHelper)
Expand Down
16 changes: 7 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ require (
github.com/improbable-eng/grpc-web v0.14.0
github.com/lib/pq v1.10.0
github.com/olebedev/emitter v0.0.0-20190110104742-e8d1457e6aee
github.com/open-policy-agent/opa v0.39.0
github.com/open-policy-agent/opa v0.24.0
github.com/paulbellamy/ratecounter v0.2.0
github.com/prometheus/client_golang v1.12.1
github.com/prometheus/client_golang v1.5.1
github.com/segmentio/textio v1.2.0
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.4.0
github.com/technosophos/moniker v0.0.0-20210218184952-3ea787d3943b
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
golang.org/x/tools v0.1.5
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
google.golang.org/grpc v1.45.0
google.golang.org/grpc v1.36.1
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
k8s.io/api v0.23.4
k8s.io/apimachinery v0.23.4
Expand Down Expand Up @@ -67,25 +67,23 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/common v0.10.0 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
github.com/rs/cors v1.7.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/yashtewari/glob-intersection v0.1.0 // indirect
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
google.golang.org/protobuf v1.28.0 // indirect
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/klog/v2 v2.30.0 // indirect
Expand Down
173 changes: 31 additions & 142 deletions go.sum

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion pkg/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ type AuthenticationProvider interface {
type AuthResponse struct {
Known bool `json:"known"`
Username string `json:"username"`
Metadata map[string]string `json:"metadata"`
Metadata map[string]string `json:"metadata,omitempty"`
Emails []string `json:"emails,omitempty"`
Teams []string `json:"teams,omitempty"`
}

type Interceptor interface {
Expand Down
63 changes: 49 additions & 14 deletions pkg/auth/opa.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"sync"

"github.com/open-policy-agent/opa/rego"
"github.com/sirupsen/logrus"
Expand All @@ -16,7 +17,7 @@ import (

func NewOPAInterceptor(ctx context.Context, authProvider AuthenticationProvider, bundle string) (Interceptor, error) {
p, err := rego.New(
rego.Query("data.werft.allow"),
rego.Query("res = data.werft.allow"),
rego.LoadBundle(bundle),
).PrepareForEval(ctx)
if err != nil {
Expand Down Expand Up @@ -73,19 +74,52 @@ func (i *opaInterceptor) Stream() grpc.StreamServerInterceptor {
}

md, _ := metadata.FromIncomingContext(ctx)
input := policyInput{
Method: info.FullMethod,
Metadata: md,
Message: srv,
Auth: auth,
}
err = i.eval(ctx, input)
if err != nil {
return err
}

return handler(srv, ss)
return handler(srv, &interceptingStream{
ServerStream: ss,
eval: func(msg interface{}) error {
input := policyInput{
Method: info.FullMethod,
Metadata: md,
Message: msg,
Auth: auth,
}
err = i.eval(ctx, input)
if err != nil {
return err
}
return nil
},
})
}
}

type interceptingStream struct {
grpc.ServerStream
eval func(msg interface{}) error
done bool
mu sync.Mutex
}

func (s *interceptingStream) RecvMsg(m interface{}) error {
err := s.ServerStream.RecvMsg(m)
if err != nil {
return err
}

s.mu.Lock()
defer s.mu.Unlock()

if s.done {
return nil
}
s.done = true

err = s.eval(m)
if err != nil {
return err
}
return nil
}

func (i *opaInterceptor) eval(ctx context.Context, input policyInput) error {
Expand All @@ -103,9 +137,10 @@ func (i *opaInterceptor) eval(ctx context.Context, input policyInput) error {
input.Metadata["x-auth-token"] = []string{"some-value"}
}
dmp, _ := json.Marshal(input)
logrus.WithField("input", string(dmp)).WithField("value", result[0].Expressions[0].Value).Debug("evaluating request")
allowed, ok := result[0].Bindings["res"].(bool)
logrus.WithField("input", string(dmp)).WithField("allowed", allowed).Debug("evaluating request")

if !result.Allowed() {
if !allowed || !ok {
return status.Error(codes.Unauthenticated, "not allowed")
}
return nil
Expand Down
51 changes: 34 additions & 17 deletions pkg/plugin/common/auth-plugin.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pkg/plugin/common/auth-plugin.proto
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ message AuthenticateResponse {
bool known = 1;
string username = 2;
map<string, string> metadata = 3;
repeated string emails = 4;
repeated string teams = 5;
}
2 changes: 2 additions & 0 deletions pkg/plugin/host/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ func (p pluginsAuthProvider) Authenticate(ctx context.Context, token string) (*a
Known: true,
Username: resp.Username,
Metadata: resp.Metadata,
Emails: resp.Emails,
Teams: resp.Teams,
}, nil
}
}
Expand Down
37 changes: 32 additions & 5 deletions plugins/github-auth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ import (
)

// Config configures this plugin
type Config struct{}
type Config struct {
EnableTeams bool `yaml:"enableTeams,omitempty"`
EnableEmails bool `yaml:"enableEmails,omitempty"`
}

func main() {
plugin.Serve(&Config{},
Expand All @@ -28,15 +31,17 @@ func main() {
type githubAuthPlugin struct{}

func (*githubAuthPlugin) Run(ctx context.Context, config interface{}) (common.AuthenticationPluginServer, error) {
_, ok := config.(*Config)
cfg, ok := config.(*Config)
if !ok {
return nil, fmt.Errorf("config has wrong type %s", reflect.TypeOf(config))
}

return &authServer{}, nil
return &authServer{Config: cfg}, nil
}

type authServer struct{}
type authServer struct {
Config *Config
}

func (as *authServer) Authenticate(ctx context.Context, req *common.AuthenticateRequest) (*common.AuthenticateResponse, error) {
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: req.Token})
Expand All @@ -57,12 +62,34 @@ func (as *authServer) Authenticate(ctx context.Context, req *common.Authenticate
return nil, err
}

var emails []string
if as.Config.EnableEmails {
rawEmails, _, _ := client.Users.ListEmails(ctx, nil)
for _, e := range rawEmails {
if !e.GetVerified() {
continue
}

emails = append(emails, e.GetEmail())
}
}

var teams []string
if as.Config.EnableTeams {
rawTeams, _, _ := client.Teams.ListUserTeams(ctx, nil)
for _, t := range rawTeams {
teams = append(teams, t.GetURL())
}
}

return &common.AuthenticateResponse{
Known: true,
Username: user.GetLogin(),
Metadata: map[string]string{
"two-factor-authentication": strconv.FormatBool(user.GetTwoFactorAuthentication()),
"email": user.GetEmail(),
"name": user.GetName(),
},
Emails: emails,
Teams: teams,
}, nil
}
2 changes: 1 addition & 1 deletion testdata/credential-helper.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash

echo this-this-the-auth-token
echo $GITHUB_TOKEN
timeout 1s cat - > /tmp/werft-debug.json
3 changes: 3 additions & 0 deletions testdata/in-gitpod-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ plugins:
- name: "github-auth"
type:
- auth
config:
enableEmails: true
enableTeams: false
command:
- sh
- -c
Expand Down
31 changes: 30 additions & 1 deletion testdata/policy/api.rego
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,34 @@ allow {
input.method == "/v1.WerftService/ListJobs"
}
allow {
input.auth.known
input.method == "/v1.WerftService/Listen"
}

# all other things are only allowed when the user is authenticated and a Gitpod employee
allow {
is_team_member

input.method == "/v1.WerftService/StartGitHubJob"
input.message.sideload != ""
not startswith(input.message.metadata.repository.ref, "refs/heads/main")
}
allow {
is_team_member

input.method == "/v1.WerftService/StartGitHubJob"
input.message.job_yaml != ""
not startswith(input.message.metadata.repository.ref, "refs/heads/main")
}
allow {
is_team_member

input.method == "/v1.WerftService/StartGitHubJob"
input.message.job_yaml == ""
input.message.job_path == ""
input.message.sideload == ""
}

is_team_member[auth] {
input.auth.known
endswith(auth.emails[_], "@gitpod.io")
}
Binary file modified testdata/policy/bundle.tar.gz
Binary file not shown.

0 comments on commit b7929ec

Please sign in to comment.