Reputation: 1348
I'm trying to get password from keyring for awesome-wm session (using DBus via lgi library).
I'm able to find keyring entry path, open communication session and unlock entry.
Then I call GetSecrets
method and store result into secret
variable.
According to the documentation it is supposed to be a struct Secret. Seems like lgi
cannot handle this type and passes it as userdata
type (at least I wasn't able to make it give access to struct fields). Is there a way to get struct Secret
value
field contents without writing custom C handler?
Here is the code:
local bus = Gio.bus_get_sync(Gio.BusType.SESSION, nil)
local attr = {}
attr[1] = {attribute = "value"} -- attribute-value pair to search for
-- search for secret path
local name = "org.freedesktop.secrets"
local object = "/org/freedesktop/secrets"
local interface = "org.freedesktop.Secret.Service"
local method = "SearchItems"
local message = Gio.DBusMessage.new_method_call(name, object, interface, method)
message:set_body(GLib.Variant("(a{ss})", attr))
local result, err = bus:send_message_with_reply_sync(message, Gio.DBusSendMessageFlags.NONE,
-1, nil)
local location
for _, l in result:get_body():pairs() do
if #l > 0 then location = l[1] end
end
print(location) -- returns "/org/freedesktop/secrets/collection/Default/1"
-- open session
local name = "org.freedesktop.secrets"
local object = "/org/freedesktop/secrets"
local interface = "org.freedesktop.Secret.Service"
local method = "OpenSession"
local message = Gio.DBusMessage.new_method_call(name, object, interface, method)
message:set_body(GLib.Variant("(sv)", {"plain", GLib.Variant("s", "")}))
local result, err = bus:send_message_with_reply_sync(message, Gio.DBusSendMessageFlags.NONE,
-1, nil)
local session = result:get_body()[2]
print(session) -- returns "/org/freedesktop/secrets/session/s4"
-- unlock key
local name = "org.freedesktop.secrets"
local object = "/org/freedesktop/secrets"
local interface = "org.freedesktop.Secret.Service"
local method = "Unlock"
local message = Gio.DBusMessage.new_method_call(name, object, interface, method)
message:set_body(GLib.Variant("(ao)", {{location}}))
local result, err = bus:send_message_with_reply_sync(message, Gio.DBusSendMessageFlags.NONE,
-1, nil)
-- at this point key property "Locked" if false. tested using d-feet
-- get secret
local name = "org.freedesktop.secrets"
local object = "/org/freedesktop/secrets"
local interface = "org.freedesktop.Secret.Service"
local method = "GetSecrets"
local message = Gio.DBusMessage.new_method_call(name, object, interface, method)
message:set_body(GLib.Variant("(aoo)", {{location},session}))
local result, err = bus:send_message_with_reply_sync(message, Gio.DBusSendMessageFlags.NONE,
-1, nil)
local secret = result:get_body()
print(#secret) -- returns "1"
print(secret) -- returns table address
print(type(secret)) -- returns "userdata"
-- lock key
local name = "org.freedesktop.secrets"
local object = "/org/freedesktop/secrets"
local interface = "org.freedesktop.Secret.Service"
local method = "Lock"
local message = Gio.DBusMessage.new_method_call(name, object, interface, method)
message:set_body(GLib.Variant("(ao)", {{location}}))
local result, err = bus:send_message_with_reply_sync(message, Gio.DBusSendMessageFlags.NONE,
-1, nil)
-- close session
local name = "org.freedesktop.secrets"
local object = location
local interface = "org.freedesktop.Secret.Session"
local method = "Close"
local message = Gio.DBusMessage.new_method_call(name, object, interface, method)
local result, err = bus:send_message_with_reply_sync(message, Gio.DBusSendMessageFlags.NONE,
-1, nil)
When I do print(secret)
, lgi.rec 0x7f57d0014960:GLib.Variant
is returned.
So, secret
is an object. How can I retrieve value
field from GLib.Variant
object?
secret:get_data_as_bytes():get_data()
dumps struct in bytearray form; secret:print()
returns formatted string of struct. I wonder if there's a better way.
type of secret
variable is (a{o(oayays)})
Code to recreate an object of that type:
local lgi = require 'lgi'
local Gio = lgi.require 'Gio'
local GLib = lgi.require 'GLib'
local var = GLib.Variant("(a{o(oayays)})", {{["/path/to/object"]={"/path/to/session","parameters","value","content_type"}}})
Upvotes: 1
Views: 459
Reputation: 1348
Finally I've found a solution. To unpack a value of a complex type, for example (a{o(oayays)})
, one should use get_child_value
function.
secret:get_child_value(0):get_child_value(0):get_child_value(1):get_child_value(2).value
Explanation: index tuple; index array; index dict; index tuple
Upvotes: 0
Reputation: 2762
(Note that I didn't install this password manager or try any of this at all)
Last time I asked the author of LGI about such issue, the answer was:
I have submitted fix to Variant which restores this functionality. So an example of use will be:
local function called_from_C(userdata)
local variant = GLib.Variant(userdata)
print(variant)
endif you have to support older (well, released :) lgi versions, you can use undocumented way:
local core = require 'lgi.core'
local function called_from_C(userdata)
local variant = core.record.new(GLib.Variant, userdata)
print(variant)
end
Note that there is other ways. To work around another such bugs, I also once created a C Lua plugin and just wrote that code in C. That's actually rather trivial with Lua [2].
Another way, if you use LuaJIT, is to use the built-in FFI to just-in-time compile the struct definition into a Lua object [1].
Finally, if the question is more about how to unpack "working" GVariant values once they are consumed properly by LGI, look at my code for this here https://github.com/Elv13/wirefu/blob/master/proxy.lua
[1] http://luajit.org/ext_ffi_api.html
[2] https://github.com/Elv13/lua_async_binding/blob/master/src/luabridge.c#L407
Upvotes: 1