From: Jakob Cornell Date: Wed, 6 Nov 2019 05:04:14 +0000 (-0600) Subject: Implement Blackboard Ultra traditional login X-Git-Url: https://jcornell.net/gitweb/gitweb.cgi?a=commitdiff_plain;h=HEAD;p=bb-sync.git Implement Blackboard Ultra traditional login --- diff --git a/session.py b/session.py index b016db3..f09bc08 100644 --- a/session.py +++ b/session.py @@ -23,6 +23,28 @@ import toml import util +def get_credentials(config_path): + try: + with config_path.open() as f: + config = toml.load(f) + username = config['username'] + password = config['password'] + return (username, password) + except Exception as e: + pass + print("Unable to load Blackboard credentials. The file `config.toml` may be inaccessible, malformed, or missing entries.", file = sys.stderr) + username = input("username: ") + password = getpass.getpass("password: ") + save = None + while save not in {'y', 'n'}: + save = input("Save credentials? (y/n): ") + if save == 'y': + config['username'] = username + config['password'] = password + with config_path.open('w') as f: + toml.dump(config, f) + return (username, password) + class Session: def __init__(self, cookie_jar, config_path): self.cookie_jar = cookie_jar @@ -50,7 +72,7 @@ class Session: if e.code != 500: raise else: - if 'bgcolor' not in soup.find('body').attrs: + if not soup.select('body.bb-login'): # looks like actual content page return soup else: @@ -63,72 +85,31 @@ class Session: raise logging.getLogger(util.LOGGER_NAME).log(logging.INFO, "not logged in; logging in") - curr_url = 'https://blackboard.oberlin.edu/' - with self.opener.open(curr_url) as resp: - soup = bs4.BeautifulSoup(resp, 'lxml') - target = soup.find('ul', id = 'loginRedirectProviderList').find('a')['href'] - curr_url = util.resolve(target, curr_url) - # get SSO no-JS landing page - with self.opener.open(curr_url) as resp: + url = 'https://oberlin-test.blackboard.com/' + with self.opener.open(url) as resp: soup = bs4.BeautifulSoup(resp, 'lxml') - form = soup.find('form') - curr_url = util.resolve(form['action'], curr_url) - method = form['method'].upper() - params = {inp['name']: inp['value'] for inp in form.find_all('input', type = 'hidden')} - body = urllib.parse.urlencode(params) + [form] = soup.select('#login-form > form[name="login"]') + to_keep = lambda elem: elem['type'] != 'submit' and elem['name'] not in {'user_id', 'password'} + inputs = filter(to_keep, form.find_all('input')) + (username, password) = get_credentials(self.config_path) + params = dict([ + ('user_id', username), + ('password', password), + *((elem['name'], elem['value']) for elem in inputs) + ]) + body = urllib.parse.urlencode(params).encode('ascii') - # post some token to SSO to get login page - with self.opener.open(urllib.request.Request(curr_url, data = body.encode('ascii'), method = method)) as resp: - soup = bs4.BeautifulSoup(resp, 'lxml') - form = soup.find('form') - curr_url = util.resolve(form['action'], curr_url) - method = form['method'].upper() - (user, pass_) = get_credentials(self.config_path) - data = { - 'j_username': user, - 'j_password': pass_, - '_eventId_proceed': '', - } - body = urllib.parse.urlencode(data) - - # post login credentials - with self.opener.open(urllib.request.Request(curr_url, data = body.encode('ascii'), method = method)) as resp: - soup = bs4.BeautifulSoup(resp, 'lxml') - form = soup.find('form') - curr_url = util.resolve(form['action'], curr_url) - method = form['method'].upper() - params = {inp['name']: inp['value'] for inp in form.find_all('input', type = 'hidden')} - body = urllib.parse.urlencode(params) - - # post token back to Blackboard - with self.opener.open(urllib.request.Request(curr_url, data = body.encode('ascii'), method = method)) as resp: - soup = bs4.BeautifulSoup(resp, 'lxml') + req = urllib.request.Request( + util.resolve(form['action'], url), + method = form['method'], + data = body, + ) + with self.opener.open(req) as resp: + resp.read() if expect_html: with self.opener.open(url) as resp: return bs4.BeautifulSoup(resp, 'lxml') else: return self.opener.open(url) - -def get_credentials(config_path): - try: - with config_path.open() as f: - config = toml.load(f) - username = config['username'] - password = config['password'] - return (username, password) - except Exception as e: - pass - print("Unable to load Blackboard credentials. The file `config.toml` may be inaccessible, malformed, or missing entries.", file = sys.stderr) - username = input("username: ") - password = getpass.getpass("password: ") - save = None - while save not in {'y', 'n'}: - save = input("Save credentials? (y/n): ") - if save == 'y': - config['username'] = username - config['password'] = password - with config_path.open('w') as f: - toml.dump(config, f) - return (username, password)