Implement Blackboard Ultra traditional login master
authorJakob Cornell <jakob@jcornell.net>
Wed, 6 Nov 2019 05:04:14 +0000 (23:04 -0600)
committerJakob Cornell <jakob@jcornell.net>
Wed, 6 Nov 2019 05:04:14 +0000 (23:04 -0600)
session.py

index b016db3869ce48690e334957b165481034618bbf..f09bc0814808ba2dfdb2a7ac31cbf604ab9e69fd 100644 (file)
@@ -23,6 +23,28 @@ import toml
 
 import util
 
 
 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
 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 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:
                                        # 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")
                                        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')
                        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)
 
                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)