smcli: login-item support + convert command (notes -> username/password/apikey)
ci / vet (push) Successful in 36s
ci / vet (push) Successful in 36s
Founder follow-up: lifted items restructured to proper login items (first-class username/password/URIs, remainder as named custom fields). Classified from the env-key semantics; zero data loss. Ticket: https://projects.knownelement.com/issues/829
This commit is contained in:
+75
-13
@@ -25,17 +25,17 @@ type CipherLogin struct {
|
||||
}
|
||||
|
||||
type cipherRaw struct {
|
||||
ID string `json:"id"`
|
||||
OrganizationID *string `json:"organizationId"`
|
||||
Type int `json:"type"` // 1=login, 2=secureNote
|
||||
Name string `json:"name"`
|
||||
Notes *string `json:"notes"`
|
||||
Fields []CipherField `json:"fields,omitempty"`
|
||||
Key *string `json:"key,omitempty"`
|
||||
Login *CipherLogin `json:"login,omitempty"`
|
||||
SecureNote map[string]any `json:"secureNote,omitempty"`
|
||||
DeletedDate *string `json:"deletedDate,omitempty"`
|
||||
Extra map[string]interface{} `json:"-"`
|
||||
ID string `json:"id"`
|
||||
OrganizationID *string `json:"organizationId"`
|
||||
Type int `json:"type"` // 1=login, 2=secureNote
|
||||
Name string `json:"name"`
|
||||
Notes *string `json:"notes"`
|
||||
Fields []CipherField `json:"fields,omitempty"`
|
||||
Key *string `json:"key,omitempty"`
|
||||
Login *CipherLogin `json:"login,omitempty"`
|
||||
SecureNote map[string]any `json:"secureNote,omitempty"`
|
||||
DeletedDate *string `json:"deletedDate,omitempty"`
|
||||
Extra map[string]interface{} `json:"-"`
|
||||
}
|
||||
|
||||
// cipherKeyFor returns the 64B key to use for a cipher's data.
|
||||
@@ -158,8 +158,8 @@ func BuildSecureNoteJSON(userKey []byte, name, folderID, notes string, fields []
|
||||
return nil, err
|
||||
}
|
||||
encFields = append(encFields, map[string]any{
|
||||
"type": f.Type,
|
||||
"name": fn,
|
||||
"type": f.Type,
|
||||
"name": fn,
|
||||
"value": fv,
|
||||
})
|
||||
}
|
||||
@@ -201,3 +201,65 @@ func parseEnvFields(text string) []PlainField {
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// BuildLoginJSON produces an encrypted login-item payload: username/password
|
||||
// as first-class login fields, URIs, and the remaining keys as custom fields
|
||||
// (hidden for secrets, text for URLs/IDs).
|
||||
func BuildLoginJSON(userKey []byte, name, folderID, username, password string, uris []string, fields []PlainField, existing *cipherRaw) (map[string]any, error) {
|
||||
nameEnc, err := encStr(userKey, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
login := map[string]any{}
|
||||
if username != "" {
|
||||
u, err := encStr(userKey, username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
login["username"] = u
|
||||
}
|
||||
if password != "" {
|
||||
p, err := encStr(userKey, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
login["password"] = p
|
||||
}
|
||||
if len(uris) > 0 {
|
||||
var list []map[string]any
|
||||
for _, u := range uris {
|
||||
ue, err := encStr(userKey, u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list = append(list, map[string]any{"uri": ue})
|
||||
}
|
||||
login["uris"] = list
|
||||
}
|
||||
var encFields []map[string]any
|
||||
for _, f := range fields {
|
||||
fn, err := encStr(userKey, f.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fv, err := encStr(userKey, f.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encFields = append(encFields, map[string]any{"type": f.Type, "name": fn, "value": fv})
|
||||
}
|
||||
payload := map[string]any{"type": 1, "name": nameEnc, "login": login}
|
||||
if len(encFields) > 0 {
|
||||
payload["fields"] = encFields
|
||||
}
|
||||
if folderID != "" {
|
||||
payload["folderId"] = folderID
|
||||
}
|
||||
if existing != nil {
|
||||
payload["id"] = existing.ID
|
||||
if existing.OrganizationID != nil {
|
||||
payload["organizationId"] = existing.OrganizationID
|
||||
}
|
||||
}
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user