From a12762a29a5f7fe5a0eabff5e2c2347ca4eb9c68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 27 Sep 2021 15:30:25 +0300 Subject: [PATCH] gs: Add support for authenticating via Service Account Credentials This allows authenticating directly with Server Account credentials instead of having it configured on host system separately, and thus allows using arbitrary accounts configured/selected at runtime. Part-of: --- .../gst-plugins-bad/ext/gs/gstgscommon.cpp | 35 ++++++++--- .../gst-plugins-bad/ext/gs/gstgscommon.h | 1 + .../gst-plugins-bad/ext/gs/gstgssink.cpp | 31 ++++++++- .../gst-plugins-bad/ext/gs/gstgssrc.cpp | 63 ++++++++++++++++++- 4 files changed, 118 insertions(+), 12 deletions(-) diff --git a/subprojects/gst-plugins-bad/ext/gs/gstgscommon.cpp b/subprojects/gst-plugins-bad/ext/gs/gstgscommon.cpp index 41f106054d..8326baaa68 100644 --- a/subprojects/gst-plugins-bad/ext/gs/gstgscommon.cpp +++ b/subprojects/gst-plugins-bad/ext/gs/gstgscommon.cpp @@ -58,18 +58,35 @@ static inline gchar* g_date_time_format_iso8601(GDateTime* datetime) { std::unique_ptr gst_gs_create_client( const gchar* service_account_email, + const gchar* service_account_credentials, GError** error) { - if (service_account_email) { - // Meant to be used from a container running in the Cloud. + if (service_account_email || service_account_credentials) { + google::cloud::StatusOr> creds; + if (service_account_credentials) { + creds = gcs::oauth2::CreateServiceAccountCredentialsFromJsonContents( + service_account_credentials, + {{"https://www.googleapis.com/auth/devstorage.full_control"}}, + absl::nullopt); + } else { + // Meant to be used from a container running in the Cloud. + creds = + gcs::oauth2::CreateComputeEngineCredentials(service_account_email); + } - google::cloud::StatusOr> creds( - std::make_shared>( - service_account_email)); if (!creds) { - g_set_error(error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_AUTHORIZED, - "Could not retrieve credentials for the given service " - "account %s (%s)", - service_account_email, creds.status().message().c_str()); + if (service_account_email) { + g_set_error(error, GST_RESOURCE_ERROR, + GST_RESOURCE_ERROR_NOT_AUTHORIZED, + "Could not retrieve credentials for the given service " + "account %s (%s)", + service_account_email, creds.status().message().c_str()); + } else { + g_set_error(error, GST_RESOURCE_ERROR, + GST_RESOURCE_ERROR_NOT_AUTHORIZED, + "Could not retrieve credentials for the given service " + "account credentials JSON (%s)", + creds.status().message().c_str()); + } return nullptr; } diff --git a/subprojects/gst-plugins-bad/ext/gs/gstgscommon.h b/subprojects/gst-plugins-bad/ext/gs/gstgscommon.h index 574212e366..e0df4ffdb5 100644 --- a/subprojects/gst-plugins-bad/ext/gs/gstgscommon.h +++ b/subprojects/gst-plugins-bad/ext/gs/gstgscommon.h @@ -30,6 +30,7 @@ std::unique_ptr gst_gs_create_client( const gchar* service_account_email, + const gchar* service_account_credentials, GError** error); gboolean gst_gs_get_buffer_date(GstBuffer* buffer, diff --git a/subprojects/gst-plugins-bad/ext/gs/gstgssink.cpp b/subprojects/gst-plugins-bad/ext/gs/gstgssink.cpp index 104ca62210..b2aed05251 100644 --- a/subprojects/gst-plugins-bad/ext/gs/gstgssink.cpp +++ b/subprojects/gst-plugins-bad/ext/gs/gstgssink.cpp @@ -110,6 +110,7 @@ enum { PROP_NEXT_FILE, PROP_SERVICE_ACCOUNT_EMAIL, PROP_START_DATE, + PROP_SERVICE_ACCOUNT_CREDENTIALS, }; class GSWriteStream; @@ -120,6 +121,7 @@ struct _GstGsSink { std::unique_ptr gcs_client; std::unique_ptr gcs_stream; gchar* service_account_email; + gchar* service_account_credentials; gchar* bucket_name; gchar* object_name; gchar* start_date_str; @@ -283,6 +285,22 @@ static void gst_gs_sink_class_init(GstGsSinkClass* klass) { (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY))); + /** + * GstGsSink:service-account-credentials: + * + * Service Account Credentials as a JSON string to use for credentials. + * + * Since: 1.20 + */ + g_object_class_install_property( + gobject_class, PROP_SERVICE_ACCOUNT_CREDENTIALS, + g_param_spec_string( + "service-account-credentials", "Service Account Credentials", + "Service Account Credentials as a JSON string to use for credentials", + NULL, + (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY))); + /** * GstGsSink:start-date: * @@ -322,6 +340,7 @@ static void gst_gs_sink_init(GstGsSink* sink) { sink->index = DEFAULT_INDEX; sink->post_messages = DEFAULT_POST_MESSAGES; sink->service_account_email = NULL; + sink->service_account_credentials = NULL; sink->bucket_name = NULL; sink->object_name = g_strdup(DEFAULT_OBJECT_NAME); sink->start_date_str = NULL; @@ -341,6 +360,8 @@ static void gst_gs_sink_finalize(GObject* object) { sink->gcs_stream = nullptr; g_free(sink->service_account_email); sink->service_account_email = NULL; + g_free(sink->service_account_credentials); + sink->service_account_credentials = NULL; g_free(sink->bucket_name); sink->bucket_name = NULL; g_free(sink->object_name); @@ -427,6 +448,10 @@ static void gst_gs_sink_set_property(GObject* object, g_free(sink->service_account_email); sink->service_account_email = g_strdup(g_value_get_string(value)); break; + case PROP_SERVICE_ACCOUNT_CREDENTIALS: + g_free(sink->service_account_credentials); + sink->service_account_credentials = g_strdup(g_value_get_string(value)); + break; case PROP_START_DATE: g_free(sink->start_date_str); if (sink->start_date) @@ -472,6 +497,9 @@ static void gst_gs_sink_get_property(GObject* object, case PROP_SERVICE_ACCOUNT_EMAIL: g_value_set_string(value, sink->service_account_email); break; + case PROP_SERVICE_ACCOUNT_CREDENTIALS: + g_value_set_string(value, sink->service_account_credentials); + break; case PROP_START_DATE: g_value_set_string(value, sink->start_date_str); break; @@ -499,7 +527,8 @@ static gboolean gst_gs_sink_start(GstBaseSink* bsink) { sink->content_type = ""; - sink->gcs_client = gst_gs_create_client(sink->service_account_email, &err); + sink->gcs_client = gst_gs_create_client( + sink->service_account_email, sink->service_account_credentials, &err); if (err) { GST_ELEMENT_ERROR(sink, RESOURCE, OPEN_READ, ("Could not create client (%s)", err->message), diff --git a/subprojects/gst-plugins-bad/ext/gs/gstgssrc.cpp b/subprojects/gst-plugins-bad/ext/gs/gstgssrc.cpp index 4636a91bc1..9cde578de4 100644 --- a/subprojects/gst-plugins-bad/ext/gs/gstgssrc.cpp +++ b/subprojects/gst-plugins-bad/ext/gs/gstgssrc.cpp @@ -63,7 +63,12 @@ enum { LAST_SIGNAL }; #define DEFAULT_BLOCKSIZE 4 * 1024 -enum { PROP_0, PROP_LOCATION, PROP_SERVICE_ACCOUNT_EMAIL }; +enum { + PROP_0, + PROP_LOCATION, + PROP_SERVICE_ACCOUNT_EMAIL, + PROP_SERVICE_ACCOUNT_CREDENTIALS +}; class GSReadStream; @@ -74,6 +79,7 @@ struct _GstGsSrc { std::unique_ptr gcs_stream; gchar* uri; gchar* service_account_email; + gchar* service_account_credentials; std::string bucket_name; std::string object_name; guint64 read_position; @@ -166,6 +172,22 @@ static void gst_gs_src_class_init(GstGsSrcClass* klass) { (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY))); + /** + * GstGsSrc:service-account-credentials: + * + * Service Account Credentials as a JSON string to use for credentials. + * + * Since: 1.20 + */ + g_object_class_install_property( + gobject_class, PROP_SERVICE_ACCOUNT_CREDENTIALS, + g_param_spec_string( + "service-account-credentials", "Service Account Credentials", + "Service Account Credentials as a JSON string to use for credentials", + NULL, + (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY))); + gobject_class->finalize = gst_gs_src_finalize; gst_element_class_set_static_metadata( @@ -186,6 +208,7 @@ static void gst_gs_src_init(GstGsSrc* src) { src->gcs_stream = nullptr; src->uri = NULL; src->service_account_email = NULL; + src->service_account_credentials = NULL; src->read_position = 0; src->object_size = 0; @@ -201,6 +224,8 @@ static void gst_gs_src_finalize(GObject* object) { src->uri = NULL; g_free(src->service_account_email); src->service_account_email = NULL; + g_free(src->service_account_credentials); + src->service_account_credentials = NULL; src->read_position = 0; src->object_size = 0; @@ -294,6 +319,30 @@ static gboolean gst_gs_src_set_service_account_email( return TRUE; } +static gboolean gst_gs_src_set_service_account_credentials( + GstGsSrc* src, + const gchar* service_account_credentials) { + if (GST_STATE(src) == GST_STATE_PLAYING || + GST_STATE(src) == GST_STATE_PAUSED) { + GST_WARNING_OBJECT( + src, + "Setting a new service account credentials not supported in " + "PLAYING or PAUSED state"); + return FALSE; + } + + GST_OBJECT_LOCK(src); + g_free(src->service_account_credentials); + src->service_account_credentials = NULL; + + if (service_account_credentials) + src->service_account_credentials = g_strdup(service_account_credentials); + + GST_OBJECT_UNLOCK(src); + + return TRUE; +} + static void gst_gs_src_set_property(GObject* object, guint prop_id, const GValue* value, @@ -309,6 +358,10 @@ static void gst_gs_src_set_property(GObject* object, case PROP_SERVICE_ACCOUNT_EMAIL: gst_gs_src_set_service_account_email(src, g_value_get_string(value)); break; + case PROP_SERVICE_ACCOUNT_CREDENTIALS: + gst_gs_src_set_service_account_credentials(src, + g_value_get_string(value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -334,6 +387,11 @@ static void gst_gs_src_get_property(GObject* object, g_value_set_string(value, src->service_account_email); GST_OBJECT_UNLOCK(src); break; + case PROP_SERVICE_ACCOUNT_CREDENTIALS: + GST_OBJECT_LOCK(src); + g_value_set_string(value, src->service_account_credentials); + GST_OBJECT_UNLOCK(src); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -470,7 +528,8 @@ static gboolean gst_gs_src_start(GstBaseSrc* basesrc) { GST_INFO_OBJECT(src, "Opening file %s", src->uri); - src->gcs_client = gst_gs_create_client(src->service_account_email, &err); + src->gcs_client = gst_gs_create_client( + src->service_account_email, src->service_account_credentials, &err); if (err) { GST_ELEMENT_ERROR(src, RESOURCE, OPEN_READ, ("Could not create client (%s)", err->message),