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
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:
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)