使用 Python 管理 Azure Data Lake Storage 中的 ACL

本文介绍如何使用 Python 来获取、设置和更新目录和文件的访问控制列表。

ACL 继承已可用于在父目录下创建的新子项。 但是你也可以为父目录的现有子项以递归方式添加、更新和删除 ACL,而不必为每个子项单独进行这些更改。

包(Python 包索引) | 示例 | 递归 ACL 示例 | API 参考 | 提供反馈

先决条件

  • Azure 订阅 - 创建试用订阅
  • 已启用分层命名空间 (HNS) 的 Azure 存储帐户。 按这些说明创建一个。
  • Python 3.8+
  • Azure CLI 版本 2.6.0 或更高版本。
  • 以下安全权限之一:
    • 一个预配的 Microsoft Entra ID 安全主体,它已分配有存储 Blob 数据所有者角色,并且范围限定为目标容器、存储帐户、父资源组或订阅。
    • 计划将 ACL 设置应用到的目标容器或目录的拥有用户。 为了以递归方式设置 ACL,这包括目标容器或目录中的所有子项。
    • 存储帐户密钥。

设置项目

本部分逐步指导你准备一个项目,使其与适用于 Python 的 Azure Data Lake Storage 客户端库配合使用。

从项目目录中,使用 pip install 命令安装 Azure Data Lake Storage 和 Azure 标识客户端库的包。 与 Azure 服务的无密码连接需要 azure-identity 包。

pip install azure-storage-file-datalake azure-identity

然后打开代码文件并添加必要的 import 语句。 在此示例中,我们将以下内容添加到 .py 文件:

from azure.identity import DefaultAzureCredential
from azure.storage.filedatalake import DataLakeServiceClient

连接到帐户

若要运行本文中的代码示例,需创建一个表示存储帐户的 DataLakeServiceClient 实例。 你可以使用 Microsoft Entra ID 凭据或帐户密钥为客户端对象授权。

可以使用适用于 Python 的 Azure 标识客户端库,通过 Microsoft Entra ID 对应用程序进行验证。

注意

如果使用 Microsoft Entra ID 来授予访问权限,请确保已为安全主体分配了存储 Blob 数据所有者角色。 若要详细了解如何应用 ACL 权限以及更改这些权限的影响,请参阅 Azure Data Lake Storage 中的访问控制模型

首先,向安全主体分配以下 Azure 基于角色的访问控制 (Azure RBAC) 角色之一:

角色 ACL 设置功能
存储 Blob 数据所有者 帐户中的所有目录和文件。
存储 Blob 数据参与者 仅限安全主体拥有的目录和文件。

接下来,创建一个 DataLakeServiceClient 实例并传入 DefaultAzureCredential 类的新实例。

def get_service_client_token_credential(self, account_name) -> DataLakeServiceClient:
    account_url = f"https://{account_name}.dfs.core.chinacloudapi.cn"
    token_credential = DefaultAzureCredential()

    service_client = DataLakeServiceClient(account_url, credential=token_credential)

    return service_client

若要详细了解如何使用 DefaultAzureCredential 授权访问数据,请参阅概述:使用 Azure SDK 向 Azure 验证 Python 应用

设置 ACL

设置 ACL 时,你将替换整个 ACL,包括其所有条目。 如果要更改安全主体的权限级别,或将新的安全主体添加到 ACL 而不影响其他现有项,则应改为更新 ACL。 若要更新 ACL 而不是替换它,请参阅本文的更新 ACL 部分。

本节介绍如何完成下列操作:

  • 设置目录的 ACL
  • 设置文件的 ACL

设置目录的 ACL

通过调用 DataLakeDirectoryClient.get_access_control 方法获取目录的访问控制列表 (ACL),并通过调用 DataLakeDirectoryClient.set_access_control 方法来设置 ACL。

此示例获取并设置名为 my-directory 的目录的 ACL。 字符串 rwxr-xrw- 为拥有用户提供读取、写入和执行权限,为拥有组授予读取和执行权限,并为所有其他用户提供读取和写入权限。

def manage_directory_permissions():
    try:
        file_system_client = service_client.get_file_system_client(file_system="my-file-system")

        directory_client = file_system_client.get_directory_client("my-directory")
        
        acl_props = directory_client.get_access_control()
        
        print(acl_props['permissions'])
        
        new_dir_permissions = "rwxr-xrw-"
        
        directory_client.set_access_control(permissions=new_dir_permissions)
        
        acl_props = directory_client.get_access_control()
        
        print(acl_props['permissions'])
    
    except Exception as e:
     print(e)

还可以获取和设置容器根目录的 ACL。 若要获取根目录,请调用 FileSystemClient._get_root_directory_client 方法。

设置文件的 ACL

通过调用 DataLakeFileClient.get_access_control 方法获取文件的访问控制列表 (ACL),并通过调用 DataLakeFileClient.set_access_control 方法来设置 ACL。

此示例获取并设置名为 my-file.txt 的文件的 ACL。 字符串 rwxr-xrw- 为拥有用户提供读取、写入和执行权限,为拥有组授予读取和执行权限,并为所有其他用户提供读取和写入权限。

def manage_file_permissions():
    try:
        file_system_client = service_client.get_file_system_client(file_system="my-file-system")

        directory_client = file_system_client.get_directory_client("my-directory")
        
        file_client = directory_client.get_file_client("uploaded-file.txt")

        acl_props = file_client.get_access_control()
        
        print(acl_props['permissions'])
        
        new_file_permissions = "rwxr-xrw-"
        
        file_client.set_access_control(permissions=new_file_permissions)
        
        acl_props = file_client.get_access_control()
        
        print(acl_props['permissions'])

    except Exception as e:
     print(e)

以递归方式设置 ACL

设置 ACL 时,你将替换整个 ACL,包括其所有条目。 如果要更改安全主体的权限级别,或将新的安全主体添加到 ACL 而不影响其他现有项,则应改为更新 ACL。 若要更新 ACL 而不是替换它,请参阅本文的以递归方式更新 ACL 部分。

通过调用 DataLakeDirectoryClient.set_access_control_recursive 方法,以递归方式设置 ACL。

如果要设置默认 ACL 条目,请将字符串 default: 添加到每个 ACL 条目字符串的开头。

此示例设置名为 my-parent-directory 的目录的 ACL。

此方法接受一个名为 is_default_scope 的布尔参数,该参数指定是否设置默认 ACL。 如果该参数为 True,则 ACL 条目列表前面将带有字符串 default:。 此示例中的条目授予以下权限:负责人用户的读取、写入和执行权限,负责人组的读取和执行权限,以及所有其他人的读取权限。 此示例中的最后一个 ACL 条目向对象 ID 为 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 的特定用户提供读取权限。

def set_permission_recursively(is_default_scope):
    
    try:
        file_system_client = service_client.get_file_system_client(file_system="my-container")

        directory_client = file_system_client.get_directory_client("my-parent-directory")

        acl = 'user::rwx,group::r-x,other::r--,user:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:r--'   

        if is_default_scope:
           acl = 'default:user::rwx,default:group::r-x,default:other::r--,default:user:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:r--'

        directory_client.set_access_control_recursive(acl=acl)
        
        acl_props = directory_client.get_access_control()
        
        print(acl_props['permissions'])

    except Exception as e:
     print(e)

若要查看通过指定批大小以递归方式成批处理 ACL 的示例,请参阅 Python 示例

以递归方式更新 ACL

更新 ACL 时,你将修改 ACL 而非替换 ACL。 例如,你可以将一个新的安全主体添加到 ACL,而不影响 ACL 中列出的其他安全主体。 若要替换 ACL 而不是更新它,请参阅本文的 设置 ACL 部分。

若要以递归方式更新 ACL,请创建包含要更新的 ACL 条目的一个新 ACL 对象,然后在“更新 ACL”操作中使用该对象。 不要获取现有 ACL,只需要提供要更新的 ACL 条目。 通过调用 DataLakeDirectoryClient.update_access_control_recursive 方法以递归方式更新 ACL。 如果要更新默认 ACL 条目,请将字符串 default: 添加到每个 ACL 条目字符串的开头。

此示例以写入权限更新某个 ACL 条目。

此示例设置名为 my-parent-directory 的目录的 ACL。 此方法接受一个名为 is_default_scope 的布尔参数,该参数指定是否更新默认 ACL。 如果该参数为 True,则更新后的 ACL 条目前面将带有字符串 default:

def update_permission_recursively(is_default_scope):
    
    try:
        file_system_client = service_client.get_file_system_client(file_system="my-container")

        directory_client = file_system_client.get_directory_client("my-parent-directory")
              
        acl = 'user:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:rwx'   

        if is_default_scope:
           acl = 'default:user:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:rwx'

        directory_client.update_access_control_recursive(acl=acl)

        acl_props = directory_client.get_access_control()
        
        print(acl_props['permissions'])

    except Exception as e:
     print(e)

若要查看通过指定批大小以递归方式成批处理 ACL 的示例,请参阅 Python 示例

以递归方式删除 ACL 条目

可以删除一个或多个 ACL 条目。 若要以递归方式删除 ACL 条目,请为要删除的 ACL 条目创建一个新的 ACL 对象,然后在“删除 ACL”操作中使用该对象。 不要获取现有 ACL,只需要提供要删除的 ACL 条目。

通过调用 DataLakeDirectoryClient.remove_access_control_recursive 方法删除 ACL 条目。 如果要删除默认 ACL 条目,请将字符串 default: 添加到 ACL 条目字符串的开头。

此示例从名为 my-parent-directory 的目录的 ACL 中删除 ACL 条目。 此方法接受一个名为 is_default_scope 的布尔参数,该参数指定是否删除默认 ACL 中的条目。 如果该参数为 True,则更新后的 ACL 条目前面将带有字符串 default:

def remove_permission_recursively(is_default_scope):

    try:
        file_system_client = service_client.get_file_system_client(file_system="my-container")

        directory_client = file_system_client.get_directory_client("my-parent-directory")

        acl = 'user:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'

        if is_default_scope:
           acl = 'default:user:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'

        directory_client.remove_access_control_recursive(acl=acl)

    except Exception as e:
     print(e)

若要查看通过指定批大小以递归方式成批处理 ACL 的示例,请参阅 Python 示例

从故障中恢复

你可能会遇到运行时错误或权限错误。 对于运行时错误,请从头开始重启此过程。 如果安全主体没有足够的权限修改要修改的目录层次结构中的目录或文件的 ACL,则会出现权限错误。 请解决权限问题,然后选择通过使用继续标记从故障点继续执行此过程,或者从头重启此过程。 如果希望从头开始重启,则无需使用继续标记。 你可以重新应用 ACL 条目,而不会产生任何负面影响。

此示例在失败时返回一个继续标记。 应用程序可以在错误得到解决后再次调用此示例方法,并传入继续标记。 如果是第一次调用此示例方法,则应用程序可以为继续标记参数传入 None 值。

def resume_set_acl_recursive(continuation_token):
    
    try:
        file_system_client = service_client.get_file_system_client(file_system="my-container")

        directory_client = file_system_client.get_directory_client("my-parent-directory")
              
        acl = 'user::rwx,group::rwx,other::rwx,user:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:r--'

        acl_change_result = directory_client.set_access_control_recursive(acl=acl, continuation=continuation_token)

        continuation_token = acl_change_result.continuation

        return continuation_token
        
    except Exception as e:
     print(e) 
     return continuation_token

若要查看通过指定批大小以递归方式成批处理 ACL 的示例,请参阅 Python 示例

如果你希望过程继续完成而不被权限错误中断,则可以指定它。

若要确保过程无中断地完成,请不要将延续令牌传递给 DataLakeDirectoryClient.set_access_control_recursive 方法。

此示例以递归方式设置 ACL 条目。 如果此代码遇到权限错误,它会记录该故障并继续执行。 此示例将故障数输出到控制台。

def continue_on_failure():
    
    try:
        file_system_client = service_client.get_file_system_client(file_system="my-container")

        directory_client = file_system_client.get_directory_client("my-parent-directory")
              
        acl = 'user::rwx,group::rwx,other::rwx,user:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:r--'

        acl_change_result = directory_client.set_access_control_recursive(acl=acl)

        print("Summary: {} directories and {} files were updated successfully, {} failures were counted."
          .format(acl_change_result.counters.directories_successful, acl_change_result.counters.files_successful,
                  acl_change_result.counters.failure_count))
        
    except Exception as e:
     print(e)

若要查看通过指定批大小以递归方式成批处理 ACL 的示例,请参阅 Python 示例

最佳实践

本部分提供了有关以递归方式设置 ACL 的一些最佳做法指南。

处理运行时错误

发生运行时错误可能有许多原因(例如:中断或客户端连接问题)。 如果遇到运行时错误,请重启递归 ACL 过程。 可以将 ACL 重新应用于项,而不会造成负面影响。

处理权限错误 (403)

如果在运行递归 ACL 过程时遇到访问控制异常,则表明 AD 安全主体可能没有足够的权限将 ACL 应用于目录层次结构中的一个或多个子项。 发生权限错误时,此过程会停止,系统会提供一个继续标记。 请修复权限问题,然后使用继续标记来处理剩余的数据集。 已成功处理的目录和文件不需要再次处理。 你还可以选择重启递归 ACL 过程。 可以将 ACL 重新应用于项,而不会造成负面影响。

凭据

建议你预配一个在目标存储帐户或容器范围中分配有存储 Blob 数据所有者角色的 Microsoft Entra 安全主体。

性能

为了减少延迟,建议你在与存储帐户位于同一区域中的 Azure 虚拟机 (VM) 中运行递归 ACL 过程。

ACL 限制

可应用于目录或文件的 ACL 的最大数目为 32 个访问 ACL 和 32 个默认 ACL。 有关详细信息,请参阅 Azure Data Lake Storage Gen2 中的访问控制

另请参阅