Chromium Code Reviews| Index: tokenserver/appengine/impl/serviceaccounts/config_validation.go |
| diff --git a/tokenserver/appengine/impl/serviceaccounts/config_validation.go b/tokenserver/appengine/impl/serviceaccounts/config_validation.go |
| index 907e33c085a2b8d79a0fbc7cc60e50446c48d826..98d6ee748399c205d9a4b381057c63224c897883 100644 |
| --- a/tokenserver/appengine/impl/serviceaccounts/config_validation.go |
| +++ b/tokenserver/appengine/impl/serviceaccounts/config_validation.go |
| @@ -15,7 +15,12 @@ |
| package serviceaccounts |
| import ( |
| + "fmt" |
| + "strings" |
| + |
| "github.com/luci/luci-go/common/config/validation" |
| + "github.com/luci/luci-go/common/data/stringset" |
| + "github.com/luci/luci-go/server/auth/identity" |
| "github.com/luci/luci-go/tokenserver/api/admin/v1" |
| "github.com/luci/luci-go/tokenserver/appengine/impl/utils/policy" |
| @@ -30,5 +35,89 @@ func validateConfigs(bundle policy.ConfigBundle, ctx *validation.Context) { |
| return |
| } |
| - // TODO(vadimsh): Validate cfg.Rules. |
| + names := stringset.New(0) |
| + accounts := map[string]string{} // service account -> rule name where its defined |
| + for i, rule := range cfg.Rules { |
| + // Rule name must be unique. |
| + if rule.Name != "" { |
| + if names.Has(rule.Name) { |
| + ctx.Error("two rules with identical name %q", rule.Name) |
| + } else { |
| + names.Add(rule.Name) |
| + } |
| + } |
|
smut
2017/08/04 23:07:37
Add comment that we don't bother handling the else
Vadim Sh.
2017/08/04 23:37:52
Done.
|
| + |
| + // There should be no overlap between service account sets covered by each |
| + // rule. |
| + for _, account := range rule.ServiceAccount { |
| + if name, ok := accounts[account]; ok { |
| + ctx.Error("service account %q is mentioned by more than one rule (%q and %q)", account, name, rule.Name) |
| + } else { |
| + accounts[account] = rule.Name |
| + } |
| + } |
| + |
| + validateRule(fmt.Sprintf("rule #%d: %q", i+1, rule.Name), rule, ctx) |
| + } |
| +} |
| + |
| +// validateRule checks single ServiceAccountRule proto. |
| +func validateRule(title string, r *admin.ServiceAccountRule, ctx *validation.Context) { |
| + ctx.Enter(title) |
| + defer ctx.Exit() |
| + |
| + if r.Name == "" { |
| + ctx.Error(`"name" is required`) |
| + } |
| + |
| + // Note: we allow any of the sets to be empty. The rule will just not match |
| + // anything in this case, this is fine. |
| + validateEmails("service_account", r.ServiceAccount, ctx) |
| + validateScopes("allowed_scope", r.AllowedScope, ctx) |
| + validateIdSet("end_user", r.EndUser, ctx) |
| + validateIdSet("proxy", r.Proxy, ctx) |
| + |
| + if r.MaxGrantValidityDuration != 0 { |
| + switch { |
| + case r.MaxGrantValidityDuration < 0: |
| + ctx.Error(`"max_grant_validity_duration" must be positive`) |
| + case r.MaxGrantValidityDuration > 7*24*3600: |
|
smut
2017/08/04 23:07:37
Can this be defined somewhere as maximum allowable
Vadim Sh.
2017/08/04 23:37:52
Done.
Though I have a nagging suspicion that in r
|
| + ctx.Error(`"max_grant_validity_duration" must be smaller than 604801`) |
|
smut
2017/08/04 23:07:37
Once maxixmum allowable grant validity duration is
Vadim Sh.
2017/08/04 23:37:52
Done.
|
| + } |
| + } |
| +} |
| + |
| +func validateEmails(field string, emails []string, ctx *validation.Context) { |
| + ctx.Enter("%q", field) |
| + defer ctx.Exit() |
| + for _, email := range emails { |
| + // We reuse 'user:' identity validator, user identities are emails too. |
| + if _, err := identity.MakeIdentity("user:" + email); err != nil { |
| + ctx.Error("bad email %q - %s", email, err) |
| + } |
| + } |
| +} |
| + |
| +func validateScopes(field string, scopes []string, ctx *validation.Context) { |
| + ctx.Enter("%q", field) |
| + defer ctx.Exit() |
| + for _, scope := range scopes { |
| + if !strings.HasPrefix(scope, "https://www.googleapis.com/") { |
| + ctx.Error("bad scope %q", scope) |
| + } |
| + } |
| +} |
| + |
| +func validateIdSet(field string, ids []string, ctx *validation.Context) { |
| + ctx.Enter("%q", field) |
| + defer ctx.Exit() |
| + for _, entry := range ids { |
| + if strings.HasPrefix(entry, "group:") { |
| + if entry[len("group:"):] == "" { |
| + ctx.Error("bad group entry - no group name") |
| + } |
| + } else if _, err := identity.MakeIdentity(entry); err != nil { |
| + ctx.Error("bad identity %q - %s", entry, err) |
| + } |
| + } |
| } |