| Index: tokenserver/appengine/impl/serviceaccounts/rpc_inspect_oauth_token_grant.go
|
| diff --git a/tokenserver/appengine/impl/serviceaccounts/rpc_inspect_oauth_token_grant.go b/tokenserver/appengine/impl/serviceaccounts/rpc_inspect_oauth_token_grant.go
|
| index a8e388694061f1631579d918e0d8d1f6a11cc1cf..69dc0fae8fbec2a33ae9399c42d96a4e63d94e71 100644
|
| --- a/tokenserver/appengine/impl/serviceaccounts/rpc_inspect_oauth_token_grant.go
|
| +++ b/tokenserver/appengine/impl/serviceaccounts/rpc_inspect_oauth_token_grant.go
|
| @@ -9,6 +9,7 @@ import (
|
| "google.golang.org/grpc"
|
| "google.golang.org/grpc/codes"
|
|
|
| + "github.com/luci/luci-go/server/auth/identity"
|
| "github.com/luci/luci-go/server/auth/signing"
|
|
|
| "github.com/luci/luci-go/tokenserver/api"
|
| @@ -21,6 +22,11 @@ type InspectOAuthTokenGrantRPC struct {
|
| //
|
| // In prod it is gaesigner.Signer.
|
| Signer signing.Signer
|
| +
|
| + // Rules returns service account rules to use for the request.
|
| + //
|
| + // In prod it is GlobalRulesCache.Rules.
|
| + Rules func(context.Context) (*Rules, error)
|
| }
|
|
|
| // InspectOAuthTokenGrant decodes the given OAuth token grant.
|
| @@ -29,15 +35,50 @@ func (r *InspectOAuthTokenGrantRPC) InspectOAuthTokenGrant(c context.Context, re
|
| if err != nil {
|
| return nil, grpc.Errorf(codes.Internal, err.Error())
|
| }
|
| +
|
| resp := &admin.InspectOAuthTokenGrantResponse{
|
| Valid: inspection.Signed && inspection.NonExpired,
|
| Signed: inspection.Signed,
|
| NonExpired: inspection.NonExpired,
|
| InvalidityReason: inspection.InvalidityReason,
|
| }
|
| +
|
| if env, _ := inspection.Envelope.(*tokenserver.OAuthTokenGrantEnvelope); env != nil {
|
| resp.SigningKeyId = env.KeyId
|
| }
|
| +
|
| + // Examine the body, even if the token is expired or unsigned. This helps to
|
| + // debug expired or unsigned tokens...
|
| resp.TokenBody, _ = inspection.Body.(*tokenserver.OAuthTokenGrantBody)
|
| + if resp.TokenBody != nil {
|
| + rules, err := r.Rules(c)
|
| + if err != nil {
|
| + return nil, grpc.Errorf(codes.Internal, "failed to load service accounts rules")
|
| + }
|
| +
|
| + // Always return the rule that matches the service account, even if the
|
| + // token itself is not allowed by it (we check it separately below).
|
| + if rule := rules.Rule(resp.TokenBody.ServiceAccount); rule != nil {
|
| + resp.MatchingRule = rule.Rule
|
| + }
|
| +
|
| + q := &RulesQuery{
|
| + ServiceAccount: resp.TokenBody.ServiceAccount,
|
| + Proxy: identity.Identity(resp.TokenBody.Proxy),
|
| + EndUser: identity.Identity(resp.TokenBody.EndUser),
|
| + }
|
| + switch _, err = rules.Check(c, q); {
|
| + case err == nil:
|
| + resp.AllowedByRules = true
|
| + case grpc.Code(err) == codes.Internal:
|
| + return nil, err // a transient error when checking rules
|
| + default: // fatal gRPC error => the rules forbid the token
|
| + if resp.Valid {
|
| + resp.Valid = false
|
| + resp.InvalidityReason = "not allowed by the rules"
|
| + }
|
| + }
|
| + }
|
| +
|
| return resp, nil
|
| }
|
|
|