diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..027acef --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.ipynb linguist-language=Python + diff --git a/OneDrive_Gaph_tutorial.ipynb b/OneDrive_Gaph_tutorial.ipynb index 085d824..c47c727 100644 --- a/OneDrive_Gaph_tutorial.ipynb +++ b/OneDrive_Gaph_tutorial.ipynb @@ -21,14 +21,19 @@ "import requests\n", "import json\n", "import urllib\n", - "import os" + "import os\n", + "from getpass import getpass\n", + "import time\n", + "from datetime import datetime" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Get Access token" + "## Get Access token\n", + "\n", + "### Token flow authentication" ] }, { @@ -85,7 +90,154 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Looks all right. We have got the access token, and included in the HEADERS. You can print response to see more. " + "Looks all right. We have got the access token, and included in the HEADERS. You can print response to see more. Go ahead with OneDrive operations." + ] + }, + { + "source": [ + "### Code flow authentication\n", + "\n", + "Code flow returns both `access_token` and `refresh_token` which can be used to\n", + "request new `access_token` and `refresh_token` for persistent session. If you \n", + "are using organization account, you might require consent of organization administrator. \n" + ], + "cell_type": "markdown", + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get code\n", + "URL = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize'\n", + "client_id = \"362422eb-d9d6-4245-9eca-2be5cf256450\"\n", + "permissions = ['offline_access', 'files.readwrite', 'User.Read']\n", + "response_type = 'code'\n", + "redirect_uri = 'http://localhost:8080/'\n", + "scope = ''\n", + "for items in range(len(permissions)):\n", + " scope = scope + permissions[items]\n", + " if items < len(permissions)-1:\n", + " scope = scope + '+'\n", + "\n", + "print('Click over this link ' +URL + '?client_id=' + client_id + '&scope=' + scope + '&response_type=' + response_type+\\\n", + " '&redirect_uri=' + urllib.parse.quote(redirect_uri))\n", + "print('Sign in to your account, copy the whole redirected URL.')\n", + "code = getpass(\"Paste the URL here :\")\n", + "code = code[(code.find('?code') + len('?code') + 1) :]\n", + "\n", + "URL = 'https://login.microsoftonline.com/common/oauth2/v2.0/token'\n", + "\n", + "response = requests.post(URL + '?client_id=' + client_id + '&scope=' + scope + '&grant_type=authorization_code' +\\\n", + " '&redirect_uri=' + urllib.parse.quote(redirect_uri)+ '&code=' + code)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get token\n", + "data = {\n", + " \"client_id\": client_id,\n", + " \"scope\": permissions,\n", + " \"code\": code,\n", + " \"redirect_uri\": redirect_uri,\n", + " \"grant_type\": 'authorization_code',\n", + " \"client_secret\": client_secret\n", + "}\n", + "\n", + "response = requests.post(URL, data=data)\n", + "\n", + "token = json.loads(response.text)[\"access_token\"]\n", + "refresh_token = json.loads(response.text)[\"refresh_token\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Refresh token\n", + "def get_refresh_token():\n", + " data = {\n", + " \"client_id\": client_id,\n", + " \"scope\": permissions,\n", + " \"refresh_token\": refresh_token,\n", + " \"redirect_uri\": redirect_uri,\n", + " \"grant_type\": 'refresh_token',\n", + " \"client_secret\": 'xxxx-yyyy-zzzz',\n", + " }\n", + "\n", + " response = requests.post(URL, data=data)\n", + "\n", + " token = json.loads(response.text)[\"access_token\"]\n", + " refresh_token = json.loads(response.text)[\"refresh_token\"]\n", + " last_updated = time.mktime(datetime.today().timetuple())\n", + "\n", + " return token, refresh_token, last_updated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "token, refresh_token, last_updated = get_refresh_token()" + ] + }, + { + "source": [ + "If you have a large data to upload, you may use below mock code inside your upload loop:\n", + "\n", + "```python\n", + "elapsed_time = time.mktime(datetime.today().timetuple()) - last_updated\n", + "\n", + "if (elapsed_time < 45*60*60):\n", + " do_something()\n", + "else if (elapsed_time < 59*60*60):\n", + " token, refresh_token, last_updated = get_refresh_token()\n", + "else:\n", + " go_to_code_flow()\n", + "```" + ], + "cell_type": "markdown", + "metadata": {} + }, + { + "source": [ + "## OneDrive operations" + ], + "cell_type": "markdown", + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "URL = 'https://graph.microsoft.com/v1.0/'\n", + "\n", + "HEADERS = {'Authorization': 'Bearer ' + token}\n", + "\n", + "response = requests.get(URL + 'me/drive/', headers = HEADERS)\n", + "if (response.status_code == 200):\n", + " response = json.loads(response.text)\n", + " print('Connected to the OneDrive of', response['owner']['user']['displayName']+' (',response['driveType']+' ).', \\\n", + " '\\nConnection valid for one hour. Refresh token if required.')\n", + "elif (response.status_code == 401):\n", + " response = json.loads(response.text)\n", + " print('API Error! : ', response['error']['code'],\\\n", + " '\\nSee response for more details.')\n", + "else:\n", + " response = json.loads(response.text)\n", + " print('Unknown error! See response for more details.')" ] }, { diff --git a/README.md b/README.md index 132a54f..c3909d9 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,16 @@ import requests import json import urllib import os +from getpass import getpass +import time +from datetime import datetime ``` ## Get Access token +### Token flow authentication + ```python URL = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize' client_id = "362422eb-d9d6-4245-9eca-2be5cf256450" @@ -63,10 +68,117 @@ else: Looks all right. We have got the access token, and included in the HEADERS. You -can print response to see more. +can print response to see more. Go ahead with OneDrive operations. + +### Code flow authentication + +Code flow returns both `access_token` and `refresh_token` which can be used to +request new `access_token` and `refresh_token` for persistent session. If you +are using organization account, you might require consent of organization +administrator. + +```python +# Get code +URL = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize' +client_id = "362422eb-d9d6-4245-9eca-2be5cf256450" +permissions = ['offline_access', 'files.readwrite', 'User.Read'] +response_type = 'code' +redirect_uri = 'http://localhost:8080/' +scope = '' +for items in range(len(permissions)): + scope = scope + permissions[items] + if items < len(permissions)-1: + scope = scope + '+' + +print('Click over this link ' +URL + '?client_id=' + client_id + '&scope=' + scope + '&response_type=' + response_type+\ + '&redirect_uri=' + urllib.parse.quote(redirect_uri)) +print('Sign in to your account, copy the whole redirected URL.') +code = getpass("Paste the URL here :") +code = code[(code.find('?code') + len('?code') + 1) :] + +URL = 'https://login.microsoftonline.com/common/oauth2/v2.0/token' + +response = requests.post(URL + '?client_id=' + client_id + '&scope=' + scope + '&grant_type=authorization_code' +\ + '&redirect_uri=' + urllib.parse.quote(redirect_uri)+ '&code=' + code) +``` + +```python +# Get token +data = { + "client_id": client_id, + "scope": permissions, + "code": code, + "redirect_uri": redirect_uri, + "grant_type": 'authorization_code', + "client_secret": client_secret +} + +response = requests.post(URL, data=data) + +token = json.loads(response.text)["access_token"] +refresh_token = json.loads(response.text)["refresh_token"] +``` + +```python +# Refresh token +def get_refresh_token(): + data = { + "client_id": client_id, + "scope": permissions, + "refresh_token": refresh_token, + "redirect_uri": redirect_uri, + "grant_type": 'refresh_token', + "client_secret": 'xxxx-yyyy-zzzz', + } + + response = requests.post(URL, data=data) + + token = json.loads(response.text)["access_token"] + refresh_token = json.loads(response.text)["refresh_token"] + last_updated = time.mktime(datetime.today().timetuple()) + + return token, refresh_token, last_updated +``` + +```python +token, refresh_token, last_updated = get_refresh_token() +``` + +If you have a large data to upload, you may use below mock code inside your +upload loop: +```python +elapsed_time = time.mktime(datetime.today().timetuple()) - last_updated + +if (elapsed_time < 45*60*60): + do_something() +else if (elapsed_time < 59*60*60): + token, refresh_token, last_updated = get_refresh_token() +else: + go_to_code_flow() +``` + +## OneDrive operations + +```python +URL = 'https://graph.microsoft.com/v1.0/' +HEADERS = {'Authorization': 'Bearer ' + token} + +response = requests.get(URL + 'me/drive/', headers = HEADERS) +if (response.status_code == 200): + response = json.loads(response.text) + print('Connected to the OneDrive of', response['owner']['user']['displayName']+' (',response['driveType']+' ).', \ + '\nConnection valid for one hour. Refresh token if required.') +elif (response.status_code == 401): + response = json.loads(response.text) + print('API Error! : ', response['error']['code'],\ + '\nSee response for more details.') +else: + response = json.loads(response.text) + print('Unknown error! See response for more details.') +``` -## List folders under root directory +### List folders under root directory We will print both directory `name` and `item-d` ```python @@ -81,7 +193,7 @@ for entries in range(len(items)): Getting started with OneDrive.pdf | item-id > C1465DBECD7188C9!102 -## Create new folder (in the root directory) +### Create new folder (in the root directory) ```python url = URL + 'me/drive/root/children/' @@ -111,7 +223,7 @@ for entries in range(len(items)): Here we go, we have successfully created the folder New_Folder. -## List folders under a sub-folder (need to use item-id) +### List folders under a sub-folder (need to use item-id) Note that if you need to create or list sub-folders, you need to use the `item-id`. The `path/folder` notation does not work everywhere. @@ -150,7 +262,7 @@ for entries in range(len(items)): sub_folder | item-id > C1465DBECD7188C9!107 -## Rename an item +### Rename an item ```python url = URL + 'me/drive/items/C1465DBECD7188C9!106' @@ -174,7 +286,7 @@ for entries in range(len(items)): Getting started with OneDrive.pdf | item-id > C1465DBECD7188C9!102 -## Move item +### Move item ```python # url = URL + 'me/drive/items/{item-id-of-item-to-be-moved}' # provide item-id-of-destination-directory under parentReference in the body @@ -188,7 +300,7 @@ response = json.loads(requests.patch(url, headers=HEADERS, json=body).text) ``` -## Delete item +### Delete item ```python url = '/me/drive/items/C1465DBECD7188C9!106' @@ -218,7 +330,7 @@ for entries in range(len(items)): Getting started with OneDrive.pdf | item-id > C1465DBECD7188C9!102 -## Find item-id by item name +### Find item-id by item name ```python items = json.loads(requests.get(URL + 'me/drive/items/root/children', headers=HEADERS).text) @@ -237,7 +349,7 @@ if(item_id==''): Item-id of Documents : C1465DBECD7188C9!103 -## Upload file +### Upload file ```python url = 'me/drive/root:/example_spectrum.txt:/content' @@ -261,7 +373,7 @@ for entries in range(len(items)): Getting started with OneDrive.pdf | item-id > C1465DBECD7188C9!102 -## Access/Download data +### Access/Download data ```python url = 'me/drive/root:/example_spectrum.txt:/content' @@ -272,7 +384,7 @@ data = requests.get(url, headers=HEADERS).text You may like to save the data in a file in your local drive. -## Upload large files (Can be used to upload small files as well) +### Upload large files (Can be used to upload small files as well) If you have files (probably larger than 4 MB), you need to create upload sessions. @@ -310,7 +422,7 @@ response2 = requests.delete(url) ``` -## OneDrive storage usage +### OneDrive storage usage ```python response = json.loads(requests.get(URL + 'me/drive/', headers = HEADERS).text)