Created
February 25, 2019 23:21
-
-
Save jlaundry/237032730c8549a6a73058e290a9407f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import argparse | |
from datetime import datetime | |
from getpass import getpass | |
import requests | |
AAD_GRAPH_SERVICE = "https://graph.microsoft.com" | |
AAD_TENANT = "<tenant>.onmicrosoft.com" | |
AAD_CLIENT_ID = "<app ID>" | |
class MicrosoftGraphClient(): | |
def __init__(self): | |
self._aad_client_secret = getpass("Client Secret for {}:".format(AAD_CLIENT_ID)) | |
data = { | |
"client_id": AAD_CLIENT_ID, | |
"scope": "https://graph.microsoft.com/.default", | |
"client_secret": self._aad_client_secret, | |
"grant_type": "client_credentials", | |
} | |
url = "https://login.microsoftonline.com/{}/oauth2/v2.0/token".format(AAD_TENANT) | |
self._session = requests.Session() | |
r = self._session.post(url, data=data) | |
if r.status_code == 200: | |
self._token = r.json()['access_token'] | |
else: | |
raise Exception("Failed to get OAuth Token: {}".format(r.json())) | |
def _get(self, url, params={}, header={}): | |
header['Authorization'] = "Bearer {}".format(self._token) | |
return self._session.get(url, params=params, headers=header) | |
def _get_values(self, endpoint, params={}): | |
url = "{}{}".format(AAD_GRAPH_SERVICE, endpoint) | |
r = self._get(url, params) | |
if r.status_code != 200: | |
raise Exception("Status code {} returned error: {}".format(r.status_code, r.json())) | |
total_count = 0 | |
while True: | |
count = len(r.json()['value']) | |
total_count = total_count + count | |
print("Processing {}/{}".format(count, total_count)) | |
yield from r.json()['value'] | |
if '@odata.nextLink' in r.json().keys(): | |
url = r.json()['@odata.nextLink'] | |
r = self._get(url) | |
else: | |
break | |
def _save_value(self, endpoint, filename): | |
r = self._get(endpoint) | |
if r.status_code != 200: | |
raise Exception("Status code {} returned error: {}".format(r.status_code, r.json())) | |
with open(filename, 'wb') as f: | |
f.write(r.content) | |
def download_eml(self, upn, subject): | |
params = { | |
"$select": "id,receivedDateTime,from,subject", | |
"$filter": "contains(subject,'{}')".format(subject), | |
} | |
for r in self._get_values("/v1.0/users/{}/messages".format(upn), params=params): | |
print(r) | |
dt = datetime.strptime(r['receivedDateTime'], "%Y-%m-%dT%H:%M:%SZ") | |
r = self._save_value( | |
"https://graph.microsoft.com/beta/users/{}/messages/{}/$value".format(upn, r['id']), | |
"{}-{}.eml".format(upn, dt.strftime("%Y-%m-%d_%H_%M_%S")) | |
) | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser(description='Download .eml message content from the Microsoft Graph API.') | |
parser.add_argument( | |
'upn', | |
type=str, | |
help="The mailbox to search (usually the user's email address)" | |
) | |
parser.add_argument( | |
'subject', | |
type=str, | |
help="The subject to search for (can be partial)" | |
) | |
args = parser.parse_args() | |
client = MicrosoftGraphClient() | |
client.download_eml(args.upn, args.subject) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment