mirror of
https://github.com/flatpak/flatpak.git
synced 2026-02-28 12:47:46 -05:00
OCI: Add flatpak_oci_registry_get_token() method
This tries to do a HEAD request to the repository and if that fails with 401 uses the OCI mechanism to (given a basic auth token) generate a token for the download.
This commit is contained in:
committed by
Alexander Larsson
parent
b1a8852895
commit
461a592b8e
@@ -78,6 +78,12 @@ int flatpak_oci_registry_download_blob (FlatpakOciRegistry
|
||||
gpointer user_data,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
char * flatpak_oci_registry_get_token (FlatpakOciRegistry *self,
|
||||
const char *repository,
|
||||
const char *digest,
|
||||
const char *basic_auth,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GBytes * flatpak_oci_registry_load_blob (FlatpakOciRegistry *self,
|
||||
const char *repository,
|
||||
gboolean manifest,
|
||||
|
||||
@@ -879,6 +879,165 @@ flatpak_oci_registry_mirror_blob (FlatpakOciRegistry *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static const char *
|
||||
object_get_string_member_with_default (JsonNode *json,
|
||||
const char *member_name,
|
||||
const char *default_value)
|
||||
{
|
||||
JsonNode *node;
|
||||
|
||||
if (json == NULL || !JSON_NODE_HOLDS_OBJECT(json))
|
||||
return default_value;
|
||||
|
||||
node = json_object_get_member (json_node_get_object (json), member_name);
|
||||
|
||||
if (node == NULL || JSON_NODE_HOLDS_NULL (node) || JSON_NODE_TYPE (node) != JSON_NODE_VALUE)
|
||||
return default_value;
|
||||
|
||||
return json_node_get_string (node);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
get_token_for_www_auth (FlatpakOciRegistry *self,
|
||||
const char *www_authenticate,
|
||||
const char *auth,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GInputStream) auth_stream = NULL;
|
||||
g_autoptr(SoupMessage) auth_msg = NULL;
|
||||
g_autoptr(GHashTable) params = NULL;
|
||||
g_autoptr(GHashTable) args = NULL;
|
||||
const char *realm, *service, *scope, *token;
|
||||
g_autoptr(SoupURI) auth_uri = NULL;
|
||||
g_autoptr(GBytes) body = NULL;
|
||||
g_autoptr(JsonNode) json = NULL;
|
||||
|
||||
if (g_ascii_strncasecmp (www_authenticate, "Bearer ", strlen ("Bearer ")) != 0)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Only Bearer authentication supported");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
params = soup_header_parse_param_list (www_authenticate + strlen ("Bearer "));
|
||||
|
||||
realm = g_hash_table_lookup (params, "realm");
|
||||
if (realm == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Only realm in authentication request");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
auth_uri = soup_uri_new (realm);
|
||||
if (auth_uri == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid realm in authentication request");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
args = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
|
||||
service = g_hash_table_lookup (params, "service");
|
||||
if (service)
|
||||
g_hash_table_insert (args, "service", (char *)service);
|
||||
scope = g_hash_table_lookup (params, "scope");
|
||||
if (scope)
|
||||
g_hash_table_insert (args, "scope", (char *)scope);
|
||||
|
||||
soup_uri_set_query_from_form (auth_uri, args);
|
||||
|
||||
auth_msg = soup_message_new_from_uri ("GET", auth_uri);
|
||||
|
||||
g_autofree char *basic_auth = g_strdup_printf ("Basic %s", auth);
|
||||
soup_message_headers_replace (auth_msg->request_headers, "Authorization", basic_auth);
|
||||
|
||||
auth_stream = soup_session_send (self->soup_session, auth_msg, NULL, error);
|
||||
if (auth_stream == NULL)
|
||||
return NULL;
|
||||
|
||||
body = flatpak_read_stream (auth_stream, TRUE, error);
|
||||
if (body == NULL)
|
||||
return NULL;
|
||||
|
||||
json = json_from_string ((char *)g_bytes_get_data (body, NULL), error);
|
||||
if (json == NULL)
|
||||
return NULL;
|
||||
|
||||
token = object_get_string_member_with_default (json, "token", NULL);
|
||||
if (token == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid authentication request response");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_strdup (token);
|
||||
}
|
||||
|
||||
char *
|
||||
flatpak_oci_registry_get_token (FlatpakOciRegistry *self,
|
||||
const char *repository,
|
||||
const char *digest,
|
||||
const char *basic_auth,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autofree char *subpath = NULL;
|
||||
g_autoptr(SoupURI) uri = NULL;
|
||||
g_autoptr(GBytes) bytes = NULL;
|
||||
g_autofree char *uri_s = NULL;
|
||||
g_autoptr(GInputStream) stream = NULL;
|
||||
g_autoptr(SoupMessage) msg = NULL;
|
||||
g_autofree char *www_authenticate = NULL;
|
||||
g_autofree char *token = NULL;
|
||||
|
||||
g_assert (self->valid);
|
||||
|
||||
subpath = get_digest_subpath (self, repository, TRUE, digest, error);
|
||||
if (subpath == NULL)
|
||||
return NULL;
|
||||
|
||||
if (self->dfd != -1)
|
||||
return g_strdup (""); // No tokens for local repos
|
||||
|
||||
uri = soup_uri_new_with_base (self->base_uri, subpath);
|
||||
if (uri == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
|
||||
"Invalid relative url %s", subpath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
msg = soup_message_new_from_uri ("HEAD", uri);
|
||||
|
||||
stream = soup_session_send (self->soup_session, msg, NULL, error);
|
||||
if (stream == NULL)
|
||||
return NULL;
|
||||
|
||||
if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
|
||||
{
|
||||
return g_strdup ("");
|
||||
}
|
||||
else if (msg->status_code != SOUP_STATUS_UNAUTHORIZED)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unexpected response status %d from repo", msg->status_code);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Need www-authenticated header */
|
||||
www_authenticate = g_strdup (soup_message_headers_get_one (msg->response_headers, "WWW-Authenticate"));
|
||||
if (www_authenticate == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Now WWW-Authenticate header from repo");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
token = get_token_for_www_auth (self, www_authenticate, basic_auth, cancellable, error);
|
||||
if (token == NULL)
|
||||
return NULL;
|
||||
|
||||
return g_steal_pointer (&token);
|
||||
}
|
||||
|
||||
GBytes *
|
||||
flatpak_oci_registry_load_blob (FlatpakOciRegistry *self,
|
||||
const char *repository,
|
||||
|
||||
Reference in New Issue
Block a user