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  }