def get_profile_config(oauth, api_base_uri, profile_id): # type: (OAuth2Session, str, str) -> str """ Return a profile configuration args: oauth (requests_oauthlib.OAuth2Session): oauth2 object api_base_uri (str): the instance base URI profile_id (str): """ logger.info("Retrieving profile config from {}".format(api_base_uri)) try: response = oauth.get( api_base_uri + '/profile_config?profile_id={}'.format(profile_id)) except InvalidGrantError as e: raise EduvpnAuthException(str(e)) if response.status_code == 401: raise EduvpnAuthException("request returned error 401") elif response.status_code != 200: raise EduvpnException("can't create profile, error code {}".format( response.status_code)) # note: this is a bit ambiguous, in case there is an error, the result is json, otherwise clear text. try: json = response.json()['profile_config'] except Exception: # probably valid response return response.text else: if not json['ok']: raise EduvpnException(json['error']) else: raise EduvpnException( "Server error! No profile config returned but no error also.")
def user_messages(oauth, api_base_uri): # type: (OAuth2Session, str) -> Any """ These are messages specific to the user. It can contain a message about the user being blocked, or other personal messages from the VPN administrator. args: oauth (requests_oauthlib.OAuth2Session): oauth2 object api_base_uri (str): the instance base URI returns: list: a list of dicts with date_time, message, type keys """ logger.info("Retrieving user messages from {}".format(api_base_uri)) try: response = oauth.get(api_base_uri + '/user_messages') except InvalidGrantError as e: raise EduvpnAuthException(str(e)) if response.status_code == 401: raise EduvpnAuthException("request returned error 401") elif response.status_code != 200: raise EduvpnException( "can't fetch user messages, error code {}".format( response.status_code)) messages = response.json()['user_messages'] _ = messages['ok'] data = messages['data'] for d in data: yield dateutil.parser.parse(d['date_time']), d['type'], d['message']
def create_config(oauth, api_base_uri, display_name, profile_id): # type: (OAuth2Session, str, str, str) -> str """ Create a configuration for a given profile. args: oauth (requests_oauthlib.OAuth2Session): oauth2 object api_base_uri (str): the instance base URI display_name (str): profile_id (str): """ logger.info("Creating config with name '{}' and profile '{}' at {}".format( display_name, profile_id, api_base_uri)) try: response = oauth.post(api_base_uri + '/create_config', data={ 'display_name': display_name, 'profile_id': profile_id }) except InvalidGrantError as e: raise EduvpnAuthException(str(e)) if response.status_code == 401: raise EduvpnAuthException("request returned error 401") elif response.status_code != 200: raise EduvpnException("can't create config, error code {}".format( response.status_code)) return response.json()
def create_keypair( oauth, api_base_uri): # type: (OAuth2Session, str) -> Tuple[str, str] """ Create remote keypair and return results args: oauth (requests_oauthlib.OAuth2Session): oauth2 object api_base_uri (str): the instance base URI returns: tuple(str, str): certificate and key """ logger.info( "Creating and retrieving key pair from {}".format(api_base_uri)) try: response = oauth.post(api_base_uri + '/create_keypair', data={'display_name': 'eduVPN for Linux'}) except InvalidGrantError as e: raise EduvpnAuthException(str(e)) if response.status_code == 401: raise EduvpnAuthException("request returned error 401") elif response.status_code != 200: raise EduvpnException("can't create keypair, error code {}".format( response.status_code)) keypair = response.json()['create_keypair']['data'] cert = keypair['certificate'] key = keypair['private_key'] return cert, key
def user_info(oauth, api_base_uri): # type: (OAuth2Session, str) -> dict """ returns the user information args: oauth (requests_oauthlib.OAuth2Session): oauth2 object api_base_uri (str): the instance base URI """ logger.info("Retrieving user info from {}".format(api_base_uri)) try: response = oauth.get(api_base_uri + '/user_info') except InvalidGrantError as e: raise EduvpnAuthException(str(e)) if response.status_code == 401: raise EduvpnAuthException("request returned error 401") elif response.status_code != 200: raise EduvpnException("can't retrieve user info, error code {}".format( response.status_code)) data = response.json()['user_info']['data'] assert ("is_disabled" in data) assert ("two_factor_enrolled" in data) if data["two_factor_enrolled"]: assert ("two_factor_enrolled_with" in data) assert ("user_id" in data) return data
def two_factor_enroll_yubi(oauth, api_base_uri, yubi_key_otp): # type : (OAuth2Session, str, str) -> None try: response = oauth.post(api_base_uri + '/two_factor_enroll_yubi', data={'yubi_key_otp': yubi_key_otp}) except InvalidGrantError as e: raise EduvpnAuthException(str(e)) if response.status_code == 401: raise EduvpnAuthException("request returned error 401") elif response.status_code != 200: raise EduvpnException("can't retrieve user info, error code {}".format(response.status_code)) data = response.json()['two_factor_enroll_yubi'] if not data['ok']: raise EduvpnException(data['error'])
def list_profiles(oauth, api_base_uri): """ List profiles on instance args: oauth (requests_oauthlib.OAuth2Session): oauth2 object api_base_uri (str): the instance base URI returns: list: of available profiles on the instance (display_name, profile_id, two_factor) """ logger.info("Retrieving profile list from {}".format(api_base_uri)) response = oauth.get(api_base_uri + '/profile_list') if response.status_code == 401: raise EduvpnAuthException("request returned error 401") elif response.status_code != 200: raise Exception("can't list profiles, error code {}".format( response.status_code)) data = response.json()['profile_list']['data'] profiles = [] for profile in data: display_name = translate_display_name(profile["display_name"]) profile_id = profile["profile_id"] two_factor = profile["two_factor"] profiles.append((display_name, profile_id, two_factor)) return profiles
def two_factor_enroll_totp(oauth, api_base_uri, secret, key): # type : (OAuth2Session, str, secret, str) -> None prefix = '/two_factor_enroll_totp' url = api_base_uri + prefix logger.info("2fa totp enroling on {} with secret={} and key={}".format(url, secret, key)) try: response = oauth.post(url, data={'totp_secret': secret, 'totp_key': key}) except InvalidGrantError as e: raise EduvpnAuthException(str(e)) if response.status_code == 401: raise EduvpnAuthException("request returned error 401") elif response.status_code != 200: logger.error(str(response.content)) raise EduvpnException("can't enable 2fa otp, error code {} response {}".format(response.status_code, response.content)) data = response.json()['two_factor_enroll_totp'] if not data['ok']: raise EduvpnException(data['error'])
def check_certificate(oauth, api_base_uri, common_name): # type : (OAuth2Session, str, common_name) -> dict prefix = '/check_certificate' url = api_base_uri + prefix logger.info("checking client certificate on {} with common_name={}".format(url, common_name)) try: response = oauth.get("{}?common_name={}".format(url, common_name)) except InvalidGrantError as e: raise EduvpnAuthException(str(e)) if response.status_code == 401: raise EduvpnAuthException("request returned error 401") elif response.status_code != 200: logger.error(str(response.content)) raise EduvpnException("can't check client certificate, error code {} response {}".format(response.status_code, response.content)) parsed = response.json()['check_certificate'] if not parsed['ok']: raise EduvpnException(parsed['error']) return parsed['data']
def system_messages(oauth, api_base_uri): """ Return all system messages args: oauth (requests_oauthlib.OAuth2Session): oauth2 object api_base_uri (str): the instance base URI """ logger.info("Retrieving system messages from {}".format(api_base_uri)) try: response = oauth.get(api_base_uri + '/system_messages') except InvalidGrantError as e: raise EduvpnAuthException(str(e)) if response.status_code == 401: raise EduvpnAuthException("request returned error 401") elif response.status_code != 200: raise EduvpnException("can't fetch system messages, error code {}".format(response.status_code)) messages = response.json()['system_messages'] _ = messages['ok'] data = messages['data'] for d in data: yield dateutil.parser.parse(d['date_time']), d['type'], d['message']
def list_profiles(oauth, api_base_uri): # type (OAuth2Session, str) -> dict """ List profiles on instance args: oauth (requests_oauthlib.OAuth2Session): oauth2 object api_base_uri (str): the instance base URI returns: list: of available profiles on the instance (display_name, profile_id, two_factor) """ logger.info("Retrieving profile list from {}".format(api_base_uri)) try: response = oauth.get(api_base_uri + '/profile_list') except InvalidGrantError as e: raise EduvpnAuthException(str(e)) if response.status_code == 401: raise EduvpnAuthException("request returned error 401") elif response.status_code != 200: raise EduvpnException("can't list profiles, error code {}".format( response.status_code)) data = response.json()['profile_list']['data'] profiles = [] for profile in data: display_name = translate_display_name(profile["display_name"]) profile_id = profile["profile_id"] two_factor = profile["two_factor"] if two_factor: if "two_factor_method" in profile: two_factor_method = profile["two_factor_method"] else: two_factor_method = ["yubi", "totp"] else: two_factor_method = [] # we load this into a GtkListModel which doesnt support lists, so we concat them with , profiles.append((display_name, profile_id, two_factor, ",".join(two_factor_method))) return profiles
def user_info(oauth, api_base_uri): """ returns the user information args: oauth (requests_oauthlib.OAuth2Session): oauth2 object api_base_uri (str): the instance base URI """ logger.info("Retrieving user info from {}".format(api_base_uri)) response = oauth.get(api_base_uri + '/user_info') if response.status_code == 401: raise EduvpnAuthException("request returned error 401") elif response.status_code != 200: raise Exception("can't retrieve user info, error code {}".format( response.status_code)) data = response.json()['user_info']['data'] return data
def disable_2fa(user, password, totp_secret, base_url): prefix = "/vpn-admin-portal" admin_url = base_url + prefix browser = mechanicalsoup.StatefulBrowser(raise_on_404=True) logger.info("opening auth_url") response = browser.open(admin_url) assert response.ok browser.select_form() browser["userName"] = user browser["userPass"] = password logger.info("logging in") response = browser.submit_selected() assert response.ok form = browser.select_form() if form.form.attrs['action'] != prefix + '/_two_factor/auth/verify/totp': logger.warning("2fa not enabled") return # redirected to totp screen totp = TOTP(totp_secret) browser['_two_factor_auth_totp_key'] = totp.now() logger.info("submitting totp key") response = browser.submit_selected() assert response.ok form = browser.select_form() if form.form.attrs['action'] == prefix + '/_two_factor/auth/verify/totp': error = browser.get_current_page().findAll( "p", {"class": "error"})[0].contents[0].strip() raise EduvpnAuthException(error) response = browser.open("{}/user?user_id={}".format(admin_url, user)) assert response.ok form = browser.select_form() button = form.form.select('button[value="deleteTotpSecret"]') if button: response = browser.submit_selected() assert (response.ok) else: logger.error(form.form) logger.error("2fa not enabled, but had to supply otp during login")
def authorize(auth_url, user, password): browser = mechanicalsoup.StatefulBrowser(raise_on_404=True) logger.info("opening auth_url") response = browser.open(auth_url) assert (response.ok) browser.select_form('form[action="/vpn-user-portal/_form/auth/verify"]') browser["userName"] = user browser["userPass"] = password logger.info("logging in") response = browser.submit_selected() assert (response.ok) form = browser.select_form() if 'action' in form.form.attrs and form.form.attrs[ 'action'] == '/vpn-user-portal/_two_factor/auth/verify/totp': raise EduvpnAuthException("otp enabled") assert (urlparse( browser.get_url()).path == "/vpn-user-portal/_oauth/authorize" ) # make sure is the right page form = browser.select_form() form.form.select('button[value="yes"]') logger.info("authorising app") response = browser.submit_selected() assert (response.ok)