From 8c0993ba7077f3d78e38df9ba848486d06a62375 Mon Sep 17 00:00:00 2001 From: Goran Sterjov Date: Sat, 1 Oct 2016 18:13:38 +1000 Subject: [PATCH] Initial commit. In working condition --- LICENSE | 22 ++++++++ collection.go | 124 ++++++++++++++++++++++++++++++++++++++++++++ item.go | 77 +++++++++++++++++++++++++++ prompt.go | 55 ++++++++++++++++++++ secret.go | 21 ++++++++ service.go | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++ session.go | 22 ++++++++ 7 files changed, 462 insertions(+) create mode 100644 LICENSE create mode 100644 collection.go create mode 100644 item.go create mode 100644 prompt.go create mode 100644 secret.go create mode 100644 service.go create mode 100644 session.go diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..51f2292 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016 Goran Sterjov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/collection.go b/collection.go new file mode 100644 index 0000000..5bd2b1d --- /dev/null +++ b/collection.go @@ -0,0 +1,124 @@ +package libsecret + +import "github.com/godbus/dbus" + + +type Collection struct { + conn *dbus.Conn + dbus dbus.BusObject +} + + +func NewCollection(conn *dbus.Conn, path dbus.ObjectPath) *Collection { + return &Collection{ + conn: conn, + dbus: conn.Object(DBUS_SERVICE_NAME, path), + } +} + + +func (collection Collection) Path() dbus.ObjectPath { + return collection.dbus.Path() +} + + +// READ Array Items; +func (collection *Collection) Items() ([]Item, error) { + val, err := collection.dbus.GetProperty("org.freedesktop.Secret.Collection.Items") + if err != nil { + return []Item{}, err + } + + items := []Item{} + for _, path := range val.Value().([]dbus.ObjectPath) { + items = append(items, *NewItem(collection.conn, path)) + } + + return items, nil +} + + +// Delete (OUT ObjectPath prompt); +func (collection *Collection) Delete() error { + var prompt dbus.ObjectPath + + err := collection.dbus.Call("org.freedesktop.Secret.Collection.Delete", 0).Store(&prompt) + if err != nil { + return err + } + + if isPrompt(prompt) { + prompt := NewPrompt(collection.conn, prompt) + + _, err := prompt.Prompt() + if err != nil { + return err + } + } + + return nil +} + + +// SearchItems (IN Dict attributes, OUT Array results); +func (collection *Collection) SearchItems(profile string) ([]Item, error) { + attributes := make(map[string]string) + attributes["profile"] = profile + + var paths []dbus.ObjectPath + + err := collection.dbus.Call("org.freedesktop.Secret.Collection.SearchItems", 0, attributes).Store(&paths) + if err != nil { + return []Item{}, err + } + + items := []Item{} + for _, path := range paths { + items = append(items, *NewItem(collection.conn, path)) + } + + return items, nil +} + + +// CreateItem (IN Dict properties, IN Secret secret, IN Boolean replace, OUT ObjectPath item, OUT ObjectPath prompt); +func (collection *Collection) CreateItem(label string, secret *Secret, replace bool) (*Item, error) { + properties := make(map[string]dbus.Variant) + attributes := make(map[string]string) + + attributes["profile"] = label + properties["org.freedesktop.Secret.Item.Label"] = dbus.MakeVariant(label) + properties["org.freedesktop.Secret.Item.Attributes"] = dbus.MakeVariant(attributes) + + var path dbus.ObjectPath + var prompt dbus.ObjectPath + + err := collection.dbus.Call("org.freedesktop.Secret.Collection.CreateItem", 0, properties, secret, replace).Store(&path, &prompt) + if err != nil { + return &Item{}, err + } + + if isPrompt(prompt) { + prompt := NewPrompt(collection.conn, prompt) + + result, err := prompt.Prompt() + if err != nil { + return &Item{}, err + } + + path = result.Value().(dbus.ObjectPath) + } + + return NewItem(collection.conn, path), nil +} + + +// READ Boolean Locked; +func (collection *Collection) Locked() (bool, error) { + val, err := collection.dbus.GetProperty("org.freedesktop.Secret.Collection.Locked") + if err != nil { + return true, err + } + + return val.Value().(bool), nil +} diff --git a/item.go b/item.go new file mode 100644 index 0000000..6e0da6b --- /dev/null +++ b/item.go @@ -0,0 +1,77 @@ +package libsecret + +import "github.com/godbus/dbus" + + +type Item struct { + conn *dbus.Conn + dbus dbus.BusObject +} + + +func NewItem(conn *dbus.Conn, path dbus.ObjectPath) *Item { + return &Item{ + conn: conn, + dbus: conn.Object(DBUS_SERVICE_NAME, path), + } +} + + +func (item Item) Path() dbus.ObjectPath { + return item.dbus.Path() +} + + +// READWRITE String Label; +func (item *Item) Label() (string, error) { + val, err := item.dbus.GetProperty("org.freedesktop.Secret.Item.Label") + if err != nil { + return "", err + } + + return val.Value().(string), nil +} + + +// READ Boolean Locked; +func (item *Item) Locked() (bool, error) { + val, err := item.dbus.GetProperty("org.freedesktop.Secret.Item.Locked") + if err != nil { + return true, err + } + + return val.Value().(bool), nil +} + + +// GetSecret (IN ObjectPath session, OUT Secret secret); +func (item *Item) GetSecret(session *Session) (*Secret, error) { + secret := Secret{} + + err := item.dbus.Call("org.freedesktop.Secret.Item.GetSecret", 0, session.Path()).Store(&secret) + if err != nil { + return &Secret{}, err + } + + return &secret, nil +} + + +// Delete (OUT ObjectPath Prompt); +func (item *Item) Delete() error { + var prompt dbus.ObjectPath + + err := item.dbus.Call("org.freedesktop.Secret.Item.Delete", 0).Store(&prompt) + if err != nil { + return err + } + + if isPrompt(prompt) { + prompt := NewPrompt(item.conn, prompt) + if _, err := prompt.Prompt(); err != nil { + return err + } + } + + return nil +} diff --git a/prompt.go b/prompt.go new file mode 100644 index 0000000..3c4b249 --- /dev/null +++ b/prompt.go @@ -0,0 +1,55 @@ +package libsecret + +import ( + "github.com/godbus/dbus" + "strings" +) + + +type Prompt struct { + conn *dbus.Conn + dbus dbus.BusObject +} + + +func NewPrompt(conn *dbus.Conn, path dbus.ObjectPath) *Prompt { + return &Prompt{ + conn: conn, + dbus: conn.Object(DBUS_SERVICE_NAME, path), + } +} + + +func (prompt Prompt) Path() dbus.ObjectPath { + return prompt.dbus.Path() +} + + +func isPrompt(path dbus.ObjectPath) bool { + promptPath := DBUS_PATH + "/prompt/" + return strings.HasPrefix(string(path), promptPath) +} + + +// Prompt (IN String window-id); +func (prompt *Prompt) Prompt() (*dbus.Variant, error) { + // prompts are asynchronous so we connect to the signal + // and block with a channel until we get a response + c := make(chan *dbus.Signal, 10) + defer close(c) + + prompt.conn.Signal(c) + defer prompt.conn.RemoveSignal(c) + + err := prompt.dbus.Call("org.freedesktop.Secret.Prompt.Prompt", 0, "").Store() + if err != nil { + return &dbus.Variant{}, err + } + + for { + if result := <-c; result.Path == prompt.Path() { + value := result.Body[1].(dbus.Variant) + return &value, nil + } + } +} diff --git a/secret.go b/secret.go new file mode 100644 index 0000000..09393be --- /dev/null +++ b/secret.go @@ -0,0 +1,21 @@ +package libsecret + +import "github.com/godbus/dbus" + + +type Secret struct { + Session dbus.ObjectPath + Parameters []byte + Value []byte + ContentType string +} + + +func NewSecret(session *Session, params []byte, value []byte, contentType string) *Secret { + return &Secret{ + Session: session.Path(), + Parameters: params, + Value: value, + ContentType: contentType, + } +} diff --git a/service.go b/service.go new file mode 100644 index 0000000..2ecb627 --- /dev/null +++ b/service.go @@ -0,0 +1,141 @@ +package libsecret + +import "github.com/godbus/dbus" + + +const ( + DBUS_SERVICE_NAME = "org.freedesktop.secrets" + DBUS_PATH = "/org/freedesktop/secrets" +) + +type DBusObject interface { + Path() dbus.ObjectPath +} + + +type Service struct { + conn *dbus.Conn + dbus dbus.BusObject +} + + +func NewService() (*Service, error) { + conn, err := dbus.SessionBus() + if err != nil { + return &Service{}, err + } + + return &Service{ + conn: conn, + dbus: conn.Object(DBUS_SERVICE_NAME, DBUS_PATH), + }, nil +} + + +func (service Service) Path() dbus.ObjectPath { + return service.dbus.Path() +} + + +// OpenSession (IN String algorithm, IN Variant input, OUT Variant output, OUT ObjectPath result); +func (service *Service) Open() (*Session, error) { + var output dbus.Variant + var path dbus.ObjectPath + + err := service.dbus.Call("org.freedesktop.Secret.Service.OpenSession", 0, "plain", dbus.MakeVariant("")).Store(&output, &path) + if err != nil { + return &Session{}, err + } + + return NewSession(service.conn, path), nil +} + + +// READ Array Collections; +func (service *Service) Collections() ([]Collection, error) { + val, err := service.dbus.GetProperty("org.freedesktop.Secret.Service.Collections") + if err != nil { + return []Collection{}, err + } + + collections := []Collection{} + for _, path := range val.Value().([]dbus.ObjectPath) { + collections = append(collections, *NewCollection(service.conn, path)) + } + + return collections, nil +} + + +// CreateCollection (IN Dict properties, IN String alias, OUT ObjectPath collection, OUT ObjectPath prompt); +func (service *Service) CreateCollection(label string) (*Collection, error) { + properties := make(map[string]dbus.Variant) + properties["org.freedesktop.Secret.Collection.Label"] = dbus.MakeVariant(label) + + var path dbus.ObjectPath + var prompt dbus.ObjectPath + + err := service.dbus.Call("org.freedesktop.Secret.Service.CreateCollection", 0, properties, "").Store(&path, &prompt) + if err != nil { + return &Collection{}, err + } + + if isPrompt(prompt) { + prompt := NewPrompt(service.conn, prompt) + + result, err := prompt.Prompt() + if err != nil { + return &Collection{}, err + } + + path = result.Value().(dbus.ObjectPath) + } + + return NewCollection(service.conn, path), nil +} + + +// Unlock (IN Array objects, OUT Array unlocked, OUT ObjectPath prompt); +func (service *Service) Unlock(object DBusObject) error { + objects := []dbus.ObjectPath{object.Path()} + + var unlocked []dbus.ObjectPath + var prompt dbus.ObjectPath + + err := service.dbus.Call("org.freedesktop.Secret.Service.Unlock", 0, objects).Store(&unlocked, &prompt) + if err != nil { + return err + } + + if isPrompt(prompt) { + prompt := NewPrompt(service.conn, prompt) + if _, err := prompt.Prompt(); err != nil { + return err + } + } + + return nil +} + + +// Lock (IN Array objects, OUT Array locked, OUT ObjectPath Prompt); +func (service *Service) Lock(object DBusObject) error { + objects := []dbus.ObjectPath{object.Path()} + + var locked []dbus.ObjectPath + var prompt dbus.ObjectPath + + err := service.dbus.Call("org.freedesktop.Secret.Service.Lock", 0, objects).Store(&locked, &prompt) + if err != nil { + return err + } + + if isPrompt(prompt) { + prompt := NewPrompt(service.conn, prompt) + if _, err := prompt.Prompt(); err != nil { + return err + } + } + + return nil +} diff --git a/session.go b/session.go new file mode 100644 index 0000000..75bd351 --- /dev/null +++ b/session.go @@ -0,0 +1,22 @@ +package libsecret + +import "github.com/godbus/dbus" + + +type Session struct { + conn *dbus.Conn + dbus dbus.BusObject +} + + +func NewSession(conn *dbus.Conn, path dbus.ObjectPath) *Session { + return &Session{ + conn: conn, + dbus: conn.Object(DBUS_SERVICE_NAME, path), + } +} + + +func (session Session) Path() dbus.ObjectPath { + return session.dbus.Path() +}