Skip to content
This repository was archived by the owner on Feb 16, 2023. It is now read-only.

Commit 65191f4

Browse files
committed
Add support for op cli V2
1 parent 3962e85 commit 65191f4

3 files changed

Lines changed: 222 additions & 24 deletions

File tree

internals/onepassword/client_v2.go

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package onepassword
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io/ioutil"
7+
"os"
8+
)
9+
10+
type OPV2Client struct {
11+
version string
12+
isV2 bool
13+
}
14+
15+
func (op *OPV2Client) IsV2() bool {
16+
return op.isV2
17+
}
18+
19+
func (op *OPV2Client) CreateVault(name string) error {
20+
_, err := execOP("vault", "create", name)
21+
if err != nil {
22+
return fmt.Errorf("could not create vault '%s': %s", name, err)
23+
}
24+
return nil
25+
}
26+
27+
func (op *OPV2Client) CreateItem(vault string, template *ItemTemplate, title string) error {
28+
jsonTemplate, err := json.Marshal(template)
29+
if err != nil {
30+
return err
31+
}
32+
33+
tempJSONFile, err := ioutil.TempFile(os.TempDir(), "jsonTemplate-")
34+
if err != nil {
35+
return err
36+
}
37+
defer os.Remove(tempJSONFile.Name())
38+
39+
if _, err = tempJSONFile.Write(jsonTemplate); err != nil {
40+
return err
41+
}
42+
43+
_, err = execOP("item", "create", "--category=apicredential", "--vault="+vault, "--template="+tempJSONFile.Name(), "--title="+title)
44+
if err != nil {
45+
return err
46+
}
47+
48+
err = tempJSONFile.Close()
49+
return err
50+
}
51+
52+
func (op *OPV2Client) SetField(vault, item, field, value string) error {
53+
_, err := execOP("item", "edit", item, fmt.Sprintf(`%s=%s`, field, value), "--vault="+vault)
54+
if err != nil {
55+
return fmt.Errorf("could not set field '%s'.'%s'.'%s'", vault, item, field)
56+
}
57+
return nil
58+
}
59+
60+
// GetFields returns a title-to-value map of the fields from the first section of the given 1Password item.
61+
// The rest of the fields are ignored as the migration tool only stores information in the first
62+
// section of each item.
63+
func (op *OPV2Client) GetFields(vault, item string) (map[string]string, error) {
64+
opItem := struct {
65+
Fields []v2ItemFieldTemplate `json:"fields"`
66+
}{}
67+
opItemJSON, err := execOP("item", "get", item, "--vault="+vault, "--format=json")
68+
if err != nil {
69+
return nil, fmt.Errorf("could not get item '%s'.'%s' from 1Password: %s", vault, item, err)
70+
}
71+
err = json.Unmarshal(opItemJSON, &opItem)
72+
if err != nil {
73+
return nil, fmt.Errorf("unexpected format of 1Password item in `op get item` command output: %s", err)
74+
}
75+
76+
fields := make(map[string]string, len(opItem.Fields))
77+
for _, field := range opItem.Fields {
78+
fields[field.Label] = field.Value
79+
}
80+
return fields, nil
81+
}
82+
83+
type v2ItemFieldTemplate struct {
84+
ID string `json:"id"`
85+
Type string `json:"type"`
86+
Label string `json:"label"`
87+
Value string `json:"value"`
88+
}
89+
90+
func (op *OPV2Client) ExistsVault(vaultName string) (bool, error) {
91+
vaultsBytes, err := execOP("vault", "list", "--format=json")
92+
if err != nil {
93+
return false, fmt.Errorf("could not list vaults: %s", err)
94+
}
95+
96+
vaultsJSON := make([]struct {
97+
UUID string `json:"uuid"`
98+
Name string `json:"name"`
99+
}, 0)
100+
101+
err = json.Unmarshal(vaultsBytes, &vaultsJSON)
102+
if err != nil {
103+
return false, fmt.Errorf("unexpected format of `op list vaults`: %s", vaultsBytes)
104+
}
105+
106+
for _, vault := range vaultsJSON {
107+
if vault.Name == vaultName {
108+
return true, nil
109+
}
110+
}
111+
112+
return false, nil
113+
}
114+
115+
func (op *OPV2Client) ExistsItemInVault(vault string, itemName string) (bool, error) {
116+
itemsBytes, err := execOP("item", "list", "--vault="+vault, "--format=json")
117+
if err != nil {
118+
return false, fmt.Errorf("could not list items in vault %s: %s", vault, err)
119+
}
120+
121+
itemsJSON := make([]struct {
122+
Title string `json:"title"`
123+
}, 0)
124+
125+
err = json.Unmarshal(itemsBytes, &itemsJSON)
126+
if err != nil {
127+
return false, fmt.Errorf("unexpected format of `op list items`: %s", itemsBytes)
128+
}
129+
130+
for _, item := range itemsJSON {
131+
if item.Title == itemName {
132+
return true, nil
133+
}
134+
}
135+
136+
return false, nil
137+
}

internals/onepassword/onepassword.go

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,54 @@ import (
1414
"github.com/mitchellh/go-homedir"
1515
)
1616

17-
func CreateVault(name string) error {
17+
type OPClient interface {
18+
IsV2() bool
19+
CreateVault(name string) error
20+
CreateItem(vault string, template *ItemTemplate, title string) error
21+
SetField(vault, item, field, value string) error
22+
GetFields(vault, item string) (map[string]string, error)
23+
ExistsVault(vaultName string) (bool, error)
24+
ExistsItemInVault(vault string, itemName string) (bool, error)
25+
}
26+
27+
func GetOPClient() (OPClient, error) {
28+
out, err := execOP("--version")
29+
if err != nil {
30+
return nil, err
31+
}
32+
33+
version := strings.TrimSpace(string(out))
34+
35+
if strings.HasPrefix(version, "2.") {
36+
return &OPV2Client{
37+
version: version,
38+
isV2: true,
39+
}, nil
40+
}
41+
return &OPV1Client{
42+
version: version,
43+
isV2: false,
44+
}, nil
45+
}
46+
47+
type OPV1Client struct {
48+
version string
49+
isV2 bool
50+
}
51+
52+
func (op *OPV1Client) IsV2() bool {
53+
return op.isV2
54+
}
55+
56+
func (op *OPV1Client) CreateVault(name string) error {
1857
_, err := execOP("create", "vault", name)
1958
if err != nil {
2059
return fmt.Errorf("could not create vault '%s': %s", name, err)
2160
}
2261
return nil
2362
}
2463

25-
func CreateItem(vault string, template *ItemTemplate, title string) error {
64+
func (op *OPV1Client) CreateItem(vault string, template *ItemTemplate, title string) error {
2665
jsonTemplate, err := json.Marshal(template)
2766
if err != nil {
2867
return err
@@ -34,7 +73,7 @@ func CreateItem(vault string, template *ItemTemplate, title string) error {
3473
return err
3574
}
3675

37-
func SetField(vault, item, field, value string) error {
76+
func (op *OPV1Client) SetField(vault, item, field, value string) error {
3877
_, err := execOP("edit", "item", item, fmt.Sprintf(`%s=%s`, field, value), "--vault="+vault)
3978
if err != nil {
4079
return fmt.Errorf("could not set field '%s'.'%s'.'%s'", vault, item, field)
@@ -45,7 +84,7 @@ func SetField(vault, item, field, value string) error {
4584
// GetFields returns a title-to-value map of the fields from the first section of the given 1Password item.
4685
// The rest of the fields are ignored as the migration tool only stores information in the first
4786
// section of each item.
48-
func GetFields(vault, item string) (map[string]string, error) {
87+
func (op *OPV1Client) GetFields(vault, item string) (map[string]string, error) {
4988
opItem := struct {
5089
Details ItemTemplate `json:"details"`
5190
}{}
@@ -108,7 +147,7 @@ type itemFieldTemplate struct {
108147
}
109148

110149
func execOP(args ...string) ([]byte, error) {
111-
command := exec.Command("op", args...)
150+
command := exec.Command("op1", args...)
112151
command.Stderr = os.Stderr
113152
var out bytes.Buffer
114153
command.Stdout = &out
@@ -203,7 +242,7 @@ func GetSignInAddress() (string, error) {
203242
return "", fmt.Errorf("unexpected format of 1password config file at %s: missing account entry for latest used account", path)
204243
}
205244

206-
func ExistsVault(vaultName string) (bool, error) {
245+
func (op *OPV1Client) ExistsVault(vaultName string) (bool, error) {
207246
vaultsBytes, err := execOP("list", "vaults")
208247
if err != nil {
209248
return false, fmt.Errorf("could not list vaults: %s", err)
@@ -228,7 +267,7 @@ func ExistsVault(vaultName string) (bool, error) {
228267
return false, nil
229268
}
230269

231-
func ExistsItemInVault(vault string, itemName string) (bool, error) {
270+
func (op *OPV1Client) ExistsItemInVault(vault string, itemName string) (bool, error) {
232271
itemsBytes, err := execOP("list", "items", "--vault", vault)
233272
if err != nil {
234273
return false, fmt.Errorf("could not list items in vault %s: %s", vault, err)

internals/secrethub/migrate.go

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -239,11 +239,18 @@ func (cmd *MigratePlanCommand) Run() error {
239239

240240
plan := newPlan()
241241

242-
signInAddress, err := onepassword.GetSignInAddress()
242+
opClient, err := onepassword.GetOPClient()
243243
if err != nil {
244244
return err
245245
}
246-
plan.SignInAddress = signInAddress
246+
247+
if !opClient.IsV2() {
248+
signInAddress, err := onepassword.GetSignInAddress()
249+
if err != nil {
250+
return err
251+
}
252+
plan.SignInAddress = signInAddress
253+
}
247254

248255
if len(cmd.paths) == 0 {
249256
err := cmd.addReposToPlan(client, nil, plan)
@@ -464,15 +471,16 @@ type change interface {
464471
}
465472

466473
type vaultCreation struct {
467-
vault string
474+
vault string
475+
opClient onepassword.OPClient
468476
}
469477

470478
func (c vaultCreation) Vault() string {
471479
return c.vault
472480
}
473481

474482
func (c vaultCreation) Apply() error {
475-
return onepassword.CreateVault(c.vault)
483+
return c.opClient.CreateVault(c.vault)
476484
}
477485

478486
func (c vaultCreation) Print(w io.Writer) {
@@ -483,14 +491,15 @@ type itemCreation struct {
483491
vault string
484492
item string
485493
itemTemplate *onepassword.ItemTemplate
494+
opClient onepassword.OPClient
486495
}
487496

488497
func (c itemCreation) Vault() string {
489498
return c.vault
490499
}
491500

492501
func (c itemCreation) Apply() error {
493-
return onepassword.CreateItem(c.vault, c.itemTemplate, c.item)
502+
return c.opClient.CreateItem(c.vault, c.itemTemplate, c.item)
494503
}
495504

496505
func (c itemCreation) Print(w io.Writer) {
@@ -501,6 +510,7 @@ type itemUpdate struct {
501510
vault string
502511
item string
503512
fieldValues map[string]string
513+
opClient onepassword.OPClient
504514
}
505515

506516
func (c itemUpdate) Vault() string {
@@ -509,7 +519,7 @@ func (c itemUpdate) Vault() string {
509519

510520
func (c itemUpdate) Apply() error {
511521
for field, value := range c.fieldValues {
512-
err := onepassword.SetField(c.vault, c.item, field, value)
522+
err := c.opClient.SetField(c.vault, c.item, field, value)
513523
if err != nil {
514524
return err
515525
}
@@ -530,17 +540,24 @@ func (cmd *MigrateApplyCommand) Run() error {
530540
return err
531541
}
532542

533-
err = onepassword.EnsureSignedIn()
543+
opClient, err := onepassword.GetOPClient()
534544
if err != nil {
535545
return err
536546
}
537547

538-
signInAddress, err := onepassword.GetSignInAddress()
539-
if err != nil {
540-
return err
541-
}
542-
if signInAddress != plan.SignInAddress {
543-
return fmt.Errorf("op is signed in to a different account than planned. Run `eval $(op signin %s) to login to the desired account or change the sign-in-address in the plan", plan.SignInAddress)
548+
if !opClient.IsV2() {
549+
err = onepassword.EnsureSignedIn()
550+
if err != nil {
551+
return err
552+
}
553+
554+
signInAddress, err := onepassword.GetSignInAddress()
555+
if err != nil {
556+
return err
557+
}
558+
if signInAddress != plan.SignInAddress {
559+
return fmt.Errorf("op is signed in to a different account than planned. Run `eval $(op signin %s) to login to the desired account or change the sign-in-address in the plan", plan.SignInAddress)
560+
}
544561
}
545562

546563
client, err := cmd.newClient()
@@ -559,19 +576,22 @@ func (cmd *MigrateApplyCommand) Run() error {
559576
i := 1
560577
for _, vault := range plan.vaults {
561578
fmt.Fprintf(cmd.io.Output(), "[%d/%d] Checking vault: %s\n", i, len(plan.vaults), vault.Name)
562-
vaultExists, err := onepassword.ExistsVault(vault.Name)
579+
vaultExists, err := opClient.ExistsVault(vault.Name)
563580
if err != nil {
564581
return fmt.Errorf("could not check vault existence: %s", err)
565582
}
566583
if !vaultExists {
567-
changes = append(changes, vaultCreation{vault: vault.Name})
584+
changes = append(changes, vaultCreation{
585+
vault: vault.Name,
586+
opClient: opClient,
587+
})
568588
vaultCreateCount++
569589
}
570590

571591
for _, item := range vault.Items {
572592
itemExists := false
573593
if vaultExists {
574-
itemExists, err = onepassword.ExistsItemInVault(vault.Name, item.Name)
594+
itemExists, err = opClient.ExistsItemInVault(vault.Name, item.Name)
575595
if err != nil {
576596
return err
577597
}
@@ -591,10 +611,11 @@ func (cmd *MigrateApplyCommand) Run() error {
591611
vault: vault.Name,
592612
item: item.Name,
593613
itemTemplate: template,
614+
opClient: opClient,
594615
})
595616
itemCreateCount++
596617
} else {
597-
opFields, err := onepassword.GetFields(vault.Name, item.Name)
618+
opFields, err := opClient.GetFields(vault.Name, item.Name)
598619
if err != nil {
599620
return err
600621
}
@@ -622,6 +643,7 @@ func (cmd *MigrateApplyCommand) Run() error {
622643
vault: vault.Name,
623644
item: item.Name,
624645
fieldValues: fieldsToUpdate,
646+
opClient: opClient,
625647
})
626648
}
627649
}

0 commit comments

Comments
 (0)