Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Unified Diff: tokenserver/appengine/delegation/validation.go

Issue 2413683004: token-server: Delegation config import, validation and evaluation. (Closed)
Patch Set: also check validity_duration Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tokenserver/appengine/delegation/validation.go
diff --git a/tokenserver/appengine/delegation/validation.go b/tokenserver/appengine/delegation/validation.go
new file mode 100644
index 0000000000000000000000000000000000000000..6fc453201aaebf9a0c430bd97b84912ada265839
--- /dev/null
+++ b/tokenserver/appengine/delegation/validation.go
@@ -0,0 +1,169 @@
+// Copyright 2016 The LUCI Authors. All rights reserved.
+// Use of this source code is governed under the Apache License, Version 2.0
+// that can be found in the LICENSE file.
+
+package delegation
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/luci/luci-go/common/data/stringset"
+ "github.com/luci/luci-go/common/errors"
+ "github.com/luci/luci-go/server/auth/identity"
+
+ "github.com/luci/luci-go/tokenserver/api/admin/v1"
+)
+
+// ValidateConfig checks delegation config for correctness.
+//
+// Tries to find all errors.
+func ValidateConfig(cfg *admin.DelegationPermissions) errors.MultiError {
+ var errs errors.MultiError
+ names := stringset.New(0)
+
+ for i, rule := range cfg.Rules {
+ prefix := fmt.Sprintf("rule #%d (%q)", i+1, rule.Name)
+ if rule.Name != "" {
+ if names.Has(rule.Name) {
+ errs = append(errs, fmt.Errorf("%s: the rule with such name is already defined", prefix))
+ }
+ names.Add(rule.Name)
+ }
+ for _, singleErr := range ValidateRule(rule) {
+ errs = append(errs, fmt.Errorf("%s: %s", prefix, singleErr))
+ }
+ }
+
+ return errs
+}
+
+// ValidateRule checks single DelegationRule proto.
+//
+// See config.proto, DelegationRule for the description of allowed values.
+func ValidateRule(r *admin.DelegationRule) errors.MultiError {
+ var out errors.MultiError
+
+ emitErr := func(msg string, args ...interface{}) {
+ out = append(out, fmt.Errorf(msg, args...))
+ }
+
+ emitMultiErr := func(prefix string, merr errors.MultiError) {
+ for _, err := range merr {
+ emitErr("%s - %s", prefix, err)
+ }
+ }
+
+ if r.Name == "" {
+ emitErr("'name' is required")
+ }
+
+ if len(r.Requestor) == 0 {
+ emitErr("'requestor' is required")
+ } else {
+ v := identitySetValidator{
+ AllowGroups: true,
+ }
+ emitMultiErr("bad 'requestor'", v.Validate(r.Requestor))
+ }
+
+ if len(r.AllowedToImpersonate) == 0 {
+ emitErr("'allowed_to_impersonate' is required")
+ } else {
+ v := identitySetValidator{
+ AllowReservedWords: []string{Requestor}, // '*' is not allowed here though
+ AllowGroups: true,
+ }
+ emitMultiErr("bad 'allowed_to_impersonate'", v.Validate(r.AllowedToImpersonate))
+ }
+
+ if len(r.AllowedAudience) == 0 {
+ emitErr("'allowed_audience' is required")
+ } else {
+ v := identitySetValidator{
+ AllowReservedWords: []string{Requestor, "*"},
+ AllowGroups: true,
+ }
+ emitMultiErr("bad 'allowed_audience'", v.Validate(r.AllowedAudience))
+ }
+
+ if len(r.TargetService) == 0 {
+ emitErr("'target_service' is required")
+ } else {
+ v := identitySetValidator{
+ AllowReservedWords: []string{"*"},
+ AllowIDKinds: []identity.Kind{identity.Service},
+ }
+ emitMultiErr("bad 'target_service'", v.Validate(r.TargetService))
+ }
+
+ if r.MaxValidityDuration == 0 {
+ emitErr("'max_validity_duration' is required")
+ }
+ if r.MaxValidityDuration < 0 {
+ emitErr("'max_validity_duration' must be positive")
+ }
+ if r.MaxValidityDuration > 24*3600 {
+ emitErr("'max_validity_duration' must be smaller than 86401")
+ }
+
+ return out
+}
+
+type identitySetValidator struct {
+ AllowReservedWords []string // to allow "*" and "REQUESTOR"
+ AllowGroups bool // true to allow "group:" entries
+ AllowIDKinds []identity.Kind // permitted identity kinds, or nil if all
+}
+
+func (v *identitySetValidator) Validate(items []string) errors.MultiError {
+ var out errors.MultiError
+
+ emitErr := func(msg string, args ...interface{}) {
+ out = append(out, fmt.Errorf(msg, args...))
+ }
+
+loop:
+ for _, s := range items {
+ // A reserved word?
+ for _, r := range v.AllowReservedWords {
+ if s == r {
+ continue loop
+ }
+ }
+
+ // A group reference?
+ if strings.HasPrefix(s, "group:") {
+ if !v.AllowGroups {
+ emitErr("group entries are not allowed - %q", s)
+ } else {
+ if s == "group:" {
+ emitErr("bad group entry %q", s)
+ }
+ }
+ continue
+ }
+
+ // An identity then.
+ id, err := identity.MakeIdentity(s)
+ if err != nil {
+ emitErr("%s", err)
+ continue
+ }
+
+ if v.AllowIDKinds != nil {
+ allowed := false
+ for _, k := range v.AllowIDKinds {
+ if id.Kind() == k {
+ allowed = true
+ break
+ }
+ }
+ if !allowed {
+ emitErr("identity of kind %q is not allowed here - %q", id.Kind(), s)
+ }
+ }
+ }
+
+ return out
+}

Powered by Google App Engine
This is Rietveld 408576698