collection.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. package keychainlinker
  2. import (
  3. "fmt"
  4. "strconv"
  5. "github.com/godbus/dbus/v5"
  6. "github.com/godbus/dbus/v5/introspect"
  7. )
  8. type CacheItem struct {
  9. Secret SecretStruct
  10. Label string
  11. LookupProps map[string]string
  12. }
  13. type Collection struct {
  14. /*
  15. Implying the org.freedesktop.Secret.Collection interface as per the v0.2 spec:
  16. https://specifications.freedesktop.org/secret-service-spec/latest-single/#org.freedesktop.Secret.Collection
  17. */
  18. Items []dbus.ObjectPath // items in the collection
  19. Cache map[dbus.ObjectPath]CacheItem // memory cache for the collection. Will be removed later
  20. PathCount int
  21. PathBase string
  22. Private string // specifies whether the collection is private or not
  23. Label string // The displayable label of this collection.
  24. Locked string // Whether the collection is locked and must be authenticated by the client application.
  25. Created uint64 // The unix time when the collection was created.
  26. Modified uint64 // The unix time when the collection was last modified.
  27. }
  28. func (c *Collection) GetAll(iface string) (map[string]dbus.Variant, *dbus.Error) {
  29. if iface == "org.freedesktop.Secret.Collection" {
  30. return map[string]dbus.Variant{
  31. "Label": dbus.MakeVariant(c.Label),
  32. "Locked": dbus.MakeVariant(c.Locked),
  33. "Created": dbus.MakeVariant(c.Created),
  34. "Modified": dbus.MakeVariant(c.Modified),
  35. "Items": dbus.MakeVariant(c.Items),
  36. }, nil
  37. }
  38. return nil, dbus.MakeFailedError(fmt.Errorf("no such interface"))
  39. }
  40. /*
  41. Create a path to assign the secret
  42. */
  43. func (c *Collection) pathBuilder() dbus.ObjectPath {
  44. return dbus.ObjectPath(c.PathBase + "/" + strconv.Itoa(c.PathCount+1))
  45. }
  46. // deletes the collection, returning an object path tied to a prompt incase it is necessary.
  47. func (c *Collection) Delete() (dbus.ObjectPath, *dbus.Error) {
  48. return dbus.ObjectPath("prompt"), nil
  49. }
  50. /*
  51. Searches the collection for matching items
  52. :param attr: the attributes to attempt to match to a key in the collection
  53. */
  54. func (c *Collection) SearchItems(attr map[string]string) ([]dbus.ObjectPath, *dbus.Error) {
  55. fmt.Println("recv call for SearchItems")
  56. matched := []dbus.ObjectPath{}
  57. for path, sec := range c.Cache {
  58. secretAttr := sec.LookupProps
  59. var passedLookup bool
  60. passedLookup = false
  61. for k, v := range attr {
  62. got, ok := secretAttr[k]
  63. if !ok {
  64. continue
  65. }
  66. if got == v {
  67. passedLookup = true
  68. continue
  69. }
  70. }
  71. if passedLookup {
  72. matched = append(matched, path)
  73. }
  74. }
  75. return matched, nil
  76. }
  77. /*
  78. Creates a new item in the collection with the properties defined in 'props'.
  79. Returns the items dbus object path, as well as a path to a dbus prompt incase it is required to edit
  80. :param props: a map of properties to assign to the item. Will be used to match during lookups
  81. :param secret: the secret to encode into the collection
  82. :param replace: replace secret if a matching one is found in the store
  83. */
  84. func (c *Collection) CreateItem(props map[string]dbus.Variant, secret SecretStruct, replace bool) (dbus.ObjectPath, dbus.ObjectPath, *dbus.Error) {
  85. v := props["org.freedesktop.Secret.Item.Attributes"]
  86. attrs, ok := v.Value().(map[string]string)
  87. if !ok {
  88. return dbus.ObjectPath("/"), dbus.ObjectPath("/"), &dbus.ErrMsgNoObject
  89. }
  90. label, ok := props["org.freedesktop.Secret.Item.Label"].Value().(string)
  91. if !ok {
  92. // no label found
  93. label = ""
  94. }
  95. if !replace {
  96. // implement the the replace option
  97. }
  98. path := c.pathBuilder()
  99. c.Cache[path] = CacheItem{
  100. LookupProps: attrs,
  101. Label: label,
  102. Secret: secret,
  103. }
  104. return path, dbus.ObjectPath("/"), nil
  105. }
  106. // implementing method to read the object property
  107. func (c *Collection) Get(iface, property string) (dbus.Variant, *dbus.Error) {
  108. if iface != "org/freedesktop/secret/service" {
  109. return dbus.Variant{}, dbus.MakeFailedError(dbus.ErrMsgUnknownInterface)
  110. }
  111. switch property {
  112. case "Items":
  113. return dbus.MakeVariant(c.Items), nil
  114. case "Label":
  115. return dbus.MakeVariant(c.Label), nil
  116. case "Locked":
  117. return dbus.MakeVariant(c.Locked), nil
  118. case "Created":
  119. return dbus.MakeVariant(c.Created), nil
  120. case "Modified":
  121. return dbus.MakeVariant(c.Modified), nil
  122. default:
  123. return dbus.Variant{}, dbus.MakeFailedError(dbus.ErrMsgUnknownMethod)
  124. }
  125. }
  126. // implementing method to read the object property
  127. func (c *Collection) Set(iface, property string, value dbus.Variant) *dbus.Error {
  128. if iface != "org/freedesktop/secret/service" {
  129. return dbus.MakeFailedError(dbus.ErrMsgUnknownInterface)
  130. }
  131. switch property {
  132. case "Label":
  133. label, ok := value.Value().(string)
  134. if !ok {
  135. return dbus.MakeFailedError(dbus.ErrMsgInvalidArg)
  136. }
  137. c.Label = label
  138. return nil
  139. default:
  140. return dbus.MakeFailedError(dbus.ErrMsgUnknownMethod)
  141. }
  142. }
  143. var CollectionNode = introspect.Node{
  144. Name: "/org/freedesktop/secrets/collection/default", // or your collection path
  145. Interfaces: []introspect.Interface{
  146. {
  147. Name: "org.freedesktop.Secret.Collection",
  148. Methods: []introspect.Method{
  149. {
  150. Name: "CreateItem",
  151. Args: []introspect.Arg{
  152. {Name: "properties", Type: "a{sv}", Direction: "in"},
  153. {Name: "secret", Type: "v", Direction: "in"},
  154. {Name: "replace", Type: "b", Direction: "in"},
  155. {Name: "item", Type: "o", Direction: "out"},
  156. {Name: "prompt", Type: "o", Direction: "out"},
  157. },
  158. },
  159. {
  160. Name: "SearchItems",
  161. Args: []introspect.Arg{
  162. {Name: "attributes", Type: "a{ss}", Direction: "in"},
  163. {Name: "unlocked", Type: "ao", Direction: "out"},
  164. {Name: "locked", Type: "ao", Direction: "out"},
  165. },
  166. },
  167. {
  168. Name: "Delete",
  169. Args: []introspect.Arg{
  170. {Name: "prompt", Type: "o", Direction: "out"},
  171. },
  172. },
  173. },
  174. Properties: []introspect.Property{
  175. {Name: "Items", Type: "ao", Access: "read"},
  176. {Name: "Label", Type: "s", Access: "readwrite"},
  177. {Name: "Locked", Type: "b", Access: "read"},
  178. {Name: "Created", Type: "t", Access: "read"},
  179. {Name: "Modified", Type: "t", Access: "read"},
  180. },
  181. },
  182. {
  183. Name: "org.freedesktop.DBus.Introspectable",
  184. Methods: []introspect.Method{
  185. {
  186. Name: "Introspect",
  187. Args: []introspect.Arg{
  188. {Name: "data", Type: "s", Direction: "out"},
  189. },
  190. },
  191. },
  192. },
  193. },
  194. }