Skip to content

Commit

Permalink
document-portal: Implement GetHostPaths
Browse files Browse the repository at this point in the history
This method allows apps to get path as exists on the host filesystem for
documents exported through the document portal.

This method takes a list of document IDs as a string array and returns
a dictionary mapping document IDs to the paths in the host filesystem.
It is expected an app making this request to have access to given list
of documents.

This is based on initial work made by @JakobDev

Fixes flatpak#475
  • Loading branch information
grulja committed Jun 7, 2024
1 parent ef1e96b commit c35a2c8
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 5 deletions.
18 changes: 17 additions & 1 deletion data/org.freedesktop.portal.Documents.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
bus name org.freedesktop.portal.Documents and the object path
/org/freedesktop/portal/documents.
This documentation describes version 4 of this interface.
This documentation describes version 5 of this interface.
-->
<interface name='org.freedesktop.portal.Documents'>
<property name="version" type="u" access="read"/>
Expand Down Expand Up @@ -273,5 +273,21 @@
<arg type='s' name='app_id' direction='in'/>
<arg type='a{say}' name='docs' direction='out'/>
</method>

<!--
GetHostPaths:
@doc_ids: the list of IDs of the files in the document store
@paths: a dictionary mapping document IDs to the paths in the host filesystem
Gets the host filesystem paths for document store entries.
This call is available inside the sandbox, if the application has the ``read`` permission for the documents.
This method was added in version 5 of this interface.
-->
<method name="GetHostPaths">
<arg type='as' name='doc_ids' direction='in'/>
<arg type='a{say}' name='paths' direction='out'/>
</method>
</interface>
</node>
103 changes: 100 additions & 3 deletions document-portal/document-portal.c
Original file line number Diff line number Diff line change
Expand Up @@ -1309,12 +1309,11 @@ get_app_permissions (PermissionDbEntry *entry)
{
g_autofree const char **apps = NULL;
GVariantBuilder builder;
int i;

apps = permission_db_entry_list_apps (entry);
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sas}"));

for (i = 0; apps[i] != NULL; i++)
for (size_t i = 0; apps[i] != NULL; i++)
{
g_autofree const char **permissions = permission_db_entry_list_permissions (entry, apps[i]);
g_variant_builder_add_value (&builder,
Expand Down Expand Up @@ -1416,6 +1415,103 @@ portal_list (GDBusMethodInvocation *invocation,
return TRUE;
}

const char *
get_host_path_internal (GDBusMethodInvocation *invocation,
XdpAppInfo *app_info,
const char *id,
GError **error)
{
g_autoptr(PermissionDbEntry) entry = NULL;

XDP_AUTOLOCK (db);

entry = permission_db_lookup (db, id);

if (!entry)
{
if (error != NULL && *error == NULL)
{
g_set_error (error,
XDG_DESKTOP_PORTAL_ERROR,
XDG_DESKTOP_PORTAL_ERROR_INVALID_ARGUMENT,
"Invalid ID passed (%s)", id);
}

return NULL;
}

if (!xdp_app_info_is_host (app_info))
{
g_autofree const char **apps = NULL;
const char *app_id = NULL;
gboolean app_found = FALSE;
int i;

app_id = xdp_app_info_get_id (app_info);

apps = permission_db_entry_list_apps (entry);
for (i = 0; apps[i] != NULL; i++)
{
if (g_strcmp0 (app_id, apps[i]) == 0)
{
app_found = TRUE;
break;
}
}

if (!app_found)
{
if (error != NULL && *error == NULL)
{
g_set_error (error,
XDG_DESKTOP_PORTAL_ERROR,
XDG_DESKTOP_PORTAL_ERROR_NOT_ALLOWED,
"Not enough permissions");
}

return NULL;
}
}

return document_entry_get_path (entry);
}

static gboolean
portal_get_host_paths (GDBusMethodInvocation *invocation,
GVariant *parameters,
XdpAppInfo *app_info)
{
g_autofree const char **id_list = NULL;
GVariantBuilder builder;
size_t i = 0;

g_variant_get (parameters, "(^a&s)", &id_list);

g_variant_builder_init (&builder, G_VARIANT_TYPE ("(a{say})"));
g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{say}"));

for (i = 0; id_list[i] != NULL; i++)
{
g_autoptr(GError) error = NULL;
g_autofree const char *path = NULL;

path = get_host_path_internal (invocation, app_info, id_list[i], &error);
if (path == NULL)
{
g_warning ("Failed to get host path for %s: %s", id_list[i], error->message);
continue;
}

g_variant_builder_add (&builder, "{s@ay}", id_list[i], g_variant_new_bytestring (path));
}

g_variant_builder_close (&builder);

g_dbus_method_invocation_return_value (invocation, g_variant_builder_end (&builder));

return TRUE;
}

static void
peer_died_cb (const char *name)
{
Expand All @@ -1432,7 +1528,7 @@ on_bus_acquired (GDBusConnection *connection,

dbus_api = xdp_dbus_documents_skeleton_new ();

xdp_dbus_documents_set_version (XDP_DBUS_DOCUMENTS (dbus_api), 4);
xdp_dbus_documents_set_version (XDP_DBUS_DOCUMENTS (dbus_api), 5);

g_signal_connect_swapped (dbus_api, "handle-get-mount-point", G_CALLBACK (handle_get_mount_point), NULL);
g_signal_connect_swapped (dbus_api, "handle-add", G_CALLBACK (handle_method), portal_add);
Expand All @@ -1445,6 +1541,7 @@ on_bus_acquired (GDBusConnection *connection,
g_signal_connect_swapped (dbus_api, "handle-lookup", G_CALLBACK (handle_method), portal_lookup);
g_signal_connect_swapped (dbus_api, "handle-info", G_CALLBACK (handle_method), portal_info);
g_signal_connect_swapped (dbus_api, "handle-list", G_CALLBACK (handle_method), portal_list);
g_signal_connect_swapped (dbus_api, "handle-get-host-paths", G_CALLBACK (handle_method), portal_get_host_paths);

file_transfer = file_transfer_create ();
g_dbus_interface_skeleton_set_flags (file_transfer,
Expand Down
54 changes: 53 additions & 1 deletion tests/test-doc-portal.c
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,57 @@ test_add_named (void)
assert_doc_not_exist (id1, basename1, "com.test.App2");
}

static void
test_get_host_paths (void)
{
g_autofree char *doc_id = NULL;
g_autofree char *expected_real_path = NULL;
g_autofree char *real_path = NULL;
const char *basename = "host-path";
GVariant *path= NULL;
g_autoptr(GVariant) reply = NULL;
g_autoptr(GVariant) result = NULL;
g_autoptr(GVariantIter) iter = NULL;
GVariantBuilder builder;
GError *error = NULL;
const gchar* key = NULL;

if (!check_fuse_or_skip_test ())
return;

doc_id = export_new_file (basename, "content", FALSE);

g_variant_builder_init (&builder, G_VARIANT_TYPE ("(as)"));
g_variant_builder_open (&builder, G_VARIANT_TYPE ("as"));
g_variant_builder_add (&builder, "s", doc_id);
g_variant_builder_close (&builder);

reply = g_dbus_connection_call_sync (session_bus,
"org.freedesktop.portal.Documents",
"/org/freedesktop/portal/documents",
"org.freedesktop.portal.Documents",
"GetHostPaths", g_variant_builder_end (&builder),
G_VARIANT_TYPE ("(a{say})"),
0, -1,
NULL,
&error);

g_assert_no_error (error);
result = g_variant_get_child_value (reply, 0);

g_assert (g_variant_is_of_type (result, G_VARIANT_TYPE ("a{say}")));

expected_real_path = g_build_filename (outdir, basename, NULL);
iter = g_variant_iter_new (result);
while (g_variant_iter_loop (iter, "{&s@ay}", &key, &path))
{
g_assert_cmpstr (key, ==, doc_id);
g_assert_cmpstr (g_variant_get_bytestring (path), ==, expected_real_path);
g_variant_unref (path);
path = NULL;
}
}

static void
global_setup (void)
{
Expand Down Expand Up @@ -853,7 +904,7 @@ test_version (void)
if (!check_fuse_or_skip_test ())
return;

g_assert_cmpint (xdp_dbus_documents_get_version (documents), ==, 4);
g_assert_cmpint (xdp_dbus_documents_get_version (documents), ==, 5);
}

int
Expand All @@ -871,6 +922,7 @@ main (int argc, char **argv)
g_test_add_func ("/db/recursive_doc", test_recursive_doc);
g_test_add_func ("/db/create_docs", test_create_docs);
g_test_add_func ("/db/add_named", test_add_named);
g_test_add_func ("/db/get_host_paths", test_get_host_paths);

global_setup ();

Expand Down

0 comments on commit c35a2c8

Please sign in to comment.