Authenticators: Add BasicAuth operation and handle it in transaction

This adds a new way for authenticators to trigger interactive authentication
in the client for simple user/password dialogs. These are not recommended
to use as a webflow is often better. However, for OCI remotes that
use http basic auth this is useful and allows the CLI experience to work
with that.
This commit is contained in:
Alexander Larsson
2019-12-11 14:40:56 +01:00
committed by Alexander Larsson
parent ba9d607a92
commit 6e6c122cb2
3 changed files with 134 additions and 1 deletions

View File

@@ -193,6 +193,7 @@ enum {
ADD_NEW_REMOTE,
WEBFLOW_START,
WEBFLOW_DONE,
BASIC_AUTH_START,
LAST_SIGNAL
};
@@ -1196,6 +1197,34 @@ flatpak_transaction_class_init (FlatpakTransactionClass *klass)
NULL, NULL,
NULL,
G_TYPE_NONE, 1, G_TYPE_INT);
/**
* FlatpakTransaction::basic-auth-start:
* @object: A #FlatpakTransaction
* @remote: The remote we're authenticating with
* @realm: The url to show
* @id: The id of the operation, can be used to finish it
*
* The ::basic-auth-start signal gets emitted when a basic user/password
* authentication is needed during the operation. If the caller handles this
* it should ask the user for the user and password and return %TRUE. Once
* the information is gathered call flatpak_transaction_complete_basic_auth()
* with it.
*
* If the client does not support basic auth then return %FALSE from this signal
* (or don't implement it). This will abort the authentication and likely
* result in the transaction failing (unless the authentication was somehow
* optional).
*
* Since: 1.5.2
*/
signals[BASIC_AUTH_START] =
g_signal_new ("basic-auth-start",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (FlatpakTransactionClass, basic_auth_start),
NULL, NULL,
NULL,
G_TYPE_BOOLEAN, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
}
@@ -2800,6 +2829,36 @@ request_tokens_webflow_done (FlatpakAuthenticatorRequest *object,
g_signal_emit (transaction, signals[WEBFLOW_DONE], 0, id);
}
static void
request_tokens_basic_auth (FlatpakAuthenticatorRequest *object,
const gchar *arg_realm,
RequestData *data)
{
g_autoptr(FlatpakTransaction) transaction = g_object_ref (data->transaction);
FlatpakTransactionPrivate *priv = flatpak_transaction_get_instance_private (transaction);
gboolean retval = FALSE;
if (data->done)
return; /* Don't respond twice */
g_assert (priv->active_request_id == 0);
priv->active_request_id = ++priv->next_request_id;
g_debug ("BasicAuth start %s", arg_realm);
g_signal_emit (transaction, signals[BASIC_AUTH_START], 0, data->remote, arg_realm, priv->active_request_id, &retval);
if (!retval)
{
g_autoptr(GError) local_error = NULL;
priv->active_request_id = 0;
/* We didn't handle the request, cancel the auth op. */
if (!flatpak_authenticator_request_call_close_sync (data->request, NULL, &local_error))
g_debug ("Failed to close auth request: %s", local_error->message);
}
}
/**
* flatpak_transaction_abort_webflow:
* @self: a #FlatpakTransaction
@@ -2837,6 +2896,48 @@ flatpak_transaction_abort_webflow (FlatpakTransaction *self,
}
}
/**
* flatpak_transaction_complete_basic_auth:
* @self: a #FlatpakTransaction
* @id: The webflow id, as passed into the webflow-start signal
* @user: The user name, or %NULL if aborting request
* @password: The password
*
* Finishes (or aborts) an ongoing basic auth request.
*
* Since: 1.5.2
*/
void
flatpak_transaction_complete_basic_auth (FlatpakTransaction *self,
guint id,
const char *user,
const char *password)
{
FlatpakTransactionPrivate *priv = flatpak_transaction_get_instance_private (self);
g_autoptr(GError) local_error = NULL;
if (priv->active_request_id == id)
{
RequestData *data = priv->active_request;
g_assert (data != NULL);
priv->active_request_id = 0;
if (user == NULL)
{
if (!flatpak_authenticator_request_call_close_sync (data->request, NULL, &local_error))
g_debug ("Failed to abort basic auth request: %s", local_error->message);
}
else
{
if (!flatpak_authenticator_request_call_basic_auth_reply_sync (data->request,
user, password,
NULL, &local_error))
g_debug ("Failed to reply to basic auth request: %s", local_error->message);
}
}
}
static void
copy_summary_data (GVariantBuilder *builder, GVariant *summary, const char *key)
{
@@ -2927,6 +3028,7 @@ request_tokens_for_remote (FlatpakTransaction *self,
g_signal_connect (request, "webflow", (GCallback)request_tokens_webflow, &data);
g_signal_connect (request, "webflow-done", (GCallback)request_tokens_webflow_done, &data);
g_signal_connect (request, "response", (GCallback)request_tokens_response, &data);
g_signal_connect (request, "basic-auth", (GCallback)request_tokens_basic_auth, &data);
priv->active_request = &data;

View File

@@ -139,7 +139,11 @@ struct _FlatpakTransactionClass
void (*webflow_done) (FlatpakTransaction *transaction,
guint id);
gpointer padding[6];
gboolean (*basic_auth_start) (FlatpakTransaction *transaction,
const char *remote,
const char *realm,
guint id);
gpointer padding[5];
};
FLATPAK_EXTERN
@@ -238,6 +242,11 @@ GList *flatpak_transaction_get_operations (FlatpakTransaction *self);
FLATPAK_EXTERN
void flatpak_transaction_abort_webflow (FlatpakTransaction *self,
guint id);
FLATPAK_EXTERN
void flatpak_transaction_complete_basic_auth (FlatpakTransaction *self,
guint id,
const char *user,
const char *password);
FLATPAK_EXTERN
gboolean flatpak_transaction_add_install (FlatpakTransaction *self,

View File

@@ -185,6 +185,28 @@
-->
<signal name="WebflowDone">
</signal>
<!--
BasicAuth:
Emitted by the authenticator when it needs to do a simple user + password authentication.
This is only useful for very simple authentication interaction, but this is still used (for
instance for http basic access authentication), and for those cases this allows a nicely
integrated UI and CLI experience.
-->
<signal name="BasicAuth">
<arg type="s" name="realm"/>
</signal>
<!--
BasicAuthReply:
@user: The user
@password: The password
Call to finish the request started with the BasicAuth signal.
-->
<method name="BasicAuthReply">
<arg type="s" name="user"/>
<arg type="s" name="password"/>
</method>
<!--
Response:
@response: Numeric response