1
0

8 Коммиты 2946fa107c ... fe40f9713a

Автор SHA1 Сообщение Дата
  aeth fe40f9713a working on publishing to dbus properly 6 месяцев назад
  aeth 057ce25175 working through publishing the interface properly 6 месяцев назад
  aeth a29564f47e merging with upstream 6 месяцев назад
  aeth a7b907b5a5 stuff 9 месяцев назад
  aeth 2a4592a89b fixed checkboxes 9 месяцев назад
  aeth ec742bf15b fixed checkboxes 9 месяцев назад
  aeth cf6b4479e0 added quick link for secret api ref 9 месяцев назад
  aeth 1329fb18f4 added quick link for secret api ref 9 месяцев назад
7 измененных файлов с 469 добавлено и 49 удалено
  1. 10 1
      README.md
  2. 48 28
      main.go
  3. 142 12
      pkg/collection.go
  4. 196 7
      pkg/service.go
  5. 44 1
      pkg/xml.go
  6. 29 0
      scripts/test_add.py
  7. BIN
      test

+ 10 - 1
README.md

@@ -1,3 +1,12 @@
 # keychain-linker
 # keychain-linker
 
 
-Linker system to bridge desktop clients attempting to query the org.freedesktop.Secret API to key management servers such as Vaultwarden, Hashicorp Vault, and the like
+Linker system to bridge desktop clients attempting to query the org.freedesktop.Secret API to key management servers such as Vaultwarden, Hashicorp Vault, and the like  
+API Reference: https://specifications.freedesktop.org/secret-service-spec/latest-single/#description  
+
+todos  
+- [x] implement org.freedesktop.Secret.Service 
+- [x] implement org.freedesktop.Secret.Collection   
+- [ ] implement org.freedesktop.Secret.Item  
+- [x] implement org.freedesktop.Secret.Session   
+- [x] implementorg.freedesktop.Secret.Prompt  
+- [ ] PoC write/read bridge for vaultwarden 

+ 48 - 28
main.go

@@ -9,6 +9,12 @@ import (
 	"github.com/godbus/dbus/v5/introspect"
 	"github.com/godbus/dbus/v5/introspect"
 )
 )
 
 
+const (
+	dbusName = "org.freedesktop.secrets"
+	objPath  = "/org/freedesktop/secrets/collection/default"
+	iface    = "org.freedesktop.Secret.Collection"
+)
+
 func main() {
 func main() {
 	conn, err := dbus.ConnectSessionBus()
 	conn, err := dbus.ConnectSessionBus()
 	if err != nil {
 	if err != nil {
@@ -16,41 +22,55 @@ func main() {
 	}
 	}
 	defer conn.Close()
 	defer conn.Close()
 
 
-	path := "/dev/aetherial/KeychainLinker"
-	session := &keychainlinker.Service{SessionBase: "/dev/aetherial/KeychainLinker/session/",
-		CollectionBase: "/dev/aetherial/KeychainLinker/collection/",
-		Collections:    []dbus.ObjectPath{}}
+	path := "/org/freedesktop/secrets"
+	service := keychainlinker.NewService(dbus.ObjectPath(path))
+
+	conn.Export(service, dbus.ObjectPath(path), "org.freedesktop.Secret.Service")
 
 
-	conn.Export(session, dbus.ObjectPath(path), "dev.aetherial.git.KeychainLinker.Service")
 	conn.Export(introspect.Introspectable(keychainlinker.DbusAdv), dbus.ObjectPath(path),
 	conn.Export(introspect.Introspectable(keychainlinker.DbusAdv), dbus.ObjectPath(path),
 		"org.freedesktop.DBus.Introspectable")
 		"org.freedesktop.DBus.Introspectable")
-	node := introspect.Node{
-		Interfaces: []introspect.Interface{
-			{
-				Name: "org.freedesktop.secret.Service",
-				Methods: []introspect.Method{
-					{Name: "Get", Args: []introspect.Arg{{Name: "interface_name", Type: "s", Direction: "in"}, {Name: "property_name", Type: "s", Direction: "in"}, {Name: "value", Type: "v", Direction: "out"}}},
-					{Name: "Set", Args: []introspect.Arg{{Name: "interface_name", Type: "s", Direction: "in"}, {Name: "property_name", Type: "s", Direction: "in"}, {Name: "value", Type: "v", Direction: "in"}}},
-					{Name: "GetAll", Args: []introspect.Arg{{Name: "interface_name", Type: "s", Direction: "in"}, {Name: "props", Type: "a{sv}", Direction: "out"}}},
+
+	/*
+		node := introspect.Node{
+			Interfaces: []introspect.Interface{
+				{
+					Name: "org.freedesktop.Secret.Service",
+					Methods: []introspect.Method{
+						{Name: "Get", Args: []introspect.Arg{{Name: "interface_name", Type: "s", Direction: "in"}, {Name: "property_name", Type: "s", Direction: "in"}, {Name: "value", Type: "v", Direction: "out"}}},
+						{Name: "Set", Args: []introspect.Arg{{Name: "interface_name", Type: "s", Direction: "in"}, {Name: "property_name", Type: "s", Direction: "in"}, {Name: "value", Type: "v", Direction: "in"}}},
+						{Name: "GetAll", Args: []introspect.Arg{{Name: "interface_name", Type: "s", Direction: "in"}, {Name: "props", Type: "a{sv}", Direction: "out"}}},
+					},
 				},
 				},
 			},
 			},
-		},
-	}
-	conn.Export(introspect.Introspectable(introspect.NewIntrospectable(&node)), "/org/freedesktop/secret", "org.freedesktop.DBus.Introspectable")
-	node = introspect.Node{
-		Interfaces: []introspect.Interface{
-			{
-				Name: "org.freedesktop.secret.Collection",
-				Methods: []introspect.Method{
-					{Name: "Get", Args: []introspect.Arg{{Name: "interface_name", Type: "s", Direction: "in"}, {Name: "property_name", Type: "s", Direction: "in"}, {Name: "value", Type: "v", Direction: "out"}}},
-					{Name: "Set", Args: []introspect.Arg{{Name: "interface_name", Type: "s", Direction: "in"}, {Name: "property_name", Type: "s", Direction: "in"}, {Name: "value", Type: "v", Direction: "in"}}},
+		}
+	*/
+	conn.Export(introspect.NewIntrospectable(&keychainlinker.ServiceNode), dbus.ObjectPath(path), "org.freedesktop.DBus.Introspectable")
+	collection := &keychainlinker.Collection{}
+	conn.Export(collection, objPath, iface)
+	/*
+		collectionNode := introspect.Node{
+			Interfaces: []introspect.Interface{
+				{
+					Name: iface,
+					Methods: []introspect.Method{
+						{Name: "Get", Args: []introspect.Arg{{Name: "interface_name", Type: "s", Direction: "in"}, {Name: "property_name", Type: "s", Direction: "in"}, {Name: "value", Type: "v", Direction: "out"}}},
+						{Name: "Set", Args: []introspect.Arg{{Name: "interface_name", Type: "s", Direction: "in"}, {Name: "property_name", Type: "s", Direction: "in"}, {Name: "value", Type: "v", Direction: "in"}}},
+						{
+							Name: "SearchItems",
+							Args: []introspect.Arg{
+								{Name: "attributes", Type: "a{ss}", Direction: "in"},
+								{Name: "locked", Type: "ao", Direction: "out"},
+								{Name: "unlocked", Type: "ao", Direction: "out"},
+							},
+						},
+					},
 				},
 				},
 			},
 			},
-		},
-	}
-	conn.Export(introspect.Introspectable(introspect.NewIntrospectable(&node)), "/org/freedesktop/secret", "org.freedesktop.DBus.IntrospectData")
+		}
+	*/
+	conn.Export(introspect.NewIntrospectable(&keychainlinker.CollectionNode), dbus.ObjectPath(path+"/collection/default"), "org.freedesktop.DBus.Introspectable")
 
 
-	reply, err := conn.RequestName("dev.aetherial.git.KeychainLinker.Service",
+	reply, err := conn.RequestName(dbusName,
 		dbus.NameFlagDoNotQueue)
 		dbus.NameFlagDoNotQueue)
 	if err != nil {
 	if err != nil {
 		panic(err)
 		panic(err)
@@ -59,6 +79,6 @@ func main() {
 		fmt.Fprintln(os.Stderr, "name already taken")
 		fmt.Fprintln(os.Stderr, "name already taken")
 		os.Exit(1)
 		os.Exit(1)
 	}
 	}
-	fmt.Println("Listening on dev.aetherial.git.KeychainLinker.Service / /dev/aetherial/git/KeychainLinker/Service ...")
+	fmt.Println("Listening on org.freedesktop.secrets / /org/freedesktop/secrets ...")
 	select {}
 	select {}
 }
 }

+ 142 - 12
pkg/collection.go

@@ -1,18 +1,54 @@
 package keychainlinker
 package keychainlinker
 
 
-import "github.com/godbus/dbus/v5"
+import (
+	"fmt"
+	"strconv"
+
+	"github.com/godbus/dbus/v5"
+	"github.com/godbus/dbus/v5/introspect"
+)
+
+type CacheItem struct {
+	Secret      SecretStruct
+	Label       string
+	LookupProps map[string]string
+}
 
 
 type Collection struct {
 type Collection struct {
 	/*
 	/*
 		Implying the org.freedesktop.Secret.Collection interface as per the v0.2 spec:
 		Implying the org.freedesktop.Secret.Collection interface as per the v0.2 spec:
 		https://specifications.freedesktop.org/secret-service-spec/latest-single/#org.freedesktop.Secret.Collection
 		https://specifications.freedesktop.org/secret-service-spec/latest-single/#org.freedesktop.Secret.Collection
 	*/
 	*/
-	Items    []dbus.ObjectPath // items in the collection
-	Private  string            // specifies whether the collection is private or not
-	Label    string            //  The displayable label of this collection.
-	Locked   string            //  Whether the collection is locked and must be authenticated by the client application.
-	Created  uint64            //  The unix time when the collection was created.
-	Modified uint64            //  The unix time when the collection was last modified.
+	Items     []dbus.ObjectPath             // items in the collection
+	Cache     map[dbus.ObjectPath]CacheItem // memory cache for the collection. Will be removed later
+	PathCount int
+	PathBase  string
+	Private   string // specifies whether the collection is private or not
+	Label     string //  The displayable label of this collection.
+	Locked    string //  Whether the collection is locked and must be authenticated by the client application.
+	Created   uint64 //  The unix time when the collection was created.
+	Modified  uint64 //  The unix time when the collection was last modified.
+}
+
+func (c *Collection) GetAll(iface string) (map[string]dbus.Variant, *dbus.Error) {
+	if iface == "org.freedesktop.Secret.Collection" {
+		return map[string]dbus.Variant{
+			"Label":    dbus.MakeVariant(c.Label),
+			"Locked":   dbus.MakeVariant(c.Locked),
+			"Created":  dbus.MakeVariant(c.Created),
+			"Modified": dbus.MakeVariant(c.Modified),
+			"Items":    dbus.MakeVariant(c.Items),
+		}, nil
+	}
+	return nil, dbus.MakeFailedError(fmt.Errorf("no such interface"))
+}
+
+/*
+Create a path to assign the secret
+*/
+func (c *Collection) pathBuilder() dbus.ObjectPath {
+	return dbus.ObjectPath(c.PathBase + "/" + strconv.Itoa(c.PathCount+1))
+
 }
 }
 
 
 // deletes the collection, returning an object path tied to a prompt incase it is necessary.
 // deletes the collection, returning an object path tied to a prompt incase it is necessary.
@@ -26,20 +62,61 @@ Searches the collection for matching items
 	:param attr: the attributes to attempt to match to a key in the collection
 	:param attr: the attributes to attempt to match to a key in the collection
 */
 */
 func (c *Collection) SearchItems(attr map[string]string) ([]dbus.ObjectPath, *dbus.Error) {
 func (c *Collection) SearchItems(attr map[string]string) ([]dbus.ObjectPath, *dbus.Error) {
-	// implement a recursive searching thing
-	return []dbus.ObjectPath{}, nil
+	fmt.Println("recv call for SearchItems")
+	matched := []dbus.ObjectPath{}
+	for path, sec := range c.Cache {
+		secretAttr := sec.LookupProps
+		var passedLookup bool
+		passedLookup = false
+		for k, v := range attr {
+			got, ok := secretAttr[k]
+			if !ok {
+				continue
+			}
+			if got == v {
+				passedLookup = true
+				continue
+			}
+		}
+		if passedLookup {
+			matched = append(matched, path)
+		}
+
+	}
+
+	return matched, nil
 }
 }
 
 
 /*
 /*
 Creates a new item in the collection with the properties defined in 'props'.
 Creates a new item in the collection with the properties defined in 'props'.
 Returns the items dbus object path, as well as a path to a dbus prompt incase it is required to edit
 Returns the items dbus object path, as well as a path to a dbus prompt incase it is required to edit
 
 
-	:param fields: a map of properties to assign to the item. Will be used to match during lookups
+	:param props: a map of properties to assign to the item. Will be used to match during lookups
 	:param secret: the secret to encode into the collection
 	:param secret: the secret to encode into the collection
 	:param replace: replace secret if a matching one is found in the store
 	:param replace: replace secret if a matching one is found in the store
 */
 */
-func (c *Collection) CreateItem(fields map[string]dbus.Variant, secret SecretStruct, replace bool) (dbus.ObjectPath, dbus.ObjectPath, *dbus.Error) {
-	return dbus.ObjectPath("/"), dbus.ObjectPath("/"), nil
+func (c *Collection) CreateItem(props map[string]dbus.Variant, secret SecretStruct, replace bool) (dbus.ObjectPath, dbus.ObjectPath, *dbus.Error) {
+	v := props["org.freedesktop.Secret.Item.Attributes"]
+	attrs, ok := v.Value().(map[string]string)
+	if !ok {
+		return dbus.ObjectPath("/"), dbus.ObjectPath("/"), &dbus.ErrMsgNoObject
+	}
+	label, ok := props["org.freedesktop.Secret.Item.Label"].Value().(string)
+	if !ok {
+		// no label found
+		label = ""
+	}
+	if !replace {
+		// implement the the replace option
+	}
+	path := c.pathBuilder()
+	c.Cache[path] = CacheItem{
+		LookupProps: attrs,
+		Label:       label,
+		Secret:      secret,
+	}
+
+	return path, dbus.ObjectPath("/"), nil
 }
 }
 
 
 // implementing method to read the object property
 // implementing method to read the object property
@@ -81,3 +158,56 @@ func (c *Collection) Set(iface, property string, value dbus.Variant) *dbus.Error
 	}
 	}
 
 
 }
 }
+
+var CollectionNode = introspect.Node{
+	Name: "/org/freedesktop/secrets/collection/default", // or your collection path
+	Interfaces: []introspect.Interface{
+		{
+			Name: "org.freedesktop.Secret.Collection",
+			Methods: []introspect.Method{
+				{
+					Name: "CreateItem",
+					Args: []introspect.Arg{
+						{Name: "properties", Type: "a{sv}", Direction: "in"},
+						{Name: "secret", Type: "v", Direction: "in"},
+						{Name: "replace", Type: "b", Direction: "in"},
+						{Name: "item", Type: "o", Direction: "out"},
+						{Name: "prompt", Type: "o", Direction: "out"},
+					},
+				},
+				{
+					Name: "SearchItems",
+					Args: []introspect.Arg{
+						{Name: "attributes", Type: "a{ss}", Direction: "in"},
+						{Name: "unlocked", Type: "ao", Direction: "out"},
+						{Name: "locked", Type: "ao", Direction: "out"},
+					},
+				},
+				{
+					Name: "Delete",
+					Args: []introspect.Arg{
+						{Name: "prompt", Type: "o", Direction: "out"},
+					},
+				},
+			},
+			Properties: []introspect.Property{
+				{Name: "Items", Type: "ao", Access: "read"},
+				{Name: "Label", Type: "s", Access: "readwrite"},
+				{Name: "Locked", Type: "b", Access: "read"},
+				{Name: "Created", Type: "t", Access: "read"},
+				{Name: "Modified", Type: "t", Access: "read"},
+			},
+		},
+		{
+			Name: "org.freedesktop.DBus.Introspectable",
+			Methods: []introspect.Method{
+				{
+					Name: "Introspect",
+					Args: []introspect.Arg{
+						{Name: "data", Type: "s", Direction: "out"},
+					},
+				},
+			},
+		},
+	},
+}

+ 196 - 7
pkg/service.go

@@ -3,23 +3,35 @@ package keychainlinker
 import (
 import (
 	"fmt"
 	"fmt"
 	"path"
 	"path"
+	"strconv"
+	"time"
 
 
 	"github.com/godbus/dbus/v5"
 	"github.com/godbus/dbus/v5"
+	"github.com/godbus/dbus/v5/introspect"
 )
 )
 
 
+const DEFAULT_COLLECTION = "/org/freedesktop/secrets/collections/default"
+
+type Cache struct {
+	Collections map[dbus.ObjectPath]Collection
+}
+
 type Service struct {
 type Service struct {
 	/*
 	/*
 		Working on implementing the org.freedesktop.Secret.Service interface, from their v0.2 spec:
 		Working on implementing the org.freedesktop.Secret.Service interface, from their v0.2 spec:
 		https://specifications.freedesktop.org/secret-service-spec/latest-single/#org.freedesktop.Secret.Service
 		https://specifications.freedesktop.org/secret-service-spec/latest-single/#org.freedesktop.Secret.Service
 	*/
 	*/
+	Cache          map[dbus.ObjectPath]*Collection
 	Collections    []dbus.ObjectPath
 	Collections    []dbus.ObjectPath
+	PathCount      int
+	Alias          map[string]dbus.ObjectPath
 	SessionBase    string // e.g. "/org/freedesktop/secrets/session/"
 	SessionBase    string // e.g. "/org/freedesktop/secrets/session/"
 	CollectionBase string // e.g. "/org/freedesktop/secrets/collection/"
 	CollectionBase string // e.g. "/org/freedesktop/secrets/collection/"
 }
 }
 
 
 // implementing method to read the object property
 // implementing method to read the object property
 func (s *Service) Get(iface, property string) (dbus.Variant, *dbus.Error) {
 func (s *Service) Get(iface, property string) (dbus.Variant, *dbus.Error) {
-	if iface != "org/freedesktop/secret/service" {
+	if iface != "/org/freedesktop/secrets/service" {
 		return dbus.Variant{}, dbus.MakeFailedError(dbus.ErrMsgUnknownInterface)
 		return dbus.Variant{}, dbus.MakeFailedError(dbus.ErrMsgUnknownInterface)
 	}
 	}
 	switch property {
 	switch property {
@@ -32,7 +44,7 @@ func (s *Service) Get(iface, property string) (dbus.Variant, *dbus.Error) {
 
 
 // implementing method to read the object property
 // implementing method to read the object property
 func (s *Service) Set(iface, property string, value dbus.Variant) *dbus.Error {
 func (s *Service) Set(iface, property string, value dbus.Variant) *dbus.Error {
-	if iface != "org/freedesktop/secret/service" {
+	if iface != "/org/freedesktop/secrets/service" {
 		return dbus.MakeFailedError(dbus.ErrMsgUnknownInterface)
 		return dbus.MakeFailedError(dbus.ErrMsgUnknownInterface)
 	}
 	}
 	switch property {
 	switch property {
@@ -51,7 +63,7 @@ func (s *Service) Set(iface, property string, value dbus.Variant) *dbus.Error {
 
 
 // implementing the get all method for the dbus interface
 // implementing the get all method for the dbus interface
 func (s *Service) GetAll(iface string) (map[string]dbus.Variant, *dbus.Error) {
 func (s *Service) GetAll(iface string) (map[string]dbus.Variant, *dbus.Error) {
-	if iface != "org.freedesktop.secret.Service" {
+	if iface != "/org/freedesktop/secrets/service" {
 		return nil, dbus.MakeFailedError(dbus.ErrMsgUnknownInterface)
 		return nil, dbus.MakeFailedError(dbus.ErrMsgUnknownInterface)
 	}
 	}
 
 
@@ -60,6 +72,56 @@ func (s *Service) GetAll(iface string) (map[string]dbus.Variant, *dbus.Error) {
 	}, nil
 	}, nil
 }
 }
 
 
+/*
+implementing the properties interface
+func (s *Service) Get(iface, property string) (dbus.Variant, *dbus.Error) {
+
+		if iface == "org.freedesktop.Secret.Service" && property == "Collections" {
+			return dbus.MakeVariant(s.Collections), nil
+		}
+		return dbus.Variant{}, dbus.MakeFailedError(fmt.Errorf("no such property"))
+	}
+
+	func (s *Service) GetAll(iface string) (map[string]dbus.Variant, *dbus.Error) {
+		if iface == "org.freedesktop.Secret.Service" {
+			return map[string]dbus.Variant{
+				"Collections": dbus.MakeVariant(s.Collections),
+			}, nil
+		}
+		return nil, dbus.MakeFailedError(fmt.Errorf("no such interface"))
+	}
+
+/*
+Create a new service interface
+*/
+func NewService(base dbus.ObjectPath) *Service {
+	return &Service{
+		Cache: map[dbus.ObjectPath]*Collection{
+			DEFAULT_COLLECTION: {
+				Items: []dbus.ObjectPath{
+					"/",
+				},
+				Label:     "default",
+				Cache:     map[dbus.ObjectPath]CacheItem{},
+				PathCount: 0,
+				PathBase:  fmt.Sprintf("%s/collection/default", base),
+				Private:   "true",
+				Created:   uint64(time.Now().Unix()),
+				Modified:  uint64(time.Now().Unix()),
+			},
+		},
+		Collections: []dbus.ObjectPath{
+			DEFAULT_COLLECTION,
+		},
+		PathCount: 0,
+		Alias: map[string]dbus.ObjectPath{
+			"default": DEFAULT_COLLECTION,
+		},
+		SessionBase:    fmt.Sprintf("%s/session", base),
+		CollectionBase: fmt.Sprintf("%s/collection", base),
+	}
+}
+
 /*
 /*
 Opens a session for the Secret Service Interface
 Opens a session for the Secret Service Interface
 
 
@@ -70,8 +132,8 @@ func (s *Service) OpenSession(algorithm string, input dbus.Variant) (dbus.Varian
 	if algorithm != "PLAIN" {
 	if algorithm != "PLAIN" {
 		return dbus.Variant{}, "/", dbus.MakeFailedError(fmt.Errorf("only PLAIN is supported"))
 		return dbus.Variant{}, "/", dbus.MakeFailedError(fmt.Errorf("only PLAIN is supported"))
 	}
 	}
-
 	sessionPath := dbus.ObjectPath(path.Join(s.SessionBase, "1"))
 	sessionPath := dbus.ObjectPath(path.Join(s.SessionBase, "1"))
+	fmt.Println(sessionPath)
 	return input, sessionPath, nil
 	return input, sessionPath, nil
 }
 }
 
 
@@ -82,8 +144,11 @@ Creates a collection with the Service object
 	:param alias: the shortname of the collection
 	:param alias: the shortname of the collection
 */
 */
 func (s *Service) CreateCollection(properties map[string]dbus.Variant, alias string) (dbus.ObjectPath, dbus.ObjectPath, *dbus.Error) {
 func (s *Service) CreateCollection(properties map[string]dbus.Variant, alias string) (dbus.ObjectPath, dbus.ObjectPath, *dbus.Error) {
-	collPath := dbus.ObjectPath(path.Join(s.CollectionBase, "login"))
+	fmt.Printf("CreateCollection called with: %+v (alias: %s)\n", properties, alias)
+	collPath := dbus.ObjectPath(path.Join(s.CollectionBase, strconv.Itoa(s.PathCount+1)))
 	s.Collections = append(s.Collections, collPath)
 	s.Collections = append(s.Collections, collPath)
+	s.PathCount = s.PathCount + 1
+	s.Alias[alias] = collPath
 	return collPath, "/", nil
 	return collPath, "/", nil
 }
 }
 
 
@@ -94,7 +159,16 @@ search for items in the keychain that satisfy 'attrs'
 */
 */
 func (s *Service) SearchItems(attrs map[string]string) ([]dbus.ObjectPath, []dbus.ObjectPath, *dbus.Error) {
 func (s *Service) SearchItems(attrs map[string]string) ([]dbus.ObjectPath, []dbus.ObjectPath, *dbus.Error) {
 	// Just return empty results for now
 	// Just return empty results for now
-	return []dbus.ObjectPath{}, []dbus.ObjectPath{}, nil
+	found := []dbus.ObjectPath{}
+	for _, v := range s.Cache {
+		f, err := v.SearchItems(attrs)
+		if err != nil {
+			return []dbus.ObjectPath{}, []dbus.ObjectPath{}, err
+		}
+		found = append(found, f...)
+
+	}
+	return found, []dbus.ObjectPath{}, nil
 }
 }
 
 
 /*
 /*
@@ -131,7 +205,11 @@ Return a collection based on the alias name
 	:param name: the alias to search for
 	:param name: the alias to search for
 */
 */
 func (s *Service) ReadAlias(name string) (dbus.ObjectPath, *dbus.Error) {
 func (s *Service) ReadAlias(name string) (dbus.ObjectPath, *dbus.Error) {
-	return dbus.ObjectPath(""), nil
+	objectPath, ok := s.Alias[name]
+	if !ok {
+		return dbus.ObjectPath("/"), &dbus.ErrMsgNoObject
+	}
+	return objectPath, nil
 
 
 }
 }
 
 
@@ -142,6 +220,117 @@ set the alias of the passed in collection
 	:param collection: the collection to modify
 	:param collection: the collection to modify
 */
 */
 func (s *Service) SetAlias(name string, collection dbus.ObjectPath) *dbus.Error {
 func (s *Service) SetAlias(name string, collection dbus.ObjectPath) *dbus.Error {
+	s.Alias[name] = collection
 	return nil
 	return nil
 
 
 }
 }
+
+var ServiceNode = introspect.Node{
+	Interfaces: []introspect.Interface{
+		{
+			Name: "org.freedesktop.Secret.Service",
+			Methods: []introspect.Method{
+				{
+					Name: "OpenSession",
+					Args: []introspect.Arg{
+						{Name: "algorithm", Type: "s", Direction: "in"},
+						{Name: "input", Type: "v", Direction: "in"},
+						{Name: "output", Type: "(sv)", Direction: "out"},
+						{Name: "session_path", Type: "o", Direction: "out"},
+					},
+				},
+				{
+					Name: "CreateCollection",
+					Args: []introspect.Arg{
+						{Name: "properties", Type: "a{sv}", Direction: "in"},
+						{Name: "alias", Type: "s", Direction: "in"},
+						{Name: "collection", Type: "o", Direction: "out"},
+						{Name: "prompt", Type: "o", Direction: "out"},
+					},
+				},
+				{
+					Name: "SearchItems",
+					Args: []introspect.Arg{
+						{Name: "attributes", Type: "a{ss}", Direction: "in"},
+						{Name: "locked", Type: "ao", Direction: "out"},
+						{Name: "unlocked", Type: "ao", Direction: "out"},
+					},
+				},
+				{
+					Name: "Unlock",
+					Args: []introspect.Arg{
+						{Name: "objects", Type: "ao", Direction: "in"},
+						{Name: "unlocked", Type: "ao", Direction: "out"},
+						{Name: "prompt", Type: "o", Direction: "out"},
+					},
+				},
+				{
+					Name: "Lock",
+					Args: []introspect.Arg{
+						{Name: "objects", Type: "ao", Direction: "in"},
+						{Name: "locked", Type: "ao", Direction: "out"},
+						{Name: "prompt", Type: "o", Direction: "out"},
+					},
+				},
+				{
+					Name: "GetSecrets",
+					Args: []introspect.Arg{
+						{Name: "items", Type: "ao", Direction: "in"},
+						{Name: "session", Type: "o", Direction: "in"},
+						{Name: "secrets", Type: "a{oa(yays)}", Direction: "out"},
+					},
+				},
+				{
+					Name: "ReadAlias",
+					Args: []introspect.Arg{
+						{Name: "name", Type: "s", Direction: "in"},
+						{Name: "collection", Type: "o", Direction: "out"},
+					},
+				},
+				{
+					Name: "SetAlias",
+					Args: []introspect.Arg{
+						{Name: "name", Type: "s", Direction: "in"},
+						{Name: "collection", Type: "o", Direction: "in"},
+					},
+				},
+			},
+			Properties: []introspect.Property{
+				{
+					Name:   "Collections",
+					Type:   "ao",
+					Access: "read",
+				},
+			},
+		},
+		{
+			Name: "org.freedesktop.DBus.Properties",
+			Methods: []introspect.Method{
+				{
+					Name: "Get",
+					Args: []introspect.Arg{
+						{Name: "interface_name", Type: "s", Direction: "in"},
+						{Name: "property_name", Type: "s", Direction: "in"},
+						{Name: "value", Type: "v", Direction: "out"},
+					},
+				},
+				{
+					Name: "Set",
+					Args: []introspect.Arg{
+						{Name: "interface_name", Type: "s", Direction: "in"},
+						{Name: "property_name", Type: "s", Direction: "in"},
+						{Name: "value", Type: "v", Direction: "in"},
+					},
+				},
+				{
+					Name: "GetAll",
+					Args: []introspect.Arg{
+						{Name: "interface_name", Type: "s", Direction: "in"},
+						{Name: "props", Type: "a{sv}", Direction: "out"},
+					},
+				},
+			},
+		},
+		introspect.IntrospectData,
+	},
+}

+ 44 - 1
pkg/xml.go

@@ -4,9 +4,52 @@ import (
 	"github.com/godbus/dbus/v5/introspect"
 	"github.com/godbus/dbus/v5/introspect"
 )
 )
 
 
+const CollectionAdv = `
+
+	  <interface name="org.freedesktop.Secret.Collection">
+
+		<property name="Label" type="s" access="readwrite"/>
+		<property name="Locked" type="b" access="read"/>
+		<property name="Created" type="u" access="read"/>
+		<property name="Modified" type="u" access="read"/>
+		<property name="Items" type="ao" access="read"/>
+
+		<method name="Delete">
+		  <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+		</method>
+
+		<method name="SearchItems">
+		  <arg name="attributes" type="a{ss}" direction="in"/>
+		  <arg name="unlocked" type="ao" direction="out"/>
+		  <arg name="locked" type="ao" direction="out"/>
+		</method>
+
+		<method name="CreateItem">
+		  <arg name="properties" type="a{sv}" direction="in"/>
+		  <arg name="secret" type="(oayays)" direction="in"/>
+		  <arg name="replace" type="b" direction="in"/>
+		  <arg name="item" type="o" direction="out"/>
+		  <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+		</method>
+
+		<signal name="ItemCreated">
+		  <arg name="item" type="o"/>
+		</signal>
+
+		<signal name="ItemDeleted">
+		  <arg name="item" type="o"/>
+		</signal>
+
+		<signal name="ItemChanged">
+		  <arg name="item" type="o"/>
+		</signal>
+	</interface>
+`
+
 const DbusAdv = `
 const DbusAdv = `
 <node>
 <node>
-	<interface name="dev.aetherial.git.KeychainLinker.Service">
+
+	<interface name="org.freedesktop.Secret.Service">
 		<method name="OpenSession">
 		<method name="OpenSession">
 		  <arg name="algorithm" type="s" direction="in"/>
 		  <arg name="algorithm" type="s" direction="in"/>
 		  <arg name="input" type="v" direction="in"/>
 		  <arg name="input" type="v" direction="in"/>

+ 29 - 0
scripts/test_add.py

@@ -0,0 +1,29 @@
+import secretstorage
+import dbus
+
+# Connect to the session bus
+bus = dbus.SessionBus()
+
+# Connect to the Secret Service
+connection = secretstorage.dbus_init()
+collection = secretstorage.get_default_collection(connection)
+
+
+# Define attributes and label
+attributes = {
+    'username': 'alice',
+    'service': 'example-app'
+}
+label = 'My Example Secret'
+secret = 'super_secret_password_123'
+
+# Create the item
+item = collection.create_item(
+    label=label,
+    attributes=attributes,
+    secret=secret,
+    replace=True  # Replace if an item with same attributes exists
+)
+
+print(f"Secret stored with label: {label}")
+