M auth/bearer.go => auth/bearer.go +45 -3
@@ 80,10 80,25 @@ func DecodeBearerToken(token string) *BearerToken {
return &bt
}
-func DecodeGrants(ctx context.Context, grants string) map[string]string {
+const (
+ RO = "RO"
+ RW = "RW"
+)
+
+type Grants struct {
+ all bool
+ grants map[string]string
+ encoded string
+}
+
+func DecodeGrants(ctx context.Context, grants string) Grants {
if grants == "" {
// All permissions
- return nil
+ return Grants{
+ all: true,
+ grants: nil,
+ encoded: "",
+ }
}
accessMap := make(map[string]string)
for _, grant := range strings.Split(grants, " ") {
@@ 108,5 123,32 @@ func DecodeGrants(ctx context.Context, grants string) map[string]string {
accessMap[scope] = access
}
}
- return accessMap
+ return Grants{
+ all: false,
+ grants: accessMap,
+ encoded: grants,
+ }
+}
+
+func (g *Grants) Has(grant string, mode string) bool {
+ if mode != RO && mode != RW {
+ panic("Invalid access mode")
+ }
+
+ if g.all {
+ return true
+ }
+
+ if access, ok := g.grants[grant]; !ok {
+ return false
+ } else {
+ if mode == RO {
+ return true
+ }
+ return mode == access
+ }
+}
+
+func (g *Grants) Encode() string {
+ return g.encoded
}
M auth/middleware.go => auth/middleware.go +3 -3
@@ 77,7 77,7 @@ type AuthContext struct {
// Only filled out if AuthMethod == AUTH_OAUTH2 or AUTH_WEBHOOK
BearerToken *BearerToken
- Access map[string]string
+ Grants Grants
TokenHash [64]byte
}
@@ 554,7 554,7 @@ func OAuth2(token string, hash [64]byte, w http.ResponseWriter,
auth.AuthMethod = AUTH_OAUTH2
auth.BearerToken = bt
auth.TokenHash = hash
- auth.Access = DecodeGrants(r.Context(), bt.Grants)
+ auth.Grants = DecodeGrants(r.Context(), bt.Grants)
ctx := context.WithValue(r.Context(), userCtxKey, &auth)
r = r.WithContext(ctx)
@@ 666,7 666,7 @@ func WebhookAuth(ctx context.Context, auth *AuthContext,
whAuth := *auth
whAuth.AuthMethod = AUTH_WEBHOOK
whAuth.TokenHash = tokenHash
- whAuth.Access = DecodeGrants(ctx, grants)
+ whAuth.Grants = DecodeGrants(ctx, grants)
whAuth.BearerToken = &BearerToken{}
if clientID != nil {
whAuth.BearerToken.ClientID = *clientID
M server/directives.go => server/directives.go +3 -9
@@ 44,25 44,19 @@ func Access(ctx context.Context, obj interface{}, next graphql.Resolver,
case auth.AUTH_INTERNAL, auth.AUTH_COOKIE:
return next(ctx)
case auth.AUTH_OAUTH_LEGACY:
- if kind == "RO" {
+ if kind == auth.RO {
// Only legacy tokens with "*" scopes ever get this far
return next(ctx)
}
case auth.AUTH_WEBHOOK:
- if kind != "RO" {
+ if kind != auth.RO {
return nil, fmt.Errorf("Access to read/write resolver denied for webhook")
}
fallthrough
case auth.AUTH_OAUTH2:
- if authctx.Access == nil {
+ if authctx.Grants.Has(scope, kind) {
return next(ctx)
}
- if access, ok := authctx.Access[scope]; !ok {
- break
- } else if access == "RO" && kind == "RW" {
- break
- }
- return next(ctx)
default:
panic(fmt.Errorf("Unknown auth method for access check"))
}