mirror of
https://github.com/Threnklyn/jira.git
synced 2026-05-19 04:33:28 +02:00
153 lines
4.2 KiB
Go
153 lines
4.2 KiB
Go
// +build !gnome_keyring
|
|
|
|
package keyring
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
dbus "github.com/guelfey/go.dbus"
|
|
)
|
|
|
|
const (
|
|
ssServiceName = "org.freedesktop.secrets"
|
|
ssServicePath = "/org/freedesktop/secrets"
|
|
ssCollectionPath = "/org/freedesktop/secrets/aliases/default"
|
|
ssServiceIface = "org.freedesktop.Secret.Service."
|
|
ssSessionIface = "org.freedesktop.Secret.Session."
|
|
ssCollectionIface = "org.freedesktop.Secret.Collection."
|
|
ssItemIface = "org.freedesktop.Secret.Item."
|
|
ssPromptIface = "org.freedesktop.Secret.Prompt."
|
|
)
|
|
|
|
// ssSecret corresponds to org.freedesktop.Secret.Item
|
|
// Note: Order is important
|
|
type ssSecret struct {
|
|
Session dbus.ObjectPath
|
|
Parameters []byte
|
|
Value []byte
|
|
ContentType string `dbus:"content_type"`
|
|
}
|
|
|
|
// newSSSecret prepares an ssSecret for use
|
|
// Uses text/plain as the Content-type which may need to change in the future
|
|
func newSSSecret(session dbus.ObjectPath, secret string) (s ssSecret) {
|
|
s = ssSecret{
|
|
ContentType: "text/plain; charset=utf8",
|
|
Parameters: []byte{},
|
|
Session: session,
|
|
Value: []byte(secret),
|
|
}
|
|
return
|
|
}
|
|
|
|
// ssProvider implements the provider interface freedesktop SecretService
|
|
type ssProvider struct {
|
|
*dbus.Conn
|
|
srv *dbus.Object
|
|
}
|
|
|
|
// This is used to open a seassion for every get/set. Alternative might be to
|
|
// defer() the call to close when constructing the ssProvider
|
|
func (s *ssProvider) openSession() (*dbus.Object, error) {
|
|
var disregard dbus.Variant
|
|
var sessionPath dbus.ObjectPath
|
|
method := fmt.Sprint(ssServiceIface, "OpenSession")
|
|
err := s.srv.Call(method, 0, "plain", dbus.MakeVariant("")).Store(&disregard, &sessionPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.Object(ssServiceName, sessionPath), nil
|
|
}
|
|
|
|
// Unsure how the .Prompt call surfaces, it hasn't come up.
|
|
func (s *ssProvider) unlock(p dbus.ObjectPath) error {
|
|
var unlocked []dbus.ObjectPath
|
|
var prompt dbus.ObjectPath
|
|
method := fmt.Sprint(ssServiceIface, "Unlock")
|
|
err := s.srv.Call(method, 0, []dbus.ObjectPath{p}).Store(&unlocked, &prompt)
|
|
if err != nil {
|
|
return fmt.Errorf("keyring/dbus: Unlock error: %s", err)
|
|
}
|
|
if prompt != dbus.ObjectPath("/") {
|
|
method = fmt.Sprint(ssPromptIface, "Prompt")
|
|
call := s.Object(ssServiceName, prompt).Call(method, 0, "unlock")
|
|
return call.Err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *ssProvider) Get(c, u string) (string, error) {
|
|
results := []dbus.ObjectPath{}
|
|
var secret ssSecret
|
|
search := map[string]string{
|
|
"username": u,
|
|
"service": c,
|
|
}
|
|
|
|
session, err := s.openSession()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer session.Call(fmt.Sprint(ssSessionIface, "Close"), 0)
|
|
s.unlock(ssCollectionPath)
|
|
collection := s.Object(ssServiceName, ssCollectionPath)
|
|
|
|
method := fmt.Sprint(ssCollectionIface, "SearchItems")
|
|
call := collection.Call(method, 0, search)
|
|
err = call.Store(&results)
|
|
if call.Err != nil {
|
|
return "", call.Err
|
|
}
|
|
// results is a slice. Just grab the first one.
|
|
if len(results) == 0 {
|
|
return "", ErrNotFound
|
|
}
|
|
|
|
method = fmt.Sprint(ssItemIface, "GetSecret")
|
|
err = s.Object(ssServiceName, results[0]).Call(method, 0, session.Path()).Store(&secret)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(secret.Value), nil
|
|
}
|
|
|
|
func (s *ssProvider) Set(c, u, p string) error {
|
|
var item, prompt dbus.ObjectPath
|
|
properties := map[string]dbus.Variant{
|
|
"org.freedesktop.Secret.Item.Label": dbus.MakeVariant(fmt.Sprintf("%s - %s", u, c)),
|
|
"org.freedesktop.Secret.Item.Attributes": dbus.MakeVariant(map[string]string{
|
|
"username": u,
|
|
"service": c,
|
|
}),
|
|
}
|
|
|
|
session, err := s.openSession()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer session.Call(fmt.Sprint(ssSessionIface, "Close"), 0)
|
|
s.unlock(ssCollectionPath)
|
|
collection := s.Object(ssServiceName, ssCollectionPath)
|
|
|
|
secret := newSSSecret(session.Path(), p)
|
|
// the bool is "replace"
|
|
err = collection.Call(fmt.Sprint(ssCollectionIface, "CreateItem"), 0, properties, secret, true).Store(&item, &prompt)
|
|
if err != nil {
|
|
return fmt.Errorf("keyring/dbus: CreateItem error: %s", err)
|
|
}
|
|
if prompt != "/" {
|
|
s.Object(ssServiceName, prompt).Call(fmt.Sprint(ssPromptIface, "Prompt"), 0, "unlock")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func initializeProvider() (provider, error) {
|
|
conn, err := dbus.SessionBus()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
srv := conn.Object(ssServiceName, ssServicePath)
|
|
p := &ssProvider{conn, srv}
|
|
return p, nil
|
|
}
|