Exemplo n.º 1
0
def local_main():
    app = create_app()

    # enable flask toolbar
    app.config["DEBUG_TB_PROFILER_ENABLED"] = True
    app.config["DEBUG_TB_INTERCEPT_REDIRECTS"] = False
    app.debug = True
    DebugToolbarExtension(app)

    # warning: only used in local
    if RESET_DB:
        from init_app import add_sl_domains

        LOG.warning("reset db, add fake data")
        with app.app_context():
            fake_data()
            add_sl_domains()

    if URL.startswith("https"):
        LOG.d("enable https")
        context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
        context.load_cert_chain("local_data/cert.pem", "local_data/key.pem")

        app.run(debug=True, port=7777, ssl_context=context)
    else:
        app.run(debug=True, port=7777)
Exemplo n.º 2
0
def create_app() -> Flask:
    app = Flask(__name__)
    # SimpleLogin is deployed behind NGINX
    app.wsgi_app = ProxyFix(app.wsgi_app, num_proxies=1)
    limiter.init_app(app)

    app.url_map.strict_slashes = False

    app.config["SQLALCHEMY_DATABASE_URI"] = DB_URI
    app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
    # enable to print all queries generated by sqlalchemy
    # app.config["SQLALCHEMY_ECHO"] = True

    app.secret_key = FLASK_SECRET

    app.config["TEMPLATES_AUTO_RELOAD"] = True

    # to avoid conflict with other cookie
    app.config["SESSION_COOKIE_NAME"] = "slapp"
    if URL.startswith("https"):
        app.config["SESSION_COOKIE_SECURE"] = True
    app.config["SESSION_COOKIE_SAMESITE"] = "Lax"

    setup_error_page(app)

    init_extensions(app)
    register_blueprints(app)
    set_index_page(app)
    jinja2_filter(app)

    setup_favicon_route(app)
    setup_openid_metadata(app)

    init_admin(app)
    setup_paddle_callback(app)
    setup_do_not_track(app)

    if FLASK_PROFILER_PATH:
        LOG.d("Enable flask-profiler")
        app.config["flask_profiler"] = {
            "enabled": True,
            "storage": {"engine": "sqlite", "FILE": FLASK_PROFILER_PATH},
            "basicAuth": {
                "enabled": True,
                "username": "******",
                "password": FLASK_PROFILER_PASSWORD,
            },
            "ignore": ["^/static/.*", "/git", "/exception"],
        }
        flask_profiler.init_app(app)

    return app
Exemplo n.º 3
0
def setup_done():
    response = make_response(redirect(url_for("dashboard.index")))

    response.set_cookie(
        "setup_done",
        value="true",
        expires=arrow.now().shift(days=30).datetime,
        secure=True if URL.startswith("https") else False,
        httponly=True,
        samesite="Lax",
    )

    return response
Exemplo n.º 4
0
def fido():
    # passed from login page
    user_id = session.get(MFA_USER_ID)

    # user access this page directly without passing by login page
    if not user_id:
        flash("Unknown error, redirect back to main page", "warning")
        return redirect(url_for("auth.login"))

    user = User.get(user_id)

    if not (user and user.fido_enabled()):
        flash("Only user with security key linked should go to this page",
              "warning")
        return redirect(url_for("auth.login"))

    auto_activate = True
    fido_token_form = FidoTokenForm()

    next_url = request.args.get("next")

    if request.cookies.get("mfa"):
        browser = MfaBrowser.get_by(token=request.cookies.get("mfa"))
        if browser and not browser.is_expired() and browser.user_id == user.id:
            login_user(user)
            flash(f"Welcome back {user.name}!", "success")
            # Redirect user to correct page
            return redirect(next_url or url_for("dashboard.index"))
        else:
            # Trigger rate limiter
            g.deduct_limit = True

    # Handling POST requests
    if fido_token_form.validate_on_submit():
        try:
            sk_assertion = json.loads(fido_token_form.sk_assertion.data)
        except Exception as e:
            flash("Key verification failed. Error: Invalid Payload", "warning")
            return redirect(url_for("auth.login"))

        challenge = session["fido_challenge"]

        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.exception(
                f"An error occurred in WebAuthn verification process: {e}")
            flash("Key verification failed.", "warning")
            # Trigger rate limiter
            g.deduct_limit = True
            auto_activate = False
        else:
            user.fido_sign_count = new_sign_count
            db.session.commit()
            del session[MFA_USER_ID]

            login_user(user)
            flash(f"Welcome back {user.name}!", "success")

            # Redirect user to correct page
            response = make_response(
                redirect(next_url or url_for("dashboard.index")))

            if fido_token_form.remember.data:
                browser = MfaBrowser.create_new(user=user)
                db.session.commit()
                response.set_cookie(
                    "mfa",
                    value=browser.token,
                    expires=browser.expires.datetime,
                    secure=True if URL.startswith("https") else False,
                    httponly=True,
                    samesite="Lax",
                )

            return response

    # Prepare information for key registration process
    session.pop("challenge", None)
    challenge = secrets.token_urlsafe(32)

    session["fido_challenge"] = challenge.rstrip("=")

    fidos = Fido.filter_by(uuid=user.fido_uuid).all()
    webauthn_users = []
    for fido in fidos:
        webauthn_users.append(
            webauthn.WebAuthnUser(
                user.fido_uuid,
                user.email,
                user.name if user.name else user.email,
                False,
                fido.credential_id,
                fido.public_key,
                fido.sign_count,
                RP_ID,
            ))

    webauthn_assertion_options = webauthn.WebAuthnAssertionOptions(
        webauthn_users, challenge)
    webauthn_assertion_options = webauthn_assertion_options.assertion_dict

    return render_template(
        "auth/fido.html",
        fido_token_form=fido_token_form,
        webauthn_assertion_options=webauthn_assertion_options,
        enable_otp=user.enable_otp,
        auto_activate=auto_activate,
        next_url=next_url,
    )
Exemplo n.º 5
0
def create_app() -> Flask:
    app = Flask(__name__)
    # SimpleLogin is deployed behind NGINX
    app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_host=1)
    limiter.init_app(app)

    app.url_map.strict_slashes = False

    app.config["SQLALCHEMY_DATABASE_URI"] = DB_URI
    app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
    # enable to print all queries generated by sqlalchemy
    # app.config["SQLALCHEMY_ECHO"] = True

    app.secret_key = FLASK_SECRET

    app.config["TEMPLATES_AUTO_RELOAD"] = True

    # to avoid conflict with other cookie
    app.config["SESSION_COOKIE_NAME"] = SESSION_COOKIE_NAME
    if URL.startswith("https"):
        app.config["SESSION_COOKIE_SECURE"] = True
    app.config["SESSION_COOKIE_SAMESITE"] = "Lax"

    setup_error_page(app)

    init_extensions(app)
    register_blueprints(app)
    set_index_page(app)
    jinja2_filter(app)

    setup_favicon_route(app)
    setup_openid_metadata(app)

    init_admin(app)
    setup_paddle_callback(app)
    setup_coinbase_commerce(app)
    setup_do_not_track(app)

    if FLASK_PROFILER_PATH:
        LOG.d("Enable flask-profiler")
        app.config["flask_profiler"] = {
            "enabled": True,
            "storage": {
                "engine": "sqlite",
                "FILE": FLASK_PROFILER_PATH
            },
            "basicAuth": {
                "enabled": True,
                "username": "******",
                "password": FLASK_PROFILER_PASSWORD,
            },
            "ignore": ["^/static/.*", "/git", "/exception"],
        }
        flask_profiler.init_app(app)

    # enable CORS on /api endpoints
    CORS(app, resources={r"/api/*": {"origins": "*"}})

    # set session to permanent so user stays signed in after quitting the browser
    # the cookie is valid for 7 days
    @app.before_request
    def make_session_permanent():
        session.permanent = True
        app.permanent_session_lifetime = timedelta(days=7)

    return app
Exemplo n.º 6
0
</script>
        """


if __name__ == "__main__":
    app = create_app()

    # enable flask toolbar
    # app.config["DEBUG_TB_PROFILER_ENABLED"] = True
    # app.config["DEBUG_TB_INTERCEPT_REDIRECTS"] = False
    #
    # toolbar = DebugToolbarExtension(app)

    # enable to print all queries generated by sqlalchemy
    # app.config["SQLALCHEMY_ECHO"] = True

    # warning: only used in local
    if RESET_DB:
        LOG.warning("reset db, add fake data")
        with app.app_context():
            fake_data()

    if URL.startswith("https"):
        LOG.d("enable https")
        context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
        context.load_cert_chain("local_data/cert.pem", "local_data/key.pem")

        app.run(debug=True, host="0.0.0.0", port=7777, ssl_context=context)
    else:
        app.run(debug=True, host="0.0.0.0", port=7777)
Exemplo n.º 7
0
def mfa():
    # passed from login page
    user_id = session.get(MFA_USER_ID)

    # user access this page directly without passing by login page
    if not user_id:
        flash("Unknown error, redirect back to main page", "warning")
        return redirect(url_for("auth.login"))

    user = User.get(user_id)

    if not (user and user.enable_otp):
        flash("Only user with MFA enabled should go to this page", "warning")
        return redirect(url_for("auth.login"))

    otp_token_form = OtpTokenForm()
    next_url = request.args.get("next")

    if request.cookies.get("mfa"):
        browser = MfaBrowser.get_by(token=request.cookies.get("mfa"))
        if browser and not browser.is_expired() and browser.user_id == user.id:
            login_user(user)
            flash(f"Welcome back!", "success")
            # Redirect user to correct page
            return redirect(next_url or url_for("dashboard.index"))
        else:
            # Trigger rate limiter
            g.deduct_limit = True

    if otp_token_form.validate_on_submit():
        totp = pyotp.TOTP(user.otp_secret)

        token = otp_token_form.token.data.replace(" ", "")

        if totp.verify(token) and user.last_otp != token:
            del session[MFA_USER_ID]
            user.last_otp = token
            Session.commit()

            login_user(user)
            flash(f"Welcome back!", "success")

            # Redirect user to correct page
            response = make_response(
                redirect(next_url or url_for("dashboard.index")))

            if otp_token_form.remember.data:
                browser = MfaBrowser.create_new(user=user)
                Session.commit()
                response.set_cookie(
                    "mfa",
                    value=browser.token,
                    expires=browser.expires.datetime,
                    secure=True if URL.startswith("https") else False,
                    httponly=True,
                    samesite="Lax",
                )

            return response

        else:
            flash("Incorrect token", "warning")
            # Trigger rate limiter
            g.deduct_limit = True
            otp_token_form.token.data = None
            send_invalid_totp_login_email(user, "TOTP")

    return render_template(
        "auth/mfa.html",
        otp_token_form=otp_token_form,
        enable_fido=(user.fido_enabled()),
        next_url=next_url,
    )