Skip to content

Commit 92481bf

Browse files
author
Jayden Thorup
committed
Prevent 1Password duplicate items - check for existence and update instead of create
1 parent 1d41995 commit 92481bf

1 file changed

Lines changed: 80 additions & 20 deletions

File tree

internal/secrets/onepassword.go

Lines changed: 80 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,36 @@ func (p *OnePasswordProvider) ResolveIfReference(value string) string {
8484
return resolved
8585
}
8686

87+
// CheckItemExists checks if an item with the given title exists in the vault
88+
// Returns the item ID if it exists, or an error if not found or multiple items exist
89+
func (p *OnePasswordProvider) CheckItemExists(vault, title string) (string, bool, error) {
90+
if !p.enabled {
91+
return "", false, fmt.Errorf("1Password CLI is not available")
92+
}
93+
94+
// Try to get the item
95+
cmd := exec.Command("op", "item", "get", title, "--vault="+vault, "--format=json")
96+
hideConsoleWindow(cmd)
97+
98+
output, err := cmd.CombinedOutput()
99+
if err != nil {
100+
// Check if error is because item doesn't exist
101+
errorMsg := string(output)
102+
if strings.Contains(errorMsg, "isn't an item") || strings.Contains(errorMsg, "not found") {
103+
return "", false, nil // Item doesn't exist
104+
}
105+
// Check for multiple items
106+
if strings.Contains(errorMsg, "More than one item matches") {
107+
return "", false, fmt.Errorf("multiple items found with title '%s' in vault '%s'. Please use a unique name or delete duplicates in 1Password", title, vault)
108+
}
109+
return "", false, fmt.Errorf("failed to check item: %s", strings.TrimSpace(errorMsg))
110+
}
111+
112+
// Item exists - extract ID from JSON (simple approach)
113+
// In production you'd want proper JSON parsing
114+
return "", true, nil
115+
}
116+
87117
// CreateItem creates a new Login item in 1Password
88118
// Returns the 1Password reference (op://vault/title/password)
89119
func (p *OnePasswordProvider) CreateItem(vault, title, username, password string) (string, error) {
@@ -95,28 +125,58 @@ func (p *OnePasswordProvider) CreateItem(vault, title, username, password string
95125
return "", fmt.Errorf("vault and title are required")
96126
}
97127

98-
// Build the command: op item create --category=login --title="title" --vault="vault" username="username" password="password"
99-
args := []string{
100-
"item", "create",
101-
"--category=login",
102-
"--title=" + title,
103-
"--vault=" + vault,
104-
}
105-
106-
if username != "" {
107-
args = append(args, "username="+username)
108-
}
109-
110-
if password != "" {
111-
args = append(args, "password="+password)
128+
// Check if item already exists
129+
_, exists, err := p.CheckItemExists(vault, title)
130+
if err != nil {
131+
return "", err // Return the error (e.g., multiple items found)
112132
}
113133

114-
cmd := exec.Command("op", args...)
115-
hideConsoleWindow(cmd)
116-
117-
output, err := cmd.CombinedOutput()
118-
if err != nil {
119-
return "", fmt.Errorf("failed to create 1Password item: %w, output: %s", err, string(output))
134+
if exists {
135+
// Item exists - update it instead of creating
136+
args := []string{
137+
"item", "edit", title,
138+
"--vault=" + vault,
139+
}
140+
141+
if username != "" {
142+
args = append(args, "username="+username)
143+
}
144+
145+
if password != "" {
146+
args = append(args, "password="+password)
147+
}
148+
149+
cmd := exec.Command("op", args...)
150+
hideConsoleWindow(cmd)
151+
152+
output, err := cmd.CombinedOutput()
153+
if err != nil {
154+
return "", fmt.Errorf("failed to update existing 1Password item: %w, output: %s", err, string(output))
155+
}
156+
} else {
157+
// Item doesn't exist - create it
158+
args := []string{
159+
"item", "create",
160+
"--category=login",
161+
"--title=" + title,
162+
"--vault=" + vault,
163+
}
164+
165+
if username != "" {
166+
args = append(args, "username="+username)
167+
}
168+
169+
if password != "" {
170+
args = append(args, "password="+password)
171+
}
172+
173+
cmd := exec.Command("op", args...)
174+
hideConsoleWindow(cmd)
175+
176+
output, err := cmd.CombinedOutput()
177+
if err != nil {
178+
return "", fmt.Errorf("failed to create 1Password item: %w, output: %s", err, string(output))
179+
}
120180
}
121181

122182
// Return the reference format

0 commit comments

Comments
 (0)