diff Azure/AzureBlobStoragePlugin.cpp @ 46:3b8fab63313d patch-azure

Add "CreateContainerIfNotExists" config for Azure
author Mark Poscablo <Mark.Poscablo@varian.com>
date Wed, 31 Mar 2021 12:10:00 -0400
parents 2a02b21f0a19
children 1691da4ae9c3
line wrap: on
line diff
--- a/Azure/AzureBlobStoragePlugin.cpp	Mon Sep 07 14:13:38 2020 +0200
+++ b/Azure/AzureBlobStoragePlugin.cpp	Wed Mar 31 12:10:00 2021 -0400
@@ -23,6 +23,7 @@
 #include <boost/lexical_cast.hpp>
 #include <boost/algorithm/string.hpp>
 #include "cpprest/rawptrstream.h"
+#include "cpprest/details/basic_types.h"
 
 
 // Create aliases to make the code easier to read.
@@ -147,11 +148,32 @@
   return "Azure Blob Storage";
 }
 
+bool isSasTokenAccountLevel(utility::string_t sasToken)
+{
+  // Use cpprestsdk's utility::string_t here since the expected argument is the return value of
+  // as::storage_credentials::sas_token(), which is type utility::string_t
+  size_t newIdx = 0;
+  size_t prevIdx = 0;
+  while ((newIdx = sasToken.find('&', prevIdx)) != utility::string_t::npos)
+  {
+    utility::string_t kvpair = sasToken.substr(prevIdx, newIdx-prevIdx);
+    prevIdx = newIdx + 1; // start next time from char after '&'
+
+    size_t equalsIdx = kvpair.find('=');
+    utility::string_t key = kvpair.substr(0, equalsIdx);
+    if (key == "srt") // only account SAS has this parameter
+      return true;
+  }
+
+  return false;
+}
+
 IStoragePlugin* AzureBlobStoragePluginFactory::CreateStoragePlugin(const OrthancPlugins::OrthancConfiguration& orthancConfig)
 {
   std::string connectionString;
   std::string containerName;
   bool enableLegacyStorageStructure;
+  bool createContainerIfNotExists;
 
   if (orthancConfig.IsSection(PLUGIN_SECTION))
   {
@@ -177,6 +199,8 @@
 
     boost::trim(connectionString); // without that, if there's an EOL in the string, it fails with "provided uri is invalid"
     boost::trim(containerName);
+
+    createContainerIfNotExists = pluginSection.GetBooleanValue("CreateContainerIfNotExists", true);
   }
   else if (orthancConfig.IsSection("BlobStorage")) // backward compatibility with the old plugin:
   {
@@ -209,6 +233,8 @@
     std::ostringstream connectionStringBuilder;
     connectionStringBuilder << "DefaultEndpointsProtocol=https;AccountName=" << accountName << ";AccountKey=" << accountKey;
     connectionString = connectionStringBuilder.str();
+
+    createContainerIfNotExists = pluginSection.GetBooleanValue("CreateContainerIfNotExists", true);
   }
   else
   {
@@ -229,12 +255,21 @@
     as::cloud_blob_container blobContainer = blobClient.get_container_reference(containerName);
     OrthancPlugins::LogInfo("Accessing blob container");
 
-    // Return value is true if the container did not exist and was successfully created.
-    bool containerCreated = blobContainer.create_if_not_exists();
+    // blobContainer.create_if_not_exists() throws an error if a service SAS (for a blob container)
+    // was used in the connectionString.
+    // Only allow the use of this function when using storage account-level credentials, whether
+    // through accountName/accountKey combo or account SAS.
+    if ((storageAccount.credentials().is_account_key() ||
+         (storageAccount.credentials().is_sas() && isSasTokenAccountLevel(storageAccount.credentials().sas_token())))
+        && createContainerIfNotExists)
+    {
+      // Return value is true if the container did not exist and was successfully created.
+      bool containerCreated = blobContainer.create_if_not_exists();
 
-    if (containerCreated)
-    {
-      OrthancPlugins::LogWarning("Blob Storage Area container has been created.  **** check in the Azure console that your container is private ****");
+      if (containerCreated)
+      {
+        OrthancPlugins::LogWarning("Blob Storage Area container has been created.  **** check in the Azure console that your container is private ****");
+      }
     }
 
     OrthancPlugins::LogInfo("Blob storage initialized");