Skip to content

Commit

Permalink
Refactoring.
Browse files Browse the repository at this point in the history
  • Loading branch information
Otávio Fernandes committed Mar 29, 2019
1 parent c1979a8 commit ba4e721
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 81 deletions.
17 changes: 10 additions & 7 deletions pkg/vault-handler/config.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package vaulthandler

import (
"errors"
"fmt"
)

// Config object for vault-handler.
type Config struct {
DryRun bool // dry-run flag
OutputDir string // output directory path
InputDir string // input directory, when uploading
VaultAddr string // vault api endpoint
VaultToken string // vault token
VaultRoleID string // vault approle role-id
Expand All @@ -18,18 +18,21 @@ type Config struct {
// Validate configuration object.
func (c *Config) Validate() error {
if c.VaultAddr == "" {
return errors.New("vault-addr is not informed")
return fmt.Errorf("vault-addr is not informed")
}
if c.VaultToken == "" && c.VaultRoleID == "" && c.VaultSecretID == "" {
return errors.New("inform vault-token, or vault-role-id and secret-id")
return fmt.Errorf("inform vault-token, or vault-role-id and secret-id")
}
if c.VaultToken != "" && (c.VaultRoleID != "" || c.VaultSecretID != "") {
return errors.New("vault-token can't be used in combination with role-id or secret-id")
return fmt.Errorf("vault-token can't be used in combination with role-id or secret-id")
}
if c.OutputDir == "" {
return errors.New("output-dir is not informed")
if c.InputDir == "" && c.OutputDir == "" {
return fmt.Errorf("both input-dir and output-dir are empty")
}
if !isDir(c.OutputDir) {
if c.InputDir != "" && !isDir(c.InputDir) {
return fmt.Errorf("input-dir '%s' is not found", c.InputDir)
}
if c.OutputDir != "" && !isDir(c.OutputDir) {
return fmt.Errorf("output-dir '%s' is not found", c.OutputDir)
}

Expand Down
12 changes: 12 additions & 0 deletions pkg/vault-handler/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func (f *File) Zip() error {
var buffer bytes.Buffer
var err error

originalPayloadLen := len(f.payload)
gz := gzip.NewWriter(&buffer)

if _, err = gz.Write(f.payload); err != nil {
Expand All @@ -35,6 +36,8 @@ func (f *File) Zip() error {
return err
}

log.Printf("[File] Zipping payload, before and after: '%d'/'%d' bytes ",
originalPayloadLen, len(f.payload))
f.payload = buffer.Bytes()
return nil
}
Expand All @@ -45,6 +48,7 @@ func (f *File) Unzip() error {
var bufferOut bytes.Buffer
var err error

originalPayloadLen := len(f.payload)
bufferIn := bytes.NewBuffer(f.payload)
if reader, err = gzip.NewReader(bufferIn); err != nil {
return err
Expand All @@ -53,6 +57,8 @@ func (f *File) Unzip() error {
return err
}

log.Printf("[File] Unzipping payload, before and after: '%d'/'%d' bytes ",
originalPayloadLen, len(f.payload))
f.payload = bufferOut.Bytes()
return nil
}
Expand All @@ -75,9 +81,15 @@ func (f *File) Read(baseDir string) error {

// Write contents to file-system.
func (f *File) Write(baseDir string) error {
log.Printf("[File] Writting '%d' bytes on '%s'", len(f.payload), f.fileName())
return ioutil.WriteFile(f.FilePath(baseDir), f.payload, 0600)
}

// Name exposes the file name from properties.
func (f *File) Name() string {
return f.properties.Name
}

// fileName compose file name based on group and SecretData settings.
func (f *File) fileName() string {
return fmt.Sprintf("%s.%s.%s", f.group, f.properties.Name, f.properties.Extension)
Expand Down
73 changes: 35 additions & 38 deletions pkg/vault-handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,6 @@ type Handler struct {
vault *Vault // vault api instance
}

// persist a slice of bytes to file-system.
func (h *Handler) persist(group string, data *SecretData, payload []byte) error {
var err error

file := NewFile(group, data, payload)

if data.Unzip {
log.Print("[Handler] Extracting ZIP payload.")
if err = file.Unzip(); err != nil {
return err
}
}

if h.config.DryRun {
log.Printf("[DRY-RUN] File '%s' is not written to file-system!",
file.FilePath(h.config.OutputDir))
} else {
if err = file.Write(h.config.OutputDir); err != nil {
return err
}
}

return nil
}

// Authenticate against vault either via token directly or via AppRole, must be invoked before other
// actions using the API.
func (h *Handler) Authenticate() error {
Expand All @@ -54,14 +29,6 @@ func (h *Handler) Authenticate() error {
return nil
}

// composeVaultPath based in the current SecretData.
func (h *Handler) composeVaultPath(secrets Secrets, data SecretData) string {
if !data.NameAsSubPath {
return secrets.Path
}
return path.Join(secrets.Path, data.Name)
}

// Download files from vault based on manifest.
func (h *Handler) Download(manifest *Manifest) error {
var err error
Expand All @@ -71,20 +38,28 @@ func (h *Handler) Download(manifest *Manifest) error {
log.Printf("[Handler/Download] [%s] Vault path '%s'", group, secrets.Path)

for _, data := range secrets.Data {
var payload []byte

log.Printf("[Handler/Download] [%s] Reading data from Vault '%s.%s' (unzip: %v)",
group, data.Name, data.Extension, data.Unzip)

vaultPath := h.composeVaultPath(secrets, data)
log.Printf("[Handler/Download] [%s] '%s' path in Vault: '%s'", data.Name, group, vaultPath)
log.Printf("[Handler/Download] [%s] '%s' path in Vault: '%s'",
data.Name, group, vaultPath)

// loading secret from vault
payload := []byte{}
if payload, err = h.vault.Read(vaultPath, data.Name); err != nil {
return err
}

// saving data to disk
if err = h.persist(group, &data, payload); err != nil {
file := NewFile(group, &data, payload)

if data.Unzip {
if err = file.Unzip(); err != nil {
return err
}
}

if err = h.persist(file); err != nil {
return err
}
}
Expand Down Expand Up @@ -128,6 +103,20 @@ func (h *Handler) Upload(manifest *Manifest) error {
return nil
}

// persist a slice of bytes to file-system.
func (h *Handler) persist(file *File) error {
if h.config.DryRun {
log.Printf("[DRY-RUN] File '%s' is not written to file-system!",
file.FilePath(h.config.OutputDir))
} else {
if err := file.Write(h.config.OutputDir); err != nil {
return err
}
}
return nil
}

// dispense a file payload to Vault server.
func (h *Handler) dispense(file *File, vaultPath string) error {
var data = make(map[string]interface{})
var err error
Expand All @@ -145,6 +134,14 @@ func (h *Handler) dispense(file *File, vaultPath string) error {
return nil
}

// composeVaultPath based in the current SecretData.
func (h *Handler) composeVaultPath(secrets Secrets, data SecretData) string {
if !data.NameAsSubPath {
return secrets.Path
}
return path.Join(secrets.Path, data.Name)
}

// NewHandler instantiates a new application.
func NewHandler(config *Config) (*Handler, error) {
var err error
Expand Down
72 changes: 36 additions & 36 deletions pkg/vault-handler/vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,32 @@ type Vault struct {
token string
}

// extractKey coming from Read method, where the user can choose one key to be taken out of the data
// read from Vault.
func (v *Vault) extractKey(payload map[string]interface{}, key string) ([]byte, error) {
var data string
var exists bool
// AppRoleAuth execute approle authentication.
func (v *Vault) AppRoleAuth(roleID, secretID string) error {
var secret *vaultapi.Secret
var err error

if _, exists = payload["data"]; exists {
log.Print("[Vault] Using V2 API style, extracting 'data' from payload.")
payload = payload["data"].(map[string]interface{})
log.Printf("[Vault] Starting AppRole authentication.")
authData := map[string]interface{}{"role_id": roleID, "secret_id": secretID}
if secret, err = v.client.Logical().Write("auth/approle/login", authData); err != nil {
return err
}

if data, exists = payload[key].(string); !exists {
return nil, fmt.Errorf("cannot extract key '%s' from vault payload", key)
if secret.Auth == nil || secret.Auth.ClientToken == "" {
return errors.New("no authentication data is returned from vault")
}

dataAsBytes := []byte(data)
log.Printf("Obtained '%d' bytes from key '%s'", len(dataAsBytes), key)
return dataAsBytes, nil
log.Printf("[Vault] Obtained a token via AppRole.")
// saving token for next API calls.
v.token = secret.Auth.ClientToken
v.setHeaders()

return nil
}

// TokenAuth execute token based authentication.
func (v *Vault) TokenAuth(token string) {
v.token = token
v.setHeaders()
}

// Read data from a given vault path and key name, and returning a slice of bytes with payload.
Expand Down Expand Up @@ -77,32 +85,24 @@ func (v *Vault) setHeaders() {
v.client.SetToken(v.token)
}

// AppRoleAuth execute approle authentication.
func (v *Vault) AppRoleAuth(roleID, secretID string) error {
var secret *vaultapi.Secret
var err error
// extractKey coming from Read method, where the user can choose one key to be taken out of the data
// read from Vault.
func (v *Vault) extractKey(payload map[string]interface{}, key string) ([]byte, error) {
var data string
var exists bool

log.Printf("[Vault] Starting AppRole authentication.")
authData := map[string]interface{}{"role_id": roleID, "secret_id": secretID}
if secret, err = v.client.Logical().Write("auth/approle/login", authData); err != nil {
return err
}
if secret.Auth == nil || secret.Auth.ClientToken == "" {
return errors.New("no authentication data is returned from vault")
if _, exists = payload["data"]; exists {
log.Print("[Vault] Using V2 API style, extracting 'data' from payload.")
payload = payload["data"].(map[string]interface{})
}

log.Printf("[Vault] Obtained a token via AppRole.")
// saving token for next API calls.
v.token = secret.Auth.ClientToken
v.setHeaders()

return nil
}
if data, exists = payload[key].(string); !exists {
return nil, fmt.Errorf("cannot extract key '%s' from vault payload", key)
}

// TokenAuth execute token based authentication.
func (v *Vault) TokenAuth(token string) {
v.token = token
v.setHeaders()
dataAsBytes := []byte(data)
log.Printf("[Vault] Obtained '%d' bytes from key '%s'", len(dataAsBytes), key)
return dataAsBytes, nil
}

// NewVault creates a Vault instance, by bootstrapping it's API client.
Expand Down

0 comments on commit ba4e721

Please sign in to comment.