Blame SOURCES/0001-Add-basic-auth-support-to-flatpak-plugin.patch

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