feat: assume role provider credentials (#28)
* feat: assume role provider credentials This commit updates the awsutil dependency to its latest version, which supports the capability of using dynamic credentials. The storage service was refactored to make static credentials optional. The credentials pkg was refactored to support several attribute fields used for the assume role provider. The host service still enforces the use of static credentials.
This commit is contained in:
1 parent
334732967d
commit
07f015be28
32 files changed
+2351
-998
No files matched your search
@@ -42,7 +42,7 @@ Example policy:
|
||||
```
|
||||
|
||||
* `iam:GetUser`, `iam:CreateAccessKey`, and `iam:DeleteAccessKey`, configured to
|
||||
the IAM user to allow credential rotation. Example policy:
|
||||
the IAM user to allow static credential rotation. Example policy:
|
||||
|
||||
```
|
||||
{
|
||||
@@ -66,18 +66,24 @@ Example policy:
|
||||
The following `attributes` are valid on an AWS storage bucket resource:
|
||||
|
||||
- `disable_credential_rotation` (bool): If `true`, credential rotation will not
|
||||
be performed. See the [Credential Rotation](../../../README.md#credential-rotation) section
|
||||
above.
|
||||
be performed.
|
||||
- `region` (string): The region to configure the storage bucket for.
|
||||
- `endpoint-url` (string): The endpoint to configure the storage.
|
||||
- `role_arn` (string): The role arn configured for the assume role provider.
|
||||
- `role_external_id` (string): The external id configured for the assume role provider.
|
||||
- `role_session_name` (string): The session name configured for the assume role provider.
|
||||
- `role_tags` (object): The key-value pair tags configured for the assume role provider.
|
||||
|
||||
An example of how to utilize the [endpoint attribute](https://docs.aws.amazon.com/vpc/latest/privatelink/vpc-endpoints-s3.html)
|
||||
See the [Credential Rotation](../../../README.md#credential-rotation) readme for more information.
|
||||
|
||||
### Secrets
|
||||
|
||||
The following `secrets` are valid on an AWS storage bucket resource:
|
||||
The following `secrets` are optional on an AWS storage bucket resource:
|
||||
|
||||
- `access_key_id` (string): The access key ID for the IAM user to use with this
|
||||
storage bucket.
|
||||
- `secret_access_key` (string): The secret access key for the IAM user to use
|
||||
with this storage bucket.
|
||||
with this storage bucket.
|
||||
|
||||
See the [Credential Rotation](../../../README.md#credential-rotation) readme for more information.
|
||||
@@ -44,6 +44,14 @@ func getStorageAttributes(in *structpb.Struct) (*StorageAttributes, error) {
|
||||
continue
|
||||
case cred.ConstDisableCredentialRotation:
|
||||
continue
|
||||
case cred.ConstRoleArn:
|
||||
continue
|
||||
case cred.ConstRoleExternalId:
|
||||
continue
|
||||
case cred.ConstRoleSessionName:
|
||||
continue
|
||||
case cred.ConstRoleTags:
|
||||
continue
|
||||
default:
|
||||
badFields[fmt.Sprintf("attributes.%s", s)] = "unrecognized field"
|
||||
}
|
||||
|
||||
@@ -16,29 +16,35 @@ import (
|
||||
func TestGetStorageAttributes(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
in map[string]any
|
||||
in *structpb.Struct
|
||||
expected *StorageAttributes
|
||||
expectedErrContains string
|
||||
}{
|
||||
{
|
||||
name: "missing region",
|
||||
in: map[string]any{},
|
||||
name: "missing region",
|
||||
in: &structpb.Struct{
|
||||
Fields: make(map[string]*structpb.Value),
|
||||
},
|
||||
expectedErrContains: "missing required value \"region\"",
|
||||
},
|
||||
{
|
||||
name: "unknown fields",
|
||||
in: map[string]any{
|
||||
"region": "us-west-2",
|
||||
"foo": true,
|
||||
"bar": true,
|
||||
in: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"region": structpb.NewStringValue("us-west-2"),
|
||||
"foo": structpb.NewBoolValue(true),
|
||||
"bar": structpb.NewBoolValue(true),
|
||||
},
|
||||
},
|
||||
expectedErrContains: "attributes.bar: unrecognized field, attributes.foo: unrecognized field",
|
||||
},
|
||||
{
|
||||
name: "good w/ endpoint",
|
||||
in: map[string]any{
|
||||
cred.ConstRegion: "us-west-2",
|
||||
ConstAwsEndpointUrl: "0.0.0.0",
|
||||
in: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"region": structpb.NewStringValue("us-west-2"),
|
||||
"endpoint_url": structpb.NewStringValue("0.0.0.0"),
|
||||
},
|
||||
},
|
||||
expected: &StorageAttributes{
|
||||
CredentialAttributes: &cred.CredentialAttributes{
|
||||
@@ -49,8 +55,10 @@ func TestGetStorageAttributes(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "good w/o endpoint",
|
||||
in: map[string]any{
|
||||
cred.ConstRegion: "us-west-2",
|
||||
in: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"region": structpb.NewStringValue("us-west-2"),
|
||||
},
|
||||
},
|
||||
expected: &StorageAttributes{
|
||||
CredentialAttributes: &cred.CredentialAttributes{
|
||||
@@ -58,6 +66,35 @@ func TestGetStorageAttributes(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "credential attributes",
|
||||
in: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"region": structpb.NewStringValue("us-west-2"),
|
||||
"disable_credential_rotation": structpb.NewBoolValue(true),
|
||||
"role_arn": structpb.NewStringValue("arn:aws:iam::123456789012:role/S3Access"),
|
||||
"role_external_id": structpb.NewStringValue("1234567890"),
|
||||
"role_session_name": structpb.NewStringValue("test-session"),
|
||||
"role_tags": structpb.NewStructValue(&structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"foo": structpb.NewStringValue("bar"),
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
expected: &StorageAttributes{
|
||||
CredentialAttributes: &cred.CredentialAttributes{
|
||||
Region: "us-west-2",
|
||||
DisableCredentialRotation: true,
|
||||
RoleArn: "arn:aws:iam::123456789012:role/S3Access",
|
||||
RoleExternalId: "1234567890",
|
||||
RoleSessionName: "test-session",
|
||||
RoleTags: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
@@ -65,10 +102,7 @@ func TestGetStorageAttributes(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
input, err := structpb.NewStruct(tc.in)
|
||||
require.NoError(err)
|
||||
|
||||
actual, err := getStorageAttributes(input)
|
||||
actual, err := getStorageAttributes(tc.in)
|
||||
if tc.expectedErrContains != "" {
|
||||
require.Error(err)
|
||||
require.Contains(err.Error(), tc.expectedErrContains)
|
||||
|
||||
@@ -51,11 +51,6 @@ func (p *StoragePlugin) OnCreateStorageBucket(ctx context.Context, req *pb.OnCre
|
||||
return nil, status.Error(codes.InvalidArgument, "bucketName is required")
|
||||
}
|
||||
|
||||
secrets := bucket.GetSecrets()
|
||||
if secrets == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "secrets is required")
|
||||
}
|
||||
|
||||
attrs := bucket.GetAttributes()
|
||||
if attrs == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "attributes is required")
|
||||
@@ -66,7 +61,7 @@ func (p *StoragePlugin) OnCreateStorageBucket(ctx context.Context, req *pb.OnCre
|
||||
return nil, err
|
||||
}
|
||||
|
||||
credConfig, err := cred.GetCredentialsConfig(secrets, storageAttributes.Region)
|
||||
credConfig, err := cred.GetCredentialsConfig(bucket.GetSecrets(), storageAttributes.CredentialAttributes, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -74,24 +69,24 @@ func (p *StoragePlugin) OnCreateStorageBucket(ctx context.Context, req *pb.OnCre
|
||||
// Set up the creds in our persisted state.
|
||||
credState, err := cred.NewAwsCredentialPersistedState(
|
||||
append([]cred.AwsCredentialPersistedStateOption{
|
||||
cred.WithAccessKeyId(credConfig.AccessKey),
|
||||
cred.WithSecretAccessKey(credConfig.SecretKey),
|
||||
cred.WithRegion(credConfig.Region),
|
||||
cred.WithCredentialsConfig(credConfig),
|
||||
}, p.testCredStateOpts...)...,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "error setting up persisted state: %s", err)
|
||||
}
|
||||
|
||||
// Try to rotate the credentials if we're not skipping them.
|
||||
if !storageAttributes.DisableCredentialRotation {
|
||||
if err := credState.RotateCreds(); err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err)
|
||||
}
|
||||
} else {
|
||||
// Simply validate if we aren't rotating.
|
||||
if err := credState.ValidateCreds(); err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "error during credential validation: %s", err)
|
||||
// Try to rotate static credentials
|
||||
if cred.HasStaticCredentials(credState.CredentialsConfig.AccessKey) {
|
||||
if !storageAttributes.DisableCredentialRotation {
|
||||
if err := credState.RotateCreds(); err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err)
|
||||
}
|
||||
} else {
|
||||
// Simply validate if we aren't rotating.
|
||||
if err := credState.ValidateCreds(); err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "error during credential validation: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,39 +155,42 @@ func (p *StoragePlugin) OnUpdateStorageBucket(ctx context.Context, req *pb.OnUpd
|
||||
// is fine and important, but this may change in the future.
|
||||
credState, err := cred.AwsCredentialPersistedStateFromProto(
|
||||
req.GetPersisted().GetData(),
|
||||
append(p.testCredStateOpts, cred.WithRegion(storageAttributes.Region))...)
|
||||
storageAttributes.CredentialAttributes,
|
||||
p.testCredStateOpts...)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "error loading persisted state: %s", err)
|
||||
}
|
||||
|
||||
if storageAttributes.DisableCredentialRotation && !updateSecrets {
|
||||
// This is a validate check to make sure that we aren't disabling
|
||||
// rotation for credentials currently being managed by rotation.
|
||||
// This is not allowed.
|
||||
if !credState.CredsLastRotatedTime.IsZero() {
|
||||
return nil, status.Error(codes.FailedPrecondition, "cannot disable rotation for already-rotated credentials")
|
||||
}
|
||||
}
|
||||
|
||||
if updateSecrets {
|
||||
storageSecrets, err := cred.GetCredentialsConfig(secrets, storageAttributes.Region)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if cred.HasStaticCredentials(credState.CredentialsConfig.AccessKey) {
|
||||
if storageAttributes.DisableCredentialRotation && !updateSecrets {
|
||||
// This is a validate check to make sure that we aren't disabling
|
||||
// rotation for credentials currently being managed by rotation.
|
||||
// This is not allowed.
|
||||
if !credState.CredsLastRotatedTime.IsZero() {
|
||||
return nil, status.Error(codes.FailedPrecondition, "cannot disable rotation for already-rotated credentials")
|
||||
}
|
||||
}
|
||||
|
||||
// Replace the credentials. This checks the timestamp on the last
|
||||
// rotation time as well and deletes the credentials if we are
|
||||
// managing them (ie: if we've rotated them before).
|
||||
if err := credState.ReplaceCreds(storageSecrets.AccessKey, storageSecrets.SecretKey); err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "error attempting to replace credentials: %s", err)
|
||||
}
|
||||
}
|
||||
if updateSecrets {
|
||||
storageSecrets, err := cred.GetCredentialsConfig(secrets, storageAttributes.CredentialAttributes, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !storageAttributes.DisableCredentialRotation && credState.CredsLastRotatedTime.IsZero() {
|
||||
// If we're enabling rotation now but didn't before, or have
|
||||
// freshly replaced credentials, we can rotate here.
|
||||
if err := credState.RotateCreds(); err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err)
|
||||
// Replace the credentials. This checks the timestamp on the last
|
||||
// rotation time as well and deletes the credentials if we are
|
||||
// managing them (ie: if we've rotated them before).
|
||||
if err := credState.ReplaceCreds(storageSecrets); err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "error attempting to replace credentials: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if !storageAttributes.DisableCredentialRotation && credState.CredsLastRotatedTime.IsZero() {
|
||||
// If we're enabling rotation now but didn't before, or have
|
||||
// freshly replaced credentials, we can rotate here.
|
||||
if err := credState.RotateCreds(); err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +243,8 @@ func (p *StoragePlugin) OnDeleteStorageBucket(ctx context.Context, req *pb.OnDel
|
||||
// may be corrupt/and or legitimately missing.
|
||||
credState, err := cred.AwsCredentialPersistedStateFromProto(
|
||||
req.GetPersisted().GetData(),
|
||||
append(p.testCredStateOpts, cred.WithRegion(storageAttributes.Region))...)
|
||||
storageAttributes.CredentialAttributes,
|
||||
p.testCredStateOpts...)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "error loading persisted state: %s", err)
|
||||
}
|
||||
@@ -259,12 +258,15 @@ func (p *StoragePlugin) OnDeleteStorageBucket(ctx context.Context, req *pb.OnDel
|
||||
return nil, status.Errorf(codes.InvalidArgument, "error loading persisted state: %s", err)
|
||||
}
|
||||
|
||||
if !credState.CredsLastRotatedTime.IsZero() {
|
||||
// Delete old/existing credentials. This is done with the same
|
||||
// credentials to ensure that it has the proper permissions to do
|
||||
// it.
|
||||
if err := credState.DeleteCreds(); err != nil {
|
||||
return nil, status.Errorf(codes.Aborted, "error removing rotated credentials during storage bucket deletion: %s", err)
|
||||
// try to delete static credentials
|
||||
if cred.HasStaticCredentials(credState.CredentialsConfig.AccessKey) {
|
||||
if !credState.CredsLastRotatedTime.IsZero() {
|
||||
// Delete old/existing credentials. This is done with the same
|
||||
// credentials to ensure that it has the proper permissions to do
|
||||
// it.
|
||||
if err := credState.DeleteCreds(); err != nil {
|
||||
return nil, status.Errorf(codes.Aborted, "error removing rotated credentials during storage bucket deletion: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,11 +288,6 @@ func (p *StoragePlugin) HeadObject(ctx context.Context, req *pb.HeadObjectReques
|
||||
return nil, status.Error(codes.InvalidArgument, "bucketName is required")
|
||||
}
|
||||
|
||||
secrets := bucket.GetSecrets()
|
||||
if secrets == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "secrets is required")
|
||||
}
|
||||
|
||||
attrs := bucket.GetAttributes()
|
||||
if attrs == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "attributes is required")
|
||||
@@ -301,7 +298,7 @@ func (p *StoragePlugin) HeadObject(ctx context.Context, req *pb.HeadObjectReques
|
||||
return nil, err
|
||||
}
|
||||
|
||||
credConfig, err := cred.GetCredentialsConfig(secrets, storageAttributes.Region)
|
||||
credConfig, err := cred.GetCredentialsConfig(bucket.GetSecrets(), storageAttributes.CredentialAttributes, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -309,9 +306,7 @@ func (p *StoragePlugin) HeadObject(ctx context.Context, req *pb.HeadObjectReques
|
||||
// Set up the creds in our persisted state.
|
||||
credState, err := cred.NewAwsCredentialPersistedState(
|
||||
append([]cred.AwsCredentialPersistedStateOption{
|
||||
cred.WithAccessKeyId(credConfig.AccessKey),
|
||||
cred.WithSecretAccessKey(credConfig.SecretKey),
|
||||
cred.WithRegion(credConfig.Region),
|
||||
cred.WithCredentialsConfig(credConfig),
|
||||
}, p.testCredStateOpts...)...,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -364,11 +359,6 @@ func (p *StoragePlugin) ValidatePermissions(ctx context.Context, req *pb.Validat
|
||||
return nil, status.Error(codes.InvalidArgument, "bucketName is required")
|
||||
}
|
||||
|
||||
secrets := bucket.GetSecrets()
|
||||
if secrets == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "secrets is required")
|
||||
}
|
||||
|
||||
attrs := bucket.GetAttributes()
|
||||
if attrs == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "attributes is required")
|
||||
@@ -379,7 +369,7 @@ func (p *StoragePlugin) ValidatePermissions(ctx context.Context, req *pb.Validat
|
||||
return nil, err
|
||||
}
|
||||
|
||||
credConfig, err := cred.GetCredentialsConfig(secrets, storageAttributes.Region)
|
||||
credConfig, err := cred.GetCredentialsConfig(bucket.GetSecrets(), storageAttributes.CredentialAttributes, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -387,9 +377,7 @@ func (p *StoragePlugin) ValidatePermissions(ctx context.Context, req *pb.Validat
|
||||
// Set up the creds in our persisted state.
|
||||
credState, err := cred.NewAwsCredentialPersistedState(
|
||||
append([]cred.AwsCredentialPersistedStateOption{
|
||||
cred.WithAccessKeyId(credConfig.AccessKey),
|
||||
cred.WithSecretAccessKey(credConfig.SecretKey),
|
||||
cred.WithRegion(credConfig.Region),
|
||||
cred.WithCredentialsConfig(credConfig),
|
||||
}, p.testCredStateOpts...)...,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -429,11 +417,6 @@ func (p *StoragePlugin) GetObject(req *pb.GetObjectRequest, stream pb.StoragePlu
|
||||
return status.Error(codes.InvalidArgument, "bucketName is required")
|
||||
}
|
||||
|
||||
secrets := bucket.GetSecrets()
|
||||
if secrets == nil {
|
||||
return status.Error(codes.InvalidArgument, "secrets is required")
|
||||
}
|
||||
|
||||
attrs := bucket.GetAttributes()
|
||||
if attrs == nil {
|
||||
return status.Error(codes.InvalidArgument, "attributes is required")
|
||||
@@ -444,7 +427,7 @@ func (p *StoragePlugin) GetObject(req *pb.GetObjectRequest, stream pb.StoragePlu
|
||||
return err
|
||||
}
|
||||
|
||||
credConfig, err := cred.GetCredentialsConfig(secrets, storageAttributes.Region)
|
||||
credConfig, err := cred.GetCredentialsConfig(bucket.GetSecrets(), storageAttributes.CredentialAttributes, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -452,9 +435,7 @@ func (p *StoragePlugin) GetObject(req *pb.GetObjectRequest, stream pb.StoragePlu
|
||||
// Set up the creds in our persisted state.
|
||||
credState, err := cred.NewAwsCredentialPersistedState(
|
||||
append([]cred.AwsCredentialPersistedStateOption{
|
||||
cred.WithAccessKeyId(credConfig.AccessKey),
|
||||
cred.WithSecretAccessKey(credConfig.SecretKey),
|
||||
cred.WithRegion(credConfig.Region),
|
||||
cred.WithCredentialsConfig(credConfig),
|
||||
}, p.testCredStateOpts...)...,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -533,10 +514,6 @@ func (p *StoragePlugin) PutObject(ctx context.Context, req *pb.PutObjectRequest)
|
||||
if bucket.GetBucketName() == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "bucketName is required")
|
||||
}
|
||||
secrets := bucket.GetSecrets()
|
||||
if secrets == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "secrets is required")
|
||||
}
|
||||
attrs := bucket.GetAttributes()
|
||||
if attrs == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "attributes is required")
|
||||
@@ -564,7 +541,7 @@ func (p *StoragePlugin) PutObject(ctx context.Context, req *pb.PutObjectRequest)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
credConfig, err := cred.GetCredentialsConfig(secrets, storageAttributes.Region)
|
||||
credConfig, err := cred.GetCredentialsConfig(bucket.GetSecrets(), storageAttributes.CredentialAttributes, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -572,9 +549,7 @@ func (p *StoragePlugin) PutObject(ctx context.Context, req *pb.PutObjectRequest)
|
||||
// Set up the creds in our persisted state.
|
||||
credState, err := cred.NewAwsCredentialPersistedState(
|
||||
append([]cred.AwsCredentialPersistedStateOption{
|
||||
cred.WithAccessKeyId(credConfig.AccessKey),
|
||||
cred.WithSecretAccessKey(credConfig.SecretKey),
|
||||
cred.WithRegion(credConfig.Region),
|
||||
cred.WithCredentialsConfig(credConfig),
|
||||
}, p.testCredStateOpts...)...,
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -34,11 +34,7 @@ func TestStoragePlugin_OnCreateStorageBucket(t *testing.T) {
|
||||
return &pb.OnCreateStorageBucketRequest{
|
||||
Bucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "foo",
|
||||
Secrets: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
}},
|
||||
Secrets: credential.MockStaticCredentialSecrets(),
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstRegion: structpb.NewStringValue("us-west-2"),
|
||||
@@ -49,12 +45,13 @@ func TestStoragePlugin_OnCreateStorageBucket(t *testing.T) {
|
||||
}
|
||||
}
|
||||
cases := []struct {
|
||||
name string
|
||||
req *pb.OnCreateStorageBucketRequest
|
||||
credOpts []credential.AwsCredentialPersistedStateOption
|
||||
storageOpts []awsStoragePersistedStateOption
|
||||
expectedErrContains string
|
||||
expectedErrCode codes.Code
|
||||
name string
|
||||
req *pb.OnCreateStorageBucketRequest
|
||||
credOpts []credential.AwsCredentialPersistedStateOption
|
||||
storageOpts []awsStoragePersistedStateOption
|
||||
expectedErrContains string
|
||||
expectedErrCode codes.Code
|
||||
expectedPersistedData map[string]any
|
||||
}{
|
||||
{
|
||||
name: "nil storage bucket",
|
||||
@@ -70,22 +67,11 @@ func TestStoragePlugin_OnCreateStorageBucket(t *testing.T) {
|
||||
expectedErrContains: "bucketName is required",
|
||||
expectedErrCode: codes.InvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "nil secrets",
|
||||
req: &pb.OnCreateStorageBucketRequest{
|
||||
Bucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "foo",
|
||||
},
|
||||
},
|
||||
expectedErrContains: "secrets is required",
|
||||
expectedErrCode: codes.InvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "nil attributes",
|
||||
req: &pb.OnCreateStorageBucketRequest{
|
||||
Bucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "foo",
|
||||
Secrets: new(structpb.Struct),
|
||||
},
|
||||
},
|
||||
expectedErrContains: "attributes is required",
|
||||
@@ -96,39 +82,17 @@ func TestStoragePlugin_OnCreateStorageBucket(t *testing.T) {
|
||||
req: &pb.OnCreateStorageBucketRequest{
|
||||
Bucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "foo",
|
||||
Secrets: new(structpb.Struct),
|
||||
Attributes: new(structpb.Struct),
|
||||
},
|
||||
},
|
||||
expectedErrContains: "missing required value \"region\"",
|
||||
expectedErrCode: codes.InvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "error reading secrets",
|
||||
req: &pb.OnCreateStorageBucketRequest{
|
||||
Bucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "foo",
|
||||
Secrets: new(structpb.Struct),
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstRegion: structpb.NewStringValue("us-west-2"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrContains: "missing required value \"access_key_id\"",
|
||||
expectedErrCode: codes.InvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "invalid region",
|
||||
req: &pb.OnCreateStorageBucketRequest{
|
||||
Bucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "foo",
|
||||
Secrets: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
}},
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstRegion: structpb.NewStringValue("foobar"),
|
||||
@@ -156,11 +120,7 @@ func TestStoragePlugin_OnCreateStorageBucket(t *testing.T) {
|
||||
req: &pb.OnCreateStorageBucketRequest{
|
||||
Bucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "foo",
|
||||
Secrets: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
}},
|
||||
Secrets: credential.MockStaticCredentialSecrets(),
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstRegion: structpb.NewStringValue("us-west-2"),
|
||||
@@ -301,6 +261,67 @@ func TestStoragePlugin_OnCreateStorageBucket(t *testing.T) {
|
||||
),
|
||||
),
|
||||
},
|
||||
expectedPersistedData: credential.MockStaticCredentialSecrets().AsMap(),
|
||||
},
|
||||
{
|
||||
name: "success rotate static creds",
|
||||
req: &pb.OnCreateStorageBucketRequest{
|
||||
Bucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "foo",
|
||||
Secrets: credential.MockStaticCredentialSecrets(),
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstRegion: structpb.NewStringValue("us-west-2"),
|
||||
credential.ConstDisableCredentialRotation: structpb.NewBoolValue(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
credOpts: []credential.AwsCredentialPersistedStateOption{
|
||||
credential.WithStateTestOpts(
|
||||
[]awsutil.Option{
|
||||
awsutil.WithSTSAPIFunc(
|
||||
awsutil.NewMockSTS(),
|
||||
),
|
||||
awsutil.WithIAMAPIFunc(
|
||||
awsutil.NewMockIAM(
|
||||
awsutil.WithGetUserOutput(
|
||||
&iam.GetUserOutput{
|
||||
User: &iam.User{
|
||||
Arn: aws.String("arn:aws:iam::123456789012:user/JohnDoe"),
|
||||
UserId: aws.String("AIDAJQABLZS4A3QDU576Q"),
|
||||
UserName: aws.String("JohnDoe"),
|
||||
},
|
||||
},
|
||||
),
|
||||
awsutil.WithCreateAccessKeyOutput(
|
||||
&iam.CreateAccessKeyOutput{
|
||||
AccessKey: &iam.AccessKey{
|
||||
AccessKeyId: aws.String("one"),
|
||||
SecretAccessKey: aws.String("two"),
|
||||
UserName: aws.String("JohnDoe"),
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
},
|
||||
storageOpts: []awsStoragePersistedStateOption{
|
||||
withTestS3APIFunc(
|
||||
newTestMockS3(
|
||||
nil,
|
||||
testMockS3WithPutObjectOutput(&s3.PutObjectOutput{}),
|
||||
testMockS3WithGetObjectOutput(&s3.GetObjectOutput{}),
|
||||
testMockS3WithHeadObjectOutput(&s3.HeadObjectOutput{}),
|
||||
),
|
||||
),
|
||||
},
|
||||
expectedPersistedData: map[string]any{
|
||||
credential.ConstAccessKeyId: "one",
|
||||
credential.ConstSecretAccessKey: "two",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -322,12 +343,12 @@ func TestStoragePlugin_OnCreateStorageBucket(t *testing.T) {
|
||||
require.NotNil(resp)
|
||||
require.NotNil(resp.GetPersisted())
|
||||
require.NotNil(resp.GetPersisted().GetData())
|
||||
require.Equal(
|
||||
resp.GetPersisted().GetData().AsMap()[credential.ConstAccessKeyId],
|
||||
tc.req.Bucket.Secrets.AsMap()[credential.ConstAccessKeyId])
|
||||
require.Equal(
|
||||
resp.GetPersisted().GetData().AsMap()[credential.ConstSecretAccessKey],
|
||||
tc.req.Bucket.Secrets.AsMap()[credential.ConstSecretAccessKey])
|
||||
actualPersistedData := resp.GetPersisted().GetData().AsMap()
|
||||
if len(actualPersistedData) != 0 {
|
||||
require.Equal(tc.expectedPersistedData[credential.ConstAccessKeyId], actualPersistedData[credential.ConstAccessKeyId])
|
||||
require.Equal(tc.expectedPersistedData[credential.ConstSecretAccessKey], actualPersistedData[credential.ConstSecretAccessKey])
|
||||
require.NotEmpty(actualPersistedData[credential.ConstCredsLastRotatedTime])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -345,12 +366,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Persisted: &storagebuckets.StorageBucketPersisted{
|
||||
Data: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
},
|
||||
},
|
||||
Data: credential.MockStaticCredentialSecrets(),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -428,7 +444,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
||||
Persisted: &storagebuckets.StorageBucketPersisted{
|
||||
Data: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
credential.ConstCredsLastRotatedTime: structpb.NewStringValue("2006-01-02T15:04:05+07:00"),
|
||||
},
|
||||
@@ -458,7 +474,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
||||
Persisted: &storagebuckets.StorageBucketPersisted{
|
||||
Data: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
credential.ConstCredsLastRotatedTime: structpb.NewStringValue("2006-01-02T15:04:05+07:00"),
|
||||
},
|
||||
@@ -468,31 +484,6 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
||||
expectedErrContains: "cannot disable rotation for already-rotated credentials",
|
||||
expectedErrCode: codes.FailedPrecondition,
|
||||
},
|
||||
{
|
||||
name: "error reading secrets",
|
||||
req: &pb.OnUpdateStorageBucketRequest{
|
||||
NewBucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "foo",
|
||||
Secrets: new(structpb.Struct),
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstRegion: structpb.NewStringValue("us-west-2"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Persisted: &storagebuckets.StorageBucketPersisted{
|
||||
Data: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
credential.ConstCredsLastRotatedTime: structpb.NewStringValue("2006-01-02T15:04:05+07:00"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrContains: "missing required value \"access_key_id\"",
|
||||
expectedErrCode: codes.InvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "updating secrets, replace error",
|
||||
req: &pb.OnUpdateStorageBucketRequest{
|
||||
@@ -500,7 +491,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
||||
BucketName: "foo",
|
||||
Secrets: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("onetwo"),
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_onetwo"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("threefour"),
|
||||
},
|
||||
},
|
||||
@@ -514,7 +505,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
||||
Persisted: &storagebuckets.StorageBucketPersisted{
|
||||
Data: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
credential.ConstCredsLastRotatedTime: structpb.NewStringValue("2006-01-02T15:04:05+07:00"),
|
||||
},
|
||||
@@ -547,7 +538,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
||||
Persisted: &storagebuckets.StorageBucketPersisted{
|
||||
Data: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
},
|
||||
},
|
||||
@@ -671,7 +662,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
||||
Persisted: &storagebuckets.StorageBucketPersisted{
|
||||
Data: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
},
|
||||
},
|
||||
@@ -696,7 +687,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
||||
awsutil.WithCreateAccessKeyOutput(
|
||||
&iam.CreateAccessKeyOutput{
|
||||
AccessKey: &iam.AccessKey{
|
||||
AccessKeyId: aws.String("one"),
|
||||
AccessKeyId: aws.String("AKIA_one"),
|
||||
SecretAccessKey: aws.String("two"),
|
||||
UserName: aws.String("JohnDoe"),
|
||||
},
|
||||
@@ -760,7 +751,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
||||
disableCredentialRotation = value.(bool)
|
||||
}
|
||||
if !disableCredentialRotation {
|
||||
expectedAccessKey = "one"
|
||||
expectedAccessKey = "AKIA_one"
|
||||
expectedSecretKey = "two"
|
||||
}
|
||||
require.Equal(expectedAccessKey, resp.GetPersisted().GetData().AsMap()[credential.ConstAccessKeyId])
|
||||
@@ -807,11 +798,7 @@ func TestStoragePlugin_OnDeleteStorageBucket(t *testing.T) {
|
||||
req: &pb.OnDeleteStorageBucketRequest{
|
||||
Bucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "foo",
|
||||
Secrets: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
}},
|
||||
Secrets: credential.MockStaticCredentialSecrets(),
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstRegion: structpb.NewStringValue("foobar"),
|
||||
@@ -835,7 +822,7 @@ func TestStoragePlugin_OnDeleteStorageBucket(t *testing.T) {
|
||||
Persisted: &storagebuckets.StorageBucketPersisted{
|
||||
Data: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
credential.ConstCredsLastRotatedTime: structpb.NewStringValue("2006-01-02T15:04:05+07:00"),
|
||||
},
|
||||
@@ -863,7 +850,7 @@ func TestStoragePlugin_OnDeleteStorageBucket(t *testing.T) {
|
||||
Persisted: &storagebuckets.StorageBucketPersisted{
|
||||
Data: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
credential.ConstCredsLastRotatedTime: structpb.NewStringValue("2006-01-02T15:04:05+07:00"),
|
||||
},
|
||||
@@ -895,7 +882,7 @@ func TestStoragePlugin_OnDeleteStorageBucket(t *testing.T) {
|
||||
Persisted: &storagebuckets.StorageBucketPersisted{
|
||||
Data: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
credential.ConstCredsLastRotatedTime: structpb.NewStringValue("2006-01-02T15:04:05+07:00"),
|
||||
},
|
||||
@@ -924,6 +911,7 @@ func TestStoragePlugin_OnDeleteStorageBucket(t *testing.T) {
|
||||
}
|
||||
resp, err := p.OnDeleteStorageBucket(context.Background(), tc.req)
|
||||
if tc.expectedErrContains != "" {
|
||||
require.NotNil(err)
|
||||
require.Contains(err.Error(), tc.expectedErrContains)
|
||||
require.Equal(status.Code(err).String(), tc.expectedErrCode.String())
|
||||
return
|
||||
@@ -940,11 +928,7 @@ func TestStoragePlugin_HeadObject(t *testing.T) {
|
||||
Key: "/foo/bar/key",
|
||||
Bucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "foo",
|
||||
Secrets: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
}},
|
||||
Secrets: credential.MockStaticCredentialSecrets(),
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstRegion: structpb.NewStringValue("us-west-2"),
|
||||
@@ -986,17 +970,6 @@ func TestStoragePlugin_HeadObject(t *testing.T) {
|
||||
expectedErrContains: "bucketName is required",
|
||||
expectedErrCode: codes.InvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "nil secrets",
|
||||
req: &pb.HeadObjectRequest{
|
||||
Key: "/foo/bar/key",
|
||||
Bucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "foo",
|
||||
},
|
||||
},
|
||||
expectedErrContains: "secrets is required",
|
||||
expectedErrCode: codes.InvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "nil attributes",
|
||||
req: &pb.HeadObjectRequest{
|
||||
@@ -1140,11 +1113,7 @@ func TestStoragePlugin_ValidatePermissions(t *testing.T) {
|
||||
return &pb.ValidatePermissionsRequest{
|
||||
Bucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "foo",
|
||||
Secrets: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
}},
|
||||
Secrets: credential.MockStaticCredentialSecrets(),
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstRegion: structpb.NewStringValue("us-west-2"),
|
||||
@@ -1175,16 +1144,6 @@ func TestStoragePlugin_ValidatePermissions(t *testing.T) {
|
||||
expectedErrContains: "bucketName is required",
|
||||
expectedErrCode: codes.InvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "nil secrets",
|
||||
req: &pb.ValidatePermissionsRequest{
|
||||
Bucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "foo",
|
||||
},
|
||||
},
|
||||
expectedErrContains: "secrets is required",
|
||||
expectedErrCode: codes.InvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "nil attributes",
|
||||
req: &pb.ValidatePermissionsRequest{
|
||||
@@ -1383,11 +1342,7 @@ func TestStoragePlugin_GetObject(t *testing.T) {
|
||||
Key: "/foo/bar/key",
|
||||
Bucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "foo",
|
||||
Secrets: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
}},
|
||||
Secrets: credential.MockStaticCredentialSecrets(),
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstRegion: structpb.NewStringValue("us-west-2"),
|
||||
@@ -1428,17 +1383,6 @@ func TestStoragePlugin_GetObject(t *testing.T) {
|
||||
expectedErrContains: "bucketName is required",
|
||||
expectedErrCode: codes.InvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "nil secrets",
|
||||
req: &pb.GetObjectRequest{
|
||||
Key: "/foo/bar/key",
|
||||
Bucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "foo",
|
||||
},
|
||||
},
|
||||
expectedErrContains: "secrets is required",
|
||||
expectedErrCode: codes.InvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "nil attributes",
|
||||
req: &pb.GetObjectRequest{
|
||||
@@ -1616,12 +1560,7 @@ func TestStoragePlugin_PutObject(t *testing.T) {
|
||||
return &pb.PutObjectRequest{
|
||||
Bucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "external-obj-store",
|
||||
Secrets: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstAccessKeyId: structpb.NewStringValue("foobar"),
|
||||
credential.ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
||||
},
|
||||
},
|
||||
Secrets: credential.MockStaticCredentialSecrets(),
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
credential.ConstRegion: structpb.NewStringValue("us-west-2"),
|
||||
@@ -1671,17 +1610,6 @@ func TestStoragePlugin_PutObject(t *testing.T) {
|
||||
expectedErrContains: "bucketName is required",
|
||||
expectedErrCode: codes.InvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "missing bucket secrets",
|
||||
request: &pb.PutObjectRequest{
|
||||
Bucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "external-obj-store",
|
||||
},
|
||||
Key: "mock-object",
|
||||
},
|
||||
expectedErrContains: "secrets is required",
|
||||
expectedErrCode: codes.InvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "missing attributes",
|
||||
request: &pb.PutObjectRequest{
|
||||
|
||||
@@ -9,35 +9,78 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/hashicorp/boundary-plugin-aws/internal/credential"
|
||||
"github.com/hashicorp/go-secure-stdlib/awsutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAwsStoragePersistedStateS3Client(t *testing.T) {
|
||||
require := require.New(t)
|
||||
cred, err := credential.NewAwsCredentialPersistedState(
|
||||
credential.WithRegion("us-west-2"),
|
||||
credential.WithAccessKeyId("foobar"),
|
||||
credential.WithSecretAccessKey("barfoo"),
|
||||
)
|
||||
require.NoError(err)
|
||||
cases := []struct {
|
||||
name string
|
||||
creds *awsutil.CredentialsConfig
|
||||
opts []awsStoragePersistedStateOption
|
||||
awsOpts []awsutil.Option
|
||||
s3Opts []s3Option
|
||||
expectedRegion string
|
||||
expectedEndpoint string
|
||||
}{
|
||||
{
|
||||
name: "static credentials",
|
||||
creds: func() *awsutil.CredentialsConfig {
|
||||
creds, err := awsutil.NewCredentialsConfig(
|
||||
awsutil.WithRegion("us-west-2"),
|
||||
awsutil.WithAccessKey("foobar"),
|
||||
awsutil.WithSecretKey("barfoo"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
return creds
|
||||
}(),
|
||||
awsOpts: []awsutil.Option{
|
||||
awsutil.WithIAMAPIFunc(
|
||||
awsutil.NewMockIAM(),
|
||||
),
|
||||
},
|
||||
opts: []awsStoragePersistedStateOption{
|
||||
withTestS3APIFunc(newTestMockS3(nil)),
|
||||
},
|
||||
s3Opts: []s3Option{
|
||||
WithRegion("us-west-2"),
|
||||
WithEndpoint("0.0.0.0"),
|
||||
},
|
||||
expectedRegion: "us-west-2",
|
||||
expectedEndpoint: "0.0.0.0",
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
state, err := newAwsStoragePersistedState(
|
||||
withCredentials(cred),
|
||||
withTestS3APIFunc(newTestMockS3(nil)),
|
||||
)
|
||||
require.NoError(err)
|
||||
cred, err := credential.NewAwsCredentialPersistedState(
|
||||
credential.WithCredentialsConfig(tc.creds),
|
||||
credential.WithStateTestOpts(tc.awsOpts),
|
||||
)
|
||||
require.NoError(err)
|
||||
|
||||
clientRaw, err := state.S3Client(context.Background(), WithRegion("us-west-2"), WithEndpoint("0.0.0.0"))
|
||||
require.NoError(err)
|
||||
require.NotNil(clientRaw)
|
||||
client, ok := clientRaw.(*testMockS3)
|
||||
require.True(ok)
|
||||
require.NotNil(client)
|
||||
require.Equal("us-west-2", client.Region)
|
||||
state, err := newAwsStoragePersistedState(
|
||||
append(tc.opts, withCredentials(cred))...,
|
||||
)
|
||||
require.NoError(err)
|
||||
|
||||
endpoint, err := client.Endpoint.ResolveEndpoint(s3.ServiceID, client.Region)
|
||||
require.NoError(err)
|
||||
require.Equal("0.0.0.0", endpoint.URL)
|
||||
require.Equal("us-west-2", endpoint.SigningRegion)
|
||||
require.Equal("aws", endpoint.PartitionID)
|
||||
clientRaw, err := state.S3Client(context.Background(), tc.s3Opts...)
|
||||
require.NoError(err)
|
||||
require.NotNil(clientRaw)
|
||||
client, ok := clientRaw.(*testMockS3)
|
||||
require.True(ok)
|
||||
require.NotNil(client)
|
||||
require.Equal(tc.expectedRegion, client.Region)
|
||||
|
||||
if tc.expectedEndpoint != "" {
|
||||
endpoint, err := client.Endpoint.ResolveEndpoint(s3.ServiceID, client.Region)
|
||||
require.NoError(err)
|
||||
require.Equal(tc.expectedEndpoint, endpoint.URL)
|
||||
require.Equal(tc.expectedRegion, endpoint.SigningRegion)
|
||||
require.Equal("aws", endpoint.PartitionID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user