Microsoft Office 365 Endpoints V2 (Python)

The Microsoft Office 365 Endpoints V1 program did a good job of outputting the endpoint data into CSV files, but what if you wanted the URLs outputted for use as a PAC file? A Proxy Auto Configuration file tells a proxy how to handle traffic depending on URL, host or IP.

Imagine the program running regularly (ie. via CRON) and updating the proxy PAC file automatically, well thats possible be using the string options in Python.

URL_AC0

URL_PAC

 

#!/usr/bin/python3
# original source from https://support.office.com/en-us/article/managing-office-365-endpoints-99cab9d4-ef59-4207-9f2b-3728eb46bf9a?ui=en-US&rs=en-US&ad=US#ID0EACAAA=4._Web_service
# modified by geektechstuff

import json
import os
import urllib.request
import uuid
import smtplib

from email_settings import (
smtp_server,
port,
login_user,
login_pass,
from_address,
to_address
)

# helper to call the webservice and parse the response
def webApiGet(methodName, instanceName, clientRequestId):
    ws = "https://endpoints.office.com"
    requestPath = ws + '/' + methodName + '/' + instanceName + '?clientRequestId=' + clientRequestId
    request = urllib.request.Request(requestPath)
    with urllib.request.urlopen(request) as response:
        return json.loads(response.read().decode())


# path where client ID and latest version number will be stored
datapath = '/Users/gary/Downloads/' + '/endpoints_clientid_latestversion.txt'

# URL List for Proxy Server, outputs as pac
url_list = '/Users/gary/Downloads/365_url_list.pac'

# IPv4 List for Firewall, outputs as CSV
ip_list = '/Users/gary/Downloads/365_ip_list.csv'

# fetch client ID and version if data exists; otherwise create new file
if os.path.exists(datapath):
    with open(datapath, 'r') as fin:
        clientRequestId = fin.readline().strip()
        latestVersion = fin.readline().strip()
else:
    clientRequestId = str(uuid.uuid4())
    latestVersion = '0000000000'
    with open(datapath, 'w') as fout:
        fout.write(clientRequestId + '\n' + latestVersion)

# call version method to check the latest version, and pull new data if version number is different
version = webApiGet('version', 'Worldwide', clientRequestId)
if version['latest'] > latestVersion:
    print('New version of Office 365 worldwide commercial service instance endpoints detected')

    # write the new version number to the data file
    with open(datapath, 'w') as fout:
        fout.write(clientRequestId + '\n' + version['latest'])

    # invoke endpoints method to get the new data
    endpointSets = webApiGet('endpoints', 'Worldwide', clientRequestId)

    # filter results for Allow and Optimize endpoints, and transform these into tuples with port and category
    flatUrls = []
    for endpointSet in endpointSets:
        if endpointSet['category'] in ('Optimize', 'Allow'):
            category = endpointSet['category']
            urls = endpointSet['urls'] if 'urls' in endpointSet else []
            tcpPorts = endpointSet['tcpPorts'] if 'tcpPorts' in endpointSet else ''
            udpPorts = endpointSet['udpPorts'] if 'udpPorts' in endpointSet else ''
            flatUrls.extend([(category, url, tcpPorts, udpPorts) for url in urls])

    flatIps = []
    for endpointSet in endpointSets:
        if endpointSet['category'] in ('Optimize', 'Allow'):
            ips = endpointSet['ips'] if 'ips' in endpointSet else []
            category = endpointSet['category']
            # IPv4 strings have dots while IPv6 strings have colons
            ip4s = [ip for ip in ips if '.' in ip]
            tcpPorts = endpointSet['tcpPorts'] if 'tcpPorts' in endpointSet else ''
            udpPorts = endpointSet['udpPorts'] if 'udpPorts' in endpointSet else ''
            flatIps.extend([(category, ip, tcpPorts, udpPorts) for ip in ip4s])

    print('IPv4 Firewall IP Address Ranges')
    print(','.join(sorted(set([ip for (category, ip, tcpPorts, udpPorts) in flatIps]))))
    # outputs the details to the ip_list variable
    with open(ip_list, 'w') as data_write:
        data_write.write(','.join(sorted(set([ip for (category, ip, tcpPorts, udpPorts) in flatIps]))))

    print('URLs for Proxy Server')
    print('\n'.join(sorted(set([url for (category, url, tcpPorts, udpPorts) in flatUrls]))))
    # outputs the details to the url_list variable
    with open(url_list, 'w') as data_write:
        data_write.write('function FindProxyForURL(url, host) { \n')
        beg_line = '    if (shExpMatch(host, '
        end_line = ')\n'
        URL_edited = (sorted(set([ip for (category, ip, tcpPorts, udpPorts) in flatUrls])))
        for ip in URL_edited:
            URL2 = beg_line+ip+end_line
            print(URL2)
            data_write.write(URL2)
        data_write.write('{\n'+' return "DIRECT";'+'\n}')

    # smtp aka email settings
    smtpObj = smtplib.SMTP_SSL(smtp_server, port)
    smtpObj.login(login_user, login_pass)
    smtpObj.sendmail(from_address, to_address,
                     'From: from_address\nSubject: 365 EndPoints Updated\n\n')
    smtpObj.quit()

else:
    print('Office 365 worldwide commercial service, no updates detected.')

The above should now output the Microsoft Office 365 Endpoint URLs into a (basic – it’s my first attempt) PAC file.

pac_pac.pngpac_end.png

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.