diff --git a/.ref/URLs b/.ref/URLs new file mode 100644 index 0000000..40a17dc --- /dev/null +++ b/.ref/URLs @@ -0,0 +1,3 @@ +https://api.kde.org/frameworks/kwallet/html/index.html + +Dbus methods etc.: https://invent.kde.org/frameworks/kwallet/-/blob/master/src/api/KWallet/org.kde.KWallet.xml diff --git a/TODO b/TODO new file mode 100644 index 0000000..ef0485a --- /dev/null +++ b/TODO @@ -0,0 +1,3 @@ +- XML import/export? +-- compat with kwalletmanager +--- Will require conversion to different struct model. diff --git a/consts.go b/consts.go new file mode 100644 index 0000000..41144c3 --- /dev/null +++ b/consts.go @@ -0,0 +1,165 @@ +package gokwallet + +// KWalletD Dbus interfaces. +const ( + // DbusService is the Dbus service bus identifier. + DbusService string = "org.kde.kwalletd5" + // DbusServiceBase is the base identifier used by interfaces. + DbusServiceBase string = "org.kde" +) + +// gokwallet defaults. +const ( + // DefaultWalletName is the name of the default Wallet to use. + DefaultWalletName string = "kdewallet" + // DefaultAppID is the default name for the application (see WalletManager.AppID). + DefaultAppID string = "GoKwallet" +) + +// WalletManager interface. +const ( + /* + DbusInterfaceWM is the Dbus interface for working with a WalletManager. + */ + DbusInterfaceWM string = DbusServiceBase + ".KWallet" + + // Methods + + // DbusWMChangePassword changes the password for a Wallet. + DbusWMChangePassword string = DbusInterfaceWM + ".changePassword" + + // DbusWMClose closes an App (WalletManager) or Wallet. + DbusWMClose string = DbusInterfaceWM + ".close" + + // DbusWMCloseAllWallets closes all WalletManager.Wallets. + DbusWMCloseAllWallets string = DbusInterfaceWM + ".closeAllWallets" + + // DbusWMCreateFolder creates a Folder. + DbusWMCreateFolder string = DbusInterfaceWM + ".createFolder" + + // DbusWMDeleteWallet deletes/removes a Wallet. + DbusWMDeleteWallet string = DbusInterfaceWM + ".deleteWallet" + + // DbusWMDisconnectApp disconnects a WalletManager (or other App). + DbusWMDisconnectApp string = DbusInterfaceWM + ".disconnectApplication" + + // DbusWMEntriesList returns a *map* of the WalletItem objects in a Folder (with their entry name as the map key). + DbusWMEntriesList string = DbusInterfaceWM + ".entriesList" + + // DbusWMEntryList returns a *slice* of WalletItem names in a Folder. + DbusWMEntryList string = DbusInterfaceWM + ".entryList" + + // DbusWMEntryType returns the type of a WalletItem. + DbusWMEntryType string = DbusInterfaceWM + ".entryType" + + // DbusWMFolderNotExist indicates if a Folder exists within a Wallet or not. + DbusWMFolderNotExist string = DbusInterfaceWM + ".folderDoesNotExist" + + // DbusWMFolderList lists the Folder objects (as Folder.Name) in a Wallet. + DbusWMFolderList string = DbusInterfaceWM + ".folderList" + + // DbusWMHasEntry indicates if a Folder has a WalletItem or not. + DbusWMHasEntry string = DbusInterfaceWM + ".hasEntry" + + // DbusWMHasFolder indicates if a Wallet has a Folder or not. + DbusWMHasFolder string = DbusInterfaceWM + ".hasFolder" + + /* + DbusWMIsEnabled indicates if KWallet is enabled. + TODO: Is this accurate? + */ + DbusWMIsEnabled string = DbusInterfaceWM + ".isEnabled" + + // DbusWMIsOpen indicates if a Wallet is open (unlocked). + DbusWMIsOpen string = DbusInterfaceWM + ".isOpen" + + // DbusWMKeyNotExist indicates if a Folder has a WalletItem or not. + DbusWMKeyNotExist string = DbusInterfaceWM + ".keyDoesNotExist" + + // DbusWMLocalWallet gives the name of the local (default?) Wallet. + DbusWMLocalWallet string = DbusInterfaceWM + ".localWallet" + + // DbusWMMapList gives a list of Map names in a Folder. + DbusWMMapList string = DbusInterfaceWM + ".mapList" + + /* + DbusWMNetWallet indicates if a Wallet is a Network Wallet or not. + TODO: is/was this ever used? + */ + DbusWMNetWallet string = DbusInterfaceWM + ".networkWallet" + + // DbusWMOpen opens (unlocks) a Wallet. + DbusWMOpen string = DbusInterfaceWM + ".open" + + // DbusWMOpenAsync opens (unlocks) a Wallet asynchronously. + DbusWMOpenAsync string = DbusInterfaceWM + ".openAsync" + + // DbusWMOpenPath opens (unlocks) a Wallet by its filepath. + DbusWMOpenPath string = DbusInterfaceWM + ".openPath" + + // DbusWMOpenPathAsync opens (unlocks) a Wallet by its filepath asynchronously. + DbusWMOpenPathAsync string = DbusInterfaceWM + ".openPath" + + // DbusWMPamOpen opens (unlocks) a Wallet via PAM. + DbusWMPamOpen string = DbusInterfaceWM + ".pamOpen" + + /* + DbusWMPasswordList returns a map of Password objects in a Folder. + Password.Name is the map key. + */ + DbusWMPasswordList string = DbusInterfaceWM + ".passwordList" + + // DbusWMReadEntry fetches a WalletItem by its name from a Folder (as a byteslice). + DbusWMReadEntry string = DbusInterfaceWM + ".readEntry" + + // DbusWMReadEntryList returns a map of WalletItem objects in a Folder. + DbusWMReadEntryList string = DbusInterfaceWM + ".readEntryList" + + // DbusWMReadMap returns a Map from a Folder (as a byteslice). + DbusWMReadMap string = DbusInterfaceWM + ".readMap" + + // DbusWMReadMapList returns a map of Map objects in a Folder. + DbusWMReadMapList string = DbusInterfaceWM + ".readMapList" + + // DbusWMReadPassword returns a Password from a Folder (as a byteslice). + DbusWMReadPassword string = DbusInterfaceWM + ".readPassword" + + // DbusWMReadPasswordList returns a map of Password objects in a Folder. + DbusWMReadPasswordList string = DbusInterfaceWM + ".readPasswordList" + + // DbusWMReconfigure is [FUNCTION UNKNOWN/UNDOCUMENTED; TODO? NOT IMPLEMENTED.] + // DbusWMReconfigure string = DbusInterfaceWM + ".reconfigure" + + // DbusWMRemoveEntry removes a WalletItem from a Folder. + DbusWMRemoveEntry string = DbusInterfaceWM + ".removeEntry" + + // DbusWMRemoveFolder removes a Folder from a Wallet. + DbusWMRemoveFolder string = DbusInterfaceWM + ".removeFolder" + + // DbusWMRenameEntry renames ("moves") a WalletItem. + DbusWMRenameEntry string = DbusInterfaceWM + ".renameEntry" + + // DbusWMSync is [FUNCTION UNKNOWN/UNDOCUMENTED; TODO? RELATED TO ASYNC? NOT IMPLEMENTED.] + // DbusWMSync string = DbusInterfaceWM + ".sync" + + // DbusWMUsers returns a slice of users. + DbusWMUsers string = DbusInterfaceWM + ".users" + + // DbusWMWallets returns an array of Wallet names. + DbusWMWallets string = DbusInterfaceWM + ".wallets" + + // DbusWMWriteEntry writes (creates) a WalletItem to/in a Folder. + DbusWMWriteEntry string = DbusInterfaceWM + ".writeEntry" + + // DbusWMWriteMap writes (creates) a Map (via a byteslice) to/in a Folder. + DbusWMWriteMap string = DbusInterfaceWM + ".writeMap" + + // DbusWMWritePassword writes (creates) a Password to/in a Folder. + DbusWMWritePassword string = DbusInterfaceWM + ".writePassword" +) + +// Dbus paths. +const ( + // DbusPath is the path for DbusService. + DbusPath string = "/modules/kwalletd5" +) diff --git a/doc.go b/doc.go index 0f02ee6..97ce7df 100644 --- a/doc.go +++ b/doc.go @@ -3,15 +3,18 @@ /* Package gokwallet serves as a Golang interface to KDE's KWallet (https://utils.kde.org/projects/kwalletmanager/). +Note that to use this library, the running machine must have both Dbus and kwalletd running. + Note that this library interfaces with kwalletd. KWallet is in the process of moving to libsecret/SecretService -(see https://bugs.kde.org/show_bug.cgi?id=313216 and https://invent.kde.org/frameworks/kwallet/-/merge_requests/11). +(see https://bugs.kde.org/show_bug.cgi?id=313216 and https://invent.kde.org/frameworks/kwallet/-/merge_requests/11), +thus replacing kwalletd. While there is a pull request in place, it has not yet been merged in (and it may be a while before downstream distributions incorporate that version). However, when that time comes I highly recommend using my `gosecret` library to interface with that (module r00t2.io/gosecret; see https://pkg.go.dev/r00t2.io/gosecret). KWallet has the following structure (modified slightly to reflect this library): -- A main Dbus service interface ("org.kde.kwalletd5") allows one to retrieve and operate on/with Wallet items. +- A main Dbus service interface ("org.kde.kwalletd5"), WalletManager, allows one to retrieve and operate on/with Wallet items. - One or more Wallet items allow one to retrieve and operate on/with Folder items. @@ -23,25 +26,51 @@ Thus, the hierarchy (as exposed by this library) looks like this: ├─ Wallet "A" │ ├─ Folder "A_1" │ │ ├─ Passwords + │ │ │ ├─ Password "A_1_a" + │ │ │ └─ Password "A_1_b" │ │ ├─ Maps + │ │ │ ├─ Map "A_1_a" + │ │ │ └─ Map "A_1_b" │ │ ├─ BinaryData + │ │ │ ├─ Blob "A_1_a" + │ │ │ └─ Blob "A_1_b" │ │ └─ Unknown + │ │ ├─ UnknownItem "A_1_a" + │ │ └─ UnknownItem "A_1_b" │ └─ Folder "A_2" │ ├─ Passwords + │ │ ├─ Password "A_2_a" + │ │ └─ Password "A_2_b" │ ├─ Maps + │ │ ├─ Map "A_2_a" + │ │ └─ Map "A_2_b" │ ├─ BinaryData + │ │ ├─ Blob "A_2_a" + │ │ └─ Blob "A_2_b" │ └─ Unknown + │ ├─ UnknownItem "A_2_a" + │ └─ UnknownItem "A_2_b" └─ Wallet "B" - ├─ Folder "B_1" - │ ├─ Passwords - │ ├─ Maps - │ ├─ BinaryData - │ └─ Unknown - └─ Folder "B_2" + └─ Folder "B_1" ├─ Passwords + │ ├─ Password "B_1_a" + │ └─ Password "B_1_b" ├─ Maps + │ ├─ Map "B_1_a" + │ └─ Map "B_1_b" ├─ BinaryData + │ ├─ Blob "B_1_a" + │ └─ Blob "B_1_b" └─ Unknown + ├─ UnknownItem "B_1_a" + └─ UnknownItem "B_1_b" +This is an approximation, but should show a relatively accurate representation of the model. +Note that most systems are likely to only have a single wallet, "kdewallet". + +Usage + +Full documentation can be found via inline documentation. +Additionally, use either https://pkg.go.dev/r00t2.io/gokwallet or https://pkg.go.dev/golang.org/x/tools/cmd/godoc (or `go doc`) in the source root. */ package gokwallet diff --git a/go.mod b/go.mod index 18f5621..9b8411b 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module r00t2.io/gokwallet go 1.17 + +require github.com/godbus/dbus/v5 v5.0.6 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..aeec758 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro= +github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= diff --git a/types.go b/types.go new file mode 100644 index 0000000..4b668d1 --- /dev/null +++ b/types.go @@ -0,0 +1,133 @@ +package gokwallet + +import ( + "github.com/godbus/dbus/v5" +) + +/* + MultiError is a type of error.Error that can contain multiple error.Errors. Confused? Don't worry about it. +*/ +type MultiError struct { + // Errors is a slice of errors to combine/concatenate when .Error() is called. + Errors []error `json:"errors"` + // ErrorSep is a string to use to separate errors for .Error(). The default is "\n". + ErrorSep string `json:"separator"` +} + +// ConnPathCheckResult contains the result of validConnPath. +type ConnPathCheckResult struct { + // ConnOK is true if the dbus.Conn is valid. + ConnOK bool `json:"conn"` + // PathOK is true if the Dbus path given is a valid type and value. + PathOK bool `json:"path"` +} + +// DbusObject is a base struct type to be anonymized by other types. +type DbusObject struct { + // Conn is an active connection to the Dbus. + Conn *dbus.Conn `json:"-"` + // Dbus is the Dbus bus object. + Dbus dbus.BusObject `json:"-"` +} + +/* + WalletManager is a general KWallet interface, sort of a handler for Dbus. + It's used for fetching Wallet objects. +*/ +type WalletManager struct { + *DbusObject + /* + AppID is the application ID. + The default is DefaultAppID. + */ + AppID string `json:"app_id"` + /* + Wallets is the collection of Wallets accessible in/to this WalletManager. + Wallet.Name is the map key. + */ + Wallets map[string]*Wallet `json:"wallets"` +} + +// Wallet contains one or more (or none) Folder objects. +type Wallet struct { + *DbusObject + // Name is the name of this Wallet. + Name string `json:"name"` + /* + Folders contains all Folder objects in this Wallet. + Folder.Name is the map key. + */ + Folders map[string]*Folder `json:"folders"` +} + +// Folder contains secret object collections of Password, Map, Blob, and UnknownItem objects. +type Folder struct { + *DbusObject + // Name is the name of this Folder. + Name string `json:"name"` + /* + Passwords contains a map of all Password objects in this Folder. + Password.Name is the map key. + */ + Passwords map[string]*Password `json:"passwords"` + /* + Maps contains a map of all Map objects in this Folder. + Map.Name is the map key. + */ + Maps map[string]*Map `json:"maps"` + /* + BinaryData contains a map if all Blob objects in this Folder. + Blob.Name is the map key. + */ + BinaryData map[string]*Blob `json:"binary_data"` + /* + Unknown contains a map of all UnknownItem objects in this Folder. + Unknown.Name is the map key. + */ + Unknown map[string]*UnknownItem `json:"unknown"` +} + +// Password is a straightforward single-value secret of text. +type Password struct { + *DbusObject + // Name is the name of this Password. + Name string `json:"name"` + // Value is this Password's value. + Value string `json:"value"` +} + +// Map is a dictionary or key/value secret. +type Map struct { + *DbusObject + // Name is the name of this Map. + Name string `json:"name"` + // Value is this Map's value. + Value map[string]string `json:"value"` +} + +// Blob (binary large object, typographically BLOB) is secret binary data. +type Blob struct { + *DbusObject + // Name is the name of this Blob. + Name string `json:"name"` + // Value is this Blob's value. + Value []byte `json:"value"` +} + +/* + UnknownItem is a secret item of unknown classification, so there isn't exactly a good way of determining a type for UnknownItem.Value. + As such, its dbus.ObjectPath is used. + TODO: There may be a method to fetch the raw bytes of the object (such as one would use with Blob) in the future. +*/ +type UnknownItem struct { + *DbusObject + // Name is the name of this UnknownItem. + Name string `json:"name"` + // Value is the Dbus path of this UnknownItem. + Value dbus.ObjectPath `json:"value"` +} + +// WalletItem is an interface to manage wallet objects: Password, Map, Blob, or UnknownItem. +type WalletItem interface { + isWalletItem() (isWalletItem bool) +} diff --git a/wallet_funcs.go b/wallet_funcs.go new file mode 100644 index 0000000..046a12b --- /dev/null +++ b/wallet_funcs.go @@ -0,0 +1,14 @@ +package gokwallet + +import ( + "github.com/godbus/dbus/v5" +) + +func NewWallet(wm *WalletManager, path dbus.ObjectPath) (wallet *Wallet, err error) { + + var open bool + + _ = open + + return +} diff --git a/walletmanager_funcs.go b/walletmanager_funcs.go new file mode 100644 index 0000000..7099868 --- /dev/null +++ b/walletmanager_funcs.go @@ -0,0 +1,50 @@ +package gokwallet + +import ( + "github.com/godbus/dbus/v5" +) + +/* + NewWalletManager returns a WalletManager. + If appId is empty/nil, DefaultAppID will be used as the app ID. + If appId is specified, only the first string is used. +*/ +func NewWalletManager(appID ...string) (wm *WalletManager, err error) { + + var realAppID string + + if appID != nil && len(appID) > 0 { + realAppID = appID[0] + } else { + realAppID = DefaultAppID + } + + wm = &WalletManager{ + DbusObject: &DbusObject{ + Conn: nil, + Dbus: nil, + }, + AppID: realAppID, + Wallets: make(map[string]*Wallet), + } + + if wm.DbusObject.Conn, err = dbus.SessionBus(); err != nil { + return + } + wm.DbusObject.Dbus = wm.DbusObject.Conn.Object(DbusService, dbus.ObjectPath(DbusPath)) + + return +} + +/* + Update fetches/updates all Wallet objects in a WalletManager. +*/ +func (wm *WalletManager) Update() (err error) { + + var wallets []*Wallet + + // TODO. + _ = wallets + + return +}