Error connecting to azure blob storage API from R

后端 未结 3 1892
长情又很酷
长情又很酷 2020-12-17 02:44

I am attempting to work with Azure storage via the REST API in R. I\'m using the package httr which overlays Curl.

Setup

You can use R-fiddle: http://www.r

相关标签:
3条回答
  • 2020-12-17 02:55

    It looks like you are using the key of your account directly in the Authorization header. To authenticate a request, you must sign the request with the key for the account that is making the request and pass that signature as part of the request. Please see Authentication for the Azure Storage Services for more information on how to construct the Authorization header.

    Please also note that the service returns StringToSign in the error response. So, what your code should have done is to apply the following formula to StringToSign="GET\n\n\n\n\n\n\n\n\n\n\n\nx-ms-date:Wed, 25 Mar 2015 22:24:12 GMT\nx-ms-version:2014-02-14\n/preconstuff/pings\ncomp:list\nrestype:container" (without quotes):

    Signature=Base64(HMAC-SHA256(AccountKey, UTF8(StringToSign)))
    

    How the service calculates StringToSign is explained in detail in the link shared above.

    0 讨论(0)
  • 2020-12-17 02:58

    Worked trough the example code by MrFlick above and to get it to work I had to change a few things.

    The date string has to be set in an English locale, for example:

    lct <- Sys.getlocale("LC_TIME") 
    Sys.setlocale("LC_TIME", "us")
    requestdate <- format(Sys.time(),"%a, %d %b %Y %H:%M:%S %Z", tz="GMT")
    Sys.setlocale("LC_TIME", lct)
    

    The 'signaturestring' should be formated with \n between parameters:

     signaturestring <- paste0("GET", paste(rep("\n", 12), collapse=""),
    "x-ms-date:", requestdate, 
    "\nx-ms-version:2009-09-19\n/preconstuff/pings\ncomp:list\nrestype:container")
    

    EDIT: Following procedure works for me. Based on Steph Locke example.

    library(httr)
    library(RCurl)
    
    azureBlobCall <- function(url, verb, key, requestBody=NULL, headers=NULL, ifMatch="", md5="") { 
      urlcomponents <- httr::parse_url(url)
      account <- gsub(".blob.core.windows.net", "", urlcomponents$hostname, fixed = TRUE)
      container <- urlcomponents$path
    
      # get timestamp in us locale
      lct <- Sys.getlocale("LC_TIME"); Sys.setlocale("LC_TIME", "us")
      `x-ms-date` <- format(Sys.time(),"%a, %d %b %Y %H:%M:%S %Z", tz="GMT")
      Sys.setlocale("LC_TIME", lct)
    
      # if requestBody exist get content length in bytes and content type
      `Content-Length` <- ""; `Content-Type` <- ""
      if(!is.null(requestBody)) {
        if(class(requestBody) == "form_file") {
          `Content-Length` <- (file.info(requestBody$path))$size
          `Content-Type` <- requestBody$type 
        } else {
          requestBody <- enc2utf8(as.character(requestBody))
          `Content-Length` <- nchar(requestBody, "bytes")
          `Content-Type` <- "text/plain; charset=UTF-8" 
        }
      } 
    
      # combine timestamp and version headers with any input headers, order and create the CanonicalizedHeaders
      headers <- setNames(c(`x-ms-date`, "2015-04-05",  unlist(headers)), 
                          c("x-ms-date", "x-ms-version", unclass(names(unlist(headers)))))
      headers <- headers[order(names(headers))]
      CanonicalizedHeaders <- paste(names(headers), headers, sep=":", collapse = "\n")
    
      # create CanonicalizedResource headers and add any queries to it
      if(!is.null(urlcomponents$query)) {
        components <- setNames(unlist(urlcomponents$query), unclass(names(unlist(urlcomponents$query))))
        componentstring <- paste0("\n", paste(names(components[order(names(components))]),
                                              components[order(names(components))], sep=":", collapse = "\n"))
      } else componentstring <- ""
      CanonicalizedResource <- paste0("/",account,"/",container, componentstring)
    
      # create the authorizationtoken
      signaturestring <- paste0(verb, "\n\n\n", `Content-Length`, "\n", md5, "\n", `Content-Type`, "\n\n\n", 
                                ifMatch, "\n\n\n\n", CanonicalizedHeaders, "\n", CanonicalizedResource)
    
      requestspecificencodedkey <- RCurl::base64(
        digest::hmac(key=RCurl::base64Decode(key, mode="raw"),
                     object=enc2utf8(signaturestring),
                     algo= "sha256", raw=TRUE)
      )
    
      authorizationtoken <- paste0("SharedKey ", account, ":", requestspecificencodedkey)
    
      # make the call
      headers_final <- add_headers(Authorization=authorizationtoken, headers, `Content-Type` = `Content-Type`)
      call <- httr::VERB(verb=verb, url=url, config=headers_final, body=requestBody, verbose())
    
      print("signaturestring");print(signaturestring); 
      print(headers_final); print(call)
      return(content(call))
    } 
    
    ## Tests. Replace 'key' and 'accountName' with yours
    key <- "YowThr***********RDw=="
    
    # Creates a container named 'test'
    azureBlobCall("https://accountName.blob.core.windows.net/test?restype=container", "PUT", key)
    # Creates a blob named 'blob' under container 'test' with the content of "Hej världen!"
    azureBlobCall("https://accountName.blob.core.windows.net/test/blob", "PUT", key, 
                  headers = c("x-ms-blob-type"="BlockBlob"), requestBody = "Hej världen!") #upload_file("blob.txt"))
    # List all blob in the container 'test'
    azureBlobCall("https://accountName.blob.core.windows.net/test?comp=list&restype=container", "GET", key)
    # deletes the blobl named 'blob' 
    azureBlobCall("https://accountName.blob.core.windows.net/test/blob", "DELETE", key)
    # Creates a blob named 'blob' under container 'test' with and upload the file 'blob.txt'
    azureBlobCall("https://accountName.blob.core.windows.net/test/blob", "PUT", key, 
                  headers = c("x-ms-blob-type"="BlockBlob"), requestBody = upload_file("blob.txt"))
    # deletes the container named 'test' 
    azureBlobCall("https://accountName.blob.core.windows.net/test?restype=container", "DELETE", key)
    
    0 讨论(0)
  • 2020-12-17 03:11

    Looks like your problem is with the key. The string of the key you have provided is actually base64 encoded. You need to decode that to the raw vector before you use it to sign the request. For example:

    url<-"https://preconstuff.blob.core.windows.net/pings?restype=container&comp=list"
    sak<-"Q8HvUVJLBJK+wkrIEG6LlsfFo19iDjneTwJxX/KXSnUCtTjgyyhYnH/5azeqa1bluGD94EcPcSRyBy2W2A/fHQ=="
    
    requestdate<-format(Sys.time(),"%a, %d %b %Y %H:%M:%S %Z", tz="GMT")
    signaturestring<-paste0("GET",paste(rep("\n",12),collapse=""),
    "x-ms-date:",requestdate,"
    x-ms-version:2009-09-19
    /preconstuff/pings
    comp:list
    restype:container")
    
    headerstuff<-add_headers(Authorization=paste0("SharedKey preconstuff:",
                             RCurl::base64(digest::hmac(key=RCurl::base64Decode(sak, mode="raw"),
                             object=enc2utf8(signaturestring),
                             algo= "sha256", raw=TRUE))),
                        `x-ms-date`=requestdate,
                        `x-ms-version`= "2009-09-19")
    
    content(GET(url,config = headerstuff, verbose() ))
    

    There are no more authentication errors this way, though no blobs are listed. Perhaps that's a different issue.

    Also, I changed the way the date/time was created to more "safely" change the local time to GMT.

    0 讨论(0)
提交回复
热议问题