123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- import os
- import arrow
- import sentry_sdk
- import stripe
- from flask import Flask, redirect, url_for, render_template, request, jsonify
- from flask_admin import Admin
- from flask_cors import cross_origin
- from flask_login import current_user
- from sentry_sdk.integrations.flask import FlaskIntegration
- from app.admin_model import SLModelView, SLAdminIndexView
- from app.auth.base import auth_bp
- from app.config import (
- DB_URI,
- FLASK_SECRET,
- ENABLE_SENTRY,
- ENV,
- URL,
- SHA1,
- LYRA_ANALYTICS_ID,
- STRIPE_SECRET_KEY,
- )
- from app.dashboard.base import dashboard_bp
- from app.developer.base import developer_bp
- from app.discover.base import discover_bp
- from app.extensions import db, login_manager, migrate
- from app.jose_utils import get_jwk_key
- from app.log import LOG
- from app.models import Client, User, Scope, ClientUser, GenEmail, RedirectUri, PlanEnum
- from app.monitor.base import monitor_bp
- from app.oauth.base import oauth_bp
- from app.oauth_models import ScopeE
- from app.partner.base import partner_bp
- if ENABLE_SENTRY:
- LOG.d("enable sentry")
- sentry_sdk.init(
- dsn="https://ad2187ed843340a1b4165bd8d5d6cdce@sentry.io/1478143",
- integrations=[FlaskIntegration()],
- )
- def create_app() -> Flask:
- app = Flask(__name__)
- app.url_map.strict_slashes = False
- app.config["SQLALCHEMY_DATABASE_URI"] = DB_URI
- app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
- app.secret_key = FLASK_SECRET
- app.config["TEMPLATES_AUTO_RELOAD"] = True
- init_extensions(app)
- register_blueprints(app)
- set_index_page(app)
- jinja2_filter(app)
- setup_error_page(app)
- setup_favicon_route(app)
- setup_openid_metadata(app)
- stripe.api_key = STRIPE_SECRET_KEY
- return app
- def fake_data():
- LOG.d("create fake data")
- # Remove db if exist
- if os.path.exists("db.sqlite"):
- LOG.d("remove existing db file")
- os.remove("db.sqlite")
- # Create all tables
- db.create_all()
- # fake data
- Scope.create(name=ScopeE.NAME.value)
- Scope.create(name=ScopeE.EMAIL.value)
- db.session.commit()
- # Create a user
- user = User.create(
- email="nguyenkims+local@gmail.com",
- name="Son Local",
- activated=True,
- is_admin=True,
- is_developer=True,
- )
- user.set_password("password")
- user.plan = PlanEnum.trial
- user.plan_expiration = arrow.now().shift(weeks=2)
- db.session.commit()
- GenEmail.create_new_gen_email(user_id=user.id)
- # Create a client
- client1 = Client.create_new(name="Demo", user_id=user.id)
- client1.home_url = "http://sl-client:7000"
- client1.oauth_client_id = "client-id"
- client1.oauth_client_secret = "client-secret"
- client1.published = True
- db.session.commit()
- RedirectUri.create(client_id=client1.id, uri="http://sl-client:7000/callback")
- RedirectUri.create(client_id=client1.id, uri="http://sl-client:7000/implicit")
- RedirectUri.create(client_id=client1.id, uri="http://sl-client:7000/implicit-jso")
- db.session.commit()
- @login_manager.user_loader
- def load_user(user_id):
- user = User.query.get(user_id)
- return user
- def register_blueprints(app: Flask):
- app.register_blueprint(auth_bp)
- app.register_blueprint(monitor_bp)
- app.register_blueprint(dashboard_bp)
- app.register_blueprint(developer_bp)
- app.register_blueprint(partner_bp)
- app.register_blueprint(oauth_bp, url_prefix="/oauth")
- app.register_blueprint(oauth_bp, url_prefix="/oauth2")
- app.register_blueprint(discover_bp)
- def set_index_page(app):
- @app.route("/")
- def index():
- if current_user.is_authenticated:
- return redirect(url_for("dashboard.index"))
- else:
- return redirect(url_for("auth.login"))
- @app.after_request
- def after_request(res):
- # not logging /static call
- if not request.path.startswith("/static") and not request.path.startswith(
- "/admin/static"
- ):
- LOG.debug(
- "%s %s %s %s %s",
- request.remote_addr,
- request.method,
- request.path,
- request.args,
- res.status_code,
- )
- return res
- def setup_openid_metadata(app):
- @app.route("/.well-known/openid-configuration")
- @cross_origin()
- def openid_config():
- res = {
- "issuer": URL,
- "authorization_endpoint": URL + "/oauth2/authorize",
- "token_endpoint": URL + "/oauth2/token",
- "jwks_uri": URL + "/jwks",
- "response_types_supported": [
- "code",
- "token",
- "id_token",
- "id_token token",
- "id_token code",
- ],
- "subject_types_supported": ["public"],
- "id_token_signing_alg_values_supported": ["RS256"],
- # todo: add introspection and revocation endpoints
- "introspection_endpoint": URL + "/oauth2/token/introspection",
- "revocation_endpoint": URL + "/oauth2/token/revocation",
- }
- return jsonify(res)
- @app.route("/jwks")
- @cross_origin()
- def jwks():
- res = {"keys": [get_jwk_key()]}
- return jsonify(res)
- def setup_error_page(app):
- @app.errorhandler(400)
- def page_not_found(e):
- return render_template("error/400.html"), 400
- @app.errorhandler(401)
- def page_not_found(e):
- return render_template("error/401.html", current_url=request.full_path), 401
- @app.errorhandler(403)
- def page_not_found(e):
- return render_template("error/403.html"), 403
- @app.errorhandler(404)
- def page_not_found(e):
- return render_template("error/404.html"), 404
- @app.errorhandler(Exception)
- def error_handler(e):
- LOG.exception(e)
- return render_template("error/500.html"), 500
- def setup_favicon_route(app):
- @app.route("/favicon.ico")
- def favicon():
- return redirect("/static/favicon.ico")
- def jinja2_filter(app):
- def format_datetime(value):
- dt = arrow.get(value)
- return dt.humanize()
- app.jinja_env.filters["dt"] = format_datetime
- @app.context_processor
- def inject_stage_and_region():
- return dict(
- YEAR=arrow.now().year,
- URL=URL,
- ENABLE_SENTRY=ENABLE_SENTRY,
- VERSION=SHA1,
- LYRA_ANALYTICS_ID=LYRA_ANALYTICS_ID,
- )
- def init_extensions(app: Flask):
- LOG.debug("init extensions")
- login_manager.init_app(app)
- db.init_app(app)
- migrate.init_app(app)
- def init_admin(app):
- admin = Admin(name="SimpleLogin", template_mode="bootstrap3")
- admin.init_app(app, index_view=SLAdminIndexView())
- admin.add_view(SLModelView(User, db.session))
- admin.add_view(SLModelView(Client, db.session))
- admin.add_view(SLModelView(GenEmail, db.session))
- admin.add_view(SLModelView(ClientUser, db.session))
- if __name__ == "__main__":
- app = create_app()
- if ENV == "local":
- LOG.d("reset db, add fake data")
- with app.app_context():
- fake_data()
- app.run(debug=True, host="0.0.0.0")
|