From 7229ef1ff9303d48d208edb5da008d1a38c9cd8e Mon Sep 17 00:00:00 2001 From: Julien Duchesne Date: Mon, 29 Jul 2019 19:47:26 -0400 Subject: [PATCH] Add secretsmanager and s3 tests --- cli/root.go | 2 +- credentials/helper_test.go | 50 +++++++++++++ credentials/source_s3_test.go | 44 ++++++++++++ credentials/source_secretsmanager_test.go | 86 +++++++++++++++++++++++ credentials/sources.go | 1 + credentials/sources_test.go | 52 ++------------ sync/config.go | 2 + targets/targets.go | 1 + targets/targets_test.go | 8 +-- 9 files changed, 193 insertions(+), 53 deletions(-) create mode 100644 credentials/helper_test.go diff --git a/cli/root.go b/cli/root.go index 59b8912..5d19c39 100644 --- a/cli/root.go +++ b/cli/root.go @@ -15,8 +15,8 @@ import ( "gopkg.in/yaml.v2" "github.com/coveooss/credentials-sync/credentials" - "github.com/coveooss/credentials-sync/targets" "github.com/coveooss/credentials-sync/sync" + "github.com/coveooss/credentials-sync/targets" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" diff --git a/credentials/helper_test.go b/credentials/helper_test.go new file mode 100644 index 0000000..df5adac --- /dev/null +++ b/credentials/helper_test.go @@ -0,0 +1,50 @@ +package credentials + +const ( + testCredentialsAsMap = `{ + "test": { + "type": "secret", + "description": "test-desc", + "secret": "my secret" + }, + "test2": { + "type": "usernamepassword", + "description": "test2-desc", + "username": "my", + "password": "secret" + } +}` + testCredentialsAsList = `[ + { + "id": "test", + "type": "secret", + "description": "test-desc", + "secret": "my secret" + }, + { + "id": "test2", + "type": "usernamepassword", + "description": "test2-desc", + "username": "my", + "password": "secret" + } +]` +) + +var testCredentials = []Credentials{ + func() (creds *SecretTextCredentials) { + creds = NewSecretText() + creds.ID = "test" + creds.Secret = "my secret" + creds.Description = "test-desc" + return + }(), + func() (creds *UsernamePasswordCredentials) { + creds = NewUsernamePassword() + creds.ID = "test2" + creds.Username = "my" + creds.Password = "secret" + creds.Description = "test2-desc" + return + }(), +} diff --git a/credentials/source_s3_test.go b/credentials/source_s3_test.go index 41576d2..1e7e1a9 100644 --- a/credentials/source_s3_test.go +++ b/credentials/source_s3_test.go @@ -2,11 +2,21 @@ package credentials import ( "fmt" + "io/ioutil" + "strings" "testing" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/aws/aws-sdk-go/service/s3/s3iface" + "github.com/stretchr/testify/assert" ) +const ( + s3Bucket = "my-bucket" + s3Key = "a/key" +) + func TestS3SourceValidate(t *testing.T) { t.Parallel() @@ -39,3 +49,37 @@ func TestS3SourceValidate(t *testing.T) { }) } } + +type mockS3Client struct { + s3iface.S3API + t *testing.T +} + +func (m *mockS3Client) GetObject(input *s3.GetObjectInput) (*s3.GetObjectOutput, error) { + assert.Equal(m.t, s3Bucket, *input.Bucket) + assert.Equal(m.t, s3Key, *input.Key) + + return &s3.GetObjectOutput{Body: ioutil.NopCloser(strings.NewReader(`test_cred: + type: usernamepassword + description: a credential + username: user + password: pass`))}, nil +} + +func TestGetCredentialsFromS3Source(t *testing.T) { + s3Source := &AWSS3Source{ + Bucket: s3Bucket, + Key: s3Key, + client: &mockS3Client{t: t}, + } + + credentials, err := s3Source.Credentials() + + expectedCred := NewUsernamePassword() + expectedCred.ID = "test_cred" + expectedCred.Description = "a credential" + expectedCred.Username = "user" + expectedCred.Password = "pass" + assert.Nil(t, err) + assert.Equal(t, []Credentials{expectedCred}, credentials) +} diff --git a/credentials/source_secretsmanager_test.go b/credentials/source_secretsmanager_test.go index 198d6ee..28a6248 100644 --- a/credentials/source_secretsmanager_test.go +++ b/credentials/source_secretsmanager_test.go @@ -2,11 +2,35 @@ package credentials import ( "fmt" + "sort" "testing" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/secretsmanager" + "github.com/aws/aws-sdk-go/service/secretsmanager/secretsmanageriface" + "github.com/stretchr/testify/assert" ) +const ( + prefix = "the_prefix" + firstSecretName = "the_prefix/id1" + firstSecretARN = "arn:aws:secretsmanager:us-east-1:123456789012:secret:the_prefix/id1-123456" + secondSecretName = "the_prefix/id2" + secondSecretARN = "arn:aws:secretsmanager:us-east-1:123456789012:secret:the_prefix/id2-123456" + thirdSecretName = "id3" + thirdSecretARN = "arn:aws:secretsmanager:us-east-1:123456789012:secret:id3-123456" +) + +var expectedSecretsManagerCredentials = func() []Credentials { + expectedCred := NewUsernamePassword() + expectedCred.ID = "test3" + expectedCred.Description = "a credential" + expectedCred.Username = "user" + expectedCred.Password = "pass" + return append(testCredentials, expectedCred) +}() + func TestSecretsManagerSourceValidate(t *testing.T) { t.Parallel() @@ -44,3 +68,65 @@ func TestSecretsManagerSourceValidate(t *testing.T) { }) } } + +type mockSecretsManagerClient struct { + secretsmanageriface.SecretsManagerAPI + t *testing.T +} + +func (m *mockSecretsManagerClient) ListSecretsPages(input *secretsmanager.ListSecretsInput, theFunc func(*secretsmanager.ListSecretsOutput, bool) bool) error { + theFunc(&secretsmanager.ListSecretsOutput{SecretList: []*secretsmanager.SecretListEntry{ + { + ARN: aws.String(firstSecretARN), + Name: aws.String(firstSecretName), + }, + { + ARN: aws.String(secondSecretARN), + Name: aws.String(secondSecretName), + }, + { + ARN: aws.String(thirdSecretARN), + Name: aws.String(thirdSecretName), + }, + }}, true) + return nil +} + +func (m *mockSecretsManagerClient) GetSecretValue(input *secretsmanager.GetSecretValueInput) (*secretsmanager.GetSecretValueOutput, error) { + if *input.SecretId == firstSecretARN { + return &secretsmanager.GetSecretValueOutput{SecretString: aws.String(testCredentialsAsMap)}, nil + } else if *input.SecretId == secondSecretARN { + return &secretsmanager.GetSecretValueOutput{SecretString: aws.String(`test3: + type: usernamepassword + description: a credential + username: user + password: pass`)}, nil + } + return nil, fmt.Errorf("Only first and second are valid") +} + +func TestGetCredentialsFromSecretsManagerSourceWithPrefix(t *testing.T) { + secretsManagerSource := &AWSSecretsManagerSource{ + SecretPrefix: prefix, + client: &mockSecretsManagerClient{t: t}, + } + + credentials, err := secretsManagerSource.Credentials() + sort.Slice(credentials, func(i, j int) bool { return credentials[i].GetID() < credentials[j].GetID() }) + + assert.Nil(t, err) + assert.Equal(t, expectedSecretsManagerCredentials, credentials) +} + +func TestGetCredentialsFromSecretsManagerSourceWithID(t *testing.T) { + secretsManagerSource := &AWSSecretsManagerSource{ + SecretID: firstSecretARN, + client: &mockSecretsManagerClient{t: t}, + } + + credentials, err := secretsManagerSource.Credentials() + sort.Slice(credentials, func(i, j int) bool { return credentials[i].GetID() < credentials[j].GetID() }) + + assert.Nil(t, err) + assert.Equal(t, testCredentials, credentials) +} diff --git a/credentials/sources.go b/credentials/sources.go index 2a6e842..20a09f2 100644 --- a/credentials/sources.go +++ b/credentials/sources.go @@ -25,6 +25,7 @@ type SourcesConfiguration struct { credentialsList []Credentials } +// SourceCollection represents a collection of sources from which credentials can be fetched type SourceCollection interface { AllSources() []Source Credentials() ([]Credentials, error) diff --git a/credentials/sources_test.go b/credentials/sources_test.go index f7c9ce9..c6b1b00 100644 --- a/credentials/sources_test.go +++ b/credentials/sources_test.go @@ -12,24 +12,6 @@ import ( "github.com/stretchr/testify/assert" ) -var testCredentials = []Credentials{ - func() (creds *SecretTextCredentials) { - creds = NewSecretText() - creds.ID = "test" - creds.Secret = "my secret" - creds.Description = "test-desc" - return - }(), - func() (creds *UsernamePasswordCredentials) { - creds = NewUsernamePassword() - creds.ID = "test2" - creds.Username = "my" - creds.Password = "secret" - creds.Description = "test2-desc" - return - }(), -} - func TestSourcesConfigWithLocalSource(t *testing.T) { tempDir, err := ioutil.TempDir("", "") if err != nil { @@ -65,40 +47,14 @@ func TestGetCredentialsFromBytes(t *testing.T) { wantErr: false, }, { - name: "List", - bytes: []byte(`[ - { - "id": "test", - "type": "secret", - "description": "test-desc", - "secret": "my secret" - }, - { - "id": "test2", - "type": "usernamepassword", - "description": "test2-desc", - "username": "my", - "password": "secret" - } - ]`), + name: "List", + bytes: []byte(testCredentialsAsList), result: testCredentials, wantErr: false, }, { - name: "Map", - bytes: []byte(`{ - "test": { - "type": "secret", - "description": "test-desc", - "secret": "my secret" - }, - "test2": { - "type": "usernamepassword", - "description": "test2-desc", - "username": "my", - "password": "secret" - } - }`), + name: "Map", + bytes: []byte(testCredentialsAsMap), result: testCredentials, wantErr: false, }, diff --git a/sync/config.go b/sync/config.go index ce23584..c8a2516 100644 --- a/sync/config.go +++ b/sync/config.go @@ -23,10 +23,12 @@ func NewConfiguration() *Configuration { } } +// SetSources sets the source configuration on synchronization configuration func (config *Configuration) SetSources(sources credentials.SourceCollection) { config.Sources = sources } +// SetTargets sets the target configuration on synchronization configuration func (config *Configuration) SetTargets(targets targets.TargetCollection) { config.Targets = targets } diff --git a/targets/targets.go b/targets/targets.go index 3d8cd5d..03459e7 100644 --- a/targets/targets.go +++ b/targets/targets.go @@ -84,6 +84,7 @@ func HasCredential(target Target, id string) bool { return false } +// TargetCollection represents a collection of targets to which credentials can be synced type TargetCollection interface { AllTargets() []Target ValidateConfiguration() error diff --git a/targets/targets_test.go b/targets/targets_test.go index fa6143f..cae9bb1 100644 --- a/targets/targets_test.go +++ b/targets/targets_test.go @@ -22,7 +22,7 @@ func TestConfigValidateConfiguration(t *testing.T) { { name: "valid target", targets: []*JenkinsTarget{ - &JenkinsTarget{ + { Base: Base{Name: "test"}, URL: "https://test.com", }, @@ -32,7 +32,7 @@ func TestConfigValidateConfiguration(t *testing.T) { { name: "no name", targets: []*JenkinsTarget{ - &JenkinsTarget{ + { Base: Base{Name: ""}, URL: "https://test.com", }, @@ -42,7 +42,7 @@ func TestConfigValidateConfiguration(t *testing.T) { { name: "two actions for unsynced credentials", targets: []*JenkinsTarget{ - &JenkinsTarget{ + { Base: Base{Name: "test", DeleteUnsynced: true, TagUnsynced: true}, URL: "https://test.com", }, @@ -52,7 +52,7 @@ func TestConfigValidateConfiguration(t *testing.T) { { name: "bad url (validated by Jenkins)", targets: []*JenkinsTarget{ - &JenkinsTarget{ + { Base: Base{Name: "test"}, URL: "bad", },