From f2f6e13af74d8a199906bae3710f08a25b9035ea Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Sun, 17 May 2020 22:05:37 -0700 Subject: [PATCH 01/21] DB & Setup ready for multi-keys --- .../templates/dashboard/fido_setup.html | 2 ++ app/dashboard/views/fido_setup.py | 14 ++++++++++---- app/models.py | 9 ++++++--- server.py | 17 +++++++++++++++++ static/assets/js/vendors/webauthn.js | 11 +++++++++-- 5 files changed, 44 insertions(+), 9 deletions(-) diff --git a/app/dashboard/templates/dashboard/fido_setup.html b/app/dashboard/templates/dashboard/fido_setup.html index f82b8895..8770fd18 100644 --- a/app/dashboard/templates/dashboard/fido_setup.html +++ b/app/dashboard/templates/dashboard/fido_setup.html @@ -33,6 +33,8 @@ JSON.parse('{{credential_create_options|tojson|safe}}') ) + console.log(pkCredentialCreateOptions) + let credential try { credential = await navigator.credentials.create({ diff --git a/app/dashboard/views/fido_setup.py b/app/dashboard/views/fido_setup.py index bf1b00fd..50ffe3b2 100644 --- a/app/dashboard/views/fido_setup.py +++ b/app/dashboard/views/fido_setup.py @@ -12,6 +12,7 @@ from app.config import RP_ID, URL from app.dashboard.base import dashboard_bp from app.extensions import db from app.log import LOG +from app.models import FIDO class FidoTokenForm(FlaskForm): @@ -21,10 +22,6 @@ class FidoTokenForm(FlaskForm): @dashboard_bp.route("/fido_setup", methods=["GET", "POST"]) @login_required def fido_setup(): - if current_user.fido_enabled(): - flash("You have already registered your security key", "warning") - return redirect(url_for("dashboard.index")) - if not current_user.can_use_fido: flash( "This feature is currently in invitation-only beta. Please send us an email if you want to try", @@ -32,6 +29,8 @@ def fido_setup(): ) return redirect(url_for("dashboard.index")) + fido_model = FIDO.filter_by(uuid=current_user.fido_uuid).all() + fido_token_form = FidoTokenForm() # Handling POST requests @@ -91,6 +90,13 @@ def fido_setup(): registration_dict = credential_create_options.registration_dict del registration_dict["extensions"]["webauthn.loc"] + for record in fido_model: + registration_dict["excludeCredentials"].append({ + 'type': 'public-key', + 'id': record.credential_id, + 'transports': ['usb', 'nfc', 'ble', 'internal'], + }) + session["fido_uuid"] = fido_uuid session["fido_challenge"] = challenge.rstrip("=") diff --git a/app/models.py b/app/models.py index 7ce2f710..72747c50 100644 --- a/app/models.py +++ b/app/models.py @@ -119,6 +119,12 @@ class AliasGeneratorEnum(EnumE): word = 1 # aliases are generated based on random words uuid = 2 # aliases are generated based on uuid +class FIDO(db.Model, ModelMixin): + __tablename__ = "fido" + credential_id = db.Column(db.String(), nullable=False, unique=True, index=True) + uuid = db.Column(db.ForeignKey("users.fido_uuid", ondelete="cascade"), unique=False, nullable=False) + public_key = db.Column(db.String(), nullable=False, unique=True) + sign_count = db.Column(db.Integer(), nullable=False) class User(db.Model, ModelMixin, UserMixin): __tablename__ = "users" @@ -150,9 +156,6 @@ class User(db.Model, ModelMixin, UserMixin): # Fields for WebAuthn fido_uuid = db.Column(db.String(), nullable=True, unique=True) - fido_credential_id = db.Column(db.String(), nullable=True, unique=True) - fido_pk = db.Column(db.String(), nullable=True, unique=True) - fido_sign_count = db.Column(db.Integer(), nullable=True) # whether user can use Fido can_use_fido = db.Column( diff --git a/server.py b/server.py index d29727a7..2576e042 100644 --- a/server.py +++ b/server.py @@ -38,6 +38,7 @@ from app.log import LOG from app.models import ( Client, User, + FIDO, ClientUser, Alias, RedirectUri, @@ -142,8 +143,24 @@ def fake_data(): otp_secret="base32secret3232", can_use_fido=True, intro_shown=True, + fido_uuid="59576167-6c37-4d67-943b-4683b24ff821", ) db.session.commit() + + fido = FIDO.create( + credential_id = "umR9q5vX61XG7vh7gi8wT0gJ9LkYwHKSzDL5vhtZs3o", + uuid = "59576167-6c37-4d67-943b-4683b24ff821", + public_key = "pQECAyYgASFYIEjQg3TOuUZJxylLE6gJDNHcNyYVW5hOAZ-vGOY9I_TDIlggfJqIh07bj3n6RVmrEsuozsYPYM6VeJKCeduz0DFp8AY", + sign_count = 1, + ) + fido = FIDO.create( + credential_id = "1mR9q5vX61XG7vh7gi8wT0gJ9LkYwHKSzDL5vhtZs3o", + uuid = "59576167-6c37-4d67-943b-4683b24ff821", + public_key = "1QECAyYgASFYIEjQg3TOuUZJxylLE6gJDNHcNyYVW5hOAZ-vGOY9I_TDIlggfJqIh07bj3n6RVmrEsuozsYPYM6VeJKCeduz0DFp8AY", + sign_count = 1, + ) + db.session.commit() + user.trial_end = None LifetimeCoupon.create(code="coupon", nb_used=10) diff --git a/static/assets/js/vendors/webauthn.js b/static/assets/js/vendors/webauthn.js index 85a8ac22..5604a41f 100644 --- a/static/assets/js/vendors/webauthn.js +++ b/static/assets/js/vendors/webauthn.js @@ -56,7 +56,7 @@ const transformCredentialRequestOptions = ( const transformCredentialCreateOptions = ( credentialCreateOptionsFromServer ) => { - let { challenge, user } = credentialCreateOptionsFromServer; + let { challenge, user, excludeCredentials } = credentialCreateOptionsFromServer; user.id = Uint8Array.from( atob( credentialCreateOptionsFromServer.user.id @@ -75,10 +75,17 @@ const transformCredentialCreateOptions = ( (c) => c.charCodeAt(0) ); + excludeCredentials = excludeCredentials.map((credentialDescriptor) => { + let { id } = credentialDescriptor; + id = id.replace(/\_/g, "/").replace(/\-/g, "+"); + id = Uint8Array.from(atob(id), (c) => c.charCodeAt(0)); + return Object.assign({}, credentialDescriptor, { id }); + }); + const transformedCredentialCreateOptions = Object.assign( {}, credentialCreateOptionsFromServer, - { challenge, user } + { challenge, user, excludeCredentials } ); return transformedCredentialCreateOptions; From 9fb91c83e7673e5e5957fe14b4938156f13b92de Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Mon, 18 May 2020 00:01:27 -0700 Subject: [PATCH 02/21] more setup --- app/dashboard/views/fido_setup.py | 23 +++++++++++++++++------ server.py | 4 ++-- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/app/dashboard/views/fido_setup.py b/app/dashboard/views/fido_setup.py index 50ffe3b2..a70bc8ee 100644 --- a/app/dashboard/views/fido_setup.py +++ b/app/dashboard/views/fido_setup.py @@ -29,7 +29,10 @@ def fido_setup(): ) return redirect(url_for("dashboard.index")) - fido_model = FIDO.filter_by(uuid=current_user.fido_uuid).all() + if current_user.fido_uuid is not None: + fido_model = FIDO.filter_by(uuid=current_user.fido_uuid).all() + else: + fido_model = [] fido_token_form = FidoTokenForm() @@ -59,18 +62,25 @@ def fido_setup(): LOG.error(f"An error occurred in WebAuthn registration process: {e}") flash("Key registration failed.", "warning") return redirect(url_for("dashboard.index")) + + if current_user.fido_uuid is None: + current_user.fido_uuid = fido_uuid - current_user.fido_pk = str(fido_credential.public_key, "utf-8") - current_user.fido_uuid = fido_uuid - current_user.fido_sign_count = fido_credential.sign_count - current_user.fido_credential_id = str(fido_credential.credential_id, "utf-8") + FIDO.create( + credential_id = str(fido_credential.credential_id, "utf-8"), + uuid = fido_uuid, + public_key = str(fido_credential.public_key, "utf-8"), + sign_count = fido_credential.sign_count, + ) db.session.commit() + LOG.d(f"credential_id={str(fido_credential.credential_id, 'utf-8')} added for {fido_uuid}") + flash("Security key has been activated", "success") return redirect(url_for("dashboard.recovery_code_route")) # Prepare information for key registration process - fido_uuid = str(uuid.uuid4()) + fido_uuid = str(uuid.uuid4()) if current_user.fido_uuid is None else current_user.fido_uuid challenge = secrets.token_urlsafe(32) credential_create_options = webauthn.WebAuthnMakeCredentialOptions( @@ -90,6 +100,7 @@ def fido_setup(): registration_dict = credential_create_options.registration_dict del registration_dict["extensions"]["webauthn.loc"] + # Prevent user from adding duplicated keys for record in fido_model: registration_dict["excludeCredentials"].append({ 'type': 'public-key', diff --git a/server.py b/server.py index 2576e042..e15fd372 100644 --- a/server.py +++ b/server.py @@ -147,13 +147,13 @@ def fake_data(): ) db.session.commit() - fido = FIDO.create( + FIDO.create( credential_id = "umR9q5vX61XG7vh7gi8wT0gJ9LkYwHKSzDL5vhtZs3o", uuid = "59576167-6c37-4d67-943b-4683b24ff821", public_key = "pQECAyYgASFYIEjQg3TOuUZJxylLE6gJDNHcNyYVW5hOAZ-vGOY9I_TDIlggfJqIh07bj3n6RVmrEsuozsYPYM6VeJKCeduz0DFp8AY", sign_count = 1, ) - fido = FIDO.create( + FIDO.create( credential_id = "1mR9q5vX61XG7vh7gi8wT0gJ9LkYwHKSzDL5vhtZs3o", uuid = "59576167-6c37-4d67-943b-4683b24ff821", public_key = "1QECAyYgASFYIEjQg3TOuUZJxylLE6gJDNHcNyYVW5hOAZ-vGOY9I_TDIlggfJqIh07bj3n6RVmrEsuozsYPYM6VeJKCeduz0DFp8AY", From 2b8febe0b9ab3d7f4049a5e30305e9749fce501b Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Mon, 18 May 2020 00:06:24 -0700 Subject: [PATCH 03/21] black --- app/dashboard/views/fido_setup.py | 30 ++++++++++++++++++------------ app/models.py | 8 +++++++- server.py | 16 ++++++++-------- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/app/dashboard/views/fido_setup.py b/app/dashboard/views/fido_setup.py index a70bc8ee..14c1b471 100644 --- a/app/dashboard/views/fido_setup.py +++ b/app/dashboard/views/fido_setup.py @@ -62,25 +62,29 @@ def fido_setup(): LOG.error(f"An error occurred in WebAuthn registration process: {e}") flash("Key registration failed.", "warning") return redirect(url_for("dashboard.index")) - + if current_user.fido_uuid is None: current_user.fido_uuid = fido_uuid FIDO.create( - credential_id = str(fido_credential.credential_id, "utf-8"), - uuid = fido_uuid, - public_key = str(fido_credential.public_key, "utf-8"), - sign_count = fido_credential.sign_count, + credential_id=str(fido_credential.credential_id, "utf-8"), + uuid=fido_uuid, + public_key=str(fido_credential.public_key, "utf-8"), + sign_count=fido_credential.sign_count, ) db.session.commit() - LOG.d(f"credential_id={str(fido_credential.credential_id, 'utf-8')} added for {fido_uuid}") + LOG.d( + f"credential_id={str(fido_credential.credential_id, 'utf-8')} added for {fido_uuid}" + ) flash("Security key has been activated", "success") return redirect(url_for("dashboard.recovery_code_route")) # Prepare information for key registration process - fido_uuid = str(uuid.uuid4()) if current_user.fido_uuid is None else current_user.fido_uuid + fido_uuid = ( + str(uuid.uuid4()) if current_user.fido_uuid is None else current_user.fido_uuid + ) challenge = secrets.token_urlsafe(32) credential_create_options = webauthn.WebAuthnMakeCredentialOptions( @@ -102,11 +106,13 @@ def fido_setup(): # Prevent user from adding duplicated keys for record in fido_model: - registration_dict["excludeCredentials"].append({ - 'type': 'public-key', - 'id': record.credential_id, - 'transports': ['usb', 'nfc', 'ble', 'internal'], - }) + registration_dict["excludeCredentials"].append( + { + "type": "public-key", + "id": record.credential_id, + "transports": ["usb", "nfc", "ble", "internal"], + } + ) session["fido_uuid"] = fido_uuid session["fido_challenge"] = challenge.rstrip("=") diff --git a/app/models.py b/app/models.py index 72747c50..4d5f57c3 100644 --- a/app/models.py +++ b/app/models.py @@ -119,13 +119,19 @@ class AliasGeneratorEnum(EnumE): word = 1 # aliases are generated based on random words uuid = 2 # aliases are generated based on uuid + class FIDO(db.Model, ModelMixin): __tablename__ = "fido" credential_id = db.Column(db.String(), nullable=False, unique=True, index=True) - uuid = db.Column(db.ForeignKey("users.fido_uuid", ondelete="cascade"), unique=False, nullable=False) + uuid = db.Column( + db.ForeignKey("users.fido_uuid", ondelete="cascade"), + unique=False, + nullable=False, + ) public_key = db.Column(db.String(), nullable=False, unique=True) sign_count = db.Column(db.Integer(), nullable=False) + class User(db.Model, ModelMixin, UserMixin): __tablename__ = "users" email = db.Column(db.String(256), unique=True, nullable=False) diff --git a/server.py b/server.py index e15fd372..3e20335e 100644 --- a/server.py +++ b/server.py @@ -148,16 +148,16 @@ def fake_data(): db.session.commit() FIDO.create( - credential_id = "umR9q5vX61XG7vh7gi8wT0gJ9LkYwHKSzDL5vhtZs3o", - uuid = "59576167-6c37-4d67-943b-4683b24ff821", - public_key = "pQECAyYgASFYIEjQg3TOuUZJxylLE6gJDNHcNyYVW5hOAZ-vGOY9I_TDIlggfJqIh07bj3n6RVmrEsuozsYPYM6VeJKCeduz0DFp8AY", - sign_count = 1, + credential_id="umR9q5vX61XG7vh7gi8wT0gJ9LkYwHKSzDL5vhtZs3o", + uuid="59576167-6c37-4d67-943b-4683b24ff821", + public_key="pQECAyYgASFYIEjQg3TOuUZJxylLE6gJDNHcNyYVW5hOAZ-vGOY9I_TDIlggfJqIh07bj3n6RVmrEsuozsYPYM6VeJKCeduz0DFp8AY", + sign_count=1, ) FIDO.create( - credential_id = "1mR9q5vX61XG7vh7gi8wT0gJ9LkYwHKSzDL5vhtZs3o", - uuid = "59576167-6c37-4d67-943b-4683b24ff821", - public_key = "1QECAyYgASFYIEjQg3TOuUZJxylLE6gJDNHcNyYVW5hOAZ-vGOY9I_TDIlggfJqIh07bj3n6RVmrEsuozsYPYM6VeJKCeduz0DFp8AY", - sign_count = 1, + credential_id="1mR9q5vX61XG7vh7gi8wT0gJ9LkYwHKSzDL5vhtZs3o", + uuid="59576167-6c37-4d67-943b-4683b24ff821", + public_key="1QECAyYgASFYIEjQg3TOuUZJxylLE6gJDNHcNyYVW5hOAZ-vGOY9I_TDIlggfJqIh07bj3n6RVmrEsuozsYPYM6VeJKCeduz0DFp8AY", + sign_count=1, ) db.session.commit() From ec91d280bb2b8891d76b09879eac70ebb65709a4 Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Mon, 18 May 2020 00:08:06 -0700 Subject: [PATCH 04/21] Verify --- app/auth/views/fido.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/app/auth/views/fido.py b/app/auth/views/fido.py index 472747ac..0b1395ef 100644 --- a/app/auth/views/fido.py +++ b/app/auth/views/fido.py @@ -12,7 +12,7 @@ from app.config import MFA_USER_ID from app.config import RP_ID, URL from app.extensions import db from app.log import LOG -from app.models import User +from app.models import User, FIDO class FidoTokenForm(FlaskForm): @@ -40,16 +40,21 @@ def fido(): next_url = request.args.get("next") - webauthn_user = webauthn.WebAuthnUser( - user.fido_uuid, - user.email, - user.name if user.name else user.email, - False, - user.fido_credential_id, - user.fido_pk, - user.fido_sign_count, - RP_ID, - ) + fido_model = FIDO.filter_by(uuid=user.fido_uuid).all() + webauthn_users = [] + for record in fido_model: + webauthn_users.append( + webauthn.WebAuthnUser( + user.fido_uuid, + user.email, + user.name if user.name else user.email, + False, + record.credential_id, + record.public_key, + record.sign_count, + RP_ID, + ) + ) # Handling POST requests if fido_token_form.validate_on_submit(): @@ -62,7 +67,7 @@ def fido(): challenge = session["fido_challenge"] webauthn_assertion_response = webauthn.WebAuthnAssertionResponse( - webauthn_user, sk_assertion, challenge, URL, uv_required=False + webauthn_users, sk_assertion, challenge, URL, uv_required=False ) try: @@ -94,7 +99,7 @@ def fido(): session["fido_challenge"] = challenge.rstrip("=") webauthn_assertion_options = webauthn.WebAuthnAssertionOptions( - webauthn_user, challenge + webauthn_users, challenge ) webauthn_assertion_options = webauthn_assertion_options.assertion_dict From 419aa95f1f64906abbf8620dd8519e6c808ff4a3 Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Mon, 18 May 2020 01:02:58 -0700 Subject: [PATCH 05/21] more verify --- app/auth/views/fido.py | 51 ++++++++++++++++++++++++++---------------- server.py | 4 ++-- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/app/auth/views/fido.py b/app/auth/views/fido.py index 0b1395ef..a752e962 100644 --- a/app/auth/views/fido.py +++ b/app/auth/views/fido.py @@ -40,21 +40,6 @@ def fido(): next_url = request.args.get("next") - fido_model = FIDO.filter_by(uuid=user.fido_uuid).all() - webauthn_users = [] - for record in fido_model: - webauthn_users.append( - webauthn.WebAuthnUser( - user.fido_uuid, - user.email, - user.name if user.name else user.email, - False, - record.credential_id, - record.public_key, - record.sign_count, - RP_ID, - ) - ) # Handling POST requests if fido_token_form.validate_on_submit(): @@ -66,11 +51,23 @@ def fido(): challenge = session["fido_challenge"] - webauthn_assertion_response = webauthn.WebAuthnAssertionResponse( - webauthn_users, sk_assertion, challenge, URL, uv_required=False - ) - try: + fido_key = FIDO.get_by( + uuid=user.fido_uuid, credential_id=sk_assertion["id"] + ) + webauthn_user = webauthn.WebAuthnUser( + user.fido_uuid, + user.email, + user.name if user.name else user.email, + False, + fido_key.credential_id, + fido_key.public_key, + fido_key.sign_count, + RP_ID, + ) + webauthn_assertion_response = webauthn.WebAuthnAssertionResponse( + webauthn_user, sk_assertion, challenge, URL, uv_required=False + ) new_sign_count = webauthn_assertion_response.verify() except Exception as e: LOG.error(f"An error occurred in WebAuthn verification process: {e}") @@ -98,6 +95,22 @@ def fido(): session["fido_challenge"] = challenge.rstrip("=") + fido_model = FIDO.filter_by(uuid=user.fido_uuid).all() + webauthn_users = [] + for record in fido_model: + webauthn_users.append( + webauthn.WebAuthnUser( + user.fido_uuid, + user.email, + user.name if user.name else user.email, + False, + record.credential_id, + record.public_key, + record.sign_count, + RP_ID, + ) + ) + webauthn_assertion_options = webauthn.WebAuthnAssertionOptions( webauthn_users, challenge ) diff --git a/server.py b/server.py index 3e20335e..a219b23d 100644 --- a/server.py +++ b/server.py @@ -154,9 +154,9 @@ def fake_data(): sign_count=1, ) FIDO.create( - credential_id="1mR9q5vX61XG7vh7gi8wT0gJ9LkYwHKSzDL5vhtZs3o", + credential_id="4SaUPugJ2sAErSnZil6_5_wXIL-Sk6QdFmNm94IL-_g", uuid="59576167-6c37-4d67-943b-4683b24ff821", - public_key="1QECAyYgASFYIEjQg3TOuUZJxylLE6gJDNHcNyYVW5hOAZ-vGOY9I_TDIlggfJqIh07bj3n6RVmrEsuozsYPYM6VeJKCeduz0DFp8AY", + public_key="pQECAyYgASFYILfse0JIp0a7Gz7n1K8b75_OgV-iliKr52FB-BGzv2R2IlggsPcViY5Dma8XooLWUHtx7X2_px0MsFR_7VRMwT2OHKg", sign_count=1, ) db.session.commit() From 35f0c094fe873d1c3568faa4f70108de40c75532 Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Mon, 18 May 2020 01:04:45 -0700 Subject: [PATCH 06/21] black --- app/auth/views/fido.py | 3 +-- app/dashboard/templates/dashboard/fido_setup.html | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/auth/views/fido.py b/app/auth/views/fido.py index a752e962..741573af 100644 --- a/app/auth/views/fido.py +++ b/app/auth/views/fido.py @@ -40,7 +40,6 @@ def fido(): next_url = request.args.get("next") - # Handling POST requests if fido_token_form.validate_on_submit(): try: @@ -110,7 +109,7 @@ def fido(): RP_ID, ) ) - + webauthn_assertion_options = webauthn.WebAuthnAssertionOptions( webauthn_users, challenge ) diff --git a/app/dashboard/templates/dashboard/fido_setup.html b/app/dashboard/templates/dashboard/fido_setup.html index 8770fd18..88318034 100644 --- a/app/dashboard/templates/dashboard/fido_setup.html +++ b/app/dashboard/templates/dashboard/fido_setup.html @@ -33,7 +33,6 @@ JSON.parse('{{credential_create_options|tojson|safe}}') ) - console.log(pkCredentialCreateOptions) let credential try { From f79eb90d2a137c74351b60998f556d738463b9c3 Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Mon, 18 May 2020 02:14:40 -0700 Subject: [PATCH 07/21] sudo mode --- app/dashboard/__init__.py | 1 + .../templates/dashboard/enter_sudo.html | 31 +++++++++++ app/dashboard/views/enter_sudo.py | 55 +++++++++++++++++++ app/dashboard/views/fido_setup.py | 4 +- app/dashboard/views/mfa_cancel.py | 2 + app/dashboard/views/mfa_setup.py | 2 + 6 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 app/dashboard/templates/dashboard/enter_sudo.html create mode 100644 app/dashboard/views/enter_sudo.py diff --git a/app/dashboard/__init__.py b/app/dashboard/__init__.py index 7a6fcabd..16ca9962 100644 --- a/app/dashboard/__init__.py +++ b/app/dashboard/__init__.py @@ -9,6 +9,7 @@ from .views import ( api_key, custom_domain, alias_contact_manager, + enter_sudo, mfa_setup, mfa_cancel, fido_setup, diff --git a/app/dashboard/templates/dashboard/enter_sudo.html b/app/dashboard/templates/dashboard/enter_sudo.html new file mode 100644 index 00000000..934f7aa3 --- /dev/null +++ b/app/dashboard/templates/dashboard/enter_sudo.html @@ -0,0 +1,31 @@ +{% extends 'default.html' %} +{% set active_page = "setting" %} +{% block title %} + SUDO MODE +{% endblock %} + + +{% block default_content %} +
+
+

Entering Sudo Mode

+

+ You are trying to change sensitive settings +

+

+ Please enter the password of your account so that we can ensure it's you. +

+ +
+ {{ password_check_form.csrf_token }} + +
Password
+ + {{ password_check_form.password(class="form-control", autofocus="true") }} + {{ render_field_errors(password_check_form.password) }} + +
+ +
+
+{% endblock %} \ No newline at end of file diff --git a/app/dashboard/views/enter_sudo.py b/app/dashboard/views/enter_sudo.py new file mode 100644 index 00000000..0e08dd42 --- /dev/null +++ b/app/dashboard/views/enter_sudo.py @@ -0,0 +1,55 @@ +from time import time + +from flask import render_template, flash, redirect, url_for, session, request +from flask_login import login_required, current_user +from flask_wtf import FlaskForm +from wtforms import PasswordField, validators +from functools import wraps + +from app.dashboard.base import dashboard_bp +from app.config import DEBUG +from app.log import LOG + + +class LoginForm(FlaskForm): + password = PasswordField("Password", validators=[validators.DataRequired()]) + + +@dashboard_bp.route("/enter_sudo", methods=["GET", "POST"]) +@login_required +def enter_sudo(): + password_check_form = LoginForm() + + if password_check_form.validate_on_submit(): + password = password_check_form.password.data + + if current_user.check_password(password): + session["sudo_time"] = int(time()) + + # User comes to sudo page from another page + next_url = request.args.get("next") + if next_url: + LOG.debug("redirect user to %s", next_url) + return redirect(next_url) + else: + LOG.debug("redirect user to dashboard") + return redirect(url_for("dashboard.index")) + else: + flash("Incorrect password", "warning") + + return render_template( + "dashboard/enter_sudo.html", password_check_form=password_check_form + ) + +def sudo_required(f): + @wraps(f) + def wrap(*args, **kwargs): + # Reset sudo mode in every 20s under dev mode + SUDO_GAP = 900 if not DEBUG else 20 + if "sudo_time" not in session or (time() - int(session["sudo_time"])) > SUDO_GAP: + return redirect( + url_for("dashboard.enter_sudo", next=request.path) + ) + return f(*args, **kwargs) + + return wrap \ No newline at end of file diff --git a/app/dashboard/views/fido_setup.py b/app/dashboard/views/fido_setup.py index 14c1b471..9107c146 100644 --- a/app/dashboard/views/fido_setup.py +++ b/app/dashboard/views/fido_setup.py @@ -1,6 +1,7 @@ import json import secrets import uuid +from time import time import webauthn from flask import render_template, flash, redirect, url_for, session @@ -13,7 +14,7 @@ from app.dashboard.base import dashboard_bp from app.extensions import db from app.log import LOG from app.models import FIDO - +from app.dashboard.views.enter_sudo import sudo_required class FidoTokenForm(FlaskForm): sk_assertion = HiddenField("sk_assertion", validators=[validators.DataRequired()]) @@ -21,6 +22,7 @@ class FidoTokenForm(FlaskForm): @dashboard_bp.route("/fido_setup", methods=["GET", "POST"]) @login_required +@sudo_required def fido_setup(): if not current_user.can_use_fido: flash( diff --git a/app/dashboard/views/mfa_cancel.py b/app/dashboard/views/mfa_cancel.py index d982b571..66c36c2a 100644 --- a/app/dashboard/views/mfa_cancel.py +++ b/app/dashboard/views/mfa_cancel.py @@ -7,6 +7,7 @@ from wtforms import StringField, validators from app.dashboard.base import dashboard_bp from app.extensions import db from app.models import RecoveryCode +from app.dashboard.views.enter_sudo import sudo_required class OtpTokenForm(FlaskForm): @@ -15,6 +16,7 @@ class OtpTokenForm(FlaskForm): @dashboard_bp.route("/mfa_cancel", methods=["GET", "POST"]) @login_required +@sudo_required def mfa_cancel(): if not current_user.enable_otp: flash("you don't have MFA enabled", "warning") diff --git a/app/dashboard/views/mfa_setup.py b/app/dashboard/views/mfa_setup.py index 255e58a1..2b7ac48c 100644 --- a/app/dashboard/views/mfa_setup.py +++ b/app/dashboard/views/mfa_setup.py @@ -7,6 +7,7 @@ from wtforms import StringField, validators from app.dashboard.base import dashboard_bp from app.extensions import db from app.log import LOG +from app.dashboard.views.enter_sudo import sudo_required class OtpTokenForm(FlaskForm): @@ -15,6 +16,7 @@ class OtpTokenForm(FlaskForm): @dashboard_bp.route("/mfa_setup", methods=["GET", "POST"]) @login_required +@sudo_required def mfa_setup(): if current_user.enable_otp: flash("you have already enabled MFA", "warning") From 0cdd0b3b07bceaa332a10595c4d86068196c73c3 Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Mon, 18 May 2020 02:15:52 -0700 Subject: [PATCH 08/21] black --- app/dashboard/views/enter_sudo.py | 14 ++++++++------ app/dashboard/views/fido_setup.py | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/dashboard/views/enter_sudo.py b/app/dashboard/views/enter_sudo.py index 0e08dd42..4cbfe25e 100644 --- a/app/dashboard/views/enter_sudo.py +++ b/app/dashboard/views/enter_sudo.py @@ -41,15 +41,17 @@ def enter_sudo(): "dashboard/enter_sudo.html", password_check_form=password_check_form ) + def sudo_required(f): @wraps(f) def wrap(*args, **kwargs): # Reset sudo mode in every 20s under dev mode SUDO_GAP = 900 if not DEBUG else 20 - if "sudo_time" not in session or (time() - int(session["sudo_time"])) > SUDO_GAP: - return redirect( - url_for("dashboard.enter_sudo", next=request.path) - ) + if ( + "sudo_time" not in session + or (time() - int(session["sudo_time"])) > SUDO_GAP + ): + return redirect(url_for("dashboard.enter_sudo", next=request.path)) return f(*args, **kwargs) - - return wrap \ No newline at end of file + + return wrap diff --git a/app/dashboard/views/fido_setup.py b/app/dashboard/views/fido_setup.py index 9107c146..53e8b175 100644 --- a/app/dashboard/views/fido_setup.py +++ b/app/dashboard/views/fido_setup.py @@ -16,6 +16,7 @@ from app.log import LOG from app.models import FIDO from app.dashboard.views.enter_sudo import sudo_required + class FidoTokenForm(FlaskForm): sk_assertion = HiddenField("sk_assertion", validators=[validators.DataRequired()]) From 6509053fa81eee57bef3ad4379e497ef68884a25 Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Mon, 18 May 2020 02:55:41 -0700 Subject: [PATCH 09/21] named key --- app/dashboard/templates/dashboard/fido_setup.html | 4 +++- app/dashboard/views/fido_setup.py | 4 +++- app/models.py | 1 + server.py | 6 ++++-- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/dashboard/templates/dashboard/fido_setup.html b/app/dashboard/templates/dashboard/fido_setup.html index 88318034..424c16fd 100644 --- a/app/dashboard/templates/dashboard/fido_setup.html +++ b/app/dashboard/templates/dashboard/fido_setup.html @@ -19,6 +19,9 @@
{{ fido_token_form.csrf_token }} {{ fido_token_form.sk_assertion(class="form-control", placeholder="") }} + + {{ fido_token_form.key_name(class="form-control", placeholder="Name of your key (Required)") }} + {{ render_field_errors(fido_token_form.key_name) }}
@@ -53,7 +56,6 @@ } $("#btnRegisterKey").click(registerKey); - $('document').ready(registerKey());
diff --git a/app/dashboard/views/fido_setup.py b/app/dashboard/views/fido_setup.py index 53e8b175..2dd4506c 100644 --- a/app/dashboard/views/fido_setup.py +++ b/app/dashboard/views/fido_setup.py @@ -7,7 +7,7 @@ import webauthn from flask import render_template, flash, redirect, url_for, session from flask_login import login_required, current_user from flask_wtf import FlaskForm -from wtforms import HiddenField, validators +from wtforms import StringField, HiddenField, validators from app.config import RP_ID, URL from app.dashboard.base import dashboard_bp @@ -18,6 +18,7 @@ from app.dashboard.views.enter_sudo import sudo_required class FidoTokenForm(FlaskForm): + key_name = StringField("key_name", validators=[validators.DataRequired()]) sk_assertion = HiddenField("sk_assertion", validators=[validators.DataRequired()]) @@ -74,6 +75,7 @@ def fido_setup(): uuid=fido_uuid, public_key=str(fido_credential.public_key, "utf-8"), sign_count=fido_credential.sign_count, + name=fido_token_form.key_name.data, ) db.session.commit() diff --git a/app/models.py b/app/models.py index 4d5f57c3..47c637e9 100644 --- a/app/models.py +++ b/app/models.py @@ -130,6 +130,7 @@ class FIDO(db.Model, ModelMixin): ) public_key = db.Column(db.String(), nullable=False, unique=True) sign_count = db.Column(db.Integer(), nullable=False) + name = db.Column(db.String(128), nullable=False, unique=False) class User(db.Model, ModelMixin, UserMixin): diff --git a/server.py b/server.py index a219b23d..542a2296 100644 --- a/server.py +++ b/server.py @@ -148,16 +148,18 @@ def fake_data(): db.session.commit() FIDO.create( - credential_id="umR9q5vX61XG7vh7gi8wT0gJ9LkYwHKSzDL5vhtZs3o", + credential_id="el29kSg3Ai0c9Tsn9F0vXDdqKrs3jHLeU0J1NLN1Sjs", uuid="59576167-6c37-4d67-943b-4683b24ff821", - public_key="pQECAyYgASFYIEjQg3TOuUZJxylLE6gJDNHcNyYVW5hOAZ-vGOY9I_TDIlggfJqIh07bj3n6RVmrEsuozsYPYM6VeJKCeduz0DFp8AY", + public_key="pQECAyYgASFYIP8Xb0pXtSlmn8oji445s_FkEZsld6NCxx19YXyEt5h1IlggTw30aVvtA2sn7qyn3BE0Ux5NPJFSmABYZmT_P35Q9EIs", sign_count=1, + name="testA", ) FIDO.create( credential_id="4SaUPugJ2sAErSnZil6_5_wXIL-Sk6QdFmNm94IL-_g", uuid="59576167-6c37-4d67-943b-4683b24ff821", public_key="pQECAyYgASFYILfse0JIp0a7Gz7n1K8b75_OgV-iliKr52FB-BGzv2R2IlggsPcViY5Dma8XooLWUHtx7X2_px0MsFR_7VRMwT2OHKg", sign_count=1, + name="testB", ) db.session.commit() From 6ea17b4febe76af609921cb4bd8b3dc894896424 Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Mon, 18 May 2020 03:03:14 -0700 Subject: [PATCH 10/21] remove debug code for sudo --- app/dashboard/views/enter_sudo.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/dashboard/views/enter_sudo.py b/app/dashboard/views/enter_sudo.py index 4cbfe25e..da58474f 100644 --- a/app/dashboard/views/enter_sudo.py +++ b/app/dashboard/views/enter_sudo.py @@ -45,8 +45,9 @@ def enter_sudo(): def sudo_required(f): @wraps(f) def wrap(*args, **kwargs): - # Reset sudo mode in every 20s under dev mode - SUDO_GAP = 900 if not DEBUG else 20 + # Reset sudo mode in every 60s under dev mode + # SUDO_GAP = 900 if not DEBUG else 60 + SUDO_GAP = 900 if ( "sudo_time" not in session or (time() - int(session["sudo_time"])) > SUDO_GAP From b64ed7ad639f0ab27c752e2d6240718c10b597c6 Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Mon, 18 May 2020 05:07:06 -0700 Subject: [PATCH 11/21] key management page --- app/dashboard/__init__.py | 2 +- .../templates/dashboard/fido_cancel.html | 28 --------- .../templates/dashboard/fido_manage.html | 53 +++++++++++++++++ .../templates/dashboard/setting.html | 2 +- app/dashboard/views/fido_cancel.py | 45 -------------- app/dashboard/views/fido_manage.py | 59 +++++++++++++++++++ app/dashboard/views/fido_setup.py | 7 ++- 7 files changed, 119 insertions(+), 77 deletions(-) delete mode 100644 app/dashboard/templates/dashboard/fido_cancel.html create mode 100644 app/dashboard/templates/dashboard/fido_manage.html delete mode 100644 app/dashboard/views/fido_cancel.py create mode 100644 app/dashboard/views/fido_manage.py diff --git a/app/dashboard/__init__.py b/app/dashboard/__init__.py index 16ca9962..fc216406 100644 --- a/app/dashboard/__init__.py +++ b/app/dashboard/__init__.py @@ -13,7 +13,7 @@ from .views import ( mfa_setup, mfa_cancel, fido_setup, - fido_cancel, + fido_manage, domain_detail, lifetime_licence, directory, diff --git a/app/dashboard/templates/dashboard/fido_cancel.html b/app/dashboard/templates/dashboard/fido_cancel.html deleted file mode 100644 index 0df4c53d..00000000 --- a/app/dashboard/templates/dashboard/fido_cancel.html +++ /dev/null @@ -1,28 +0,0 @@ -{% extends 'default.html' %} -{% set active_page = "setting" %} -{% block title %} - Unlink Security Key -{% endblock %} - - -{% block default_content %} -
-
-

Unlink Your Security Key

-

- Please enter the password of your account so that we can ensure it's you. -

- -
- {{ password_check_form.csrf_token }} - -
Password
- - {{ password_check_form.password(class="form-control", autofocus="true") }} - {{ render_field_errors(password_check_form.password) }} - -
- -
-
-{% endblock %} \ No newline at end of file diff --git a/app/dashboard/templates/dashboard/fido_manage.html b/app/dashboard/templates/dashboard/fido_manage.html new file mode 100644 index 00000000..3f76c159 --- /dev/null +++ b/app/dashboard/templates/dashboard/fido_manage.html @@ -0,0 +1,53 @@ +{% extends 'default.html' %} +{% set active_page = "setting" %} +{% block title %} + Security Key Manage +{% endblock %} + +{% block head %} + +{% endblock %} + +{% block default_content %} +
+
+

Manage Your Security Key

+

Delete all keys will also disable WebAuthn 2FA.

+ +
+ {{ fido_manage_form.csrf_token }} + {{ fido_manage_form.credential_id(class="form-control", placeholder="") }} +
+ + + + + + + + + + + + {%for key in keys%} + + + + + + + {%endfor%} + + + + + + + +
IDNameLinked AtOperation
{{ key.id }}{{ key.name }}
#Link a New Key
+ + + +
+
+{% endblock %} diff --git a/app/dashboard/templates/dashboard/setting.html b/app/dashboard/templates/dashboard/setting.html index b4e1eb8e..d0c66a2d 100644 --- a/app/dashboard/templates/dashboard/setting.html +++ b/app/dashboard/templates/dashboard/setting.html @@ -97,7 +97,7 @@ {% if current_user.fido_uuid is none %} Setup WebAuthn {% else %} - Disable WebAuthn + Manage WebAuthn Recovery Codes {% endif %} diff --git a/app/dashboard/views/fido_cancel.py b/app/dashboard/views/fido_cancel.py deleted file mode 100644 index 621aa6f5..00000000 --- a/app/dashboard/views/fido_cancel.py +++ /dev/null @@ -1,45 +0,0 @@ -from flask import render_template, flash, redirect, url_for -from flask_login import login_required, current_user -from flask_wtf import FlaskForm -from wtforms import PasswordField, validators - -from app.dashboard.base import dashboard_bp -from app.extensions import db -from app.models import RecoveryCode - - -class LoginForm(FlaskForm): - password = PasswordField("Password", validators=[validators.DataRequired()]) - - -@dashboard_bp.route("/fido_cancel", methods=["GET", "POST"]) -@login_required -def fido_cancel(): - if not current_user.fido_enabled(): - flash("You haven't registed a security key", "warning") - return redirect(url_for("dashboard.index")) - - password_check_form = LoginForm() - - if password_check_form.validate_on_submit(): - password = password_check_form.password.data - - if current_user.check_password(password): - current_user.fido_pk = None - current_user.fido_uuid = None - current_user.fido_sign_count = None - current_user.fido_credential_id = None - db.session.commit() - - # user does not have any 2FA enabled left, delete all recovery codes - if not current_user.two_factor_authentication_enabled(): - RecoveryCode.empty(current_user) - - flash("We've unlinked your security key.", "success") - return redirect(url_for("dashboard.index")) - else: - flash("Incorrect password", "warning") - - return render_template( - "dashboard/fido_cancel.html", password_check_form=password_check_form - ) diff --git a/app/dashboard/views/fido_manage.py b/app/dashboard/views/fido_manage.py new file mode 100644 index 00000000..f4b46573 --- /dev/null +++ b/app/dashboard/views/fido_manage.py @@ -0,0 +1,59 @@ +from flask import render_template, flash, redirect, url_for +from flask_login import login_required, current_user +from flask_wtf import FlaskForm +from wtforms import HiddenField, validators + +from app.dashboard.base import dashboard_bp +from app.extensions import db +from app.log import LOG +from app.models import RecoveryCode, FIDO +from app.dashboard.views.enter_sudo import sudo_required + + +class FidoManageForm(FlaskForm): + credential_id = HiddenField("credential_id", validators=[validators.DataRequired()]) + + +@dashboard_bp.route("/fido_manage", methods=["GET", "POST"]) +@login_required +@sudo_required +def fido_manage(): + if not current_user.fido_enabled(): + flash("You haven't registed a security key", "warning") + return redirect(url_for("dashboard.index")) + + fido_manage_form = FidoManageForm() + + if fido_manage_form.validate_on_submit(): + credential_id = fido_manage_form.credential_id.data + + fido_key = FIDO.get_by(uuid=current_user.fido_uuid, credential_id=credential_id) + + if not fido_key: + flash("Unknown error, redirect back to manage page", "warning") + return redirect(url_for("dashboard.fido_manage")) + + FIDO.delete(fido_key.id) + db.session.commit() + + LOG.d(f"FIDO Key ID={fido_key.id} Removed") + flash(f"Key {fido_key.name} successfully unlinked", "success") + + # Disable FIDO for the user if all keys have been deleted + if not FIDO.filter_by(uuid=current_user.fido_uuid).all(): + current_user.fido_uuid = None + db.session.commit() + + # user does not have any 2FA enabled left, delete all recovery codes + if not current_user.two_factor_authentication_enabled(): + RecoveryCode.empty(current_user) + + return redirect(url_for("dashboard.index")) + + return redirect(url_for("dashboard.fido_manage")) + + return render_template( + "dashboard/fido_manage.html", + fido_manage_form=fido_manage_form, + keys=FIDO.filter_by(uuid=current_user.fido_uuid), + ) diff --git a/app/dashboard/views/fido_setup.py b/app/dashboard/views/fido_setup.py index 2dd4506c..3b86c0c9 100644 --- a/app/dashboard/views/fido_setup.py +++ b/app/dashboard/views/fido_setup.py @@ -13,7 +13,7 @@ from app.config import RP_ID, URL from app.dashboard.base import dashboard_bp from app.extensions import db from app.log import LOG -from app.models import FIDO +from app.models import FIDO, RecoveryCode from app.dashboard.views.enter_sudo import sudo_required @@ -84,7 +84,10 @@ def fido_setup(): ) flash("Security key has been activated", "success") - return redirect(url_for("dashboard.recovery_code_route")) + if not RecoveryCode.query.filter_by(user_id=current_user.id).all(): + return redirect(url_for("dashboard.recovery_code_route")) + else: + return redirect(url_for("dashboard.fido_manage")) # Prepare information for key registration process fido_uuid = ( From 45023cd775b72c37112f868f27ac353807daff67 Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Mon, 18 May 2020 05:10:19 -0700 Subject: [PATCH 12/21] Copywriting consistency --- app/dashboard/templates/dashboard/fido_manage.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/dashboard/templates/dashboard/fido_manage.html b/app/dashboard/templates/dashboard/fido_manage.html index 3f76c159..0d64fd22 100644 --- a/app/dashboard/templates/dashboard/fido_manage.html +++ b/app/dashboard/templates/dashboard/fido_manage.html @@ -12,7 +12,7 @@

Manage Your Security Key

-

Delete all keys will also disable WebAuthn 2FA.

+

Unlink all keys will also disable WebAuthn 2FA.

{{ fido_manage_form.csrf_token }} @@ -34,7 +34,7 @@ {{ key.id }} {{ key.name }} - + {%endfor%} From d91fbb563a71fa846ce2831ec742a37d9e02490d Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Mon, 18 May 2020 05:11:54 -0700 Subject: [PATCH 13/21] Remove debug keys --- .../templates/dashboard/fido_manage.html | 2 +- server.py | 17 +---------------- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/app/dashboard/templates/dashboard/fido_manage.html b/app/dashboard/templates/dashboard/fido_manage.html index 0d64fd22..d9860b91 100644 --- a/app/dashboard/templates/dashboard/fido_manage.html +++ b/app/dashboard/templates/dashboard/fido_manage.html @@ -1,7 +1,7 @@ {% extends 'default.html' %} {% set active_page = "setting" %} {% block title %} - Security Key Manage + Manage Security Key {% endblock %} {% block head %} diff --git a/server.py b/server.py index 542a2296..bfa1e133 100644 --- a/server.py +++ b/server.py @@ -143,25 +143,10 @@ def fake_data(): otp_secret="base32secret3232", can_use_fido=True, intro_shown=True, - fido_uuid="59576167-6c37-4d67-943b-4683b24ff821", + fido_uuid=None, ) db.session.commit() - FIDO.create( - credential_id="el29kSg3Ai0c9Tsn9F0vXDdqKrs3jHLeU0J1NLN1Sjs", - uuid="59576167-6c37-4d67-943b-4683b24ff821", - public_key="pQECAyYgASFYIP8Xb0pXtSlmn8oji445s_FkEZsld6NCxx19YXyEt5h1IlggTw30aVvtA2sn7qyn3BE0Ux5NPJFSmABYZmT_P35Q9EIs", - sign_count=1, - name="testA", - ) - FIDO.create( - credential_id="4SaUPugJ2sAErSnZil6_5_wXIL-Sk6QdFmNm94IL-_g", - uuid="59576167-6c37-4d67-943b-4683b24ff821", - public_key="pQECAyYgASFYILfse0JIp0a7Gz7n1K8b75_OgV-iliKr52FB-BGzv2R2IlggsPcViY5Dma8XooLWUHtx7X2_px0MsFR_7VRMwT2OHKg", - sign_count=1, - name="testB", - ) - db.session.commit() user.trial_end = None From 0a59dd56388274476792545376a06bae4aa576b4 Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Mon, 18 May 2020 05:27:57 -0700 Subject: [PATCH 14/21] Activate key on enter --- app/dashboard/templates/dashboard/fido_setup.html | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/dashboard/templates/dashboard/fido_setup.html b/app/dashboard/templates/dashboard/fido_setup.html index 424c16fd..198591f8 100644 --- a/app/dashboard/templates/dashboard/fido_setup.html +++ b/app/dashboard/templates/dashboard/fido_setup.html @@ -56,6 +56,13 @@ } $("#btnRegisterKey").click(registerKey); + + document.getElementById("key_name").addEventListener("keydown", event => { + if (event.keyCode === 13) { + event.preventDefault(); + document.getElementById("btnRegisterKey").click(); + } + });
From e8925352871b61f5a5c27d520fc2cbbb5648e47d Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Mon, 18 May 2020 05:28:18 -0700 Subject: [PATCH 15/21] Black --- server.py | 1 - 1 file changed, 1 deletion(-) diff --git a/server.py b/server.py index bfa1e133..da8a073c 100644 --- a/server.py +++ b/server.py @@ -147,7 +147,6 @@ def fake_data(): ) db.session.commit() - user.trial_end = None LifetimeCoupon.create(code="coupon", nb_used=10) From c0a751ff132432f15e7ef4d17b9d724b43a2d01f Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Mon, 18 May 2020 13:45:02 -0700 Subject: [PATCH 16/21] Put button inside the form --- app/auth/templates/auth/fido.html | 9 +++++---- app/dashboard/templates/dashboard/fido_setup.html | 15 ++++----------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/app/auth/templates/auth/fido.html b/app/auth/templates/auth/fido.html index 35f8b257..b2d2ea75 100644 --- a/app/auth/templates/auth/fido.html +++ b/app/auth/templates/auth/fido.html @@ -22,10 +22,12 @@ {{ fido_token_form.csrf_token }} {{ fido_token_form.sk_assertion(class="form-control", placeholder="") }} + +
+ +
-
- -
+ {% if enable_otp %}
@@ -68,7 +70,6 @@ $('#formRegisterKey').submit(); } - $("#btnVerifyKey").click(verifyKey); {% if auto_activate %} diff --git a/app/dashboard/templates/dashboard/fido_setup.html b/app/dashboard/templates/dashboard/fido_setup.html index 198591f8..3a4c16aa 100644 --- a/app/dashboard/templates/dashboard/fido_setup.html +++ b/app/dashboard/templates/dashboard/fido_setup.html @@ -22,10 +22,11 @@ {{ fido_token_form.key_name(class="form-control", placeholder="Name of your key (Required)") }} {{ render_field_errors(fido_token_form.key_name) }} + +
+ +
-
- -
From 5c3d2c19c8c384ca9c03b859370187a3d7893bc5 Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Mon, 18 May 2020 13:46:13 -0700 Subject: [PATCH 17/21] module level and can be prefixed --- app/dashboard/views/enter_sudo.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/dashboard/views/enter_sudo.py b/app/dashboard/views/enter_sudo.py index da58474f..aef88a6e 100644 --- a/app/dashboard/views/enter_sudo.py +++ b/app/dashboard/views/enter_sudo.py @@ -7,9 +7,9 @@ from wtforms import PasswordField, validators from functools import wraps from app.dashboard.base import dashboard_bp -from app.config import DEBUG from app.log import LOG +_SUDO_GAP = 900 class LoginForm(FlaskForm): password = PasswordField("Password", validators=[validators.DataRequired()]) @@ -45,12 +45,9 @@ def enter_sudo(): def sudo_required(f): @wraps(f) def wrap(*args, **kwargs): - # Reset sudo mode in every 60s under dev mode - # SUDO_GAP = 900 if not DEBUG else 60 - SUDO_GAP = 900 if ( "sudo_time" not in session - or (time() - int(session["sudo_time"])) > SUDO_GAP + or (time() - int(session["sudo_time"])) > _SUDO_GAP ): return redirect(url_for("dashboard.enter_sudo", next=request.path)) return f(*args, **kwargs) From 7d1a744fe2478db00bc3e7123767c0d8e496d3ec Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Mon, 18 May 2020 13:47:42 -0700 Subject: [PATCH 18/21] typo --- app/dashboard/views/fido_manage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/dashboard/views/fido_manage.py b/app/dashboard/views/fido_manage.py index f4b46573..96bba85b 100644 --- a/app/dashboard/views/fido_manage.py +++ b/app/dashboard/views/fido_manage.py @@ -19,7 +19,7 @@ class FidoManageForm(FlaskForm): @sudo_required def fido_manage(): if not current_user.fido_enabled(): - flash("You haven't registed a security key", "warning") + flash("You haven't registered a security key", "warning") return redirect(url_for("dashboard.index")) fido_manage_form = FidoManageForm() From ea914e0378040e034e1c423c6844c862be3d131f Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Mon, 18 May 2020 13:54:05 -0700 Subject: [PATCH 19/21] Rename FIDO->Fido --- app/auth/views/fido.py | 6 +++--- app/dashboard/views/enter_sudo.py | 1 + app/dashboard/views/fido_manage.py | 10 +++++----- app/dashboard/views/fido_setup.py | 6 +++--- app/models.py | 2 +- server.py | 2 +- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/app/auth/views/fido.py b/app/auth/views/fido.py index 741573af..05201d43 100644 --- a/app/auth/views/fido.py +++ b/app/auth/views/fido.py @@ -12,7 +12,7 @@ from app.config import MFA_USER_ID from app.config import RP_ID, URL from app.extensions import db from app.log import LOG -from app.models import User, FIDO +from app.models import User, Fido class FidoTokenForm(FlaskForm): @@ -51,7 +51,7 @@ def fido(): challenge = session["fido_challenge"] try: - fido_key = FIDO.get_by( + fido_key = Fido.get_by( uuid=user.fido_uuid, credential_id=sk_assertion["id"] ) webauthn_user = webauthn.WebAuthnUser( @@ -94,7 +94,7 @@ def fido(): session["fido_challenge"] = challenge.rstrip("=") - fido_model = FIDO.filter_by(uuid=user.fido_uuid).all() + fido_model = Fido.filter_by(uuid=user.fido_uuid).all() webauthn_users = [] for record in fido_model: webauthn_users.append( diff --git a/app/dashboard/views/enter_sudo.py b/app/dashboard/views/enter_sudo.py index aef88a6e..c2c4819e 100644 --- a/app/dashboard/views/enter_sudo.py +++ b/app/dashboard/views/enter_sudo.py @@ -11,6 +11,7 @@ from app.log import LOG _SUDO_GAP = 900 + class LoginForm(FlaskForm): password = PasswordField("Password", validators=[validators.DataRequired()]) diff --git a/app/dashboard/views/fido_manage.py b/app/dashboard/views/fido_manage.py index 96bba85b..db684868 100644 --- a/app/dashboard/views/fido_manage.py +++ b/app/dashboard/views/fido_manage.py @@ -6,7 +6,7 @@ from wtforms import HiddenField, validators from app.dashboard.base import dashboard_bp from app.extensions import db from app.log import LOG -from app.models import RecoveryCode, FIDO +from app.models import RecoveryCode, Fido from app.dashboard.views.enter_sudo import sudo_required @@ -27,20 +27,20 @@ def fido_manage(): if fido_manage_form.validate_on_submit(): credential_id = fido_manage_form.credential_id.data - fido_key = FIDO.get_by(uuid=current_user.fido_uuid, credential_id=credential_id) + fido_key = Fido.get_by(uuid=current_user.fido_uuid, credential_id=credential_id) if not fido_key: flash("Unknown error, redirect back to manage page", "warning") return redirect(url_for("dashboard.fido_manage")) - FIDO.delete(fido_key.id) + Fido.delete(fido_key.id) db.session.commit() LOG.d(f"FIDO Key ID={fido_key.id} Removed") flash(f"Key {fido_key.name} successfully unlinked", "success") # Disable FIDO for the user if all keys have been deleted - if not FIDO.filter_by(uuid=current_user.fido_uuid).all(): + if not Fido.filter_by(uuid=current_user.fido_uuid).all(): current_user.fido_uuid = None db.session.commit() @@ -55,5 +55,5 @@ def fido_manage(): return render_template( "dashboard/fido_manage.html", fido_manage_form=fido_manage_form, - keys=FIDO.filter_by(uuid=current_user.fido_uuid), + keys=Fido.filter_by(uuid=current_user.fido_uuid), ) diff --git a/app/dashboard/views/fido_setup.py b/app/dashboard/views/fido_setup.py index 3b86c0c9..8e43726c 100644 --- a/app/dashboard/views/fido_setup.py +++ b/app/dashboard/views/fido_setup.py @@ -13,7 +13,7 @@ from app.config import RP_ID, URL from app.dashboard.base import dashboard_bp from app.extensions import db from app.log import LOG -from app.models import FIDO, RecoveryCode +from app.models import Fido, RecoveryCode from app.dashboard.views.enter_sudo import sudo_required @@ -34,7 +34,7 @@ def fido_setup(): return redirect(url_for("dashboard.index")) if current_user.fido_uuid is not None: - fido_model = FIDO.filter_by(uuid=current_user.fido_uuid).all() + fido_model = Fido.filter_by(uuid=current_user.fido_uuid).all() else: fido_model = [] @@ -70,7 +70,7 @@ def fido_setup(): if current_user.fido_uuid is None: current_user.fido_uuid = fido_uuid - FIDO.create( + Fido.create( credential_id=str(fido_credential.credential_id, "utf-8"), uuid=fido_uuid, public_key=str(fido_credential.public_key, "utf-8"), diff --git a/app/models.py b/app/models.py index 47c637e9..187a007b 100644 --- a/app/models.py +++ b/app/models.py @@ -120,7 +120,7 @@ class AliasGeneratorEnum(EnumE): uuid = 2 # aliases are generated based on uuid -class FIDO(db.Model, ModelMixin): +class Fido(db.Model, ModelMixin): __tablename__ = "fido" credential_id = db.Column(db.String(), nullable=False, unique=True, index=True) uuid = db.Column( diff --git a/server.py b/server.py index da8a073c..d6aed844 100644 --- a/server.py +++ b/server.py @@ -38,7 +38,7 @@ from app.log import LOG from app.models import ( Client, User, - FIDO, + Fido, ClientUser, Alias, RedirectUri, From 7bd97e13b008b4a3b265f44fc96c2cf8907b3b2a Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Mon, 18 May 2020 13:55:38 -0700 Subject: [PATCH 20/21] fido_model -> fidos --- app/auth/views/fido.py | 10 +++++----- app/dashboard/views/fido_setup.py | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/auth/views/fido.py b/app/auth/views/fido.py index 05201d43..71c1f429 100644 --- a/app/auth/views/fido.py +++ b/app/auth/views/fido.py @@ -94,18 +94,18 @@ def fido(): session["fido_challenge"] = challenge.rstrip("=") - fido_model = Fido.filter_by(uuid=user.fido_uuid).all() + fidos = Fido.filter_by(uuid=user.fido_uuid).all() webauthn_users = [] - for record in fido_model: + for fido in fidos: webauthn_users.append( webauthn.WebAuthnUser( user.fido_uuid, user.email, user.name if user.name else user.email, False, - record.credential_id, - record.public_key, - record.sign_count, + fido.credential_id, + fido.public_key, + fido.sign_count, RP_ID, ) ) diff --git a/app/dashboard/views/fido_setup.py b/app/dashboard/views/fido_setup.py index 8e43726c..5f7283b2 100644 --- a/app/dashboard/views/fido_setup.py +++ b/app/dashboard/views/fido_setup.py @@ -34,9 +34,9 @@ def fido_setup(): return redirect(url_for("dashboard.index")) if current_user.fido_uuid is not None: - fido_model = Fido.filter_by(uuid=current_user.fido_uuid).all() + fidos = Fido.filter_by(uuid=current_user.fido_uuid).all() else: - fido_model = [] + fidos = [] fido_token_form = FidoTokenForm() @@ -113,11 +113,11 @@ def fido_setup(): del registration_dict["extensions"]["webauthn.loc"] # Prevent user from adding duplicated keys - for record in fido_model: + for fido in fidos: registration_dict["excludeCredentials"].append( { "type": "public-key", - "id": record.credential_id, + "id": fido.credential_id, "transports": ["usb", "nfc", "ble", "internal"], } ) From 4fd7bf40abcc5a5a44febde18819d0a2da740887 Mon Sep 17 00:00:00 2001 From: devStorm <59678453+developStorm@users.noreply.github.com> Date: Mon, 18 May 2020 14:05:03 -0700 Subject: [PATCH 21/21] use dt filter --- app/dashboard/templates/dashboard/fido_manage.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/dashboard/templates/dashboard/fido_manage.html b/app/dashboard/templates/dashboard/fido_manage.html index d9860b91..996a2760 100644 --- a/app/dashboard/templates/dashboard/fido_manage.html +++ b/app/dashboard/templates/dashboard/fido_manage.html @@ -33,7 +33,7 @@ {{ key.id }} {{ key.name }} - + {{key.created_at | dt}} {%endfor%}