Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The LUCI Authors. All rights reserved. | |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | |
| 3 // that can be found in the LICENSE file. | |
| 4 | |
| 5 package delegation | |
| 6 | |
| 7 import ( | |
| 8 "fmt" | |
| 9 "strings" | |
| 10 | |
| 11 "github.com/luci/luci-go/common/data/stringset" | |
| 12 "github.com/luci/luci-go/common/errors" | |
| 13 "github.com/luci/luci-go/server/auth/identity" | |
| 14 | |
| 15 "github.com/luci/luci-go/tokenserver/api/admin/v1" | |
| 16 ) | |
| 17 | |
| 18 // ValidateConfig checks delegation config for correctness. | |
| 19 // | |
| 20 // Tries to find all errors. | |
| 21 func ValidateConfig(cfg *admin.DelegationPermissions) errors.MultiError { | |
| 22 var errs errors.MultiError | |
| 23 names := stringset.New(0) | |
| 24 | |
| 25 for i, rule := range cfg.Rules { | |
| 26 prefix := fmt.Sprintf("rule #%d (%q)", i+1, rule.Name) | |
|
nodir
2016/10/13 22:03:52
i think it is a perfect opportunity to make MultiE
Vadim Sh.
2016/10/27 04:12:00
Discussed this.
| |
| 27 if names.Has(rule.Name) { | |
| 28 errs = append(errs, fmt.Errorf("%s: duplicate name", pre fix)) | |
| 29 } | |
| 30 names.Add(rule.Name) | |
| 31 for _, singleErr := range ValidateRule(rule) { | |
| 32 errs = append(errs, fmt.Errorf("%s: %s", prefix, singleE rr)) | |
| 33 } | |
| 34 } | |
| 35 | |
| 36 return errs | |
| 37 } | |
| 38 | |
| 39 // ValidateRule checks single DelegationRule proto. | |
| 40 // | |
| 41 // See config.proto, DelegationRule for the description of allowed values. | |
| 42 func ValidateRule(r *admin.DelegationRule) errors.MultiError { | |
| 43 var out errors.MultiError | |
| 44 | |
| 45 emitErr := func(msg string, args ...interface{}) { | |
| 46 out = append(out, fmt.Errorf(msg, args...)) | |
| 47 } | |
| 48 | |
| 49 emitMultiErr := func(prefix string, merr errors.MultiError) { | |
| 50 for _, err := range merr { | |
| 51 emitErr("%s - %s", prefix, err) | |
| 52 } | |
| 53 } | |
| 54 | |
| 55 if r.Name == "" { | |
| 56 emitErr("'name' is required") | |
| 57 } | |
| 58 | |
| 59 if len(r.Requestor) == 0 { | |
| 60 emitErr("'requestor' is required") | |
| 61 } else { | |
| 62 v := identitySetValidator{ | |
| 63 AllowGroups: true, | |
| 64 } | |
| 65 emitMultiErr("bad 'requestor'", v.Validate(r.Requestor)) | |
| 66 } | |
| 67 | |
| 68 if len(r.AllowedToImpersonate) == 0 { | |
| 69 emitErr("'allowed_to_impersonate' is required") | |
| 70 } else { | |
| 71 v := identitySetValidator{ | |
| 72 AllowReservedWords: []string{Requestor}, // '*' is not a llowed here though | |
| 73 AllowGroups: true, | |
| 74 } | |
| 75 emitMultiErr("bad 'allowed_to_impersonate'", v.Validate(r.Allowe dToImpersonate)) | |
| 76 } | |
| 77 | |
| 78 if len(r.AllowedAudience) == 0 { | |
| 79 emitErr("'allowed_audience' is required") | |
| 80 } else { | |
| 81 v := identitySetValidator{ | |
| 82 AllowReservedWords: []string{Requestor, "*"}, | |
| 83 AllowGroups: true, | |
| 84 } | |
| 85 emitMultiErr("bad 'allowed_audience'", v.Validate(r.AllowedAudie nce)) | |
| 86 } | |
| 87 | |
| 88 if len(r.TargetService) == 0 { | |
| 89 emitErr("'target_service' is required") | |
| 90 } else { | |
| 91 v := identitySetValidator{ | |
| 92 AllowReservedWords: []string{"*"}, | |
| 93 AllowIDKinds: []identity.Kind{identity.Service}, | |
| 94 } | |
| 95 emitMultiErr("bad 'target_service'", v.Validate(r.TargetService) ) | |
| 96 } | |
| 97 | |
| 98 if r.MaxValidityDuration == 0 { | |
| 99 emitErr("'max_validity_duration' is required") | |
| 100 } | |
| 101 if r.MaxValidityDuration < 0 { | |
| 102 emitErr("'max_validity_duration' must be positive") | |
| 103 } | |
| 104 if r.MaxValidityDuration > 24*3600 { | |
| 105 emitErr("'max_validity_duration' must be smaller than 86401") | |
| 106 } | |
| 107 | |
| 108 return out | |
| 109 } | |
| 110 | |
| 111 type identitySetValidator struct { | |
| 112 AllowReservedWords []string // to allow "*" and "REQUESTOR" | |
| 113 AllowGroups bool // true to allow "group:" entries | |
| 114 AllowIDKinds []identity.Kind // permitted identity kinds, or nil i f all | |
| 115 } | |
| 116 | |
| 117 func (v *identitySetValidator) Validate(items []string) errors.MultiError { | |
| 118 var out errors.MultiError | |
| 119 | |
| 120 emitErr := func(msg string, args ...interface{}) { | |
| 121 out = append(out, fmt.Errorf(msg, args...)) | |
| 122 } | |
| 123 | |
| 124 loop: | |
| 125 for _, s := range items { | |
| 126 // A reserved word? | |
| 127 for _, r := range v.AllowReservedWords { | |
| 128 if s == r { | |
| 129 continue loop | |
| 130 } | |
| 131 } | |
| 132 | |
| 133 // A group reference? | |
| 134 if strings.HasPrefix(s, "group:") { | |
| 135 if !v.AllowGroups { | |
| 136 emitErr("group entries are not allowed - %q", s) | |
| 137 } else { | |
| 138 if s == "group:" { | |
| 139 emitErr("bad group entry %q", s) | |
| 140 } | |
| 141 } | |
| 142 continue | |
| 143 } | |
| 144 | |
| 145 // An identity then. | |
| 146 id, err := identity.MakeIdentity(s) | |
| 147 if err != nil { | |
| 148 emitErr("%s", err) | |
| 149 continue | |
| 150 } | |
| 151 | |
| 152 if v.AllowIDKinds != nil { | |
| 153 allowed := false | |
| 154 for _, k := range v.AllowIDKinds { | |
| 155 if id.Kind() == k { | |
| 156 allowed = true | |
| 157 break | |
| 158 } | |
| 159 } | |
| 160 if !allowed { | |
| 161 emitErr("identity of kind %q is not allowed here - %q", id.Kind(), s) | |
| 162 } | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 return out | |
| 167 } | |
| OLD | NEW |