def page(self) -> CBORPageResult: assert user.id is not None user.need_permission("general.manage_2fa") raw_data = request.get_data() logger.debug("Raw request: %r", raw_data) data: dict[str, object] = cbor.decode(raw_data) client_data = ClientData(data["clientDataJSON"]) att_obj = AttestationObject(data["attestationObject"]) logger.debug("Client data: %r", client_data) logger.debug("Attestation object: %r", att_obj) auth_data = make_fido2_server().register_complete( session.session_info.webauthn_action_state, client_data, att_obj ) ident = auth_data.credential_data.credential_id.hex() credentials = load_two_factor_credentials(user.id, lock=True) if ident in credentials["webauthn_credentials"]: raise MKGeneralException(_("Your WebAuthn credetial is already in use")) credentials["webauthn_credentials"][ident] = WebAuthnCredential( { "credential_id": ident, "registered_at": int(time.time()), "alias": "", "credential_data": bytes(auth_data.credential_data), } ) save_two_factor_credentials(user.id, credentials) flash(_("Registration successful")) return {"status": "OK"}
def _action(self) -> None: assert user.id is not None credentials = load_two_factor_credentials(user.id, lock=True) credential_id = request.get_ascii_input_mandatory("_edit") credential = credentials["webauthn_credentials"].get(credential_id) if credential is None: raise MKUserError("_edit", _("The credential does not exist")) vs = self._valuespec(credential) settings = vs.from_html_vars("profile") vs.validate_value(settings, "profile") credential["alias"] = settings["alias"] save_two_factor_credentials(user.id, credentials) flash(_("Successfully changed the credential.")) # In distributed setups with remote sites where the user can login, start the # user profile replication now which will redirect the user to the destination # page after completion. Otherwise directly open up the destination page. origtarget = "user_two_factor_overview.py" if user.authorized_login_sites(): raise redirect( makeuri_contextless(request, [("back", origtarget)], filename="user_profile_replicate.py")) raise redirect(origtarget)
def _action(self) -> None: assert user.id is not None credentials = load_two_factor_credentials(user.id) if credential_id := request.get_ascii_input("_delete"): if credential_id not in credentials["webauthn_credentials"]: return del credentials["webauthn_credentials"][credential_id] save_two_factor_credentials(user.id, credentials) flash(_("Credential has been deleted"))
def test_is_two_factor_backup_code_valid_matches(user_id) -> None: display_codes, store_codes = userdb.make_two_factor_backup_codes() credentials = userdb.load_two_factor_credentials(user_id) credentials["backup_codes"] = store_codes assert len(credentials["backup_codes"]) == 10 userdb.save_two_factor_credentials(user_id, credentials) assert userdb.is_two_factor_backup_code_valid(user_id, display_codes[3]) is True credentials = userdb.load_two_factor_credentials(user_id) assert len(credentials["backup_codes"]) == 9
def test_is_two_factor_backup_code_valid_matches(user_id: UserId) -> None: codes = userdb.make_two_factor_backup_codes(rounds=5) credentials = userdb.load_two_factor_credentials(user_id) credentials["backup_codes"] = [pwhashed for _password, pwhashed in codes] userdb.save_two_factor_credentials(user_id, credentials) assert len(credentials["backup_codes"]) == 10 valid = userdb.is_two_factor_backup_code_valid(user_id, codes[3][0]) assert valid credentials = userdb.load_two_factor_credentials(user_id) assert len(credentials["backup_codes"]) == 9
class UserTwoFactorOverview(ABCUserProfilePage): def _page_title(self) -> str: return _("Two-factor authentication") def __init__(self) -> None: super().__init__("general.manage_2fa") def _action(self) -> None: assert user.id is not None credentials = load_two_factor_credentials(user.id) if credential_id := request.get_ascii_input("_delete"): if credential_id not in credentials["webauthn_credentials"]: return del credentials["webauthn_credentials"][credential_id] save_two_factor_credentials(user.id, credentials) flash(_("Credential has been deleted")) if request.has_var("_backup_codes"): display_codes, credentials[ "backup_codes"] = make_two_factor_backup_codes() save_two_factor_credentials(user.id, credentials) flash( _("The following backup codes have been generated: <ul>%s</ul> These codes are " "displayed only now. Save them securely.") % "".join(f"<li><tt>{c}</tt></li>" for c in display_codes))
def page(self) -> CBORPageResult: assert user.id is not None user.need_permission("general.manage_2fa") raw_data = request.get_data() logger.debug("Raw request: %r", raw_data) data: dict[str, object] = cbor.decode(raw_data) client_data = ClientData(data["clientDataJSON"]) att_obj = AttestationObject(data["attestationObject"]) logger.debug("Client data: %r", client_data) logger.debug("Attestation object: %r", att_obj) try: auth_data = make_fido2_server().register_complete( session.session_info.webauthn_action_state, client_data, att_obj) except ValueError as e: if "Invalid origin in ClientData" in str(e): raise MKGeneralException( "The origin %r is not valid. You need to access the UI via HTTPS " "and you need to use a valid host or domain name. See werk #13325 for " "further information" % client_data.get("origin")) from e raise ident = auth_data.credential_data.credential_id.hex() credentials = load_two_factor_credentials(user.id, lock=True) if ident in credentials["webauthn_credentials"]: raise MKGeneralException( _("Your WebAuthn credential is already in use")) credentials["webauthn_credentials"][ident] = WebAuthnCredential({ "credential_id": ident, "registered_at": int(time.time()), "alias": "", "credential_data": bytes(auth_data.credential_data), }) save_two_factor_credentials(user.id, credentials) flash(_("Registration successful")) return {"status": "OK"}
def test_disable_two_factor_authentication(user_id: UserId) -> None: credentials = userdb.TwoFactorCredentials({ "webauthn_credentials": { "id": userdb.WebAuthnCredential({ "credential_id": "id", "registered_at": 1337, "alias": "Steckding", "credential_data": b"whatever", }), }, "backup_codes": [], }) userdb.save_two_factor_credentials(user_id, credentials) assert userdb.is_two_factor_login_enabled(user_id) is True userdb.disable_two_factor_authentication(user_id) assert userdb.is_two_factor_login_enabled(user_id) is False
def test_save_two_factor_credentials(user_id: UserId) -> None: credentials = userdb.TwoFactorCredentials({ "webauthn_credentials": { "id": userdb.WebAuthnCredential({ "credential_id": "id", "registered_at": 1337, "alias": "Steckding", "credential_data": b"whatever", }), }, "backup_codes": [ "asdr2ar2a2ra2rara2", "dddddddddddddddddd", ], }) userdb.save_two_factor_credentials(user_id, credentials) assert userdb.load_two_factor_credentials(user_id) == credentials