File Sas Token not working with Access Policy - Python

Ben C 166 Reputation points
2022-09-29T15:26:38.467+00:00

Hello,

We are generating sas token for a file using generate_file_sas method from azure.storage.filedatalake module.

When we try to use the sas token with DataLakeFileClient and AccessPolicy, it shows this error:

   <Error>  
   <Code>AuthenticationFailed</Code>  
   <Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. RequestId:7335936d-b01e-0006-410a-d472a6000000 Time:2022-09-29T13:47:24.5400377Z</Message>  
   <AuthenticationErrorDetail>Signature fields not well formed.</AuthenticationErrorDetail>  
   </Error>  

We use the user_delegation_key for credentials, and for Auth to AD we are using the service principal client id and secret.
The sas token and delegate key expiry are set by datetime.utcnow() + timedelta(hours=1).
For permission we add stored access policy using set_file_system_access_policy and signed_identifiers param along with the dict policies.
We are using the uuid.uuid4 generated from python lib to assign the ID, which is 32chars.

If we replace the uuid on permission param on generate_file_sas with the FileSasPermissions it works!

Anyone know why the stored access policy is not working for datalake account?

Ben C.

Azure Data Lake Storage
Azure Data Lake Storage
An Azure service that provides an enterprise-wide hyper-scale repository for big data analytic workloads and is integrated with Azure Blob Storage.
1,559 questions
{count} votes

2 answers

Sort by: Most helpful
  1. Ben C 166 Reputation points
    2022-10-03T14:01:13.563+00:00

    @HimanshuSinha-msft
    here is the code to create stored access policy:
    from api.services import BaseService
    from functools import cached_property
    from azure.storage.filedatalake import (
    DataLakeServiceClient,
    AccessPolicy,
    FileSasPermissions,
    )
    from datetime import timedelta, datetime

    class CreateAccessPolicyService(BaseService):  
        shared_key = storage_account = uuid = None  
      
        def __call__(self):  
            self.access_policies[self.uuid] = self.access_policy  
      
            self.filesystem_client.set_file_system_access_policy(  
                signed_identifiers=self.access_policies  
            )  
      
        @cached_property  
        def filesystem_client(self):  
            return self.datalake_client.get_file_system_client(file_system='default')  
      
        @cached_property  
        def datalake_client(self):  
            return DataLakeServiceClient(  
                account_url='{}://{}.dfs.core.windows.net'.format(  
                    'https', self.storage_account  
                ),  
                credential=self.shared_key,  
            )  
      
        @cached_property  
        def access_policies(self):  
            payload = self.filesystem_client.get_file_system_access_policy()  
      
            return {e.id: e.access_policy for e in payload['signed_identifiers']}  
      
        @cached_property  
        def access_policy(self):  
            return AccessPolicy(  
                permission=FileSasPermissions(create=True, write=True),  
                start=self.start_time,  
                expiry=self.end_time,  
            )  
      
        @cached_property  
        def start_time(self):  
            return datetime.utcnow()  
      
        @cached_property  
        def end_time(self):  
            return self.start_time + timedelta(hours=1)  
    

    the self.uuid is a string in format of uuid4, which has 32 length.
    the code that uses the stored access policy:

    from api.services import BaseService  
    from functools import cached_property  
    from azure.identity import ClientSecretCredential  
    from django.conf import settings  
    from datetime import timedelta, datetime  
    from azure.storage.filedatalake import (  
        DataLakeServiceClient,  
        generate_file_sas,  
        FileSasPermissions,  
    )  
      
      
    class GenerateFileSasService(BaseService):  
        client_id = client_secret = storage_account = uuid = file_name = None  
      
        def __call__(self):  
            self.create_file_system()  
            self.create_directory()  
      
            sas_token = generate_file_sas(  
                account_name=self.directory_client.account_name,  
                file_system_name=self.directory_client.file_system_name,  
                directory_name='mnt/raw/Data',  
                file_name=self.file_name,  
                credential=self.user_delegation_key,  
                permission=self.uuid,  
                expiry=self.end_time,  
            )  
      
            return self.directory_client.url + '/' + self.file_name + '?' + sas_token  
      
        def create_file_system(self):  
            if self.filesystem_client.exists() is True:  
                return  
      
            self.filesystem_client.create_file_system()  
      
        def create_directory(self):  
            if self.directory_client.exists() is True:  
                return  
      
            self.directory_client.create_directory()  
      
        @cached_property  
        def filesystem_client(self):  
            return self.datalake_client.get_file_system_client(file_system='default')  
      
        @cached_property  
        def directory_client(self):  
            return self.filesystem_client.get_directory_client(directory='/mnt/raw/Data')  
      
        @cached_property  
        def credentials(self):  
            return ClientSecretCredential(  
                settings.AZURE_TENANT_ID, self.client_id, self.client_secret  
            )  
      
        @cached_property  
        def datalake_client(self):  
            return DataLakeServiceClient(  
                account_url='{}://{}.dfs.core.windows.net'.format(  
                    'https', self.storage_account  
                ),  
                credential=self.credentials,  
            )  
      
        @cached_property  
        def start_time(self):  
            return datetime.utcnow()  
      
        @cached_property  
        def end_time(self):  
            return self.start_time + timedelta(hours=1)  
      
        @cached_property  
        def user_delegation_key(self):  
            return self.datalake_client.get_user_delegation_key(  
                key_start_time=self.start_time,  
                key_expiry_time=self.end_time,  
            )  
    
    0 comments No comments

  2. Ben C 166 Reputation points
    2022-10-20T12:46:53.707+00:00

    @HimanshuSinha-msft any news on what the issue might be?

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.