【问题标题】:Uploading a folder to Sharepoint using Python, on an O365 tenant with MFA在具有 MFA 的 O365 租户上使用 Python 将文件夹上传到 Sharepoint
【发布时间】:2021-03-17 16:26:35
【问题描述】:

我最近编写了一个脚本,简单地说,就是将一个文件夹上传到我的 office365 租户上的 sharepoint。出于显而易见的原因,原始身份验证是使用只能访问一个文档库的服务帐户完成的。这很好用:

from shareplum import Office365
from shareplum import Site 
from shareplum.site import Version
import os 
import sys 

folderName = sys.argv[1]
authcookie = Office365('mySharepoint',username='username',password='password').GetCookies()
site= Site('mysharepoint/sites/mySite', version=Version.v365, authcookie=authcookie)

for filename in os.listdir('myDir'):
    with open(foldername+"/"+filename, mode='rb') as file: 
        fileContent = file.read()
        folder.upload_file(fileContent,filename)

所以在当前的基本身份验证下,这工作得非常好,它将遍历任何目录并将我的所有文件(无论是文本还是 jpgs)上传到我的共享点。

现在的问题是,在生产中,一切都是 MFA,所以基本身份验证不起作用。我已经尝试过应用密码,但尽管轻触 Azure AD 中适用的所有设置,但该选项根本不存在。

是否有一种简单的方法可以让此帐户上传并绕过 MFA?

【问题讨论】:

    标签: python sharepoint azure-active-directory office365


    【解决方案1】:

    关于这个问题,您可以使用 Azure AD App-Only 身份验证来访问 SharePoint,然后我们可以通过 MFA。更多详情请参考herehere

    例如

    1. 创建证书
    #Requires -RunAsAdministrator
    <#
    .SYNOPSIS
    Creates a Self Signed Certificate for use in server to server authentication
    .DESCRIPTION
    .EXAMPLE
    .\Create-SelfSignedCertificate.ps1 -CommonName "MyCert" -StartDate 2015-11-21 -EndDate 2017-11-21
    This will create a new self signed certificate with the common name "CN=MyCert". During creation you will be asked to provide a password to protect the private key.
    .EXAMPLE
    .\Create-SelfSignedCertificate.ps1 -CommonName "MyCert" -StartDate 2015-11-21 -EndDate 2017-11-21 -Password (ConvertTo-SecureString -String "MyPassword" -AsPlainText -Force)
    This will create a new self signed certificate with the common name "CN=MyCert". The password as specified in the Password parameter will be used to protect the private key
    .EXAMPLE
    .\Create-SelfSignedCertificate.ps1 -CommonName "MyCert" -StartDate 2015-11-21 -EndDate 2017-11-21 -Force
    This will create a new self signed certificate with the common name "CN=MyCert". During creation you will be asked to provide a password to protect the private key. If there is already a certificate with the common name you specified, it will be removed first.
    #>
    Param(
    
       [Parameter(Mandatory=$true)]
       [string]$CommonName,
    
       [Parameter(Mandatory=$true)]
       [DateTime]$StartDate,
       
       [Parameter(Mandatory=$true)]
       [DateTime]$EndDate,
    
       [Parameter(Mandatory=$false, HelpMessage="Will overwrite existing certificates")]
       [Switch]$Force,
    
       [Parameter(Mandatory=$false)]
       [SecureString]$Password
    )
    
    # DO NOT MODIFY BELOW
    
    function CreateSelfSignedCertificate(){
        
        #Remove and existing certificates with the same common name from personal and root stores
        #Need to be very wary of this as could break something
        if($CommonName.ToLower().StartsWith("cn="))
        {
            # Remove CN from common name
            $CommonName = $CommonName.Substring(3)
        }
        $certs = Get-ChildItem -Path Cert:\LocalMachine\my | Where-Object{$_.Subject -eq "CN=$CommonName"}
        if($certs -ne $null -and $certs.Length -gt 0)
        {
            if($Force)
            {
            
                foreach($c in $certs)
                {
                    remove-item $c.PSPath
                }
            } else {
                Write-Host -ForegroundColor Red "One or more certificates with the same common name (CN=$CommonName) are already located in the local certificate store. Use -Force to remove them";
                return $false
            }
        }
    
        $name = new-object -com "X509Enrollment.CX500DistinguishedName.1"
        $name.Encode("CN=$CommonName", 0)
    
        $key = new-object -com "X509Enrollment.CX509PrivateKey.1"
        $key.ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
        $key.KeySpec = 1
        $key.Length = 2048 
        $key.SecurityDescriptor = "D:PAI(A;;0xd01f01ff;;;SY)(A;;0xd01f01ff;;;BA)(A;;0x80120089;;;NS)"
        $key.MachineContext = 1
        $key.ExportPolicy = 1 # This is required to allow the private key to be exported
        $key.Create()
    
        $serverauthoid = new-object -com "X509Enrollment.CObjectId.1"
        $serverauthoid.InitializeFromValue("1.3.6.1.5.5.7.3.1") # Server Authentication
        $ekuoids = new-object -com "X509Enrollment.CObjectIds.1"
        $ekuoids.add($serverauthoid)
        $ekuext = new-object -com "X509Enrollment.CX509ExtensionEnhancedKeyUsage.1"
        $ekuext.InitializeEncode($ekuoids)
    
        $cert = new-object -com "X509Enrollment.CX509CertificateRequestCertificate.1"
        $cert.InitializeFromPrivateKey(2, $key, "")
        $cert.Subject = $name
        $cert.Issuer = $cert.Subject
        $cert.NotBefore = $StartDate
        $cert.NotAfter = $EndDate
        $cert.X509Extensions.Add($ekuext)
        $cert.Encode()
    
        $enrollment = new-object -com "X509Enrollment.CX509Enrollment.1"
        $enrollment.InitializeFromRequest($cert)
        $certdata = $enrollment.CreateRequest(0)
        $enrollment.InstallResponse(2, $certdata, 0, "")
        return $true
    }
    
    function ExportPFXFile()
    {
        if($CommonName.ToLower().StartsWith("cn="))
        {
            # Remove CN from common name
            $CommonName = $CommonName.Substring(3)
        }
        if($Password -eq $null)
        {
            $Password = Read-Host -Prompt "Enter Password to protect private key" -AsSecureString
        }
        $cert = Get-ChildItem -Path Cert:\LocalMachine\my | where-object{$_.Subject -eq "CN=$CommonName"}
        
        Export-PfxCertificate -Cert $cert -Password $Password -FilePath "$($CommonName).pfx"
        Export-Certificate -Cert $cert -Type CERT -FilePath "$CommonName.cer"
    }
    
    function RemoveCertsFromStore()
    {
        # Once the certificates have been been exported we can safely remove them from the store
        if($CommonName.ToLower().StartsWith("cn="))
        {
            # Remove CN from common name
            $CommonName = $CommonName.Substring(3)
        }
        $certs = Get-ChildItem -Path Cert:\LocalMachine\my | Where-Object{$_.Subject -eq "CN=$CommonName"}
        foreach($c in $certs)
        {
            remove-item $c.PSPath
        }
    }
    
    if(CreateSelfSignedCertificate)
    {
        ExportPFXFile
        RemoveCertsFromStore
    }
    
    1. 注册 Azure AD 应用程序
    2. 配置 API 权限。
    SharePoint
         Application permissions
              Sites
                 Sites.FullControl.All
    
    1. 将您的 cer 文件上传到 Azure AD 应用程序。具体操作方法请参考here

    2. 代码

    一个。 sdk

    pip install Office365-REST-Python-Client 
    

    b.代码

    import os
    from office365.sharepoint.client_context import ClientContext
    site_url = "https://{your-tenant-prefix}.sharepoint.com"
    
    cert_settings = {
        'client_id': '',
        'thumbprint': "6B36FBFC86FB1C019EB6496494B9195E6D179DDB",
        'certificate_path': '{0}/selfsigncert.cer'.format(os.path.dirname(__file__))
    }
    
    
    ctx = ClientContext(site_url ).with_client_certificate( "<azure ad tenant domain>",
                                                                 cert_settings['client_id'],
                                                                 cert_settings['thumbprint'],
                                                                 cert_settings['certificate_path'])
    
    target_url = "/Shared Documents"
    target_folder = ctx.web.get_folder_by_server_relative_url(target_url)
    size_chunk = 1000000
    local_path = "<file path>"
    file_size = os.path.getsize(local_path)
    def print_upload_progress(offset):
        print("Uploaded '{}' bytes from '{}'...[{}%]".format(offset, file_size, round(offset / file_size * 100, 2)))
    uploaded_file = target_folder.files.create_upload_session(local_path, size_chunk, print_upload_progress)
    ctx.execute_query()
    print('File {0} has been uploaded successfully'.format(uploaded_file.serverRelativeUrl))
    

    【讨论】:

    • 这似乎有道理,但是……“”,这只是我的 o365 的地址吗? IE。 example.com 另外,这是触发 utf-8 编解码器无法解码位置 1 中的字节 0x82:无效起始字节
    • 我假设我必须用应用程序 client_id 填写 client_id,同样的问题
    猜你喜欢
    • 2020-08-19
    • 1970-01-01
    • 2018-12-28
    • 2020-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多