mirror of
https://github.com/docker/docker-credential-helpers.git
synced 2026-06-13 16:01:28 +05:30
c2ca986943
Secretservice entries have a "label". This is intended to be a human-readable description. It's actually called "Description" in UIs like seahorse, and the listing of existing secrets shows this as a name for each one. The entries stored by the credential helper set this to simply the repository URL. This is rather unfriendly, since entries like "gitlab.com" and "index.docker.io/v1" show up. Mixed in with entries from all other applications, it's hard to figure out what application owns each entry. This commit changes the label used when saving entries to be something human-readable (this is the intent of the "label" field, btw). Because of the naming scheme, this also results in all entries being shown together by default (since UIs tend to sort lexicographically). New entries will now be stores as: Registry credentials for $REGISTRY_URL Note that items stored by the secret service have multiple fields inside of them. One of those fields is called "label", and is used by the helper to filter items from the secret service. This "label" field is entirely unrelated to the items' label. The naming is most unfortunate. Signed-off-by: Hugo Osvaldo Barrera <hugo@barrera.io> Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
125 lines
3.6 KiB
Go
125 lines
3.6 KiB
Go
//go:build linux && cgo
|
|
|
|
package secretservice
|
|
|
|
/*
|
|
#cgo pkg-config: libsecret-1
|
|
|
|
#include "secretservice.h"
|
|
#include <stdlib.h>
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"errors"
|
|
"unsafe"
|
|
|
|
"github.com/docker/docker-credential-helpers/credentials"
|
|
)
|
|
|
|
// Secretservice handles secrets using Linux secret-service as a store.
|
|
type Secretservice struct{}
|
|
|
|
// Add adds new credentials to the keychain.
|
|
func (h Secretservice) Add(creds *credentials.Credentials) error {
|
|
if creds == nil {
|
|
return errors.New("missing credentials")
|
|
}
|
|
credsLabel := C.CString(credentials.CredsLabel)
|
|
defer C.free(unsafe.Pointer(credsLabel))
|
|
server := C.CString(creds.ServerURL)
|
|
defer C.free(unsafe.Pointer(server))
|
|
username := C.CString(creds.Username)
|
|
defer C.free(unsafe.Pointer(username))
|
|
secret := C.CString(creds.Secret)
|
|
defer C.free(unsafe.Pointer(secret))
|
|
displayLabel := C.CString("Registry credentials for " + creds.ServerURL)
|
|
defer C.free(unsafe.Pointer(displayLabel))
|
|
|
|
if err := C.add(credsLabel, server, username, secret, displayLabel); err != nil {
|
|
defer C.g_error_free(err)
|
|
errMsg := (*C.char)(unsafe.Pointer(err.message))
|
|
return errors.New(C.GoString(errMsg))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Delete removes credentials from the store.
|
|
func (h Secretservice) Delete(serverURL string) error {
|
|
if serverURL == "" {
|
|
return errors.New("missing server url")
|
|
}
|
|
server := C.CString(serverURL)
|
|
defer C.free(unsafe.Pointer(server))
|
|
|
|
if err := C.delete(server); err != nil {
|
|
defer C.g_error_free(err)
|
|
errMsg := (*C.char)(unsafe.Pointer(err.message))
|
|
return errors.New(C.GoString(errMsg))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Get returns the username and secret to use for a given registry server URL.
|
|
func (h Secretservice) Get(serverURL string) (string, string, error) {
|
|
if serverURL == "" {
|
|
return "", "", errors.New("missing server url")
|
|
}
|
|
var username *C.char
|
|
defer C.free(unsafe.Pointer(username))
|
|
var secret *C.char
|
|
defer C.free(unsafe.Pointer(secret))
|
|
server := C.CString(serverURL)
|
|
defer C.free(unsafe.Pointer(server))
|
|
|
|
err := C.get(server, &username, &secret)
|
|
if err != nil {
|
|
defer C.g_error_free(err)
|
|
errMsg := (*C.char)(unsafe.Pointer(err.message))
|
|
return "", "", errors.New(C.GoString(errMsg))
|
|
}
|
|
user := C.GoString(username)
|
|
pass := C.GoString(secret)
|
|
if pass == "" {
|
|
return "", "", credentials.NewErrCredentialsNotFound()
|
|
}
|
|
return user, pass, nil
|
|
}
|
|
|
|
// List returns the stored URLs and corresponding usernames for a given credentials label
|
|
func (h Secretservice) List() (map[string]string, error) {
|
|
credsLabelC := C.CString(credentials.CredsLabel)
|
|
defer C.free(unsafe.Pointer(credsLabelC))
|
|
|
|
var pathsC **C.char
|
|
defer C.free(unsafe.Pointer(pathsC))
|
|
var acctsC **C.char
|
|
defer C.free(unsafe.Pointer(acctsC))
|
|
var listLenC C.uint
|
|
err := C.list(credsLabelC, &pathsC, &acctsC, &listLenC)
|
|
defer C.freeListData(&pathsC, listLenC)
|
|
defer C.freeListData(&acctsC, listLenC)
|
|
if err != nil {
|
|
defer C.g_error_free(err)
|
|
errMsg := (*C.char)(unsafe.Pointer(err.message))
|
|
return nil, errors.New(C.GoString(errMsg))
|
|
}
|
|
|
|
resp := make(map[string]string)
|
|
|
|
listLen := int(listLenC)
|
|
if listLen == 0 {
|
|
return resp, nil
|
|
}
|
|
// The maximum capacity of the following two slices is limited to (2^29)-1 to remain compatible
|
|
// with 32-bit platforms. The size of a `*C.char` (a pointer) is 4 Byte on a 32-bit system
|
|
// and (2^29)*4 == math.MaxInt32 + 1. -- See issue golang/go#13656
|
|
pathTmp := (*[(1 << 29) - 1]*C.char)(unsafe.Pointer(pathsC))[:listLen:listLen]
|
|
acctTmp := (*[(1 << 29) - 1]*C.char)(unsafe.Pointer(acctsC))[:listLen:listLen]
|
|
for i := 0; i < listLen; i++ {
|
|
resp[C.GoString(pathTmp[i])] = C.GoString(acctTmp[i])
|
|
}
|
|
|
|
return resp, nil
|
|
}
|