go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/tokenserver/appengine/impl/serviceaccounts/config_validation_test.go (about) 1 // Copyright 2020 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (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 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package serviceaccounts 16 17 import ( 18 "context" 19 "testing" 20 21 "google.golang.org/protobuf/encoding/prototext" 22 23 "go.chromium.org/luci/config/validation" 24 25 "go.chromium.org/luci/tokenserver/api/admin/v1" 26 "go.chromium.org/luci/tokenserver/appengine/impl/utils/policy" 27 28 . "github.com/smartystreets/goconvey/convey" 29 . "go.chromium.org/luci/common/testing/assertions" 30 ) 31 32 func TestValidation(t *testing.T) { 33 t.Parallel() 34 35 cases := []struct { 36 Cfg string 37 Errors []string 38 }{ 39 { 40 // Non-trivial good config. 41 Cfg: ` 42 mapping { 43 project: "proj1" 44 service_account: "exclusive-1@example.com" 45 service_account: "exclusive-2@example.com" 46 } 47 48 mapping { 49 project: "proj1" 50 project: "proj2" 51 service_account: "shared-1@example.com" 52 service_account: "shared-2@example.com" 53 } 54 55 mapping { 56 project: "proj3" 57 service_account: "exclusive-3@example.com" 58 } 59 60 mapping { 61 project: "@internal" 62 service_account: "exclusive-4@example.com" 63 } 64 65 use_project_scoped_account: "proj4" 66 use_project_scoped_account: "proj5" 67 `, 68 }, 69 70 // Minimal config. 71 { 72 Cfg: ``, 73 }, 74 75 // Empty list of project is not OK. 76 { 77 Cfg: ` 78 mapping { 79 service_account: "sa1@example.com" 80 } 81 `, 82 Errors: []string{"at least one project must be given"}, 83 }, 84 85 // Empty list of accounts is not OK. 86 { 87 Cfg: ` 88 mapping { 89 project: "proj" 90 } 91 `, 92 Errors: []string{"at least one service account must be given"}, 93 }, 94 95 // Bad project names. 96 { 97 Cfg: ` 98 mapping { 99 project: "" 100 project: " " 101 service_account: "sa@example.com" 102 } 103 `, 104 Errors: []string{ 105 `bad project ""`, 106 `bad project " "`, 107 }, 108 }, 109 110 // Bad service account names. 111 { 112 Cfg: ` 113 mapping { 114 project: "proj" 115 service_account: "" 116 service_account: "not-email" 117 } 118 `, 119 Errors: []string{ 120 `bad service_account ""`, 121 `bad service_account "not-email"`, 122 }, 123 }, 124 125 // Multiple mappings with the same account. 126 { 127 Cfg: ` 128 mapping { 129 project: "proj1" 130 service_account: "sa@example.com" 131 } 132 mapping { 133 project: "proj2" 134 service_account: "sa@example.com" 135 } 136 `, 137 Errors: []string{ 138 `service_account "sa@example.com" appears in more that one mapping`, 139 }, 140 }, 141 142 // Bad use_project_scoped_account. 143 { 144 Cfg: ` 145 use_project_scoped_account: " " 146 `, 147 Errors: []string{ 148 `bad project in use_project_scoped_account #1 " "`, 149 }, 150 }, 151 152 // Mapping for a project that uses scoped accounts. 153 { 154 Cfg: ` 155 mapping { 156 project: "proj" 157 service_account: "sa@example.com" 158 } 159 use_project_scoped_account: "proj" 160 `, 161 Errors: []string{ 162 `project "proj" is in use_project_scoped_account list, but also has mapping entries`, 163 }, 164 }, 165 } 166 167 Convey("Validation works", t, func(c C) { 168 for idx, cs := range cases { 169 c.Printf("Case #%d\n", idx) 170 171 cfg := &admin.ServiceAccountsProjectMapping{} 172 err := prototext.Unmarshal([]byte(cs.Cfg), cfg) 173 So(err, ShouldBeNil) 174 175 ctx := &validation.Context{Context: context.Background()} 176 validateConfigBundle(ctx, policy.ConfigBundle{configFileName: cfg}) 177 verr := ctx.Finalize() 178 179 if len(cs.Errors) == 0 { 180 So(verr, ShouldBeNil) 181 } else { 182 verr := verr.(*validation.Error) 183 So(verr.Errors, ShouldHaveLength, len(cs.Errors)) 184 for i, err := range verr.Errors { 185 So(err, ShouldErrLike, cs.Errors[i]) 186 } 187 } 188 } 189 }) 190 }