gs: Add support for authenticating via Service Account Credentials
authorSebastian Dröge <sebastian@centricular.com>
Mon, 27 Sep 2021 12:30:25 +0000 (15:30 +0300)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Mon, 27 Sep 2021 15:36:05 +0000 (15:36 +0000)
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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/934>

subprojects/gst-plugins-bad/ext/gs/gstgscommon.cpp
subprojects/gst-plugins-bad/ext/gs/gstgscommon.h
subprojects/gst-plugins-bad/ext/gs/gstgssink.cpp
subprojects/gst-plugins-bad/ext/gs/gstgssrc.cpp

index 41f1060..8326baa 100644 (file)
@@ -58,18 +58,35 @@ static inline gchar* g_date_time_format_iso8601(GDateTime* datetime) {
 
 std::unique_ptr<google::cloud::storage::Client> 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<std::shared_ptr<gcs::oauth2::Credentials>> 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<std::shared_ptr<gcs::oauth2::Credentials>> creds(
-        std::make_shared<gcs::oauth2::ComputeEngineCredentials<>>(
-            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;
     }
 
index 574212e..e0df4ff 100644 (file)
@@ -30,6 +30,7 @@
 
 std::unique_ptr<google::cloud::storage::Client> gst_gs_create_client(
     const gchar* service_account_email,
+    const gchar* service_account_credentials,
     GError** error);
 
 gboolean gst_gs_get_buffer_date(GstBuffer* buffer,
index 104ca62..b2aed05 100644 (file)
@@ -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<google::cloud::storage::Client> gcs_client;
   std::unique_ptr<GSWriteStream> gcs_stream;
   gchar* service_account_email;
+  gchar* service_account_credentials;
   gchar* bucket_name;
   gchar* object_name;
   gchar* start_date_str;
@@ -284,6 +286,22 @@ static void gst_gs_sink_class_init(GstGsSinkClass* klass) {
                         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:
    *
    * Start date in iso8601 format.
@@ -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),
index 4636a91..9cde578 100644 (file)
@@ -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<GSReadStream> 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),