Added Azure SAS Token Authentication

This commit is contained in:
Kaltenbach, Jonas 2022-09-15 10:18:41 +02:00
parent 920deb3df7
commit 01dd4c6290
2 changed files with 60 additions and 6 deletions

View File

@ -135,8 +135,10 @@ func CreateComposer() {
} }
accountKey := os.Getenv("AZURE_STORAGE_KEY") accountKey := os.Getenv("AZURE_STORAGE_KEY")
if accountKey == "" { sasUrl := os.Getenv("AZURE_STORAGE_SAS_URL")
stderr.Fatalf("No service account key for Azure BlockBlob Storage using the AZURE_STORAGE_KEY environment variable.\n")
if accountKey == "" && sasUrl == "" {
stderr.Fatalf("No service account key or SAS URL for Azure BlockBlob Storage using the AZURE_STORAGE_KEY or AZURE_STORAGE_SAS_URL environment variable.\n")
} }
azureEndpoint := Flags.AzEndpoint azureEndpoint := Flags.AzEndpoint
@ -152,6 +154,7 @@ func CreateComposer() {
azConfig := &azurestore.AzConfig{ azConfig := &azurestore.AzConfig{
AccountName: accountName, AccountName: accountName,
SasUrl: sasUrl,
AccountKey: accountKey, AccountKey: accountKey,
ContainerName: Flags.AzStorage, ContainerName: Flags.AzStorage,
ContainerAccessType: Flags.AzContainerAccessType, ContainerAccessType: Flags.AzContainerAccessType,

View File

@ -20,9 +20,11 @@ import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io" "io"
"net/http"
"net/url" "net/url"
"sort" "sort"
"strings" "strings"
"time"
"github.com/Azure/azure-storage-blob-go/azblob" "github.com/Azure/azure-storage-blob-go/azblob"
"github.com/tus/tusd/pkg/handler" "github.com/tus/tusd/pkg/handler"
@ -38,6 +40,8 @@ type azService struct {
BlobAccessTier azblob.AccessTierType BlobAccessTier azblob.AccessTierType
ContainerURL *azblob.ContainerURL ContainerURL *azblob.ContainerURL
ContainerName string ContainerName string
SasUrl string
Endpoint string
} }
type AzService interface { type AzService interface {
@ -46,6 +50,7 @@ type AzService interface {
type AzConfig struct { type AzConfig struct {
AccountName string AccountName string
SasUrl string
AccountKey string AccountKey string
BlobAccessTier string BlobAccessTier string
ContainerName string ContainerName string
@ -79,10 +84,25 @@ type InfoBlob struct {
// New Azure service for communication to Azure BlockBlob Storage API // New Azure service for communication to Azure BlockBlob Storage API
func NewAzureService(config *AzConfig) (AzService, error) { func NewAzureService(config *AzConfig) (AzService, error) {
// struct to store your credentials. // struct to store your credentials.
credential, err := azblob.NewSharedKeyCredential(config.AccountName, config.AccountKey) var credential azblob.Credential
var credError error
var cURL *url.URL
if config.AccountKey != "" {
// Use AccountKey for shared key credentials
credential, credError = azblob.NewSharedKeyCredential(config.AccountName, config.AccountKey)
if credError != nil {
return nil, credError
}
cURL, _ = url.Parse(fmt.Sprintf("%s/%s", config.Endpoint, config.ContainerName))
} else {
// Use anonymous credentials, request initial SAS and append it to URL
credential = azblob.NewAnonymousCredential()
sas, err := getSasToken(config.SasUrl)
if err != nil { if err != nil {
return nil, err return nil, err
} }
cURL, _ = url.Parse(fmt.Sprintf("%s/%s?%s", config.Endpoint, config.ContainerName, sas))
}
// Might be limited by the storage account // Might be limited by the storage account
// "" or default inherits the access type from the Storage Account // "" or default inherits the access type from the Storage Account
@ -113,7 +133,6 @@ func NewAzureService(config *AzConfig) (AzService, error) {
// The pipeline specifies things like retry policies, logging, deserialization of HTTP response payloads, and more. // The pipeline specifies things like retry policies, logging, deserialization of HTTP response payloads, and more.
p := azblob.NewPipeline(credential, azblob.PipelineOptions{}) p := azblob.NewPipeline(credential, azblob.PipelineOptions{})
cURL, _ := url.Parse(fmt.Sprintf("%s/%s", config.Endpoint, config.ContainerName))
// Get the ContainerURL URL // Get the ContainerURL URL
containerURL := azblob.NewContainerURL(*cURL, p) containerURL := azblob.NewContainerURL(*cURL, p)
@ -124,12 +143,31 @@ func NewAzureService(config *AzConfig) (AzService, error) {
BlobAccessTier: blobAccessTierType, BlobAccessTier: blobAccessTierType,
ContainerURL: &containerURL, ContainerURL: &containerURL,
ContainerName: config.ContainerName, ContainerName: config.ContainerName,
SasUrl: config.SasUrl,
Endpoint: config.Endpoint,
}, nil }, nil
} }
// Determine if we return a InfoBlob or BlockBlob, based on the name // Determine if we return a InfoBlob or BlockBlob, based on the name
func (service *azService) NewBlob(ctx context.Context, name string) (AzBlob, error) { func (service *azService) NewBlob(ctx context.Context, name string) (AzBlob, error) {
var fileBlob AzBlob var fileBlob AzBlob
url := service.ContainerURL.URL()
skeParam := url.Query().Get("ske")
if skeParam != "" {
sasEndDate, _ := time.Parse(time.RFC3339, skeParam)
if sasEndDate.Before(time.Now()) {
// Renew SAS and append it to URL
sas, err := getSasToken(service.SasUrl)
if err != nil {
return nil, err
}
cURL, _ := url.Parse(fmt.Sprintf("%s/%s?%s", service.Endpoint, service.ContainerName, sas))
p := azblob.NewPipeline(azblob.NewAnonymousCredential(), azblob.PipelineOptions{})
newContainerURL := azblob.NewContainerURL(*cURL, p)
service.ContainerURL = &newContainerURL
}
}
bb := service.ContainerURL.NewBlockBlobURL(name) bb := service.ContainerURL.NewBlockBlobURL(name)
if strings.HasSuffix(name, InfoBlobSuffix) { if strings.HasSuffix(name, InfoBlobSuffix) {
fileBlob = &InfoBlob{ fileBlob = &InfoBlob{
@ -306,3 +344,16 @@ func isAzureError(err error, code string) bool {
} }
return false return false
} }
func getSasToken(sasUrl string) (sas string, err error) {
resp, err := http.Get(sasUrl)
if err != nil {
return "", err
}
defer resp.Body.Close()
sasResponse, readErr := io.ReadAll(resp.Body)
if readErr != nil {
return "", readErr
}
return string(sasResponse), nil
}