server.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. import os
  2. import arrow
  3. import sentry_sdk
  4. import stripe
  5. from flask import Flask, redirect, url_for, render_template, request, jsonify
  6. from flask_admin import Admin
  7. from flask_cors import cross_origin
  8. from flask_login import current_user
  9. from sentry_sdk.integrations.flask import FlaskIntegration
  10. from app.admin_model import SLModelView, SLAdminIndexView
  11. from app.auth.base import auth_bp
  12. from app.config import (
  13. DB_URI,
  14. FLASK_SECRET,
  15. ENABLE_SENTRY,
  16. ENV,
  17. URL,
  18. SHA1,
  19. LYRA_ANALYTICS_ID,
  20. STRIPE_SECRET_KEY,
  21. )
  22. from app.dashboard.base import dashboard_bp
  23. from app.developer.base import developer_bp
  24. from app.discover.base import discover_bp
  25. from app.extensions import db, login_manager, migrate
  26. from app.jose_utils import get_jwk_key
  27. from app.log import LOG
  28. from app.models import Client, User, Scope, ClientUser, GenEmail, RedirectUri, PlanEnum
  29. from app.monitor.base import monitor_bp
  30. from app.oauth.base import oauth_bp
  31. from app.oauth_models import ScopeE
  32. from app.partner.base import partner_bp
  33. if ENABLE_SENTRY:
  34. LOG.d("enable sentry")
  35. sentry_sdk.init(
  36. dsn="https://ad2187ed843340a1b4165bd8d5d6cdce@sentry.io/1478143",
  37. integrations=[FlaskIntegration()],
  38. )
  39. def create_app() -> Flask:
  40. app = Flask(__name__)
  41. app.url_map.strict_slashes = False
  42. app.config["SQLALCHEMY_DATABASE_URI"] = DB_URI
  43. app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
  44. app.secret_key = FLASK_SECRET
  45. app.config["TEMPLATES_AUTO_RELOAD"] = True
  46. init_extensions(app)
  47. register_blueprints(app)
  48. set_index_page(app)
  49. jinja2_filter(app)
  50. setup_error_page(app)
  51. setup_favicon_route(app)
  52. setup_openid_metadata(app)
  53. stripe.api_key = STRIPE_SECRET_KEY
  54. return app
  55. def fake_data():
  56. LOG.d("create fake data")
  57. # Remove db if exist
  58. if os.path.exists("db.sqlite"):
  59. LOG.d("remove existing db file")
  60. os.remove("db.sqlite")
  61. # Create all tables
  62. db.create_all()
  63. # fake data
  64. Scope.create(name=ScopeE.NAME.value)
  65. Scope.create(name=ScopeE.EMAIL.value)
  66. db.session.commit()
  67. # Create a user
  68. user = User.create(
  69. email="nguyenkims+local@gmail.com",
  70. name="Son Local",
  71. activated=True,
  72. is_admin=True,
  73. is_developer=True,
  74. )
  75. user.set_password("password")
  76. user.plan = PlanEnum.trial
  77. user.plan_expiration = arrow.now().shift(weeks=2)
  78. db.session.commit()
  79. GenEmail.create_new_gen_email(user_id=user.id)
  80. # Create a client
  81. client1 = Client.create_new(name="Demo", user_id=user.id)
  82. client1.home_url = "http://sl-client:7000"
  83. client1.oauth_client_id = "client-id"
  84. client1.oauth_client_secret = "client-secret"
  85. client1.published = True
  86. db.session.commit()
  87. RedirectUri.create(client_id=client1.id, uri="http://sl-client:7000/callback")
  88. RedirectUri.create(client_id=client1.id, uri="http://sl-client:7000/implicit")
  89. RedirectUri.create(client_id=client1.id, uri="http://sl-client:7000/implicit-jso")
  90. db.session.commit()
  91. @login_manager.user_loader
  92. def load_user(user_id):
  93. user = User.query.get(user_id)
  94. return user
  95. def register_blueprints(app: Flask):
  96. app.register_blueprint(auth_bp)
  97. app.register_blueprint(monitor_bp)
  98. app.register_blueprint(dashboard_bp)
  99. app.register_blueprint(developer_bp)
  100. app.register_blueprint(partner_bp)
  101. app.register_blueprint(oauth_bp, url_prefix="/oauth")
  102. app.register_blueprint(oauth_bp, url_prefix="/oauth2")
  103. app.register_blueprint(discover_bp)
  104. def set_index_page(app):
  105. @app.route("/")
  106. def index():
  107. if current_user.is_authenticated:
  108. return redirect(url_for("dashboard.index"))
  109. else:
  110. return redirect(url_for("auth.login"))
  111. @app.after_request
  112. def after_request(res):
  113. # not logging /static call
  114. if not request.path.startswith("/static") and not request.path.startswith(
  115. "/admin/static"
  116. ):
  117. LOG.debug(
  118. "%s %s %s %s %s",
  119. request.remote_addr,
  120. request.method,
  121. request.path,
  122. request.args,
  123. res.status_code,
  124. )
  125. return res
  126. def setup_openid_metadata(app):
  127. @app.route("/.well-known/openid-configuration")
  128. @cross_origin()
  129. def openid_config():
  130. res = {
  131. "issuer": URL,
  132. "authorization_endpoint": URL + "/oauth2/authorize",
  133. "token_endpoint": URL + "/oauth2/token",
  134. "jwks_uri": URL + "/jwks",
  135. "response_types_supported": [
  136. "code",
  137. "token",
  138. "id_token",
  139. "id_token token",
  140. "id_token code",
  141. ],
  142. "subject_types_supported": ["public"],
  143. "id_token_signing_alg_values_supported": ["RS256"],
  144. # todo: add introspection and revocation endpoints
  145. "introspection_endpoint": URL + "/oauth2/token/introspection",
  146. "revocation_endpoint": URL + "/oauth2/token/revocation",
  147. }
  148. return jsonify(res)
  149. @app.route("/jwks")
  150. @cross_origin()
  151. def jwks():
  152. res = {"keys": [get_jwk_key()]}
  153. return jsonify(res)
  154. def setup_error_page(app):
  155. @app.errorhandler(400)
  156. def page_not_found(e):
  157. return render_template("error/400.html"), 400
  158. @app.errorhandler(401)
  159. def page_not_found(e):
  160. return render_template("error/401.html", current_url=request.full_path), 401
  161. @app.errorhandler(403)
  162. def page_not_found(e):
  163. return render_template("error/403.html"), 403
  164. @app.errorhandler(404)
  165. def page_not_found(e):
  166. return render_template("error/404.html"), 404
  167. @app.errorhandler(Exception)
  168. def error_handler(e):
  169. LOG.exception(e)
  170. return render_template("error/500.html"), 500
  171. def setup_favicon_route(app):
  172. @app.route("/favicon.ico")
  173. def favicon():
  174. return redirect("/static/favicon.ico")
  175. def jinja2_filter(app):
  176. def format_datetime(value):
  177. dt = arrow.get(value)
  178. return dt.humanize()
  179. app.jinja_env.filters["dt"] = format_datetime
  180. @app.context_processor
  181. def inject_stage_and_region():
  182. return dict(
  183. YEAR=arrow.now().year,
  184. URL=URL,
  185. ENABLE_SENTRY=ENABLE_SENTRY,
  186. VERSION=SHA1,
  187. LYRA_ANALYTICS_ID=LYRA_ANALYTICS_ID,
  188. )
  189. def init_extensions(app: Flask):
  190. LOG.debug("init extensions")
  191. login_manager.init_app(app)
  192. db.init_app(app)
  193. migrate.init_app(app)
  194. def init_admin(app):
  195. admin = Admin(name="SimpleLogin", template_mode="bootstrap3")
  196. admin.init_app(app, index_view=SLAdminIndexView())
  197. admin.add_view(SLModelView(User, db.session))
  198. admin.add_view(SLModelView(Client, db.session))
  199. admin.add_view(SLModelView(GenEmail, db.session))
  200. admin.add_view(SLModelView(ClientUser, db.session))
  201. if __name__ == "__main__":
  202. app = create_app()
  203. if ENV == "local":
  204. LOG.d("reset db, add fake data")
  205. with app.app_context():
  206. fake_data()
  207. app.run(debug=True, host="0.0.0.0")