smcli setfield: surgical single-field rotation updates; env exposes login triple
ci / vet (push) Successful in 1m2s
ci / vet (push) Successful in 1m2s
Rotation waves need per-field updates on login items without touching the rest (password/username/custom fields), and consumers need URI/ USERNAME/PASSWORD surfaced by env. Ticket: https://projects.knownelement.com/issues/829
This commit is contained in:
@@ -424,6 +424,11 @@ func main() {
|
|||||||
for _, f := range sync.Folders {
|
for _, f := range sync.Folders {
|
||||||
fmt.Println(f.ID, f.Name)
|
fmt.Println(f.ID, f.Name)
|
||||||
}
|
}
|
||||||
|
case "setfield":
|
||||||
|
if len(os.Args) < 5 {
|
||||||
|
fatal("setfield <name> <key> <value>")
|
||||||
|
}
|
||||||
|
err = cmdSetField(os.Args[2], os.Args[3], os.Args[4])
|
||||||
case "convert":
|
case "convert":
|
||||||
if len(os.Args) < 3 {
|
if len(os.Args) < 3 {
|
||||||
fatal("convert <name>")
|
fatal("convert <name>")
|
||||||
@@ -548,3 +553,98 @@ func cmdConvert(name string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func urisOf(pc *PlainCipher) []string { return pc.URIs }
|
func urisOf(pc *PlainCipher) []string { return pc.URIs }
|
||||||
|
|
||||||
|
// cmdSetField surgically updates one key on an existing item: login.password,
|
||||||
|
// login.username, an existing custom field, or appends a new hidden field.
|
||||||
|
func cmdSetField(name, key, value string) error {
|
||||||
|
s, err := loadState()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c, err := newClientFromState(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
raw, err := c.Sync()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var sync struct {
|
||||||
|
Ciphers []json.RawMessage `json:"ciphers"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(raw, &sync); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var cr *cipherRaw
|
||||||
|
for _, r := range sync.Ciphers {
|
||||||
|
var x cipherRaw
|
||||||
|
if json.Unmarshal(r, &x) == nil && x.ID != "" {
|
||||||
|
pc2, derr := DecryptCipher(c.UserSymKey, r)
|
||||||
|
if derr == nil && pc2.Name == name {
|
||||||
|
cr = &x
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cr == nil {
|
||||||
|
return fmt.Errorf("item not found: %s", name)
|
||||||
|
}
|
||||||
|
ck, err := cipherKeyFor(c.UserSymKey, cr.Key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch strings.ToLower(key) {
|
||||||
|
case "password":
|
||||||
|
p, err := encStr(c.UserSymKey, value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if cr.Login == nil {
|
||||||
|
cr.Login = &CipherLogin{}
|
||||||
|
}
|
||||||
|
cr.Login.Password = &p
|
||||||
|
case "username":
|
||||||
|
p, err := encStr(c.UserSymKey, value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if cr.Login == nil {
|
||||||
|
cr.Login = &CipherLogin{}
|
||||||
|
}
|
||||||
|
cr.Login.Username = &p
|
||||||
|
default:
|
||||||
|
found := false
|
||||||
|
for i := range cr.Fields {
|
||||||
|
fn, derr := decStr(ck, ck, cr.Fields[i].Name)
|
||||||
|
if derr == nil && fn == key {
|
||||||
|
fv, err := encStr(c.UserSymKey, value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cr.Fields[i].Value = &fv
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
fn, _ := encStr(c.UserSymKey, key)
|
||||||
|
fv, _ := encStr(c.UserSymKey, value)
|
||||||
|
cr.Fields = append(cr.Fields, CipherField{Type: 1, Name: &fn, Value: &fv})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
payload := map[string]any{"type": cr.Type, "name": cr.Name}
|
||||||
|
if cr.Login != nil {
|
||||||
|
payload["login"] = cr.Login
|
||||||
|
}
|
||||||
|
if cr.Fields != nil {
|
||||||
|
payload["fields"] = cr.Fields
|
||||||
|
}
|
||||||
|
if cr.Notes != nil {
|
||||||
|
payload["notes"] = *cr.Notes
|
||||||
|
}
|
||||||
|
if _, err := c.EditCipher(cr.ID, payload); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println("updated:", name, key)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@
|
|||||||
name: knel-secretsmanager
|
name: knel-secretsmanager
|
||||||
services:
|
services:
|
||||||
smcli:
|
smcli:
|
||||||
image: git.knownelement.com/knel/knel-secretsmanager-cli@sha256:554de767afbf80ca2db772edda350200b741c211495dda3fde96c6496a7a13cc
|
image: git.knownelement.com/knel/knel-secretsmanager-cli@sha256:46d80c0a0ef53a9303dd54b3799a64a61cd4e4dc892282bc4d8a220378117ce9
|
||||||
container_name: ukrrs-secretsmgr-cli
|
container_name: ukrrs-secretsmgr-cli
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
entrypoint: ["sleep", "infinity"]
|
entrypoint: ["sleep", "infinity"]
|
||||||
|
|||||||
Reference in New Issue
Block a user