diff --git a/meet.cabal b/meet.cabal index 1e0a42a..269260c 100644 --- a/meet.cabal +++ b/meet.cabal @@ -1,13 +1,13 @@ cabal-version: 3.0 name: meet -version: 0.1.0.4 +version: 0.2.0.0 license: MIT license-file: LICENSE maintainer: hut23@turing.ac.uk author: Research Engineering @ The Alan Turing Institute synopsis: Find meeting times and rooms at the Turing description: - `meet` is a command-line tool for finding available meeting times at the Turing. It can additionally filter by room availability and capacity. + Command-line tools for finding available meeting times and rooms at the Turing. It can additionally filter by room availability and capacity. category: Math build-type: Simple diff --git a/python-old/auth_flow.py b/python-old/auth_flow.py deleted file mode 100644 index 68bed6c..0000000 --- a/python-old/auth_flow.py +++ /dev/null @@ -1,149 +0,0 @@ -import os -import sys -import random -import requests -import urllib.parse -import http.server -import socketserver - -### CONFIG - -# The rum app is installed on the main Turing directory. However, it doesn't -# have the appropriate set up for auth flow. It probably also does not have -# enough permissions to do all of what we would like to do. However, it might -# be enough to make a start with. - -# CLIENT_ID = "a462354f-fd23-4fdf-94f5-5cce5a6c27c7" # rum -# TENANT_ID = "4395f4a7-e455-4f95-8a9f-1fbaef6384f9" # turing Azure directory - -# The hut23meet app is installed on the hut23meet directory, which I set up -# myself. It has been set up for auth-flow, and I can grant the app any -# permissions I want, since I have admin privileges on that directory. -# Unfortunately, that directory does not have Outlook, so there are no -# calendars... - -CLIENT_ID = "60ae4dcf-bec0-4078-9e35-8141e609cae9" # hut23meet/meet -TENANT_ID = "2fd2cbb5-743e-4d35-b783-92f98c93380d" # hut23meet - -# Ideally read this in from a config file. For now I just source ~/.secrets -CLIENT_SECRET = os.getenv("HUT23MEET_CLIENT_SECRET") -if not CLIENT_SECRET: - raise ValueError("HUT23MEET_CLIENT_SECRET env variable not set") - -PORT = 8912 -STATE = ''.join(random.choice("abcdefghijklmnopqrstuvwxyz1234567890") for _ in range(25)) -REDIRECT_URI = f"http://localhost:{PORT}" - -# https://learn.microsoft.com/en-us/graph/auth-v2-user?tabs=http -auth_url = f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/authorize/?" -auth_url += urllib.parse.urlencode({ - "client_id": CLIENT_ID, - "response_type": "code", - "redirect_uri": REDIRECT_URI, - "response_mode": "query", - "scope": "user.read", # calendars.read.shared calendars.readwrite - "state": STATE, -}) - - -### CUSTOM HTTP SERVER CLASSES - -class AuthorisationSuccess(Exception): - def __init__(self, code): - self.code = code - -class MyRequestHandler(http.server.BaseHTTPRequestHandler): - def return_text(self, code, body): - self.send_response(code) - self.send_header("Content-type", "text/html") - self.end_headers() - self.wfile.write(body) - - # Silence logging - def log_request(self, code='-', size='-'): - pass - - def do_GET(self): - try: - query = urllib.parse.parse_qs(urllib.parse.urlparse(self.path).query) - code = query['code'][0] - state = query['state'][0] - except Exception as e: - self.return_text(400, b"Bad request") - raise e - - if state != STATE: - self.return_text(400, b"Bad request, state mismatch") - raise Exception("State mismatch") - - self.return_text(200, b"Code received; you can close this window now.") - raise AuthorisationSuccess(code) - -class MyTCPServer(socketserver.TCPServer): - # Convenience function to allow us to run the script multiple times in - # quick succession - allow_reuse_address = True - - # Must subclass to re-raise AuthorisationSuccess otherwise it gets - # swallowed and doesn't bubble up - def _handle_request_noblock(self): - try: - request, client_address = self.get_request() - except OSError: - return - if self.verify_request(request, client_address): - try: - self.process_request(request, client_address) - except AuthorisationSuccess as e: - self.shutdown_request(request) - raise AuthorisationSuccess(e.code) - except: - self.handle_error(request, client_address) - self.shutdown_request(request) - else: - self.shutdown_request(request) - - def __exit__(self, *args): - self.server_close() - - -### MAIN - -# User logs in to get auth code - -try: - with MyTCPServer(("", PORT), MyRequestHandler) as httpd: - # Open browser and listen on port for auth code - os.system(f"open '{auth_url}'") - httpd.handle_request() -except AuthorisationSuccess as e: - auth_code = e.code -except Exception as e: - print("Error:", e, file=sys.stderr) - sys.exit(1) - - -token_url = f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token/?" -token_post_body = { - "client_id": CLIENT_ID, - "code": auth_code, - "redirect_uri": REDIRECT_URI, - "scope": "calendars.read.shared", - "grant_type": "authorization_code", - "client_secret": CLIENT_SECRET, -} - -# Exchange auth code for token - -response = requests.post(token_url, data=token_post_body) -json = response.json() -try: - token = json['access_token'] -except Exception as e: - print("Error getting token:", json) - sys.exit(1) - -# At this point we can use the token for whatever -me_url = "https://graph.microsoft.com/v1.0/me" -resp = requests.get(me_url, headers={"Authorization": f"Bearer {token}"}) -print(resp.json()) diff --git a/python-old/device_flow.py b/python-old/device_flow.py deleted file mode 100644 index d71bf7a..0000000 --- a/python-old/device_flow.py +++ /dev/null @@ -1,74 +0,0 @@ -# https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-device-code -import os -import sys -import requests -import time -import itertools - -### CONFIG - -CLIENT_ID = "a462354f-fd23-4fdf-94f5-5cce5a6c27c7" # rum -TENANT_ID = "4395f4a7-e455-4f95-8a9f-1fbaef6384f9" # turing Azure directory - -url = f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/devicecode" -data = { - "client_id": CLIENT_ID, - "scope": "user.read calendars.read.shared", -} -response_json = requests.post(url, data=data).json() -os.system(f"open {response_json['verification_uri']}") -os.system(f"pbcopy <<< {response_json['user_code']}") -print(response_json['message'], "The code has already been copied to your clipboard.") - -def spinner(): - yield from itertools.cycle(["|", "/", "-", "\\"]) -spinner = spinner() - -# TODO implement a timeout here -polling_message_shown = False -token = None -while True: - poll_url = f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token" - poll_data = { - "grant_type": "urn:ietf:params:oauth:grant-type:device_code", - "client_id": CLIENT_ID, - "device_code": response_json['device_code'], - } - poll_response = requests.post(poll_url, data=poll_data).json() - - # Aren't match statements just AMAZING? - match poll_response.get("error"): - case None: - if "access_token" in poll_response: - token = poll_response['access_token'] - print("Authorised!") - break - else: - raise Exception(f"No error raised but also no token provided: {poll_response}") - - case "authorization_pending": - if polling_message_shown: - sys.stdout.write("\033[F") # Clear input line - - print(f"{next(spinner)} Waiting for authorisation...") - polling_message_shown = True - time.sleep(0.2) - - case "authorization_declined": - print("Authorisation declined.") - sys.exit(1) - - case "expired_token": - print("Device code expired.") - sys.exit(1) - - case "bad_verification_code": - raise Exception("Bad verification code: this is a bug and should not happen.") - - case _: - raise Exception(f"Unknown error: {poll_response}") - -# At this point we can use the token for whatever -me_url = "https://graph.microsoft.com/v1.0/me" -resp = requests.get(me_url, headers={"Authorization": f"Bearer {token}"}) -print(resp.json())