github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/utils/aws/identifiers_test.go (about) 1 /* 2 Copyright 2022 Gravitational, Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package aws 18 19 import ( 20 "strings" 21 "testing" 22 23 "github.com/gravitational/trace" 24 "github.com/stretchr/testify/require" 25 ) 26 27 func isBadParamErrFn(t require.TestingT, err error, i ...any) { 28 require.True(t, trace.IsBadParameter(err), "expected bad parameter, got %v", err) 29 } 30 31 func TestIsValidAccountID(t *testing.T) { 32 for _, tt := range []struct { 33 name string 34 accountID string 35 errCheck require.ErrorAssertionFunc 36 }{ 37 { 38 name: "valid account id", 39 accountID: "123456789012", 40 errCheck: require.NoError, 41 }, 42 { 43 name: "empty", 44 accountID: "", 45 errCheck: isBadParamErrFn, 46 }, 47 { 48 name: "less digits", 49 accountID: "12345678901", 50 errCheck: isBadParamErrFn, 51 }, 52 { 53 name: "more digits", 54 accountID: "1234567890123", 55 errCheck: isBadParamErrFn, 56 }, 57 { 58 name: "invalid chars", 59 accountID: "12345678901A", 60 errCheck: isBadParamErrFn, 61 }, 62 { 63 name: "invalid chars with emojis", 64 accountID: "12345678901✅", 65 errCheck: isBadParamErrFn, 66 }, 67 { 68 name: "unicode digit is invalid", 69 accountID: "123456789৩", // ৩ is a valid unicode digit and its len("৩") is 3 70 errCheck: isBadParamErrFn, 71 }, 72 } { 73 t.Run(tt.name, func(t *testing.T) { 74 tt.errCheck(t, IsValidAccountID(tt.accountID)) 75 }) 76 } 77 } 78 79 func TestIsValidIAMRoleName(t *testing.T) { 80 for _, tt := range []struct { 81 name string 82 role string 83 errCheck require.ErrorAssertionFunc 84 }{ 85 { 86 name: "valid", 87 role: "valid", 88 errCheck: require.NoError, 89 }, 90 { 91 name: "valid with numbers", 92 role: "00VALID11", 93 errCheck: require.NoError, 94 }, 95 { 96 name: "only one symbol", 97 role: "_", 98 errCheck: require.NoError, 99 }, 100 { 101 name: "all symbols", 102 role: "Test+1=2,3.4@5-6_7", 103 errCheck: require.NoError, 104 }, 105 { 106 name: "empty", 107 role: "", 108 errCheck: isBadParamErrFn, 109 }, 110 { 111 name: "too large", 112 role: strings.Repeat("r", 65), 113 errCheck: isBadParamErrFn, 114 }, 115 { 116 name: "invalid symbols", 117 role: "role/admin", 118 errCheck: isBadParamErrFn, 119 }, 120 } { 121 t.Run(tt.name, func(t *testing.T) { 122 tt.errCheck(t, IsValidIAMRoleName(tt.role)) 123 }) 124 } 125 } 126 127 func TestIsValidIAMPolicyName(t *testing.T) { 128 for _, tt := range []struct { 129 name string 130 policy string 131 errCheck require.ErrorAssertionFunc 132 }{ 133 { 134 name: "valid", 135 policy: "valid", 136 errCheck: require.NoError, 137 }, 138 { 139 name: "valid with numbers", 140 policy: "00VALID11", 141 errCheck: require.NoError, 142 }, 143 { 144 name: "only one symbol", 145 policy: "_", 146 errCheck: require.NoError, 147 }, 148 { 149 name: "all symbols", 150 policy: "Test+1=2,3.4@5-6_7", 151 errCheck: require.NoError, 152 }, 153 { 154 name: "empty", 155 policy: "", 156 errCheck: isBadParamErrFn, 157 }, 158 { 159 name: "too large", 160 policy: strings.Repeat("p", 129), 161 errCheck: isBadParamErrFn, 162 }, 163 { 164 name: "invalid symbols", 165 policy: "policy/admin", 166 errCheck: isBadParamErrFn, 167 }, 168 } { 169 t.Run(tt.name, func(t *testing.T) { 170 tt.errCheck(t, IsValidIAMPolicyName(tt.policy)) 171 }) 172 } 173 } 174 175 func TestIsValidRegion(t *testing.T) { 176 for _, tt := range []struct { 177 name string 178 region string 179 errCheck require.ErrorAssertionFunc 180 }{ 181 { 182 name: "us region", 183 region: "us-east-1", 184 errCheck: require.NoError, 185 }, 186 { 187 name: "eu region", 188 region: "eu-west-1", 189 errCheck: require.NoError, 190 }, 191 { 192 name: "us gov", 193 region: "us-gov-east-1", 194 errCheck: require.NoError, 195 }, 196 { 197 name: "valid format", 198 region: "xx-iso-somewhere-100", 199 errCheck: require.NoError, 200 }, 201 { 202 name: "empty", 203 region: "", 204 errCheck: isBadParamErrFn, 205 }, 206 { 207 name: "symbols", 208 region: "us@east-1", 209 errCheck: isBadParamErrFn, 210 }, 211 { 212 name: "invalid country code", 213 region: "xxx-east-1", 214 errCheck: isBadParamErrFn, 215 }, 216 } { 217 t.Run(tt.name, func(t *testing.T) { 218 tt.errCheck(t, IsValidRegion(tt.region)) 219 }) 220 } 221 } 222 223 func TestCheckRoleARN(t *testing.T) { 224 for _, tt := range []struct { 225 name string 226 arn string 227 errCheck require.ErrorAssertionFunc 228 }{ 229 { 230 name: "valid", 231 arn: "arn:aws:iam:us-west-2:123456789012:role/foo/bar", 232 errCheck: require.NoError, 233 }, 234 { 235 name: "empty string", 236 arn: "", 237 errCheck: isBadParamErrFn, 238 }, 239 { 240 name: "arn identifier but no other section", 241 arn: "arn:nil", 242 errCheck: isBadParamErrFn, 243 }, 244 { 245 name: "valid with resource that has spaces", 246 arn: "arn:aws:iam:us-west-2:123456789012:role/foo bar", 247 errCheck: require.NoError, 248 }, 249 { 250 name: "valid when resource section has :", 251 arn: "arn:aws:iam:us-west-2:123456789012:role/foo bar:a", 252 errCheck: require.NoError, 253 }, 254 { 255 name: "invalid when resource is missing", 256 arn: "arn:aws:iam:us-west-2:123456789012", 257 errCheck: isBadParamErrFn, 258 }, 259 { 260 name: "valid even if region is missing", 261 arn: "arn:aws:iam::123456789012:role/foo bar", 262 errCheck: require.NoError, 263 }, 264 { 265 name: "invalid when the resource is not role", 266 arn: "arn:aws:iam::123456789012:user/foo bar", 267 errCheck: isBadParamErrFn, 268 }, 269 { 270 name: "invalid when the resource is of type role, but role name section is missing", 271 arn: "arn:aws:iam::123456789012:role", 272 errCheck: isBadParamErrFn, 273 }, 274 { 275 name: "invalid when the resource is of type role, but role is empty", 276 arn: "arn:aws:iam::123456789012:role/", 277 errCheck: isBadParamErrFn, 278 }, 279 } { 280 t.Run(tt.name, func(t *testing.T) { 281 tt.errCheck(t, CheckRoleARN(tt.arn)) 282 }) 283 } 284 } 285 286 func TestIsValidPartition(t *testing.T) { 287 for _, tt := range []struct { 288 name string 289 partition string 290 errCheck require.ErrorAssertionFunc 291 }{ 292 { 293 name: "aws", 294 partition: "aws", 295 errCheck: require.NoError, 296 }, 297 { 298 name: "china", 299 partition: "aws-cn", 300 errCheck: require.NoError, 301 }, 302 { 303 name: "govcloud", 304 partition: "aws-us-gov", 305 errCheck: require.NoError, 306 }, 307 } { 308 t.Run(tt.name, func(t *testing.T) { 309 tt.errCheck(t, IsValidPartition(tt.partition)) 310 }) 311 } 312 } 313 314 func TestIsValidAthenaWorkgroupName(t *testing.T) { 315 for _, tt := range []struct { 316 name string 317 workgroup string 318 errCheck require.ErrorAssertionFunc 319 }{ 320 { 321 name: "valid", 322 workgroup: "test-Workgroup-123_456", 323 errCheck: require.NoError, 324 }, 325 { 326 name: "empty", 327 workgroup: "", 328 errCheck: isBadParamErrFn, 329 }, 330 { 331 name: "too long", 332 workgroup: strings.Repeat("w", 129), 333 errCheck: isBadParamErrFn, 334 }, 335 { 336 name: "symbols", 337 workgroup: "!bad%workgroup", 338 errCheck: isBadParamErrFn, 339 }, 340 } { 341 t.Run(tt.name, func(t *testing.T) { 342 tt.errCheck(t, IsValidAthenaWorkgroupName(tt.workgroup)) 343 }) 344 } 345 } 346 347 func TestIsValidGlueResourceName(t *testing.T) { 348 for _, tt := range []struct { 349 name string 350 resourceName string 351 errCheck require.ErrorAssertionFunc 352 }{ 353 { 354 name: "valid", 355 resourceName: "test_database_123_456", 356 errCheck: require.NoError, 357 }, 358 { 359 name: "empty", 360 resourceName: "", 361 errCheck: isBadParamErrFn, 362 }, 363 { 364 name: "too long", 365 resourceName: strings.Repeat("g", 256), 366 errCheck: isBadParamErrFn, 367 }, 368 { 369 name: "hyphen", 370 resourceName: "bad-table", 371 errCheck: isBadParamErrFn, 372 }, 373 { 374 name: "capital", 375 resourceName: "bad-Table", 376 errCheck: isBadParamErrFn, 377 }, 378 { 379 name: "symbols", 380 resourceName: "!bad%table", 381 errCheck: isBadParamErrFn, 382 }, 383 } { 384 t.Run(tt.name, func(t *testing.T) { 385 tt.errCheck(t, IsValidGlueResourceName(tt.resourceName)) 386 }) 387 } 388 }