feat(loopback): support dynamic credentials (#4982)
This commit is contained in:
1 parent
48038c3c33
commit
85b9adea81
3 files changed
+182
-23
No files matched your search
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package loopback
|
||||
|
||||
const (
|
||||
// ConstDynamicCredentials should be used as a key for an storage bucket attribute, to mock dynamic credentials.
|
||||
ConstDynamicCredentials = "dynamic_credentials"
|
||||
)
|
||||
@@ -145,11 +145,21 @@ func (l *LoopbackStorage) onCreateStorageBucket(ctx context.Context, req *plgpb.
|
||||
return nil, status.Errorf(err.ErrCode, err.ErrMsg)
|
||||
}
|
||||
}
|
||||
secrets := req.GetBucket().GetSecrets()
|
||||
if secrets == nil {
|
||||
secrets = &structpb.Struct{
|
||||
Fields: make(map[string]*structpb.Value),
|
||||
}
|
||||
secrets := &structpb.Struct{
|
||||
Fields: make(map[string]*structpb.Value),
|
||||
}
|
||||
var hasDynamicCreds bool
|
||||
attrs := req.GetBucket().GetAttributes()
|
||||
if attrs != nil {
|
||||
_, hasDynamicCreds = attrs.Fields[ConstDynamicCredentials]
|
||||
}
|
||||
var hasStaticCreds bool
|
||||
if req.GetBucket().GetSecrets() != nil && len(req.GetBucket().GetSecrets().AsMap()) > 0 {
|
||||
hasStaticCreds = true
|
||||
secrets = req.GetBucket().GetSecrets()
|
||||
}
|
||||
if hasDynamicCreds && hasStaticCreds {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "%s: cannot use both dynamic and static credentials", op)
|
||||
}
|
||||
return &plgpb.OnCreateStorageBucketResponse{
|
||||
Persisted: &storagebuckets.StorageBucketPersisted{
|
||||
@@ -179,14 +189,25 @@ func (l *LoopbackStorage) onUpdateStorageBucket(ctx context.Context, req *plgpb.
|
||||
return nil, status.Errorf(err.ErrCode, "%s: %s", op, err.ErrMsg)
|
||||
}
|
||||
}
|
||||
var secrets *structpb.Struct
|
||||
if req.GetNewBucket().GetSecrets() != nil {
|
||||
secrets := &structpb.Struct{
|
||||
Fields: make(map[string]*structpb.Value),
|
||||
}
|
||||
var hasDynamicCreds bool
|
||||
attrs := req.GetNewBucket().GetAttributes()
|
||||
if attrs != nil {
|
||||
_, hasDynamicCreds = attrs.Fields[ConstDynamicCredentials]
|
||||
}
|
||||
var hasStaticCreds bool
|
||||
if req.GetNewBucket().GetSecrets() != nil && len(req.GetNewBucket().GetSecrets().AsMap()) > 0 {
|
||||
hasStaticCreds = true
|
||||
secrets = req.GetNewBucket().GetSecrets()
|
||||
} else if req.GetPersisted().GetData() != nil {
|
||||
secrets = req.GetPersisted().GetData()
|
||||
} else {
|
||||
secrets = &structpb.Struct{
|
||||
Fields: make(map[string]*structpb.Value),
|
||||
}
|
||||
if hasDynamicCreds && hasStaticCreds {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "%s: cannot use both dynamic and static credentials", op)
|
||||
}
|
||||
if !hasDynamicCreds && len(secrets.AsMap()) == 0 {
|
||||
if req.GetPersisted() != nil && req.GetPersisted().GetData() != nil && len(req.GetPersisted().GetData().AsMap()) > 0 {
|
||||
secrets = req.GetPersisted().GetData()
|
||||
}
|
||||
}
|
||||
return &plgpb.OnUpdateStorageBucketResponse{
|
||||
|
||||
@@ -52,9 +52,11 @@ func TestLoopbackOnCreateStorageBucket(t *testing.T) {
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
request *plgpb.OnCreateStorageBucketRequest
|
||||
expectedErr codes.Code
|
||||
name string
|
||||
request *plgpb.OnCreateStorageBucketRequest
|
||||
expectedErr codes.Code
|
||||
expectedStaticCreds bool
|
||||
expectedDynamicCreds bool
|
||||
}{
|
||||
{
|
||||
name: "missing request",
|
||||
@@ -104,6 +106,22 @@ func TestLoopbackOnCreateStorageBucket(t *testing.T) {
|
||||
},
|
||||
expectedErr: codes.PermissionDenied,
|
||||
},
|
||||
{
|
||||
name: "error both credential types",
|
||||
request: &plgpb.OnCreateStorageBucketRequest{
|
||||
Bucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "aws_s3_mock",
|
||||
Secrets: secrets,
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"endpoint": structpb.NewStringValue("0.0.0.0"),
|
||||
ConstDynamicCredentials: structpb.NewBoolValue(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErr: codes.InvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "valid without credentials",
|
||||
request: &plgpb.OnCreateStorageBucketRequest{
|
||||
@@ -116,9 +134,25 @@ func TestLoopbackOnCreateStorageBucket(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedDynamicCreds: true,
|
||||
},
|
||||
{
|
||||
name: "valid with credentials",
|
||||
name: "valid with dynamic credentials",
|
||||
request: &plgpb.OnCreateStorageBucketRequest{
|
||||
Bucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "aws_s3_mock",
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"endpoint": structpb.NewStringValue("0.0.0.0"),
|
||||
ConstDynamicCredentials: structpb.NewBoolValue(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedDynamicCreds: true,
|
||||
},
|
||||
{
|
||||
name: "valid with static credentials",
|
||||
request: &plgpb.OnCreateStorageBucketRequest{
|
||||
Bucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "aws_s3_mock",
|
||||
@@ -130,6 +164,7 @@ func TestLoopbackOnCreateStorageBucket(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStaticCreds: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -148,7 +183,17 @@ func TestLoopbackOnCreateStorageBucket(t *testing.T) {
|
||||
require.NotNil(response)
|
||||
assert.NotNil(response.GetPersisted())
|
||||
assert.NotNil(response.GetPersisted().GetData())
|
||||
assert.EqualValues(tt.request.GetBucket().GetSecrets().AsMap(), response.GetPersisted().GetData().AsMap())
|
||||
|
||||
if tt.expectedDynamicCreds {
|
||||
assert.Empty(response.GetPersisted().GetData().AsMap())
|
||||
return
|
||||
}
|
||||
|
||||
if tt.expectedStaticCreds {
|
||||
assert.NotEmpty(response.GetPersisted().GetData().AsMap())
|
||||
assert.EqualValues(tt.request.GetBucket().GetSecrets().AsMap(), response.GetPersisted().GetData().AsMap())
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -181,9 +226,11 @@ func TestLoopbackOnUpdateStorageBucket(t *testing.T) {
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
request *plgpb.OnUpdateStorageBucketRequest
|
||||
expectedErr codes.Code
|
||||
name string
|
||||
request *plgpb.OnUpdateStorageBucketRequest
|
||||
expectedErr codes.Code
|
||||
expectedStaticCreds bool
|
||||
expectedDynamicCreds bool
|
||||
}{
|
||||
{
|
||||
name: "missing request",
|
||||
@@ -236,7 +283,23 @@ func TestLoopbackOnUpdateStorageBucket(t *testing.T) {
|
||||
expectedErr: codes.PermissionDenied,
|
||||
},
|
||||
{
|
||||
name: "valid without credentials",
|
||||
name: "error both credential types",
|
||||
request: &plgpb.OnUpdateStorageBucketRequest{
|
||||
NewBucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "aws_s3_mock",
|
||||
Secrets: secrets,
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"endpoint": structpb.NewStringValue("0.0.0.0"),
|
||||
ConstDynamicCredentials: structpb.NewBoolValue(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErr: codes.InvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "valid without static credentials",
|
||||
request: &plgpb.OnUpdateStorageBucketRequest{
|
||||
NewBucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "aws_s3_mock",
|
||||
@@ -247,9 +310,25 @@ func TestLoopbackOnUpdateStorageBucket(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedDynamicCreds: true,
|
||||
},
|
||||
{
|
||||
name: "valid with credentials",
|
||||
name: "valid with dynamic credentials attribute",
|
||||
request: &plgpb.OnUpdateStorageBucketRequest{
|
||||
NewBucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "aws_s3_mock",
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"endpoint": structpb.NewStringValue("0.0.0.0"),
|
||||
ConstDynamicCredentials: structpb.NewBoolValue(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedDynamicCreds: true,
|
||||
},
|
||||
{
|
||||
name: "valid with both persisted and static credentials",
|
||||
request: &plgpb.OnUpdateStorageBucketRequest{
|
||||
NewBucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "aws_s3_mock",
|
||||
@@ -262,6 +341,37 @@ func TestLoopbackOnUpdateStorageBucket(t *testing.T) {
|
||||
},
|
||||
Persisted: persisted,
|
||||
},
|
||||
expectedStaticCreds: true,
|
||||
},
|
||||
{
|
||||
name: "valid with only persisted static credentials",
|
||||
request: &plgpb.OnUpdateStorageBucketRequest{
|
||||
NewBucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "aws_s3_mock",
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"endpoint": structpb.NewStringValue("0.0.0.0"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Persisted: persisted,
|
||||
},
|
||||
expectedStaticCreds: true,
|
||||
},
|
||||
{
|
||||
name: "valid with only static credentials",
|
||||
request: &plgpb.OnUpdateStorageBucketRequest{
|
||||
NewBucket: &storagebuckets.StorageBucket{
|
||||
BucketName: "aws_s3_mock",
|
||||
Secrets: secrets,
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"endpoint": structpb.NewStringValue("0.0.0.0"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStaticCreds: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -280,7 +390,26 @@ func TestLoopbackOnUpdateStorageBucket(t *testing.T) {
|
||||
require.NotNil(response)
|
||||
assert.NotNil(response.GetPersisted())
|
||||
assert.NotNil(response.GetPersisted().GetData())
|
||||
assert.EqualValues(tt.request.GetNewBucket().GetSecrets().AsMap(), response.GetPersisted().GetData().AsMap())
|
||||
|
||||
if tt.expectedDynamicCreds {
|
||||
assert.Empty(response.GetPersisted().GetData().AsMap())
|
||||
return
|
||||
}
|
||||
|
||||
if tt.expectedStaticCreds {
|
||||
assert.NotEmpty(response.GetPersisted().GetData().AsMap())
|
||||
newSecrets := tt.request.GetNewBucket().GetSecrets()
|
||||
if newSecrets != nil && len(newSecrets.AsMap()) > 0 {
|
||||
assert.EqualValues(newSecrets.AsMap(), response.GetPersisted().GetData().AsMap())
|
||||
return
|
||||
}
|
||||
oldSecrets := tt.request.GetPersisted().GetData()
|
||||
if oldSecrets != nil && len(oldSecrets.AsMap()) > 0 {
|
||||
assert.EqualValues(oldSecrets.AsMap(), response.GetPersisted().GetData().AsMap())
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user