From b6a41a1b9e9020a23dbc418183ebe4746b6ec027 Mon Sep 17 00:00:00 2001
From: Kalev Lember <klember@redhat.com>
Date: Mon, 18 May 2020 14:45:35 +0200
Subject: [PATCH 1/2] Add basic auth support to flatpak plugin
This is useful for e.g. OCI remotes that can use basic auth.
All user visible strings in the basic auth dialog are taken from the
flatpak CLI client.
---
lib/gs-plugin-loader.c | 29 +++-
lib/gs-plugin-loader.h | 7 +-
lib/gs-plugin.c | 68 +++++++++-
lib/gs-plugin.h | 13 +-
plugins/flatpak/gs-plugin-flatpak.c | 55 +++++++-
po/POTFILES.in | 2 +
src/gnome-software.gresource.xml | 1 +
src/gs-basic-auth-dialog.c | 130 ++++++++++++++++++
src/gs-basic-auth-dialog.h | 28 ++++
src/gs-basic-auth-dialog.ui | 203 ++++++++++++++++++++++++++++
src/gs-shell.c | 25 +++-
src/meson.build | 1 +
12 files changed, 556 insertions(+), 6 deletions(-)
create mode 100644 src/gs-basic-auth-dialog.c
create mode 100644 src/gs-basic-auth-dialog.h
create mode 100644 src/gs-basic-auth-dialog.ui
diff --git a/lib/gs-plugin-loader.c b/lib/gs-plugin-loader.c
index 979f3d5d..35382e3f 100644
--- a/lib/gs-plugin-loader.c
+++ b/lib/gs-plugin-loader.c
@@ -1,7 +1,7 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2007-2018 Richard Hughes <richard@hughsie.com>
- * Copyright (C) 2014-2018 Kalev Lember <klember@redhat.com>
+ * Copyright (C) 2014-2020 Kalev Lember <klember@redhat.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
@@ -74,6 +74,7 @@ enum {
SIGNAL_PENDING_APPS_CHANGED,
SIGNAL_UPDATES_CHANGED,
SIGNAL_RELOAD,
+ SIGNAL_BASIC_AUTH_START,
SIGNAL_LAST
};
@@ -2016,6 +2017,23 @@ gs_plugin_loader_status_changed_cb (GsPlugin *plugin,
0, app, status);
}
+static void
+gs_plugin_loader_basic_auth_start_cb (GsPlugin *plugin,
+ const gchar *remote,
+ const gchar *realm,
+ GCallback callback,
+ gpointer user_data,
+ GsPluginLoader *plugin_loader)
+{
+ g_debug ("emitting basic-auth-start %s", realm);
+ g_signal_emit (plugin_loader,
+ signals[SIGNAL_BASIC_AUTH_START], 0,
+ remote,
+ realm,
+ callback,
+ user_data);
+}
+
static gboolean
gs_plugin_loader_job_actions_changed_delay_cb (gpointer user_data)
{
@@ -2102,6 +2120,9 @@ gs_plugin_loader_open_plugin (GsPluginLoader *plugin_loader,
g_signal_connect (plugin, "status-changed",
G_CALLBACK (gs_plugin_loader_status_changed_cb),
plugin_loader);
+ g_signal_connect (plugin, "basic-auth-start",
+ G_CALLBACK (gs_plugin_loader_basic_auth_start_cb),
+ plugin_loader);
g_signal_connect (plugin, "report-event",
G_CALLBACK (gs_plugin_loader_report_event_cb),
plugin_loader);
@@ -2712,6 +2733,12 @@ gs_plugin_loader_class_init (GsPluginLoaderClass *klass)
G_STRUCT_OFFSET (GsPluginLoaderClass, reload),
NULL, NULL, g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
+ signals [SIGNAL_BASIC_AUTH_START] =
+ g_signal_new ("basic-auth-start",
+ G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsPluginLoaderClass, basic_auth_start),
+ NULL, NULL, g_cclosure_marshal_generic,
+ G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER);
}
static void
diff --git a/lib/gs-plugin-loader.h b/lib/gs-plugin-loader.h
index 74cbfa53..e88ea2d1 100644
--- a/lib/gs-plugin-loader.h
+++ b/lib/gs-plugin-loader.h
@@ -1,7 +1,7 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2007-2017 Richard Hughes <richard@hughsie.com>
- * Copyright (C) 2015 Kalev Lember <klember@redhat.com>
+ * Copyright (C) 2015-2020 Kalev Lember <klember@redhat.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
@@ -31,6 +31,11 @@ struct _GsPluginLoaderClass
void (*pending_apps_changed) (GsPluginLoader *plugin_loader);
void (*updates_changed) (GsPluginLoader *plugin_loader);
void (*reload) (GsPluginLoader *plugin_loader);
+ void (*basic_auth_start) (GsPluginLoader *plugin_loader,
+ const gchar *remote,
+ const gchar *realm,
+ GCallback callback,
+ gpointer user_data);
};
GsPluginLoader *gs_plugin_loader_new (void);
diff --git a/lib/gs-plugin.c b/lib/gs-plugin.c
index 5aed1058..3f63fa97 100644
--- a/lib/gs-plugin.c
+++ b/lib/gs-plugin.c
@@ -1,7 +1,7 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2013-2016 Richard Hughes <richard@hughsie.com>
- * Copyright (C) 2014-2018 Kalev Lember <klember@redhat.com>
+ * Copyright (C) 2014-2020 Kalev Lember <klember@redhat.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
@@ -87,6 +87,7 @@ enum {
SIGNAL_RELOAD,
SIGNAL_REPORT_EVENT,
SIGNAL_ALLOW_UPDATES,
+ SIGNAL_BASIC_AUTH_START,
SIGNAL_LAST
};
@@ -851,6 +852,64 @@ gs_plugin_status_update (GsPlugin *plugin, GsApp *app, GsPluginStatus status)
g_source_attach (idle_source, NULL);
}
+typedef struct {
+ GsPlugin *plugin;
+ gchar *remote;
+ gchar *realm;
+ GCallback callback;
+ gpointer user_data;
+} GsPluginBasicAuthHelper;
+
+static gboolean
+gs_plugin_basic_auth_start_cb (gpointer user_data)
+{
+ GsPluginBasicAuthHelper *helper = user_data;
+ g_signal_emit (helper->plugin,
+ signals[SIGNAL_BASIC_AUTH_START], 0,
+ helper->remote,
+ helper->realm,
+ helper->callback,
+ helper->user_data);
+ g_free (helper->remote);
+ g_free (helper->realm);
+ g_slice_free (GsPluginBasicAuthHelper, helper);
+ return FALSE;
+}
+
+/**
+ * gs_plugin_basic_auth_start:
+ * @plugin: a #GsPlugin
+ * @remote: a string
+ * @realm: a string
+ * @callback: callback to invoke to submit the user/password
+ * @user_data: callback data to pass to the callback
+ *
+ * Emit the basic-auth-start signal in the main thread.
+ *
+ * Since: 3.38
+ **/
+void
+gs_plugin_basic_auth_start (GsPlugin *plugin,
+ const gchar *remote,
+ const gchar *realm,
+ GCallback callback,
+ gpointer user_data)
+{
+ GsPluginBasicAuthHelper *helper;
+ g_autoptr(GSource) idle_source = NULL;
+
+ helper = g_slice_new0 (GsPluginBasicAuthHelper);
+ helper->plugin = plugin;
+ helper->remote = g_strdup (remote);
+ helper->realm = g_strdup (realm);
+ helper->callback = callback;
+ helper->user_data = user_data;
+
+ idle_source = g_idle_source_new ();
+ g_source_set_callback (idle_source, gs_plugin_basic_auth_start_cb, helper, NULL);
+ g_source_attach (idle_source, NULL);
+}
+
static gboolean
gs_plugin_app_launch_cb (gpointer user_data)
{
@@ -1959,6 +2018,13 @@ gs_plugin_class_init (GsPluginClass *klass)
G_STRUCT_OFFSET (GsPluginClass, allow_updates),
NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN,
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+
+ signals [SIGNAL_BASIC_AUTH_START] =
+ g_signal_new ("basic-auth-start",
+ G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsPluginClass, basic_auth_start),
+ NULL, NULL, g_cclosure_marshal_generic,
+ G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER);
}
static void
diff --git a/lib/gs-plugin.h b/lib/gs-plugin.h
index 7dd2d864..d07afd3b 100644
--- a/lib/gs-plugin.h
+++ b/lib/gs-plugin.h
@@ -1,6 +1,7 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2012-2016 Richard Hughes <richard@hughsie.com>
+ * Copyright (C) 2020 Kalev Lember <klember@redhat.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
@@ -37,7 +38,12 @@ struct _GsPluginClass
GsPluginEvent *event);
void (*allow_updates) (GsPlugin *plugin,
gboolean allow_updates);
- gpointer padding[26];
+ void (*basic_auth_start) (GsPlugin *plugin,
+ const gchar *remote,
+ const gchar *realm,
+ GCallback callback,
+ gpointer user_data);
+ gpointer padding[25];
};
typedef struct GsPluginData GsPluginData;
@@ -116,5 +122,10 @@ void gs_plugin_report_event (GsPlugin *plugin,
void gs_plugin_set_allow_updates (GsPlugin *plugin,
gboolean allow_updates);
gboolean gs_plugin_get_network_available (GsPlugin *plugin);
+void gs_plugin_basic_auth_start (GsPlugin *plugin,
+ const gchar *remote,
+ const gchar *realm,
+ GCallback callback,
+ gpointer user_data);
G_END_DECLS
diff --git a/plugins/flatpak/gs-plugin-flatpak.c b/plugins/flatpak/gs-plugin-flatpak.c
index 4d6a81ba..2518025d 100644
--- a/plugins/flatpak/gs-plugin-flatpak.c
+++ b/plugins/flatpak/gs-plugin-flatpak.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2016 Joaquim Rocha <jrocha@endlessm.com>
* Copyright (C) 2016-2018 Richard Hughes <richard@hughsie.com>
- * Copyright (C) 2017-2018 Kalev Lember <klember@redhat.com>
+ * Copyright (C) 2017-2020 Kalev Lember <klember@redhat.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
@@ -456,6 +456,55 @@ _group_apps_by_installation (GsPlugin *plugin,
return g_steal_pointer (&applist_by_flatpaks);
}
+#if FLATPAK_CHECK_VERSION(1,6,0)
+typedef struct {
+ FlatpakTransaction *transaction;
+ guint id;
+} BasicAuthData;
+
+static void
+basic_auth_data_free (BasicAuthData *data)
+{
+ g_object_unref (data->transaction);
+ g_slice_free (BasicAuthData, data);
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(BasicAuthData, basic_auth_data_free)
+
+static void
+_basic_auth_cb (const gchar *user, const gchar *password, gpointer user_data)
+{
+ g_autoptr(BasicAuthData) data = user_data;
+
+ g_debug ("Submitting basic auth data");
+
+ /* NULL user aborts the basic auth request */
+ flatpak_transaction_complete_basic_auth (data->transaction, data->id, user, password, NULL /* options */);
+}
+
+static gboolean
+_basic_auth_start (FlatpakTransaction *transaction,
+ const char *remote,
+ const char *realm,
+ GVariant *options,
+ guint id,
+ GsPlugin *plugin)
+{
+ BasicAuthData *data;
+
+ if (!gs_plugin_has_flags (plugin, GS_PLUGIN_FLAGS_INTERACTIVE))
+ return FALSE;
+
+ data = g_slice_new0 (BasicAuthData);
+ data->transaction = g_object_ref (transaction);
+ data->id = id;
+
+ g_debug ("Login required remote %s (realm %s)\n", remote, realm);
+ gs_plugin_basic_auth_start (plugin, remote, realm, G_CALLBACK (_basic_auth_cb), data);
+ return TRUE;
+}
+#endif
+
static FlatpakTransaction *
_build_transaction (GsPlugin *plugin, GsFlatpak *flatpak,
GCancellable *cancellable, GError **error)
@@ -491,6 +540,10 @@ _build_transaction (GsPlugin *plugin, GsFlatpak *flatpak,
/* connect up signals */
g_signal_connect (transaction, "ref-to-app",
G_CALLBACK (_ref_to_app), plugin);
+#if FLATPAK_CHECK_VERSION(1,6,0)
+ g_signal_connect (transaction, "basic-auth-start",
+ G_CALLBACK (_basic_auth_start), plugin);
+#endif
/* use system installations as dependency sources for user installations */
flatpak_transaction_add_default_dependency_sources (transaction);
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 20721c4a..a44a6ad3 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -10,6 +10,8 @@ src/gs-app-row.c
src/gs-app-row.ui
src/gs-app-tile.c
src/gs-app-tile.ui
+src/gs-basic-auth-dialog.c
+src/gs-basic-auth-dialog.ui
lib/gs-category.c
src/gs-category-page.c
src/gs-category-page.ui
diff --git a/src/gnome-software.gresource.xml b/src/gnome-software.gresource.xml
index 3eaabca2..459ecf82 100644
--- a/src/gnome-software.gresource.xml
+++ b/src/gnome-software.gresource.xml
@@ -4,6 +4,7 @@
<file preprocess="xml-stripblanks">gnome-software.ui</file>
<file preprocess="xml-stripblanks">gs-app-addon-row.ui</file>
<file preprocess="xml-stripblanks">gs-app-row.ui</file>
+ <file preprocess="xml-stripblanks">gs-basic-auth-dialog.ui</file>
<file preprocess="xml-stripblanks">gs-category-page.ui</file>
<file preprocess="xml-stripblanks">gs-category-tile.ui</file>
<file preprocess="xml-stripblanks">gs-details-page.ui</file>
diff --git a/src/gs-basic-auth-dialog.c b/src/gs-basic-auth-dialog.c
new file mode 100644
index 00000000..c690a327
--- /dev/null
+++ b/src/gs-basic-auth-dialog.c
@@ -0,0 +1,130 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2020 Kalev Lember <klember@redhat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include "config.h"
+
+#include "gs-basic-auth-dialog.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+struct _GsBasicAuthDialog
+{
+ GtkDialog parent_instance;
+
+ GsBasicAuthCallback callback;
+ gpointer callback_data;
+
+ /* template widgets */
+ GtkButton *login_button;
+ GtkLabel *description_label;
+ GtkEntry *user_entry;
+ GtkEntry *password_entry;
+};
+
+G_DEFINE_TYPE (GsBasicAuthDialog, gs_basic_auth_dialog, GTK_TYPE_DIALOG)
+
+static void
+cancel_button_clicked_cb (GsBasicAuthDialog *dialog)
+{
+ /* abort the basic auth request */
+ dialog->callback (NULL, NULL, dialog->callback_data);
+
+ gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
+}
+
+static void
+login_button_clicked_cb (GsBasicAuthDialog *dialog)
+{
+ const gchar *user;
+ const gchar *password;
+
+ user = gtk_entry_get_text (dialog->user_entry);
+ password = gtk_entry_get_text (dialog->password_entry);
+
+ /* submit the user/password to basic auth */
+ dialog->callback (user, password, dialog->callback_data);
+
+ gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
+}
+
+static void
+dialog_validate (GsBasicAuthDialog *dialog)
+{
+ const gchar *user;
+ const gchar *password;
+ gboolean valid_user;
+ gboolean valid_password;
+
+ /* require user */
+ user = gtk_entry_get_text (dialog->user_entry);
+ valid_user = user != NULL && strlen (user) != 0;
+
+ /* require password */
+ password = gtk_entry_get_text (dialog->password_entry);
+ valid_password = password != NULL && strlen (password) != 0;
+
+ gtk_widget_set_sensitive (GTK_WIDGET (dialog->login_button), valid_user && valid_password);
+}
+
+static void
+update_description (GsBasicAuthDialog *dialog, const gchar *remote, const gchar *realm)
+{
+ g_autofree gchar *description = NULL;
+
+ /* TRANSLATORS: This is a description for entering user/password */
+ description = g_strdup_printf (_("Login required remote %s (realm %s)"),
+ remote, realm);
+ gtk_label_set_text (dialog->description_label, description);
+}
+
+static void
+gs_basic_auth_dialog_init (GsBasicAuthDialog *dialog)
+{
+ gtk_widget_init_template (GTK_WIDGET (dialog));
+}
+
+static void
+gs_basic_auth_dialog_class_init (GsBasicAuthDialogClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/gs-basic-auth-dialog.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, GsBasicAuthDialog, login_button);
+ gtk_widget_class_bind_template_child (widget_class, GsBasicAuthDialog, description_label);
+ gtk_widget_class_bind_template_child (widget_class, GsBasicAuthDialog, user_entry);
+ gtk_widget_class_bind_template_child (widget_class, GsBasicAuthDialog, password_entry);
+
+ gtk_widget_class_bind_template_callback (widget_class, dialog_validate);
+ gtk_widget_class_bind_template_callback (widget_class, cancel_button_clicked_cb);
+ gtk_widget_class_bind_template_callback (widget_class, login_button_clicked_cb);
+}
+
+GtkWidget *
+gs_basic_auth_dialog_new (GtkWindow *parent,
+ const gchar *remote,
+ const gchar *realm,
+ GsBasicAuthCallback callback,
+ gpointer callback_data)
+{
+ GsBasicAuthDialog *dialog;
+
+ dialog = g_object_new (GS_TYPE_BASIC_AUTH_DIALOG,
+ "use-header-bar", TRUE,
+ "transient-for", parent,
+ "modal", TRUE,
+ NULL);
+ dialog->callback = callback;
+ dialog->callback_data = callback_data;
+
+ update_description (dialog, remote, realm);
+ dialog_validate (dialog);
+
+ return GTK_WIDGET (dialog);
+}
diff --git a/src/gs-basic-auth-dialog.h b/src/gs-basic-auth-dialog.h
new file mode 100644
index 00000000..ec5f1d03
--- /dev/null
+++ b/src/gs-basic-auth-dialog.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2020 Kalev Lember <klember@redhat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "gnome-software-private.h"
+
+G_BEGIN_DECLS
+
+typedef void (*GsBasicAuthCallback) (const gchar *user, const gchar *password, gpointer callback_data);
+
+#define GS_TYPE_BASIC_AUTH_DIALOG (gs_basic_auth_dialog_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsBasicAuthDialog, gs_basic_auth_dialog, GS, BASIC_AUTH_DIALOG, GtkDialog)
+
+GtkWidget *gs_basic_auth_dialog_new (GtkWindow *parent,
+ const gchar *remote,
+ const gchar *realm,
+ GsBasicAuthCallback callback,
+ gpointer callback_data);
+
+G_END_DECLS
diff --git a/src/gs-basic-auth-dialog.ui b/src/gs-basic-auth-dialog.ui
new file mode 100644
index 00000000..339e831d
--- /dev/null
+++ b/src/gs-basic-auth-dialog.ui
@@ -0,0 +1,203 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GsBasicAuthDialog" parent="GtkDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">dialog</property>
+ <property name="title" translatable="yes">Login Required</property>
+ <property name="use_header_bar">1</property>
+ <child internal-child="headerbar">
+ <object class="GtkHeaderBar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="show_close_button">False</property>
+ <child>
+ <object class="GtkButton" id="cancel_button">
+ <property name="label" translatable="yes">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="valign">center</property>
+ <signal name="clicked" handler="cancel_button_clicked_cb" object="GsBasicAuthDialog" swapped="yes"/>
+ <style>
+ <class name="text-button"/>
+ </style>
+ </object>
+ <packing>
+ <property name="pack_type">start</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="login_button">
+ <property name="label" translatable="yes">_Login</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="valign">center</property>
+ <signal name="clicked" handler="login_button_clicked_cb" object="GsBasicAuthDialog" swapped="yes"/>
+ <style>
+ <class name="text-button"/>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ <packing>
+ <property name="pack_type">end</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="row_spacing">8</property>
+ <property name="column_spacing">6</property>
+ <property name="border_width">20</property>
+ <property name="margin_end">20</property>
+ <child>
+ <object class="GtkLabel" id="description_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="wrap">True</property>
+ <property name="wrap_mode">word-char</property>
+ <property name="margin_bottom">20</property>
+ <property name="max_width_chars">55</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">2</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="user_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">_User</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">user_entry</property>
+ <property name="margin_start">20</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="password_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">_Password</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">password_entry</property>
+ <property name="margin_start">20</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">4</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="user_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="invisible_char">●</property>
+ <property name="activates_default">True</property>
+ <property name="invisible_char_set">True</property>
+ <property name="input_purpose">password</property>
+ <signal name="changed" handler="dialog_validate" object="GsBasicAuthDialog" swapped="yes"/>
+ <signal name="activate" handler="dialog_validate" object="GsBasicAuthDialog" swapped="yes"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">3</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="password_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="visibility">False</property>
+ <property name="invisible_char">●</property>
+ <property name="activates_default">True</property>
+ <property name="invisible_char_set">True</property>
+ <property name="input_purpose">password</property>
+ <signal name="changed" handler="dialog_validate" object="GsBasicAuthDialog" swapped="yes"/>
+ <signal name="activate" handler="dialog_validate" object="GsBasicAuthDialog" swapped="yes"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">4</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkSizeGroup">
+ <widgets>
+ <widget name="user_label"/>
+ <widget name="password_label"/>
+ </widgets>
+ </object>
+ <object class="GtkSizeGroup">
+ <widgets>
+ <widget name="user_entry"/>
+ <widget name="password_entry"/>
+ </widgets>
+ </object>
+ <object class="GtkSizeGroup">
+ <property name="mode">horizontal</property>
+ <widgets>
+ <widget name="login_button"/>
+ <widget name="cancel_button"/>
+ </widgets>
+ </object>
+</interface>
diff --git a/src/gs-shell.c b/src/gs-shell.c
index 009776ad..41503cf8 100644
--- a/src/gs-shell.c
+++ b/src/gs-shell.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2013-2017 Richard Hughes <richard@hughsie.com>
* Copyright (C) 2013 Matthias Clasen <mclasen@redhat.com>
- * Copyright (C) 2014-2018 Kalev Lember <klember@redhat.com>
+ * Copyright (C) 2014-2020 Kalev Lember <klember@redhat.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
@@ -18,6 +18,7 @@
#include "gs-common.h"
#include "gs-shell.h"
+#include "gs-basic-auth-dialog.h"
#include "gs-details-page.h"
#include "gs-installed-page.h"
#include "gs-metered-data-dialog.h"
@@ -362,6 +363,25 @@ scheduler_ready_cb (GObject *source_object,
}
#endif /* HAVE_MOGWAI */
+static void
+gs_shell_basic_auth_start_cb (GsPluginLoader *plugin_loader,
+ const gchar *remote,
+ const gchar *realm,
+ GsBasicAuthCallback callback,
+ gpointer callback_data,
+ GsShell *shell)
+{
+ GsShellPrivate *priv = gs_shell_get_instance_private (shell);
+ GtkWidget *dialog;
+
+ dialog = gs_basic_auth_dialog_new (priv->main_window, remote, realm, callback, callback_data);
+ gs_shell_modal_dialog_present (shell, GTK_DIALOG (dialog));
+
+ /* just destroy */
+ g_signal_connect_swapped (dialog, "response",
+ G_CALLBACK (gtk_widget_destroy), dialog);
+}
+
static void
free_back_entry (BackEntry *entry)
{
@@ -2126,6 +2146,9 @@ gs_shell_setup (GsShell *shell, GsPluginLoader *plugin_loader, GCancellable *can
g_signal_connect_object (priv->plugin_loader, "notify::network-metered",
G_CALLBACK (gs_shell_network_metered_notify_cb),
shell, 0);
+ g_signal_connect_object (priv->plugin_loader, "basic-auth-start",
+ G_CALLBACK (gs_shell_basic_auth_start_cb),
+ shell, 0);
priv->cancellable = g_object_ref (cancellable);
priv->settings = g_settings_new ("org.gnome.software");
diff --git a/src/meson.build b/src/meson.build
index cbd0a511..6581e77c 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -20,6 +20,7 @@ gnome_software_sources = [
'gs-application.c',
'gs-app-row.c',
'gs-app-tile.c',
+ 'gs-basic-auth-dialog.c',
'gs-category-page.c',
'gs-category-tile.c',
'gs-common.c',
--
2.26.2