OneDrive API Python request.post для загрузки файла приводит к ошибке 401

Я пытаюсь использовать OneDrive API, ADAL и Python request.post для загрузки файла в папку Sharepoint, но получаю ошибку 401.

Авторизация ADAL работает, так как получает правильный ответ токена и создает жизнеспособный access_token.

Я могу использовать access_token для загрузки файла из той же папки Sharepoint.

Хотя это работает только тогда, когда я вручную копирую print file_url URL-адрес, созданный кодом, в браузер. Однако urllib.urlretrieve(file_url, local_file_name) создает только файл с именем myfilename.csv и содержимым 403 Forbidden.

В моей авторизации используются жестко закодированные имя пользователя и пароль, и я сохраняю токен обновления в виде открытого текста и всегда извлекаю его при получении ответа токена:

import adal
import urllib
import requests

## set variables
username = '[email protected]'
password = 'mypassword'
authorization_url = 'https://login.windows.net/mydomain.onmicrosoft.com' # Authority
redirect_uri = 'https://login.microsoftonline.com/login.srf'
client_id = 'xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx' # Client id

## use ADAL to create token response
token_response = adal.acquire_token_with_username_password(
        authorization_url,
        username,
        password
    )

## create refresh token and save it to use later 
refresh_token = token_response['refreshToken']
refresh_token_file = open('refresh_token.txt', 'w')
refresh_token_file.write(refresh_token)
refresh_token_file.close()

## get saved refresh token and use it to get new token response
refresh_token = open('refresh_token.txt', 'r').read()
token_response = adal.acquire_token_with_refresh_token(authorization_url, str(refresh_token))

## get access_token from token response
access_token = token_response.get('accessToken') 

Ответ токена правильный, и, используя токен доступа из него, следующий код создает URL-адрес file_url, который я могу вручную скопировать и вставить в браузер, который успешно загружает файл. Однако urllib.urlretrieve(file_url, local_file_name) создает только файл с именем myfilename.csv и содержимым 403 Forbidden:

## download file
file_url = 'https://mydomain.sharepoint.com/Shared%20Documents/myfoldername/myfilename.csv?token_response=' + str(access_token)
local_file_name = 'myfilename.csv'
urllib.urlretrieve(file_url, local_file_name)

Однако мне еще не удалось успешно загрузить в эту папку Sharepoint. В настоящее время у меня есть следующее:

# upload file
site_url = 'https://mydomain.sharepoint.com/'
headers = {'Authorization':'BEARER ' + str(access_token)}
r = requests.post(site_url, files={'Shared%20Documents/myfoldername/myfilename.csv': open('myfilename.csv', 'rb')}, headers=headers)

print r.text

который производит ответ

401 UNAUTHORIZED 

У моего приложения Azure AD есть разрешения:

Read and write all user files 
Read and write items and lists in all site collections
(not sure both are needed to upload files)

Мой request.post выглядит нормально? Почти уверен, что я правильно отправляю заголовок, папку и файл.

ИЗМЕНИТЬ, ЧТОБЫ ДОБАВИТЬ:

Учитывая тот факт, что код загрузки возвращает 403 Forbidden, а код загрузки возвращает 401 Unauthorized, я подозреваю, что проблема связана с тем, как urllib и requests отправляют URL-адреса.

ИЗМЕНИТЬ, ЧТОБЫ ДОБАВИТЬ:

Попытка создать URL-адрес файла для использования в GET и PUT. После аутентификации я могу вручную ввести этот URL-адрес в браузер:

https://mydomain.sharepoint.com/_api/v1.0/files/root

который возвращает следующий XML:

{"@odata.context":"https://mydomain.sharepoint.com/_api/v1.0/$metadata#files/$entity"
,"@odata.type":"#Microsoft.FileServices.Folder"
,"@odata.id":"https://mydomain.sharepoint.com/_api/v1.0/files/01QEW7725BZO3N6Y2GOV54373IPWSELRRZ"
,"@odata.editLink":"files/01QEW7725BZO3N6Y2GOV54373IPWSELRRZ"
,"createdBy":null
,"eTag":null
,"id":"01QEW7725BZO3N6Y2GOV54373IPWSELRRZ"
,"lastModifiedBy":null
,"name":"/"
,"parentReference":null
,"size":0
,"dateTimeCreated":"2013-07-31T02:35:57Z"
,"dateTimeLastModified":"2016-05-23T03:55:46Z"
,"type":"Folder"
,"webUrl":"https://mydomain.sharepoint.com/Shared%20Documents"
,"childCount":1}

Однако не нашел правильного синтаксиса для ссылки на файл. Например, это не работает:

https://mydomain.sharepoint.com/_api/v1.0/files/root:/myfoldername/myfilename.csv:/content

Это возвращает ошибку:

    {"error":"invalid_client","error_description":"Invalid audience Uri 'https:\/\/m
anagement.core.windows.net\/'."}

Я думаю, что получение этой конкретной ссылки на файл - это то, что мне нужно, чтобы заставить его работать.


person curtisp    schedule 23.05.2016    source источник
comment
Вы уверены, что правильно скачали файлы? Я попробовал ваш код, вместо загрузки файла он дает мне html-файл, который перенаправляет меня куда-то еще.   -  person Jack Zeng    schedule 24.05.2016
comment
Блин, вы правы, он не загружается правильно. Он создает файл с именем myfilename.csv, но содержит только 403 Forbidden. Когда я печатаю file_url и копирую полученный URL-адрес напрямую в браузер, он загружает файл правильно. Я обновлю вопрос.   -  person curtisp    schedule 24.05.2016


Ответы (1)


Здесь есть несколько вещей, которые нужно исправить.

Во-первых, вам нужно передать идентификатор клиента и ресурс в вашAcquire_token_with_username_password. Это позволит вам получить доступ к графику (где вы можете получить доступ к OneDrive), в противном случае вы просто получаете разрешения на вход в этот токен.

resource_url = "https://graph.microsoft.com";

token_response = ada.acquire_token_with_username_password(
    authorization_url,
    username,
    password,
    client_id
    resource_url
)

Если вы получаете сообщение об ошибке, говорящее о необходимости client_secret или client_assertion. Вам необходимо зарегистрировать новое приложение Azure и выбрать собственный клиент. Если вы получите сообщение об ошибке, говорящее о том, что вам нужно интерактивное приглашение, вам потребуется вручную дать согласие на использование приложения в браузере в первый раз, перейдя к

https://login.microsoftonline.com/mydomain.com/oauth2/authorize?client_id=client_id&resource=resource_url&response_type=code+id_token&nonce=1234

Убедитесь, что вы заменили mydomain.com, client_id и resource_url.

Вам нужно будет сделать это только один раз, в первый раз. После входа в браузер и согласия вы больше не должны получать эту интерактивную ошибку подсказки.

Чтобы загрузить файл, не знаю, откуда вы взяли этот подход «?token_response=x», но я не думаю, что он работает (не работал, когда я его тестировал). Вам нужно указать другой URL-адрес и отправить токен носителя, как вы делаете в своем почтовом запросе.

site_url = 'https://graph.microsoft.com/v1.0/drive/root:/myfoldername/myfilename.csv:/content'
headers = {'Authorization':'BEARER ' + str(access_token)}
r = requests.get(site_url, headers=headers)
print r.text

Подробнее о получении файла здесь: https://dev.onedrive.com/items/download.htm

Что касается загрузки, вам также нужно будет изменить свой URL-адрес на то же значение, что и в случае GET, а также перейти с POST на PUT.

site_url = 'https://graph.microsoft.com/v1.0/drive/root:/myfoldername/myfilename.csv:/content'
headers = {'Authorization':'BEARER ' + str(access_token)}
r = requests.put(site_url, data = open('myfilename.csv', 'rb')}, headers=headers)
print r.text

Подробнее о загрузке файла здесь: https://dev.onedrive.com/items/upload_put.htm

person Saca    schedule 25.05.2016
comment
Хорошо, теперь я понял, я только что аутентифицировал пользователя с помощью Azure AD, а не приложения. Но adal.acquire_token_with_username_password устанавливает оба client_id и resource до None. - person curtisp; 26.05.2016
comment
Мне удалось авторизовать login.microsoftonline.com . Требуется изменить ADAL class _DefaultValues в файле __init__.py со значениями по умолчанию для client_id и resource, чтобы они соответствовали значениям для моего приложения, например, client_id = client_id моего приложения Azure AD и resource = https://tenant.sharepoint.com/для устранения проблемы с недопустимой аудиторией. Также изменен file_url на 'tenant.sharepoint .com/_api/v2.0/drive/root:/myfoldername/'. - person curtisp; 26.05.2016