[gnome-software/wip/rancell/snapauth] snapd: Use snap authentication
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/wip/rancell/snapauth] snapd: Use snap authentication
- Date: Sun, 3 Jul 2016 22:53:03 +0000 (UTC)
commit 0bf6bfed4756d275ee3bcec27282c441a17ed80f
Author: Robert Ancell <robert ancell canonical com>
Date: Fri Jul 1 15:33:07 2016 +1200
snapd: Use snap authentication
src/plugins/gs-plugin-snap.c | 149 +++++++++++++++++++++++++++++++++++++++---
src/plugins/gs-snapd.c | 114 +++++++++++++++++++++-----------
src/plugins/gs-snapd.h | 15 ++++-
3 files changed, 229 insertions(+), 49 deletions(-)
---
diff --git a/src/plugins/gs-plugin-snap.c b/src/plugins/gs-plugin-snap.c
index 3e15380..786e60a 100644
--- a/src/plugins/gs-plugin-snap.c
+++ b/src/plugins/gs-plugin-snap.c
@@ -26,17 +26,27 @@
#include "gs-snapd.h"
+struct GsPluginData {
+ GsAuth *auth;
+};
+
typedef gboolean (*AppFilterFunc)(const gchar *id, JsonObject *object, gpointer data);
void
gs_plugin_initialize (GsPlugin *plugin)
{
+ GsPluginData *priv = gs_plugin_alloc_data (plugin, sizeof(GsPluginData));
+
if (!gs_snapd_exists ()) {
g_debug ("disabling '%s' as snapd not running",
gs_plugin_get_name (plugin));
gs_plugin_set_enabled (plugin, FALSE);
}
+ priv->auth = gs_auth_new ("snapd");
+ gs_auth_set_provider_name (priv->auth, "Snap Store");
+ gs_plugin_add_auth (plugin, priv->auth);
+
gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "desktop-categories");
gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "ubuntu-reviews");
gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_BETTER_THAN, "packagekit");
@@ -124,7 +134,7 @@ refine_app (GsPlugin *plugin, GsApp *app, JsonObject *package)
g_autofree gchar *icon_response = NULL;
gsize icon_response_length;
- if (gs_snapd_request ("GET", icon_url, NULL, NULL, TRUE, NULL,
+ if (gs_snapd_request (SOUP_METHOD_GET, icon_url, NULL, NULL,
NULL, NULL, NULL,
&icon_response, &icon_response_length,
NULL)) {
@@ -207,7 +217,7 @@ get_apps (GsPlugin *plugin,
g_string_append (path, fields);
}
g_ptr_array_free (query_fields, TRUE);
- if (!gs_snapd_request ("GET", path->str, NULL, NULL, TRUE, NULL,
+ if (!gs_snapd_request (SOUP_METHOD_GET, path->str, NULL, NULL,
&status_code, &reason_phrase,
&response_type, &response,
NULL, error))
@@ -271,7 +281,7 @@ get_app (GsPlugin *plugin, GsApp *app, GError **error)
guint i;
path = g_strdup_printf ("/v2/snaps/%s", gs_app_get_id (app));
- if (!gs_snapd_request ("GET", path, NULL, NULL, TRUE, NULL,
+ if (!gs_snapd_request (SOUP_METHOD_GET, path, NULL, NULL,
&status_code, &reason_phrase,
&response_type, &response,
NULL, error))
@@ -284,7 +294,7 @@ get_app (GsPlugin *plugin, GsApp *app, GError **error)
g_clear_pointer (&response, g_free);
path = g_strdup_printf ("/v2/find?q=%s", gs_app_get_id (app));
- if (!gs_snapd_request ("GET", path, NULL, NULL, TRUE, NULL,
+ if (!gs_snapd_request (SOUP_METHOD_GET, path, NULL, NULL,
&status_code, &reason_phrase,
&response_type, &response,
NULL, error))
@@ -337,6 +347,8 @@ get_app (GsPlugin *plugin, GsApp *app, GError **error)
void
gs_plugin_destroy (GsPlugin *plugin)
{
+ GsPluginData *priv = gs_plugin_get_data (plugin);
+ g_clear_object (&priv->auth);
}
static gboolean
@@ -401,16 +413,22 @@ send_package_action (GsPlugin *plugin,
const gchar *resource_path;
const gchar *type;
const gchar *change_id;
- g_autoptr(GVariant) macaroon = NULL;
content = g_strdup_printf ("{\"action\": \"%s\"}", action);
path = g_strdup_printf ("/v2/snaps/%s", id);
- if (!gs_snapd_request ("POST", path, content, NULL, TRUE,
- &macaroon, &status_code,
- &reason_phrase, &response_type,
+ if (!gs_snapd_request (SOUP_METHOD_POST, path, content, NULL,
+ &status_code, &reason_phrase, &response_type,
&response, NULL, error))
return FALSE;
+ if (status_code == SOUP_STATUS_UNAUTHORIZED) {
+ g_set_error_literal (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_AUTH_REQUIRED,
+ "Requires authentication with @snapd");
+ return FALSE;
+ }
+
if (status_code != SOUP_STATUS_ACCEPTED) {
g_set_error (error,
GS_PLUGIN_ERROR,
@@ -440,7 +458,7 @@ send_package_action (GsPlugin *plugin,
/* Wait for a little bit before polling */
g_usleep (100 * 1000);
- if (!gs_snapd_request ("GET", resource_path, NULL, macaroon, TRUE, NULL,
+ if (!gs_snapd_request (SOUP_METHOD_GET, resource_path, NULL, NULL,
&status_code, &status_reason_phrase,
&status_response_type, &status_response,
NULL, error)) {
@@ -545,3 +563,116 @@ gs_plugin_app_remove (GsPlugin *plugin,
gs_app_set_state (app, AS_APP_STATE_AVAILABLE);
return TRUE;
}
+
+gboolean
+gs_plugin_auth_login (GsPlugin *plugin, GsAuth *auth,
+ GCancellable *cancellable, GError **error)
+{
+ GsPluginData *priv = gs_plugin_get_data (plugin);
+ g_autoptr(JsonBuilder) builder = NULL;
+ g_autoptr(JsonNode) json_root = NULL;
+ g_autoptr(JsonGenerator) json_generator = NULL;
+ g_autofree gchar *data = NULL;
+ guint status_code;
+ g_autofree gchar *reason_phrase = NULL;
+ g_autofree gchar *response_type = NULL;
+ g_autofree gchar *response = NULL;
+ g_autoptr(JsonObject) result = NULL;
+
+ if (auth != priv->auth)
+ return TRUE;
+
+ builder = json_builder_new ();
+ json_builder_begin_object (builder);
+ json_builder_set_member_name (builder, "username");
+ json_builder_add_string_value (builder, gs_auth_get_username (auth));
+ json_builder_set_member_name (builder, "password");
+ json_builder_add_string_value (builder, gs_auth_get_password (auth));
+ if (gs_auth_get_pin (auth)) {
+ json_builder_set_member_name (builder, "otp");
+ json_builder_add_string_value (builder, gs_auth_get_pin (auth));
+ }
+ json_builder_end_object (builder);
+
+ json_root = json_builder_get_root (builder);
+ json_generator = json_generator_new ();
+ json_generator_set_pretty (json_generator, TRUE);
+ json_generator_set_root (json_generator, json_root);
+ data = json_generator_to_data (json_generator, NULL);
+ if (data == NULL) {
+ g_set_error_literal (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "Failed to generate JSON request");
+ return FALSE;
+ }
+
+ if (!gs_snapd_request (SOUP_METHOD_POST, "/v2/login", data, NULL,
+ &status_code, &reason_phrase,
+ &response_type, &response, NULL,
+ error))
+ return FALSE;
+
+ if (status_code != SOUP_STATUS_OK) {
+ g_autofree gchar *error_message = NULL;
+ g_autofree gchar *error_kind = NULL;
+
+ if (!gs_snapd_parse_error (response_type, response, &error_message, &error_kind, error))
+ return FALSE;
+
+ if (g_strcmp0 (error_kind, "two-factor-required") == 0) {
+ g_set_error_literal (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_PIN_REQUIRED,
+ error_message);
+ }
+ else {
+ g_set_error_literal (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ error_message);
+ }
+ return FALSE;
+ }
+
+ if (!gs_snapd_parse_result (response_type, response, &result, error))
+ return FALSE;
+
+ const gchar *macaroon = json_object_get_string_member (result, "macaroon");
+
+ return TRUE;
+}
+
+gboolean
+gs_plugin_auth_lost_password (GsPlugin *plugin, GsAuth *auth,
+ GCancellable *cancellable, GError **error)
+{
+ GsPluginData *priv = gs_plugin_get_data (plugin);
+
+ if (auth != priv->auth)
+ return TRUE;
+
+ /* return with data */
+ /*g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_AUTH_INVALID,
+ "do online using @%s/+forgot_password", UBUNTU_LOGIN_HOST);*/
+ return FALSE;
+}
+
+gboolean
+gs_plugin_auth_register (GsPlugin *plugin, GsAuth *auth,
+ GCancellable *cancellable, GError **error)
+{
+ GsPluginData *priv = gs_plugin_get_data (plugin);
+
+ if (auth != priv->auth)
+ return TRUE;
+
+ /* return with data */
+ /*g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_AUTH_INVALID,
+ "do online using @%s/+login", UBUNTU_LOGIN_HOST);*/
+ return FALSE;
+}
diff --git a/src/plugins/gs-snapd.c b/src/plugins/gs-snapd.c
index c11b1ce..ff688ce 100644
--- a/src/plugins/gs-snapd.c
+++ b/src/plugins/gs-snapd.c
@@ -95,8 +95,6 @@ gs_snapd_request (const gchar *method,
const gchar *path,
const gchar *content,
GVariant *macaroon,
- gboolean retry_after_login,
- GVariant **out_macaroon,
guint *status_code,
gchar **reason_phrase,
gchar **response_type,
@@ -116,7 +114,6 @@ gs_snapd_request (const gchar *method,
const gchar *discharge;
GVariantIter *iter;
guint code;
- gboolean ret;
// NOTE: Would love to use libsoup but it doesn't support unix sockets
// https://bugzilla.gnome.org/show_bug.cgi?id=727563
@@ -187,39 +184,6 @@ gs_snapd_request (const gchar *method,
if (status_code != NULL)
*status_code = code;
- if ((code == 401 || code == 403) && retry_after_login) {
- g_socket_close (socket, NULL);
-
- if (macaroon == NULL) {
- g_set_error_literal (error,
- GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_AUTH_REQUIRED,
- "failed to authenticate");
- return FALSE;
- }
-
- ret = gs_snapd_request (method,
- path,
- content,
- macaroon,
- FALSE,
- NULL,
- status_code,
- reason_phrase,
- response_type,
- response,
- response_length,
- error);
-
- if (ret && out_macaroon != NULL) {
- *out_macaroon = macaroon;
- } else {
- g_variant_unref (macaroon);
- }
-
- return ret;
- }
-
/* work out how much data to follow */
if (g_strcmp0 (soup_message_headers_get_one (headers, "Transfer-Encoding"),
"chunked") == 0) {
@@ -281,8 +245,6 @@ gs_snapd_request (const gchar *method,
error))
return FALSE;
- if (out_macaroon != NULL)
- *out_macaroon = g_variant_ref (macaroon);
if (response_type)
*response_type = g_strdup (soup_message_headers_get_one (headers, "Content-Type"));
if (response) {
@@ -296,3 +258,79 @@ gs_snapd_request (const gchar *method,
return TRUE;
}
+
+gboolean
+gs_snapd_parse_result (const gchar *response_type,
+ const gchar *response,
+ JsonObject **result,
+ GError **error)
+{
+ g_autoptr(JsonParser) parser = NULL;
+ g_autoptr(GError) error_local = NULL;
+ g_autoptr(JsonObject) root = NULL;
+
+ if (response_type == NULL) {
+ g_set_error_literal (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "snapd returned no content type");
+ return FALSE;
+ }
+ if (g_strcmp0 (response_type, "application/json") != 0) {
+ g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "snapd returned unexpected content type %s", response_type);
+ return FALSE;
+ }
+
+ parser = json_parser_new ();
+ if (!json_parser_load_from_data (parser, response, -1, &error_local)) {
+ g_set_error (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "Unable to parse snapd response: %s",
+ error_local->message);
+ return FALSE;
+ }
+
+ if (!JSON_NODE_HOLDS_OBJECT (json_parser_get_root (parser))) {
+ g_set_error_literal (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "snapd response does is not a valid JSON object");
+ return FALSE;
+ }
+ root = json_node_get_object (json_parser_get_root (parser));
+ if (!json_object_has_member (root, "result")) {
+ g_set_error_literal (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "snapd response does not contain a \"result\" field");
+ return FALSE;
+ }
+ if (result != NULL)
+ *result = json_object_get_object_member (root, "result");
+
+ return TRUE;
+}
+
+gboolean
+gs_snapd_parse_error (const gchar *response_type,
+ const gchar *response,
+ gchar **error_message,
+ gchar **error_kind,
+ GError **error)
+{
+ g_autoptr(JsonObject) result = NULL;
+
+ if (!gs_snapd_parse_result (response_type, response, &result, error))
+ return FALSE;
+
+ if (error_message != NULL)
+ *error_message = g_strdup (json_object_get_string_member (result, "message"));
+ if (error_kind != NULL)
+ *error_kind = g_strdup (json_object_get_string_member (result, "kind"));
+
+ return TRUE;
+}
diff --git a/src/plugins/gs-snapd.h b/src/plugins/gs-snapd.h
index 58ac6fe..8008555 100644
--- a/src/plugins/gs-snapd.h
+++ b/src/plugins/gs-snapd.h
@@ -23,6 +23,7 @@
#define __GS_SNAPD_H__
#include <gio/gio.h>
+#include <json-glib/json-glib.h>
gboolean gs_snapd_exists (void);
@@ -30,8 +31,6 @@ gboolean gs_snapd_request (const gchar *method,
const gchar *path,
const gchar *content,
GVariant *macaroon,
- gboolean retry_after_login,
- GVariant **out_macaroon,
guint *status_code,
gchar **reason_phrase,
gchar **response_type,
@@ -39,4 +38,16 @@ gboolean gs_snapd_request (const gchar *method,
gsize *response_length,
GError **error);
+gboolean gs_snapd_parse_result (const gchar *response_type,
+ const gchar *response,
+ JsonObject **result,
+ GError **error);
+
+gboolean gs_snapd_parse_error (const gchar *response_type,
+ const gchar *response,
+ gchar **error_message,
+ gchar **error_kind,
+ GError **error);
+
+
#endif /* __GS_SNAPD_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]