Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2017 The LUCI Authors. | 1 // Copyright 2017 The LUCI Authors. |
| 2 // | 2 // |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 // you may not use this file except in compliance with the License. | 4 // you may not use this file except in compliance with the License. |
| 5 // You may obtain a copy of the License at | 5 // You may obtain a copy of the License at |
| 6 // | 6 // |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 // | 8 // |
| 9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and |
| 13 // limitations under the License. | 13 // limitations under the License. |
| 14 | 14 |
| 15 package serviceaccounts | 15 package serviceaccounts |
| 16 | 16 |
| 17 import ( | 17 import ( |
| 18 "fmt" | |
| 19 "strings" | |
| 20 | |
| 18 "github.com/luci/luci-go/common/config/validation" | 21 "github.com/luci/luci-go/common/config/validation" |
| 22 "github.com/luci/luci-go/common/data/stringset" | |
| 23 "github.com/luci/luci-go/server/auth/identity" | |
| 19 | 24 |
| 20 "github.com/luci/luci-go/tokenserver/api/admin/v1" | 25 "github.com/luci/luci-go/tokenserver/api/admin/v1" |
| 21 "github.com/luci/luci-go/tokenserver/appengine/impl/utils/policy" | 26 "github.com/luci/luci-go/tokenserver/appengine/impl/utils/policy" |
| 22 ) | 27 ) |
| 23 | 28 |
| 24 // validateConfigs validates the structure of configs fetched by fetchConfigs. | 29 // validateConfigs validates the structure of configs fetched by fetchConfigs. |
| 25 func validateConfigs(bundle policy.ConfigBundle, ctx *validation.Context) { | 30 func validateConfigs(bundle policy.ConfigBundle, ctx *validation.Context) { |
| 26 ctx.SetFile(serviceAccountsCfg) | 31 ctx.SetFile(serviceAccountsCfg) |
| 27 cfg, ok := bundle[serviceAccountsCfg].(*admin.ServiceAccountsPermissions ) | 32 cfg, ok := bundle[serviceAccountsCfg].(*admin.ServiceAccountsPermissions ) |
| 28 if !ok { | 33 if !ok { |
| 29 ctx.Error("unexpectedly wrong proto type %T", cfg) | 34 ctx.Error("unexpectedly wrong proto type %T", cfg) |
| 30 return | 35 return |
| 31 } | 36 } |
| 32 | 37 |
| 33 » // TODO(vadimsh): Validate cfg.Rules. | 38 » names := stringset.New(0) |
| 39 » accounts := map[string]string{} // service account -> rule name where it s defined | |
| 40 » for i, rule := range cfg.Rules { | |
| 41 » » // Rule name must be unique. | |
| 42 » » if rule.Name != "" { | |
| 43 » » » if names.Has(rule.Name) { | |
| 44 » » » » ctx.Error("two rules with identical name %q", ru le.Name) | |
| 45 » » » } else { | |
| 46 » » » » names.Add(rule.Name) | |
| 47 » » » } | |
| 48 » » } | |
|
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.
| |
| 49 | |
| 50 » » // There should be no overlap between service account sets cover ed by each | |
| 51 » » // rule. | |
| 52 » » for _, account := range rule.ServiceAccount { | |
| 53 » » » if name, ok := accounts[account]; ok { | |
| 54 » » » » ctx.Error("service account %q is mentioned by mo re than one rule (%q and %q)", account, name, rule.Name) | |
| 55 » » » } else { | |
| 56 » » » » accounts[account] = rule.Name | |
| 57 » » » } | |
| 58 » » } | |
| 59 | |
| 60 » » validateRule(fmt.Sprintf("rule #%d: %q", i+1, rule.Name), rule, ctx) | |
| 61 » } | |
| 34 } | 62 } |
| 63 | |
| 64 // validateRule checks single ServiceAccountRule proto. | |
| 65 func validateRule(title string, r *admin.ServiceAccountRule, ctx *validation.Con text) { | |
| 66 ctx.Enter(title) | |
| 67 defer ctx.Exit() | |
| 68 | |
| 69 if r.Name == "" { | |
| 70 ctx.Error(`"name" is required`) | |
| 71 } | |
| 72 | |
| 73 // Note: we allow any of the sets to be empty. The rule will just not ma tch | |
| 74 // anything in this case, this is fine. | |
| 75 validateEmails("service_account", r.ServiceAccount, ctx) | |
| 76 validateScopes("allowed_scope", r.AllowedScope, ctx) | |
| 77 validateIdSet("end_user", r.EndUser, ctx) | |
| 78 validateIdSet("proxy", r.Proxy, ctx) | |
| 79 | |
| 80 if r.MaxGrantValidityDuration != 0 { | |
| 81 switch { | |
| 82 case r.MaxGrantValidityDuration < 0: | |
| 83 ctx.Error(`"max_grant_validity_duration" must be positiv e`) | |
| 84 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
| |
| 85 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.
| |
| 86 } | |
| 87 } | |
| 88 } | |
| 89 | |
| 90 func validateEmails(field string, emails []string, ctx *validation.Context) { | |
| 91 ctx.Enter("%q", field) | |
| 92 defer ctx.Exit() | |
| 93 for _, email := range emails { | |
| 94 // We reuse 'user:' identity validator, user identities are emai ls too. | |
| 95 if _, err := identity.MakeIdentity("user:" + email); err != nil { | |
| 96 ctx.Error("bad email %q - %s", email, err) | |
| 97 } | |
| 98 } | |
| 99 } | |
| 100 | |
| 101 func validateScopes(field string, scopes []string, ctx *validation.Context) { | |
| 102 ctx.Enter("%q", field) | |
| 103 defer ctx.Exit() | |
| 104 for _, scope := range scopes { | |
| 105 if !strings.HasPrefix(scope, "https://www.googleapis.com/") { | |
| 106 ctx.Error("bad scope %q", scope) | |
| 107 } | |
| 108 } | |
| 109 } | |
| 110 | |
| 111 func validateIdSet(field string, ids []string, ctx *validation.Context) { | |
| 112 ctx.Enter("%q", field) | |
| 113 defer ctx.Exit() | |
| 114 for _, entry := range ids { | |
| 115 if strings.HasPrefix(entry, "group:") { | |
| 116 if entry[len("group:"):] == "" { | |
| 117 ctx.Error("bad group entry - no group name") | |
| 118 } | |
| 119 } else if _, err := identity.MakeIdentity(entry); err != nil { | |
| 120 ctx.Error("bad identity %q - %s", entry, err) | |
| 121 } | |
| 122 } | |
| 123 } | |
| OLD | NEW |