kube-1password-secrets/internal/onepassword/client.go

115 lines
2.6 KiB
Go

package onepassword
import (
"encoding/json"
"fmt"
"io"
"log"
"os/exec"
"strings"
)
// Client is the 1Password client
type Client struct {
Domain string
Email string
Password string
SecretKey string
Session string
}
// Secret contains the credentials from a 1Password secret
type Secret struct {
ID string `json:"id"`
Title string `json:"title"`
Username string `json:"username"`
Password string `json:"password"`
SecretText string `json:"secretText"`
}
// New authenticates with the provided values and returns a new 1Password client
func New(domain string, email string, password string, secretKey string) (*Client, error) {
client := &Client{
Domain: domain,
Email: email,
Password: password,
SecretKey: secretKey,
}
if err := client.authenticate(); err != nil {
return nil, err
}
return client, nil
}
func (op *Client) authenticate() error {
cmd := exec.Command("op", "signin", op.Domain, op.Email, op.SecretKey, "--output=raw")
stdin, err := cmd.StdinPipe()
if err != nil {
return fmt.Errorf("Cannot attach to stdin: %s", err)
}
go func() {
defer stdin.Close()
if _, err := io.WriteString(stdin, fmt.Sprintf("%s\n", op.Password)); err != nil {
log.Println("[Error]", err)
}
}()
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("Cannot signin: %s\n%s", err, output)
}
op.Session = strings.Trim(string(output), "\n")
return nil
}
func (op Client) runCmd(args ...string) ([]byte, error) {
args = append(args, fmt.Sprintf("--session=%s", op.Session))
cmd := exec.Command("op", args...)
res, err := cmd.CombinedOutput()
if err != nil {
err = fmt.Errorf("error calling 1Password: %s\n%s", err, res)
}
return res, err
}
// GetSecret returns the values from the secret stored in 1Password with a UUID matching the secretID
func (op *Client) GetSecret(vault, secretID string) (*Secret, error) {
res, err := op.runCmd("get", "item", secretID, fmt.Sprintf("--vault=%s", vault))
if err != nil {
return nil, err
}
item := response{}
if err := json.Unmarshal(res, &item); err != nil {
return nil, err
}
secret := &Secret{
ID: item.UUID,
Title: item.Overview.Title,
Username: "",
Password: "",
SecretText: "",
}
if len(item.Details.Fields) > 0 {
for _, field := range item.Details.Fields {
switch field.Name {
case "username":
secret.Username = field.Value
case "password":
secret.Password = field.Value
}
}
}
if item.Details.Password != nil && *item.Details.Password != "" {
secret.Password = *item.Details.Password
}
if item.Details.Notes != "" {
secret.SecretText = item.Details.Notes
}
return secret, nil
}