def activate_connection(meta, builder): """do the actual connecting action""" logger.info("Connecting to {}".format(meta.display_name)) notify("eduVPN connecting...", "Connecting to '{}'".format(meta.display_name)) try: if not meta.token: logger.error("metadata for {} doesn't contain oauth2 token".format(meta.uuid)) else: oauth = oauth_from_token(meta=meta) config = get_profile_config(oauth, meta.api_base_uri, meta.profile_id) meta.config = config update_config_provider(meta) if datetime.now() > datetime.fromtimestamp(meta.token['expires_at']): logger.info("key pair is expired") cert, key = create_keypair(oauth, meta.api_base_uri) update_keys_provider(meta.uuid, cert, key) connect_provider(meta.uuid) except Exception as e: switch = builder.get_object('connect-switch') GLib.idle_add(switch.set_active, False) window = builder.get_object('eduvpn-window') error_helper(window, "can't enable connection", "{}: {}".format(type(e).__name__, str(e))) raise
def delete_profile(builder): """called when the user presses the - button""" logger.info("delete provider clicked") meta = metadata_of_selected(builder) if not meta: logger.info("nothing selected") return window = builder.get_object('eduvpn-window') dialog = Gtk.MessageDialog( window, Gtk.DialogFlags.MODAL, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, "Are you sure you want to remove '{}'?".format(meta.display_name)) dialog.format_secondary_text("This action can't be undone.") response = dialog.run() if response == Gtk.ResponseType.YES: logger.info("deleting provider config") try: delete_provider(meta.uuid) notify("eduVPN provider deleted", "Deleted '{}'".format(meta.display_name)) except Exception as e: error_helper(window, "can't delete profile", str(e)) dialog.destroy() raise GLib.idle_add(lambda: update_providers(builder)) elif response == Gtk.ResponseType.NO: logger.info("not deleting provider config") dialog.destroy()
def custom_url(builder, meta, verifier, lets_connect): # type: (Gtk.builder, Metadata, str, bool) -> None """the custom URL dialog where a user can enter a custom instance URL""" dialog = builder.get_object('custom-url-dialog') window = builder.get_object('eduvpn-window') dialog.set_transient_for(window) entry = builder.get_object('custom-url-entry') # entry.set_text('https://debian-vpn.tuxed.net') entry.set_position(len(entry.get_text())) dialog.show_all() while True: response = dialog.run() if response == 1: url = entry.get_text().strip() logger.info("ok pressed, entry text: {}".format(url)) if not url.startswith('https://'): error_helper(dialog, "Invalid URL", "URL should start with https://") elif url == 'https://': error_helper(dialog, "Invalid URL", "Please enter a URL") else: dialog.hide() meta.display_name = url[8:].split('/')[0] logger.info("using {} for display name".format(meta.display_name)) meta.instance_base_uri = url meta.connection_type = 'Custom Instance' meta.authorization_type = 'local' meta.icon_data = None browser_step(builder=builder, meta=meta, verifier=verifier, lets_connect=lets_connect) break else: # cancel or close logger.info("cancel or close button pressed (response {})".format(response)) dialog.hide() return
def activate_connection( meta, builder, verifier, lets_connect): # type: (Metadata, Gtk.builder, str, bool) -> None """do the actual connecting action""" logger.info("Connecting to {}".format(meta.display_name)) disconnect_all() _, name = get_brand(lets_connect) notification = init_notify(lets_connect) notify(notification, "{} connecting...".format(name), "Connecting to '{}'".format(meta.display_name)) try: if not meta.token: logger.error("metadata for {} doesn't contain oauth2 token".format( meta.uuid)) connect_provider(meta.uuid) else: oauth = oauth_from_token(meta=meta, lets_connect=lets_connect) thread_helper(lambda: _auth_check( oauth, meta, verifier, builder, lets_connect=lets_connect)) except Exception as e: switch = builder.get_object('connect-switch') GLib.idle_add(switch.set_active, False) window = builder.get_object('eduvpn-window') error_helper(window, "can't enable connection", "{}: {}".format(type(e).__name__, str(e))) raise
def _background(meta, oauth, builder, config_dict, lets_connect): # type: (Metadata, str, Gtk.builder, dict, bool) -> None window = builder.get_object('eduvpn-window') try: info = user_info(oauth, meta.api_base_uri) meta.user_id = info['user_id'] except Exception as e: error = e GLib.idle_add(lambda: error_helper(window, "Can't fetch user info", str(error))) raise if info['is_disabled']: GLib.idle_add(lambda: error_helper(window, "This account has been disabled", "")) if info['two_factor_enrolled']: # Multiple 2fa can be enrolled, but we only support one. meta.username = info['two_factor_enrolled_with'][0] GLib.idle_add(lambda: finalizing_step(meta=meta, builder=builder, config_dict=config_dict, lets_connect=lets_connect)) else: if len(meta.two_factor_method) == 0: logger.info("no two factor auth enabled on server") GLib.idle_add(lambda: finalizing_step(meta=meta, builder=builder, config_dict=config_dict, lets_connect=lets_connect)) elif len(meta.two_factor_method) > 1: logger.info("Multi two factor methods available") GLib.idle_add(lambda: _choice_window(options=meta.two_factor_method, meta=meta, oauth=oauth, builder=builder, config_dict=config_dict, lets_connect=lets_connect)) else: GLib.idle_add(lambda: _enroll(oauth=oauth, meta=meta, builder=builder, config_dict=config_dict, lets_connect=lets_connect))
def delete_profile(builder, lets_connect): # type: (Gtk.builder, bool) -> None """called when the user presses the - button""" logger.info("delete provider clicked") meta = metadata_of_selected(builder) if not meta: logger.info("nothing selected") return window = builder.get_object('eduvpn-window') dialog = Gtk.MessageDialog( window, Gtk.DialogFlags.MODAL, Gtk.MessageType.WARNING, Gtk.ButtonsType.YES_NO, "Are you sure you want to remove '{}'?".format(meta.display_name)) dialog.format_secondary_text("This action can't be undone.") response = dialog.run() if response == Gtk.ResponseType.YES: logger.info("deleting provider config") try: delete_provider(meta.uuid) notification = init_notify(lets_connect) notify(notification, "eduVPN provider deleted", "Deleted '{}'".format(meta.display_name)) except Exception as e: error_helper(window, "can't delete profile", str(e)) dialog.hide() raise GLib.idle_add( lambda: refresh_start(builder, lets_connect=lets_connect)) elif response == Gtk.ResponseType.NO: logger.info("not deleting provider config") dialog.hide()
def _background(meta, oauth, dialog, builder): try: cert, key = create_keypair(oauth, meta.api_base_uri) meta.cert = cert meta.key = key meta.config = get_profile_config(oauth, meta.api_base_uri, meta.profile_id) except Exception as e: GLib.idle_add( lambda: error_helper(dialog, "can't finalize configuration", "{}: {}".format(type(e).__name__, str(e)))) GLib.idle_add(lambda: dialog.hide()) raise else: try: uuid = store_provider(meta) monitor_vpn( uuid=uuid, callback=lambda *args, **kwargs: vpn_change(builder=builder)) GLib.idle_add( lambda: notify("eduVPN provider added", "added provider '{}'". format(meta.display_name))) except Exception as e: GLib.idle_add( lambda: error_helper(dialog, "can't store configuration", "{} {}".format(type(e).__name__, str(e)))) GLib.idle_add(lambda: dialog.hide()) raise else: GLib.idle_add(lambda: dialog.hide()) GLib.idle_add(lambda: update_providers(builder))
def _phase1_background(meta, dialog, verifier, builder): try: logger.info("starting token obtaining in background") r = get_instance_info(instance_uri=meta.instance_base_uri, verifier=verifier) meta.api_base_uri, meta.authorization_endpoint, meta.token_endpoint = r except Exception as e: GLib.idle_add(lambda: error_helper(dialog, "Can't fetch instance info", "{}".format(str(e)))) GLib.idle_add(lambda: dialog.hide()) raise meta.refresh_token() if not meta.token: code_verifier = gen_code_verifier() port = get_open_port() try: oauth = create_oauth_session(port, auto_refresh_url=meta.token_endpoint) auth_url = get_auth_url(oauth, code_verifier, meta.authorization_endpoint) except Exception as e: GLib.idle_add(lambda: error_helper( dialog, "Can't create oauth session", "{}".format(str(e)))) GLib.idle_add(lambda: dialog.hide()) raise else: GLib.idle_add(lambda: _phase1_callback( meta, port, code_verifier, oauth, auth_url, dialog, builder)) else: logger.info("we already have a token, skipping browser step") oauth = oauth_from_token(meta=meta) GLib.idle_add(lambda: _phase2_callback( meta=meta, oauth=oauth, dialog=dialog, builder=builder))
def switched(meta, builder, verifier, lets_connect): # type: (Metadata, Gtk.builder, str, bool) -> None switch = builder.get_object('connect-switch') state = switch.get_active() logger.info(u"switch activated, old state {}".format(state)) if not state: logger.info(u"setting switch ON") GLib.idle_add(lambda: switch.set_active(True)) activate_connection(meta=meta, builder=builder, verifier=verifier, lets_connect=lets_connect) else: notification = init_notify(lets_connect) notify(notification, u"eduVPN disconnecting...", u"Disconnecting from {}".format(meta.display_name)) logger.info(u"setting switch OFF") GLib.idle_add(lambda: switch.set_active(False)) try: disconnect_provider(meta.uuid) except Exception as e: window = builder.get_object('eduvpn-window') error_helper(window, u"can't disconnect", "{}: {}".format(type(e).__name__, str(e))) GLib.idle_add(lambda: switch.set_active(True)) raise
def _phase1_background(meta, dialog, verifier, builder, force_token_refresh, lets_connect): # type: (Metadata, Any, str, Gtk.builder, Optional[bool], bool) -> None try: logger.info(u"starting token obtaining in background") r = get_instance_info(instance_uri=meta.instance_base_uri, verifier=verifier) meta.api_base_uri, meta.authorization_endpoint, meta.token_endpoint = r # type: ignore except Exception as e: error = e GLib.idle_add(lambda: error_helper(dialog, "Can't fetch instance info", "{}".format(str(error)))) GLib.idle_add(lambda: dialog.hide()) raise meta.refresh_token() if not meta.token and not force_token_refresh: # lets see if other profiles already have a token we can use token = reuse_token_from_base_uri(meta.instance_base_uri) if token: meta.token = token if not meta.token: code_verifier = gen_code_verifier() port = get_open_port() try: oauth = create_oauth_session(port, lets_connect=lets_connect, auto_refresh_url=meta.token_endpoint) auth_url, state = get_auth_url( oauth, code_verifier, meta.authorization_endpoint) # type: ignore except Exception as e: error = e GLib.idle_add(lambda: error_helper( dialog, "Can't create oauth session", "{}".format(str(error)))) GLib.idle_add(lambda: dialog.hide()) raise else: GLib.idle_add(lambda: _phase1_callback(meta, port, code_verifier, oauth, auth_url, dialog, builder, state, lets_connect=lets_connect)) else: logger.info(u"we already have a token, skipping browser step") oauth = oauth_from_token(meta=meta, lets_connect=lets_connect) GLib.idle_add(lambda: _phase2_callback(meta=meta, oauth=oauth, dialog=dialog, builder=builder, lets_connect=lets_connect))
def _phase2_background(meta, port, oauth, code_verifier, auth_url, dialog, builder, state, lets_connect): # type: (Metadata, int, Any, str, str, Any, Gtk.builder, str, bool) -> None session = random() logger.info("opening browser with url {}".format(auth_url)) try: webbrowser.open(auth_url) dialog.session = session code, other_state = get_oauth_token_code(port, lets_connect=lets_connect, timeout=120) logger.info("control returned by browser") if state != other_state: logger.error("received from state, expected: {}, received: {}".format(state, other_state)) raise Exception("oauth state has been tampered with") logger.info("setting oauth token for metadata") meta.token = oauth.fetch_token(meta.token_endpoint, code=code, code_verifier=code_verifier, client_id=oauth.client_id, include_client_id=True) except Exception as e: error = e if dialog.get_property("visible") and dialog.session == session: GLib.idle_add(lambda: error_helper(dialog, "Can't obtain token", "{}".format(str(error)))) GLib.idle_add(lambda: dialog.hide()) else: logging.error(error) raise else: GLib.idle_add(lambda: _phase2_callback(meta=meta, oauth=oauth, dialog=dialog, builder=builder, lets_connect=lets_connect))
def _background(oauth, meta, builder, dialog, lets_connect): # type: (str, Metadata, Gtk.builder, Any, bool) -> None try: profiles = list_profiles(oauth, meta.api_base_uri) logger.info("There are {} profiles on {}".format( len(profiles), meta.api_base_uri)) if len(profiles) > 1: GLib.idle_add(lambda: dialog.hide()) GLib.idle_add( lambda: _select_profile_step(builder=builder, profiles=profiles, meta=meta, oauth=oauth, lets_connect=lets_connect)) elif len(profiles) == 1: _parse_choice(builder, meta, oauth, profiles[0], lets_connect=lets_connect) else: raise EduvpnException( "Either there are no VPN profiles defined, or this account does not have the " "required permissions to create a new VPN configurations for any of the " "available profiles.") except Exception as e: error = str(e) GLib.idle_add( lambda: error_helper(dialog, "Can't fetch profile list", error)) GLib.idle_add(lambda: dialog.hide()) raise
def custom_url(builder, meta, verifier): """the custom URL dialog where a user can enter a custom instance URL""" dialog = builder.get_object('custom-url-dialog') entry = builder.get_object('custom-url-entry') dialog.show_all() while True: response = dialog.run() if response == 1: url = entry.get_text() logger.info("ok pressed, entry text: {}".format(url)) if not url.startswith('https://'): GLib.idle_add(lambda: error_helper( dialog, "Invalid URL", "URL should start with https://")) else: GLib.idle_add(lambda: dialog.hide()) meta.display_name = url[8:].split('/')[0] logger.info("using {} for display name".format( meta.display_name)) meta.instance_base_uri = url meta.connection_type = 'Custom Instance' meta.authorization_type = 'local' meta.icon_data = None GLib.idle_add(lambda: browser_step( builder=builder, meta=meta, verifier=verifier)) break else: # cancel or close logger.info("cancel or close button pressed (response {})".format( response)) dialog.hide() return
def _background(meta, oauth, dialog, builder, lets_connect): try: cert, key = create_keypair(oauth, meta.api_base_uri) meta.cert = cert meta.key = key meta.config = get_profile_config(oauth, meta.api_base_uri, meta.profile_id) ovpn_text = format_like_ovpn(meta.config, meta.cert, meta.key) config_dict = parse_ovpn(ovpn_text) if meta.two_factor: GLib.idle_add(lambda: two_auth_step(builder, oauth, meta, config_dict=config_dict, lets_connect=lets_connect)) else: GLib.idle_add(lambda: finalizing_step(meta=meta, builder=builder, config_dict=config_dict, lets_connect=lets_connect)) GLib.idle_add(lambda: dialog.hide()) except Exception as e: error = e GLib.idle_add(lambda: error_helper( dialog, "can't finalize configuration", "{}: {}".format( type(error).__name__, str(error)))) GLib.idle_add(lambda: dialog.hide()) raise
def _background(meta, builder, verifier, lets_connect): # type: (Metadata, Gtk.builder, str, bool) -> None label = builder.get_object('messages-label') window = builder.get_object('eduvpn-window') try: oauth = oauth_from_token(meta=meta, lets_connect=lets_connect) except Exception as e: error = e GLib.idle_add(lambda: error_helper( window, "Can't reconstruct OAuth2 session", (str(error)))) print(meta) raise text = "" try: messages_user = list(user_messages(oauth, meta.api_base_uri)) messages_system = list(system_messages(oauth, meta.api_base_uri)) info = user_info(oauth, meta.api_base_uri) except EduvpnAuthException: GLib.idle_add(lambda: reauth(meta=meta, verifier=verifier, builder=builder, lets_connect=lets_connect)) except Exception as e: error = e GLib.idle_add(lambda: error_helper(window, "Can't fetch user messages", str(error))) raise else: if info['is_disabled']: GLib.idle_add(lambda: error_helper( window, "This account has been disabled", "")) for date_time, type_, message in messages_user: logger.info("user message at {}: {}".format(date_time, message)) text += "<b><big>{}</big></b>\n".format(date_time) text += "<small><i>user, {}</i></small>\n".format(type_) text += "{}\n\n".format(message) for date_time, type_, message in messages_system: logger.info("system message at {}: {}".format(date_time, message)) text += "<b><big>{}</big></b>\n".format(date_time) text += "<small><i>system, {}</i></small>\n".format(type_) text += "{}\n\n".format(message) GLib.idle_add(lambda: label.set_markup(text))
def switched(meta, builder): switch = builder.get_object('connect-switch') state = switch.get_active() logger.info("switch activated, old state {}".format(state)) if not state: logger.info("setting switch ON") GLib.idle_add(lambda: switch.set_active(True)) activate_connection(meta=meta, builder=builder) else: notify("eduVPN disconnecting...", "Disconnecting from {}".format(meta.display_name)) logger.info("setting switch OFF") GLib.idle_add(lambda: switch.set_active(False)) try: disconnect_provider(meta.uuid) except Exception as e: window = builder.get_object('eduvpn-window') error_helper(window, "can't disconnect", "{}: {}".format(type(e).__name__, str(e))) GLib.idle_add(lambda: switch.set_active(True)) raise
def _fetch_background(dialog, meta, verifier, builder): try: authorization_type, instances = get_instances( discovery_uri=meta.discovery_uri, verifier=verifier) except Exception as e: GLib.idle_add(lambda: error_helper(dialog, "can't fetch instances", "{} {}".format(type(e), str(e)))) GLib.idle_add(lambda: dialog.hide()) raise else: GLib.idle_add(lambda: dialog.hide()) meta.authorization_type = authorization_type GLib.idle_add(lambda: select_instance_step( meta, instances, builder=builder, verifier=verifier))
def background(meta, oauth, builder): window = builder.get_object('eduvpn-window') try: info = user_info(oauth, meta.api_base_uri) except Exception as e: GLib.idle_add( lambda: error_helper(window, "Can't fetch user info", str(e))) raise if info['is_disabled']: GLib.idle_add( lambda: error_helper(window, "This account has been disabled", "")) if not info['two_factor_enrolled']: logger.info("no two factor auth enabled") GLib.idle_add( lambda: finalizing_step(oauth=oauth, meta=meta, builder=builder)) elif 'two_factor_enrolled_with' in info: options = info['two_factor_enrolled_with'] if len(options) > 1: GLib.idle_add(lambda: choice_window( options=options, meta=meta, oauth=oauth, builder=builder)) return elif len(options) == 1: logger.info( "selection only one two-factor auth methods available ({})". format(options[0])) meta.username = options[0] else: GLib.idle_add(lambda: error_helper( window, "two_factor_enrolled_with' doesn't contain any fields", "")) GLib.idle_add( lambda: finalizing_step(oauth=oauth, meta=meta, builder=builder))
def _background(meta, dialog, builder, config_dict, lets_connect): try: uuid = store_provider(meta, config_dict) monitor_vpn(uuid=uuid, callback=lambda *args, **kwargs: vpn_change(builder=builder, lets_connect=lets_connect)) notification = init_notify(lets_connect) GLib.idle_add(lambda: notify(notification, "eduVPN provider added", "added provider '{}'".format(meta.display_name))) except Exception as e: error = e GLib.idle_add(lambda: error_helper(dialog, "can't store configuration", "{}: {}".format(type(error).__name__, str(error)))) GLib.idle_add(lambda: dialog.hide()) raise else: GLib.idle_add(lambda: dialog.hide()) GLib.idle_add(lambda: refresh_start(builder, lets_connect=lets_connect))
def _fetch_background(meta, verifier, builder, lets_connect): # type: (Metadata, str, Gtk.builder, bool) -> None dialog = builder.get_object('fetch-dialog') window = builder.get_object('eduvpn-window') try: authorization_type, instances = get_instances(discovery_uri=meta.discovery_uri, verifier=verifier) except Exception as e: error = e GLib.idle_add(lambda: dialog.hide()) GLib.idle_add(lambda: error_helper(window, "can't fetch instances", "{} {}".format(type(error), str(error)))) raise else: GLib.idle_add(lambda: dialog.hide()) meta.authorization_type = authorization_type GLib.idle_add(lambda: select_instance_step(meta, instances, builder=builder, verifier=verifier, lets_connect=lets_connect))
def _auth_check(oauth, meta, verifier, builder, lets_connect): """quickly see if the can fetch messages, otherwise reauth""" try: info = user_info(oauth, meta.api_base_uri) _cert_check(meta, oauth, builder, info) except EduvpnAuthException: GLib.idle_add(lambda: reauth(meta=meta, verifier=verifier, builder=builder, lets_connect=lets_connect)) except Exception as e: error = e window = builder.get_object('eduvpn-window') GLib.idle_add(lambda: error_helper( window, "Can't check account status", "{}".format(str(error)))) raise
def _phase2_background(meta, port, oauth, code_verifier, auth_url, dialog, builder): try: logger.info("opening browser with url {}".format(auth_url)) webbrowser.open(auth_url) code = get_oauth_token_code(port) logger.info("control returned by browser") logger.info("setting oauth token for metadata") meta.token = oauth.fetch_token(meta.token_endpoint, code=code, code_verifier=code_verifier) except Exception as e: GLib.idle_add(lambda: error_helper(dialog, "Can't obtain token", "{}". format(str(e)))) GLib.idle_add(lambda: dialog.hide()) raise else: GLib.idle_add(lambda: _phase2_callback( meta=meta, oauth=oauth, dialog=dialog, builder=builder))
def _background(oauth, meta, builder, dialog): try: profiles = list_profiles(oauth, meta.api_base_uri) logger.info("There are {} profiles on {}".format(len(profiles), meta.api_base_uri)) if len(profiles) > 1: GLib.idle_add(lambda: dialog.hide()) GLib.idle_add(lambda: select_profile_step(builder=builder, profiles=profiles, meta=meta, oauth=oauth)) elif len(profiles) == 1: meta.profile_display_name, meta.profile_id, meta.two_factor = profiles[0] two_auth_step(builder=builder, oauth=oauth, meta=meta) else: raise EduvpnException("Either there are no VPN profiles defined, or this account does not have the " "required permissions to create a new VPN configurations for any of the " "available profiles.") except Exception as e: GLib.idle_add(lambda: error_helper(dialog, "Can't fetch profile list", str(e))) GLib.idle_add(lambda: dialog.hide()) raise
def test_error_helper(self, *_): error_helper(None, None, None)