├── LICENSE ├── collection.go ├── item.go ├── prompt.go ├── secret.go ├── service.go └── session.go /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Goran Sterjov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /collection.go: -------------------------------------------------------------------------------- 1 | package libsecret 2 | 3 | import "github.com/godbus/dbus" 4 | 5 | 6 | type Collection struct { 7 | conn *dbus.Conn 8 | dbus dbus.BusObject 9 | } 10 | 11 | 12 | func NewCollection(conn *dbus.Conn, path dbus.ObjectPath) *Collection { 13 | return &Collection{ 14 | conn: conn, 15 | dbus: conn.Object(DBusServiceName, path), 16 | } 17 | } 18 | 19 | 20 | func (collection Collection) Path() dbus.ObjectPath { 21 | return collection.dbus.Path() 22 | } 23 | 24 | 25 | // READ Array Items; 26 | func (collection *Collection) Items() ([]Item, error) { 27 | val, err := collection.dbus.GetProperty("org.freedesktop.Secret.Collection.Items") 28 | if err != nil { 29 | return []Item{}, err 30 | } 31 | 32 | items := []Item{} 33 | for _, path := range val.Value().([]dbus.ObjectPath) { 34 | items = append(items, *NewItem(collection.conn, path)) 35 | } 36 | 37 | return items, nil 38 | } 39 | 40 | 41 | // Delete (OUT ObjectPath prompt); 42 | func (collection *Collection) Delete() error { 43 | var prompt dbus.ObjectPath 44 | 45 | err := collection.dbus.Call("org.freedesktop.Secret.Collection.Delete", 0).Store(&prompt) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | if isPrompt(prompt) { 51 | prompt := NewPrompt(collection.conn, prompt) 52 | 53 | _, err := prompt.Prompt() 54 | if err != nil { 55 | return err 56 | } 57 | } 58 | 59 | return nil 60 | } 61 | 62 | 63 | // SearchItems (IN Dict attributes, OUT Array results); 64 | func (collection *Collection) SearchItems(profile string) ([]Item, error) { 65 | attributes := make(map[string]string) 66 | attributes["profile"] = profile 67 | 68 | var paths []dbus.ObjectPath 69 | 70 | err := collection.dbus.Call("org.freedesktop.Secret.Collection.SearchItems", 0, attributes).Store(&paths) 71 | if err != nil { 72 | return []Item{}, err 73 | } 74 | 75 | items := []Item{} 76 | for _, path := range paths { 77 | items = append(items, *NewItem(collection.conn, path)) 78 | } 79 | 80 | return items, nil 81 | } 82 | 83 | 84 | // CreateItem (IN Dict properties, IN Secret secret, IN Boolean replace, OUT ObjectPath item, OUT ObjectPath prompt); 85 | func (collection *Collection) CreateItem(label string, secret *Secret, replace bool) (*Item, error) { 86 | properties := make(map[string]dbus.Variant) 87 | attributes := make(map[string]string) 88 | 89 | attributes["profile"] = label 90 | properties["org.freedesktop.Secret.Item.Label"] = dbus.MakeVariant(label) 91 | properties["org.freedesktop.Secret.Item.Attributes"] = dbus.MakeVariant(attributes) 92 | 93 | var path dbus.ObjectPath 94 | var prompt dbus.ObjectPath 95 | 96 | err := collection.dbus.Call("org.freedesktop.Secret.Collection.CreateItem", 0, properties, secret, replace).Store(&path, &prompt) 97 | if err != nil { 98 | return &Item{}, err 99 | } 100 | 101 | if isPrompt(prompt) { 102 | prompt := NewPrompt(collection.conn, prompt) 103 | 104 | result, err := prompt.Prompt() 105 | if err != nil { 106 | return &Item{}, err 107 | } 108 | 109 | path = result.Value().(dbus.ObjectPath) 110 | } 111 | 112 | return NewItem(collection.conn, path), nil 113 | } 114 | 115 | 116 | // READ Boolean Locked; 117 | func (collection *Collection) Locked() (bool, error) { 118 | val, err := collection.dbus.GetProperty("org.freedesktop.Secret.Collection.Locked") 119 | if err != nil { 120 | return true, err 121 | } 122 | 123 | return val.Value().(bool), nil 124 | } 125 | -------------------------------------------------------------------------------- /item.go: -------------------------------------------------------------------------------- 1 | package libsecret 2 | 3 | import "github.com/godbus/dbus" 4 | 5 | 6 | type Item struct { 7 | conn *dbus.Conn 8 | dbus dbus.BusObject 9 | } 10 | 11 | 12 | func NewItem(conn *dbus.Conn, path dbus.ObjectPath) *Item { 13 | return &Item{ 14 | conn: conn, 15 | dbus: conn.Object(DBusServiceName, path), 16 | } 17 | } 18 | 19 | 20 | func (item Item) Path() dbus.ObjectPath { 21 | return item.dbus.Path() 22 | } 23 | 24 | 25 | // READWRITE String Label; 26 | func (item *Item) Label() (string, error) { 27 | val, err := item.dbus.GetProperty("org.freedesktop.Secret.Item.Label") 28 | if err != nil { 29 | return "", err 30 | } 31 | 32 | return val.Value().(string), nil 33 | } 34 | 35 | 36 | // READ Boolean Locked; 37 | func (item *Item) Locked() (bool, error) { 38 | val, err := item.dbus.GetProperty("org.freedesktop.Secret.Item.Locked") 39 | if err != nil { 40 | return true, err 41 | } 42 | 43 | return val.Value().(bool), nil 44 | } 45 | 46 | 47 | // GetSecret (IN ObjectPath session, OUT Secret secret); 48 | func (item *Item) GetSecret(session *Session) (*Secret, error) { 49 | secret := Secret{} 50 | 51 | err := item.dbus.Call("org.freedesktop.Secret.Item.GetSecret", 0, session.Path()).Store(&secret) 52 | if err != nil { 53 | return &Secret{}, err 54 | } 55 | 56 | return &secret, nil 57 | } 58 | 59 | 60 | // Delete (OUT ObjectPath Prompt); 61 | func (item *Item) Delete() error { 62 | var prompt dbus.ObjectPath 63 | 64 | err := item.dbus.Call("org.freedesktop.Secret.Item.Delete", 0).Store(&prompt) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | if isPrompt(prompt) { 70 | prompt := NewPrompt(item.conn, prompt) 71 | if _, err := prompt.Prompt(); err != nil { 72 | return err 73 | } 74 | } 75 | 76 | return nil 77 | } 78 | -------------------------------------------------------------------------------- /prompt.go: -------------------------------------------------------------------------------- 1 | package libsecret 2 | 3 | import ( 4 | "github.com/godbus/dbus" 5 | "strings" 6 | ) 7 | 8 | 9 | type Prompt struct { 10 | conn *dbus.Conn 11 | dbus dbus.BusObject 12 | } 13 | 14 | 15 | func NewPrompt(conn *dbus.Conn, path dbus.ObjectPath) *Prompt { 16 | return &Prompt{ 17 | conn: conn, 18 | dbus: conn.Object(DBusServiceName, path), 19 | } 20 | } 21 | 22 | 23 | func (prompt Prompt) Path() dbus.ObjectPath { 24 | return prompt.dbus.Path() 25 | } 26 | 27 | 28 | func isPrompt(path dbus.ObjectPath) bool { 29 | promptPath := DBusPath + "/prompt/" 30 | return strings.HasPrefix(string(path), promptPath) 31 | } 32 | 33 | 34 | // Prompt (IN String window-id); 35 | func (prompt *Prompt) Prompt() (*dbus.Variant, error) { 36 | // prompts are asynchronous so we connect to the signal 37 | // and block with a channel until we get a response 38 | c := make(chan *dbus.Signal, 10) 39 | defer close(c) 40 | 41 | prompt.conn.Signal(c) 42 | defer prompt.conn.RemoveSignal(c) 43 | 44 | err := prompt.dbus.Call("org.freedesktop.Secret.Prompt.Prompt", 0, "").Store() 45 | if err != nil { 46 | return &dbus.Variant{}, err 47 | } 48 | 49 | for { 50 | if result := <-c; result.Path == prompt.Path() { 51 | value := result.Body[1].(dbus.Variant) 52 | return &value, nil 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /secret.go: -------------------------------------------------------------------------------- 1 | package libsecret 2 | 3 | import "github.com/godbus/dbus" 4 | 5 | 6 | type Secret struct { 7 | Session dbus.ObjectPath 8 | Parameters []byte 9 | Value []byte 10 | ContentType string 11 | } 12 | 13 | 14 | func NewSecret(session *Session, params []byte, value []byte, contentType string) *Secret { 15 | return &Secret{ 16 | Session: session.Path(), 17 | Parameters: params, 18 | Value: value, 19 | ContentType: contentType, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /service.go: -------------------------------------------------------------------------------- 1 | package libsecret 2 | 3 | import "github.com/godbus/dbus" 4 | 5 | 6 | const ( 7 | DBusServiceName = "org.freedesktop.secrets" 8 | DBusPath = "/org/freedesktop/secrets" 9 | ) 10 | 11 | type DBusObject interface { 12 | Path() dbus.ObjectPath 13 | } 14 | 15 | 16 | type Service struct { 17 | conn *dbus.Conn 18 | dbus dbus.BusObject 19 | } 20 | 21 | 22 | func NewService() (*Service, error) { 23 | conn, err := dbus.SessionBus() 24 | if err != nil { 25 | return &Service{}, err 26 | } 27 | 28 | return &Service{ 29 | conn: conn, 30 | dbus: conn.Object(DBusServiceName, DBusPath), 31 | }, nil 32 | } 33 | 34 | 35 | func (service Service) Path() dbus.ObjectPath { 36 | return service.dbus.Path() 37 | } 38 | 39 | 40 | // OpenSession (IN String algorithm, IN Variant input, OUT Variant output, OUT ObjectPath result); 41 | func (service *Service) Open() (*Session, error) { 42 | var output dbus.Variant 43 | var path dbus.ObjectPath 44 | 45 | err := service.dbus.Call("org.freedesktop.Secret.Service.OpenSession", 0, "plain", dbus.MakeVariant("")).Store(&output, &path) 46 | if err != nil { 47 | return &Session{}, err 48 | } 49 | 50 | return NewSession(service.conn, path), nil 51 | } 52 | 53 | 54 | // READ Array Collections; 55 | func (service *Service) Collections() ([]Collection, error) { 56 | val, err := service.dbus.GetProperty("org.freedesktop.Secret.Service.Collections") 57 | if err != nil { 58 | return []Collection{}, err 59 | } 60 | 61 | collections := []Collection{} 62 | for _, path := range val.Value().([]dbus.ObjectPath) { 63 | collections = append(collections, *NewCollection(service.conn, path)) 64 | } 65 | 66 | return collections, nil 67 | } 68 | 69 | 70 | // CreateCollection (IN Dict properties, IN String alias, OUT ObjectPath collection, OUT ObjectPath prompt); 71 | func (service *Service) CreateCollection(label string) (*Collection, error) { 72 | properties := make(map[string]dbus.Variant) 73 | properties["org.freedesktop.Secret.Collection.Label"] = dbus.MakeVariant(label) 74 | 75 | var path dbus.ObjectPath 76 | var prompt dbus.ObjectPath 77 | 78 | err := service.dbus.Call("org.freedesktop.Secret.Service.CreateCollection", 0, properties, "").Store(&path, &prompt) 79 | if err != nil { 80 | return &Collection{}, err 81 | } 82 | 83 | if isPrompt(prompt) { 84 | prompt := NewPrompt(service.conn, prompt) 85 | 86 | result, err := prompt.Prompt() 87 | if err != nil { 88 | return &Collection{}, err 89 | } 90 | 91 | path = result.Value().(dbus.ObjectPath) 92 | } 93 | 94 | return NewCollection(service.conn, path), nil 95 | } 96 | 97 | 98 | // Unlock (IN Array objects, OUT Array unlocked, OUT ObjectPath prompt); 99 | func (service *Service) Unlock(object DBusObject) error { 100 | objects := []dbus.ObjectPath{object.Path()} 101 | 102 | var unlocked []dbus.ObjectPath 103 | var prompt dbus.ObjectPath 104 | 105 | err := service.dbus.Call("org.freedesktop.Secret.Service.Unlock", 0, objects).Store(&unlocked, &prompt) 106 | if err != nil { 107 | return err 108 | } 109 | 110 | if isPrompt(prompt) { 111 | prompt := NewPrompt(service.conn, prompt) 112 | if _, err := prompt.Prompt(); err != nil { 113 | return err 114 | } 115 | } 116 | 117 | return nil 118 | } 119 | 120 | 121 | // Lock (IN Array objects, OUT Array locked, OUT ObjectPath Prompt); 122 | func (service *Service) Lock(object DBusObject) error { 123 | objects := []dbus.ObjectPath{object.Path()} 124 | 125 | var locked []dbus.ObjectPath 126 | var prompt dbus.ObjectPath 127 | 128 | err := service.dbus.Call("org.freedesktop.Secret.Service.Lock", 0, objects).Store(&locked, &prompt) 129 | if err != nil { 130 | return err 131 | } 132 | 133 | if isPrompt(prompt) { 134 | prompt := NewPrompt(service.conn, prompt) 135 | if _, err := prompt.Prompt(); err != nil { 136 | return err 137 | } 138 | } 139 | 140 | return nil 141 | } 142 | -------------------------------------------------------------------------------- /session.go: -------------------------------------------------------------------------------- 1 | package libsecret 2 | 3 | import "github.com/godbus/dbus" 4 | 5 | 6 | type Session struct { 7 | conn *dbus.Conn 8 | dbus dbus.BusObject 9 | } 10 | 11 | 12 | func NewSession(conn *dbus.Conn, path dbus.ObjectPath) *Session { 13 | return &Session{ 14 | conn: conn, 15 | dbus: conn.Object(DBusServiceName, path), 16 | } 17 | } 18 | 19 | 20 | func (session Session) Path() dbus.ObjectPath { 21 | return session.dbus.Path() 22 | } 23 | --------------------------------------------------------------------------------