@@ -34,6 +34,7 @@ import (
3434 corev1 "k8s.io/api/core/v1"
3535 ctrl "sigs.k8s.io/controller-runtime"
3636
37+ "github.com/fluxcd/pkg/masktoken"
3738 sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
3839)
3940
@@ -52,6 +53,7 @@ const (
5253 clientCertificateSendChainField = "clientCertificateSendChain"
5354 authorityHostField = "authorityHost"
5455 accountKeyField = "accountKey"
56+ sasKeyField = "sasKey"
5557)
5658
5759// BlobClient is a minimal Azure Blob client for fetching objects.
@@ -104,6 +106,14 @@ func NewClient(obj *sourcev1.Bucket, secret *corev1.Secret) (c *BlobClient, err
104106 c .ServiceClient , err = azblob .NewServiceClientWithSharedKey (obj .Spec .Endpoint , cred , & azblob.ClientOptions {})
105107 return
106108 }
109+
110+ var fullPath string
111+ if fullPath , err = sasTokenFromSecret (obj .Spec .Endpoint , secret ); err != nil {
112+ return
113+ }
114+
115+ c .ServiceClient , err = azblob .NewServiceClientWithNoCredential (fullPath , & azblob.ClientOptions {})
116+ return
107117 }
108118
109119 // Compose token chain based on environment.
@@ -148,6 +158,9 @@ func ValidateSecret(secret *corev1.Secret) error {
148158 if _ , hasAccountKey := secret .Data [accountKeyField ]; hasAccountKey {
149159 valid = true
150160 }
161+ if _ , hasSasKey := secret .Data [sasKeyField ]; hasSasKey {
162+ valid = true
163+ }
151164 if _ , hasAuthorityHost := secret .Data [authorityHostField ]; hasAuthorityHost {
152165 valid = true
153166 }
@@ -343,6 +356,39 @@ func sharedCredentialFromSecret(endpoint string, secret *corev1.Secret) (*azblob
343356 return nil , nil
344357}
345358
359+ // sasTokenFromSecret retrieves the SAS Token from the `sasKey`. It returns an empty string if the Secret
360+ // does not contain a valid set of credentials.
361+ func sasTokenFromSecret (ep string , secret * corev1.Secret ) (string , error ) {
362+ if sasKey , hasSASKey := secret .Data [sasKeyField ]; hasSASKey {
363+ queryString := strings .TrimPrefix (string (sasKey ), "?" )
364+ values , err := url .ParseQuery (queryString )
365+ if err != nil {
366+ maskedErrorString , maskErr := masktoken .MaskTokenFromString (err .Error (), string (sasKey ))
367+ if maskErr != nil {
368+ return "" , fmt .Errorf ("error redacting token from error message: %s" , maskErr )
369+ }
370+ return "" , fmt .Errorf ("unable to parse SAS token: %s" , maskedErrorString )
371+ }
372+
373+ epURL , err := url .Parse (ep )
374+ if err != nil {
375+ return "" , fmt .Errorf ("unable to parse endpoint URL: %s" , err )
376+ }
377+
378+ //merge the query values in the endpoint wuth the token
379+ epValues := epURL .Query ()
380+ for key , val := range epValues {
381+ for _ , str := range val {
382+ values .Set (key , str )
383+ }
384+ }
385+
386+ epURL .RawQuery = values .Encode ()
387+ return epURL .String (), nil
388+ }
389+ return "" , nil
390+ }
391+
346392// chainCredentialWithSecret tries to create a set of tokens, and returns an
347393// azidentity.ChainedTokenCredential if at least one of the following tokens was
348394// successfully created:
0 commit comments