Skip to content

Commit

Permalink
Add Secret Text credentials
Browse files Browse the repository at this point in the history
Add SSM source validation
Add credentials list validations
  • Loading branch information
julienduchesne committed Apr 7, 2019
1 parent 204c564 commit e5b117e
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 19 deletions.
18 changes: 8 additions & 10 deletions cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,14 @@ var rootCmd = &cobra.Command{
Support Jenkins only for now.`,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
configuration = &sync.Configuration{}
if configurationFile != "" {
var (
err error
fileContent []byte
)
if fileContent, err = ioutil.ReadFile(configurationFile); err != nil {
return err
}
return yaml.Unmarshal(fileContent, configuration)
var (
err error
fileContent []byte
)
if fileContent, err = ioutil.ReadFile(configurationFile); err != nil {
return err
}
return nil
return yaml.Unmarshal(fileContent, configuration)
},
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
Expand All @@ -46,6 +43,7 @@ var rootCmd = &cobra.Command{

func init() {
rootCmd.PersistentFlags().StringVarP(&configurationFile, "config", "c", "", "configuration file")
rootCmd.MarkPersistentFlagRequired("config")
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output")
initListCredentials()
rootCmd.AddCommand(listTargetsCmd, syncCmd, validateCmd)
Expand Down
5 changes: 3 additions & 2 deletions cli/validate.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cli

import (
"os"
log "github.com/sirupsen/logrus"

"github.com/spf13/cobra"
)
Expand All @@ -11,7 +11,8 @@ var validateCmd = &cobra.Command{
Short: "Parses and validates the given configuration",
Run: func(cmd *cobra.Command, args []string) {
if !configuration.Sources.ValidateConfiguration() {
os.Exit(1)
log.Fatal("The config file is invalid")
}
log.Info("The config file is valid!")
},
}
10 changes: 9 additions & 1 deletion credentials/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
// Credentials defines the methods that can be called by all types of credentials
type Credentials interface {
BaseValidate() bool
GetID() string
ToString(bool) string
Validate() bool
}
Expand All @@ -24,7 +25,7 @@ type Base struct {

// BaseToString prints out the credentials fields common to all types of credentials
func (credBase *Base) BaseToString() string {
return fmt.Sprintf("Type: %s, ID: %s, Description: %s", credBase.CredType, credBase.ID, credBase.Description)
return fmt.Sprintf("%s -> Type: %s, Description: %s", credBase.ID, credBase.CredType, credBase.Description)
}

// BaseValidate verifies that the credentials fields common to all types of credentials contain valid values
Expand All @@ -41,6 +42,11 @@ func (credBase *Base) BaseValidate() bool {
return credBase.ID != "" && credBase.Description != "" && credBase.CredType != ""
}

// GetID returns a credentials' ID
func (credBase *Base) GetID() string {
return credBase.ID
}

// 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 @@ -71,6 +77,8 @@ func ParseSingleCredentials(credentialsMap map[string]interface{}) (Credentials,
switch credentialsType {
case "usernamepassword":
credentials = NewUsernamePassword()
case "secret":
credentials = NewSecretText()
default:
return nil, errors.New("Unknown credentials type")
}
Expand Down
31 changes: 31 additions & 0 deletions credentials/credentials_secret.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package credentials

import (
"fmt"
)

type SecretTextCredentials struct {
Base `mapstructure:",squash"`
Secret string
}

func NewSecretText() *SecretTextCredentials {
cred := &SecretTextCredentials{}
cred.CredType = "Secret text"
return cred
}

func (cred *SecretTextCredentials) ToString(showSensitive bool) string {
secretText := "*******"
if showSensitive {
secretText = cred.Secret
}
if cred.Secret == "" {
secretText = "<empty>"
}
return fmt.Sprintf("%s - %s", cred.BaseToString(), secretText)
}

func (cred *SecretTextCredentials) Validate() bool {
return true
}
21 changes: 20 additions & 1 deletion credentials/source_ssm.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
package credentials

type AWSSSMSource struct{}
import (
"regexp"
"strings"

log "github.com/sirupsen/logrus"
)

const ssmPathRegex = `^/(?:[A-Za-z0-9]+/?)+$`

type AWSSSMSource struct {
Path string
}

func (source *AWSSSMSource) Credentials() ([]Credentials, error) {
return []Credentials{}, nil
Expand All @@ -11,5 +22,13 @@ func (source *AWSSSMSource) Type() string {
}

func (source *AWSSSMSource) ValidateConfiguration() bool {
if strings.HasPrefix(source.Path, "/aws") {
log.Errorf("%s should not start with /aws. This path is reserved to AWS", source.Path)
return false
}
if matched, err := regexp.MatchString(ssmPathRegex, source.Path); !matched || err != nil {
log.Errorf("%s does not match the allowed SSM path regex: %s", source.Path, ssmPathRegex)
return false
}
return true
}
32 changes: 27 additions & 5 deletions credentials/sources.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package credentials

import (
"fmt"
"sort"
)

type Source interface {
Credentials() ([]Credentials, error)
Type() string
Expand Down Expand Up @@ -37,13 +42,30 @@ func (sc *SourcesConfiguration) ValidateConfiguration() bool {
}

func (sc *SourcesConfiguration) Credentials() ([]Credentials, error) {
credentials := []Credentials{}
credentialsList := []Credentials{}

// Fetch all credentials
for _, source := range sc.AllSources() {
if newCredentials, err := source.Credentials(); err != nil {
newCredentials, err := source.Credentials()
if err != nil {
return nil, err
} else {
credentials = append(credentials, newCredentials...)
}
credentialsList = append(credentialsList, newCredentials...)
}
return credentials, nil

// Sort credentials by ID
sort.Slice(credentialsList[:], func(i, j int) bool {
return credentialsList[i].GetID() < credentialsList[j].GetID()
})

// Throw an error if IDs are not unique
credentialIds := map[string]bool{}
for _, cred := range credentialsList {
if _, ok := credentialIds[cred.GetID()]; ok {
return nil, fmt.Errorf("There more than one credentials with this ID: %s", cred.GetID())
}
credentialIds[cred.GetID()] = true
}

return credentialsList, nil
}

0 comments on commit e5b117e

Please sign in to comment.