Skip to content

Commit

Permalink
Add tagging logic. Allows syncing creds only on certain targets
Browse files Browse the repository at this point in the history
  • Loading branch information
Julien Duchesne committed Jun 12, 2019
1 parent 4c0834a commit 301f71c
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 1 deletion.
46 changes: 45 additions & 1 deletion credentials/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,24 @@ import (
type Credentials interface {
BaseValidate() bool
GetID() string
ShouldSync(targetTags map[string]string) bool
ToString(bool) string
Validate() bool
}

type targetTagsMatcher struct {
DoMatch map[string]interface{} `mapstructure:"do_match"`
DontMatch map[string]interface{} `mapstructure:"dont_match"`
}

// Base defines that fields that are common to all types of credentials
type Base struct {
ID string
Description string
CredType string
TargetTags targetTagsMatcher `mapstructure:"target_tags"`

// Field set by constructor
CredType string

// For multi-value fields. Such as SSM
Value string
Expand Down Expand Up @@ -50,6 +59,32 @@ func (credBase *Base) GetID() string {
return credBase.ID
}

func (credBase *Base) ShouldSync(targetTags map[string]string) bool {
findMatch := func(match map[string]interface{}) bool {
for key, value := range match {
for tagKey, tag := range targetTags {
if key != tagKey {
continue
}
if valueAsString, ok := value.(string); ok {
if valueAsString == tag {
return true
}
} else if valueAsList, ok := value.([]string); ok {
if listContainsElement(valueAsList, tag) {
return true
}
} else {
log.Warningf("%s ignored. Its value should either be a string or a list of string", key)
}
}
}
return false
}

return !findMatch(credBase.TargetTags.DontMatch) && (len(credBase.TargetTags.DoMatch) == 0 || findMatch(credBase.TargetTags.DoMatch))
}

// ParseCredentials transforms a list of maps into a list of Credentials
// The credentials type is determined by the `type` attribute
func ParseCredentials(credentialsMaps []map[string]interface{}) ([]Credentials, error) {
Expand Down Expand Up @@ -91,3 +126,12 @@ func ParseSingleCredentials(credentialsMap map[string]interface{}) (Credentials,
}
return credentials, nil
}

func listContainsElement(list []string, element string) bool {
for _, listElement := range list {
if listElement == element {
return true
}
}
return false
}
141 changes: 141 additions & 0 deletions credentials/credentials_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package credentials

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestShouldSyncCredentials(t *testing.T) {
t.Parallel()

cases := []struct {
name string
wantedTargetTags targetTagsMatcher
targetTags map[string]string
expected bool
}{
{
name: "No tags",
wantedTargetTags: targetTagsMatcher{
DoMatch: map[string]interface{}{},
DontMatch: map[string]interface{}{},
},
targetTags: map[string]string{},
expected: true,
},
{
name: "No filter",
wantedTargetTags: targetTagsMatcher{
DoMatch: map[string]interface{}{},
DontMatch: map[string]interface{}{},
},
targetTags: map[string]string{
"MyTag": "MyValue",
},
expected: true,
},
{
name: "Match",
wantedTargetTags: targetTagsMatcher{
DoMatch: map[string]interface{}{
"MyFirstTag": "MyValue",
"MyTag": "MyValue",
},
DontMatch: map[string]interface{}{},
},
targetTags: map[string]string{
"MyTag": "MyValue",
},
expected: true,
},
{
name: "Match List Item",
wantedTargetTags: targetTagsMatcher{
DoMatch: map[string]interface{}{
"MyTag": []string{"FirstValue", "MyValue"},
},
DontMatch: map[string]interface{}{},
},
targetTags: map[string]string{
"MyTag": "MyValue",
},
expected: true,
},
{
name: "List Without Matches",
wantedTargetTags: targetTagsMatcher{
DoMatch: map[string]interface{}{
"MyTag": []string{"FirstValue", "SecondValue"},
},
DontMatch: map[string]interface{}{},
},
targetTags: map[string]string{
"MyTag": "MyValue",
},
expected: false,
},
{
name: "String Doesnt Match",
wantedTargetTags: targetTagsMatcher{
DoMatch: map[string]interface{}{
"MyTag": "AValue",
},
DontMatch: map[string]interface{}{},
},
targetTags: map[string]string{
"MyTag": "MyValue",
},
expected: false,
},
{
name: "String that shouldn't match",
wantedTargetTags: targetTagsMatcher{
DoMatch: map[string]interface{}{},
DontMatch: map[string]interface{}{
"MyTag": "MyValue",
},
},
targetTags: map[string]string{
"MyTag": "MyValue",
},
expected: false,
},
{
name: "String in list that shouldn't match",
wantedTargetTags: targetTagsMatcher{
DoMatch: map[string]interface{}{},
DontMatch: map[string]interface{}{
"MyTag": []string{"Test", "MyValue"},
},
},
targetTags: map[string]string{
"MyTag": "MyValue",
},
expected: false,
},
{
name: "Match and exclude",
wantedTargetTags: targetTagsMatcher{
DoMatch: map[string]interface{}{
"MyTag": "Value",
},
DontMatch: map[string]interface{}{
"MyOtherTag": []string{"Test", "MyValue"},
},
},
targetTags: map[string]string{
"MyTag": "Value",
"MyOtherTag": "MyValue",
},
expected: false,
},
}

for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
credentials := &Base{TargetTags: tt.wantedTargetTags}
assert.Equal(t, tt.expected, credentials.ShouldSync(tt.targetTags))
})
}
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/sirupsen/logrus v1.4.1
github.com/spf13/cobra v0.0.3
github.com/spf13/pflag v1.0.3 // indirect
github.com/stretchr/testify v1.2.2
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 // indirect
gopkg.in/yaml.v2 v2.2.2
)
Expand Down
3 changes: 3 additions & 0 deletions sync/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ func syncCredentials(target targets.Target, credentialsList []credentials.Creden
credChannel <- true
go func(cred credentials.Credentials) {
defer func() { <-credChannel }()
if !cred.ShouldSync(target.GetTags()) {
return
}
log.Infof("[%s] Syncing %s", target.GetName(), cred.GetID())
if err := target.UpdateCredentials(cred); err != nil {
message := fmt.Sprintf("Failed to send credential %s to %s: %v", cred.GetID(), target.GetName(), err)
Expand Down
5 changes: 5 additions & 0 deletions targets/targets.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
type Target interface {
BaseValidateConfiguration() bool
GetName() string
GetTags() map[string]string
Initialize([]credentials.Credentials) error
ToString() string
UpdateListOfCredentials([]credentials.Credentials) error
Expand Down Expand Up @@ -40,6 +41,10 @@ func (targetBase *Base) BaseValidateConfiguration() bool {
return true
}

func (targetBase *Base) GetTags() map[string]string {
return targetBase.Tags
}

type Configuration struct {
JenkinsTargets []*JenkinsTarget `mapstructure:"jenkins"`
}
Expand Down

0 comments on commit 301f71c

Please sign in to comment.