2021-07-01 09:04:57 +00:00
|
|
|
import datetime
|
2020-08-17 13:49:48 +00:00
|
|
|
import ldap
|
|
|
|
import os
|
|
|
|
import toml
|
|
|
|
|
2020-10-21 12:04:40 +00:00
|
|
|
import canaille.admin
|
|
|
|
import canaille.admin.authorizations
|
|
|
|
import canaille.admin.clients
|
2020-10-29 11:00:19 +00:00
|
|
|
import canaille.admin.mail
|
|
|
|
import canaille.admin.tokens
|
2020-10-21 12:04:40 +00:00
|
|
|
import canaille.consents
|
2021-10-12 16:24:51 +00:00
|
|
|
import canaille.configuration
|
2020-10-21 12:04:40 +00:00
|
|
|
import canaille.oauth
|
|
|
|
import canaille.account
|
2021-07-01 16:21:20 +00:00
|
|
|
import canaille.groups
|
2020-10-21 12:04:40 +00:00
|
|
|
import canaille.well_known
|
2020-09-01 15:11:30 +00:00
|
|
|
|
2021-07-01 09:04:57 +00:00
|
|
|
from flask import Flask, g, request, render_template, session
|
2020-09-01 15:11:30 +00:00
|
|
|
from flask_babel import Babel
|
|
|
|
|
2020-08-19 14:20:57 +00:00
|
|
|
from .flaskutils import current_user
|
2020-09-24 13:16:25 +00:00
|
|
|
from .ldaputils import LDAPObject
|
2020-08-19 14:20:57 +00:00
|
|
|
from .oauth2utils import config_oauth
|
2021-06-03 13:00:11 +00:00
|
|
|
from .models import User, Token, AuthorizationCode, Client, Consent, Group
|
2020-08-17 13:49:48 +00:00
|
|
|
|
2020-09-01 15:27:56 +00:00
|
|
|
try: # pragma: no cover
|
|
|
|
import sentry_sdk
|
|
|
|
from sentry_sdk.integrations.flask import FlaskIntegration
|
|
|
|
|
|
|
|
SENTRY = True
|
|
|
|
except Exception:
|
|
|
|
SENTRY = False
|
|
|
|
|
2020-08-17 13:49:48 +00:00
|
|
|
|
|
|
|
def create_app(config=None):
|
|
|
|
app = Flask(__name__)
|
2020-08-31 09:23:50 +00:00
|
|
|
dir_path = os.path.dirname(os.path.realpath(__file__))
|
2020-08-17 13:49:48 +00:00
|
|
|
|
2020-08-26 07:41:53 +00:00
|
|
|
app.config.from_mapping(
|
2021-10-11 23:06:25 +00:00
|
|
|
{
|
|
|
|
"SESSION_COOKIE_NAME": "canaille",
|
|
|
|
"OAUTH2_REFRESH_TOKEN_GENERATOR": True,
|
|
|
|
}
|
2020-08-26 07:41:53 +00:00
|
|
|
)
|
2020-08-18 15:39:34 +00:00
|
|
|
if config:
|
|
|
|
app.config.from_mapping(config)
|
|
|
|
elif "CONFIG" in os.environ:
|
|
|
|
app.config.from_mapping(toml.load(os.environ.get("CONFIG")))
|
2020-08-31 09:23:50 +00:00
|
|
|
elif os.path.exists(os.path.join(dir_path, "conf", "config.toml")):
|
2020-08-31 11:55:45 +00:00
|
|
|
app.config.from_mapping(
|
|
|
|
toml.load(os.path.join(dir_path, "conf", "config.toml"))
|
|
|
|
)
|
2020-08-27 13:30:21 +00:00
|
|
|
else:
|
2020-08-27 14:08:26 +00:00
|
|
|
raise Exception(
|
|
|
|
"No configuration file found. "
|
|
|
|
"Either create conf/config.toml or set the 'CONFIG' variable environment."
|
|
|
|
)
|
2020-08-17 13:49:48 +00:00
|
|
|
|
2021-10-29 13:32:38 +00:00
|
|
|
if os.environ.get("FLASK_ENV") == "development":
|
2021-10-12 16:24:51 +00:00
|
|
|
canaille.configuration.setup_dev_keypair(app.config)
|
2021-10-11 23:06:25 +00:00
|
|
|
|
2021-10-12 16:24:51 +00:00
|
|
|
canaille.configuration.validate(app.config)
|
2020-08-17 13:49:48 +00:00
|
|
|
setup_app(app)
|
2020-08-28 14:08:29 +00:00
|
|
|
|
2020-08-17 13:49:48 +00:00
|
|
|
return app
|
|
|
|
|
|
|
|
|
2020-09-03 15:28:52 +00:00
|
|
|
def setup_ldap_tree(app):
|
|
|
|
conn = ldap.initialize(app.config["LDAP"]["URI"])
|
2021-08-31 13:47:06 +00:00
|
|
|
if app.config["LDAP"].get("TIMEOUT"):
|
|
|
|
conn.set_option(ldap.OPT_NETWORK_TIMEOUT, app.config["LDAP"]["TIMEOUT"])
|
2020-09-03 15:28:52 +00:00
|
|
|
conn.simple_bind_s(app.config["LDAP"]["BIND_DN"], app.config["LDAP"]["BIND_PW"])
|
|
|
|
Token.initialize(conn)
|
|
|
|
AuthorizationCode.initialize(conn)
|
|
|
|
Client.initialize(conn)
|
2020-09-17 08:00:39 +00:00
|
|
|
Consent.initialize(conn)
|
2020-09-03 15:28:52 +00:00
|
|
|
conn.unbind_s()
|
|
|
|
|
|
|
|
|
2021-10-12 16:24:51 +00:00
|
|
|
def setup_ldap_connection(app):
|
2020-10-23 09:31:16 +00:00
|
|
|
g.ldap = ldap.initialize(app.config["LDAP"]["URI"])
|
2021-10-13 14:04:08 +00:00
|
|
|
if app.config["LDAP"].get("TIMEOUT"):
|
|
|
|
g.ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, app.config["LDAP"]["TIMEOUT"])
|
2020-10-23 09:31:16 +00:00
|
|
|
g.ldap.simple_bind_s(app.config["LDAP"]["BIND_DN"], app.config["LDAP"]["BIND_PW"])
|
|
|
|
|
|
|
|
|
2021-10-12 16:24:51 +00:00
|
|
|
def teardown_ldap_connection(app):
|
2020-10-23 09:31:16 +00:00
|
|
|
if "ldap" in g:
|
|
|
|
g.ldap.unbind_s()
|
|
|
|
|
|
|
|
|
2020-08-17 13:49:48 +00:00
|
|
|
def setup_app(app):
|
2020-09-01 15:27:56 +00:00
|
|
|
if SENTRY and app.config.get("SENTRY_DSN"):
|
|
|
|
sentry_sdk.init(dsn=app.config["SENTRY_DSN"], integrations=[FlaskIntegration()])
|
|
|
|
|
2020-09-03 15:55:02 +00:00
|
|
|
try:
|
2020-09-24 13:16:25 +00:00
|
|
|
LDAPObject.root_dn = app.config["LDAP"]["ROOT_DN"]
|
2021-06-03 13:00:11 +00:00
|
|
|
user_base = app.config["LDAP"]["USER_BASE"]
|
|
|
|
if user_base.endswith(app.config["LDAP"]["ROOT_DN"]):
|
|
|
|
user_base = user_base[: -len(app.config["LDAP"]["ROOT_DN"]) - 1]
|
|
|
|
User.base = user_base
|
2021-07-01 09:36:35 +00:00
|
|
|
group_base = app.config["LDAP"].get("GROUP_BASE")
|
2021-06-03 13:00:11 +00:00
|
|
|
Group.base = group_base
|
2020-09-03 15:55:02 +00:00
|
|
|
|
|
|
|
app.url_map.strict_slashes = False
|
|
|
|
|
|
|
|
config_oauth(app)
|
|
|
|
setup_ldap_tree(app)
|
2020-10-21 12:04:40 +00:00
|
|
|
app.register_blueprint(canaille.account.bp)
|
2021-07-01 16:21:20 +00:00
|
|
|
app.register_blueprint(canaille.groups.bp, url_prefix="/groups")
|
2020-10-21 12:04:40 +00:00
|
|
|
app.register_blueprint(canaille.oauth.bp, url_prefix="/oauth")
|
|
|
|
app.register_blueprint(canaille.consents.bp, url_prefix="/consent")
|
2020-10-21 15:15:33 +00:00
|
|
|
app.register_blueprint(canaille.well_known.bp, url_prefix="/.well-known")
|
|
|
|
app.register_blueprint(canaille.admin.tokens.bp, url_prefix="/admin/token")
|
2020-09-03 15:55:02 +00:00
|
|
|
app.register_blueprint(
|
2020-10-21 12:04:40 +00:00
|
|
|
canaille.admin.authorizations.bp, url_prefix="/admin/authorization"
|
2020-09-03 15:55:02 +00:00
|
|
|
)
|
2020-10-21 15:15:33 +00:00
|
|
|
app.register_blueprint(canaille.admin.clients.bp, url_prefix="/admin/client")
|
2020-10-29 11:00:19 +00:00
|
|
|
app.register_blueprint(canaille.admin.mail.bp, url_prefix="/admin/mail")
|
2020-08-18 15:39:34 +00:00
|
|
|
|
2020-09-03 15:55:02 +00:00
|
|
|
babel = Babel(app)
|
2020-09-27 15:32:23 +00:00
|
|
|
|
|
|
|
@babel.localeselector
|
|
|
|
def get_locale():
|
|
|
|
user = getattr(g, "user", None)
|
|
|
|
if user is not None:
|
|
|
|
return user.locale
|
|
|
|
|
|
|
|
if app.config.get("LANGUAGE"):
|
|
|
|
return app.config.get("LANGUAGE")
|
|
|
|
|
2020-10-20 15:28:06 +00:00
|
|
|
return request.accept_languages.best_match(["fr_FR", "en_US"])
|
2020-09-27 15:32:23 +00:00
|
|
|
|
|
|
|
@babel.timezoneselector
|
|
|
|
def get_timezone():
|
|
|
|
user = getattr(g, "user", None)
|
|
|
|
if user is not None:
|
|
|
|
return user.timezone
|
|
|
|
|
|
|
|
@app.before_request
|
|
|
|
def before_request():
|
2021-10-12 16:24:51 +00:00
|
|
|
setup_ldap_connection(app)
|
2020-09-27 15:32:23 +00:00
|
|
|
|
|
|
|
@app.after_request
|
|
|
|
def after_request(response):
|
2021-10-12 16:24:51 +00:00
|
|
|
teardown_ldap_connection(app)
|
2020-09-27 15:32:23 +00:00
|
|
|
return response
|
|
|
|
|
2021-07-01 09:04:57 +00:00
|
|
|
@app.before_request
|
|
|
|
def make_session_permanent():
|
|
|
|
session.permanent = True
|
|
|
|
app.permanent_session_lifetime = datetime.timedelta(days=365)
|
|
|
|
|
2020-09-27 15:32:23 +00:00
|
|
|
@app.context_processor
|
|
|
|
def global_processor():
|
|
|
|
return {
|
|
|
|
"logo_url": app.config.get("LOGO"),
|
2020-11-05 11:18:17 +00:00
|
|
|
"favicon_url": app.config.get("FAVICON", app.config.get("LOGO")),
|
2020-09-27 15:32:23 +00:00
|
|
|
"website_name": app.config.get("NAME"),
|
|
|
|
"user": current_user(),
|
|
|
|
"menu": True,
|
|
|
|
}
|
|
|
|
|
2020-10-26 18:09:38 +00:00
|
|
|
@app.errorhandler(400)
|
|
|
|
def bad_request(e):
|
|
|
|
return render_template("error.html", error=400), 400
|
|
|
|
|
2020-09-27 15:32:23 +00:00
|
|
|
@app.errorhandler(403)
|
|
|
|
def unauthorized(e):
|
|
|
|
return render_template("error.html", error=403), 403
|
|
|
|
|
|
|
|
@app.errorhandler(404)
|
|
|
|
def page_not_found(e):
|
|
|
|
return render_template("error.html", error=404), 404
|
|
|
|
|
|
|
|
@app.errorhandler(500)
|
|
|
|
def server_error(e):
|
|
|
|
return render_template("error.html", error=500), 500
|
|
|
|
|
2020-09-03 15:55:02 +00:00
|
|
|
except Exception as exc:
|
|
|
|
if SENTRY and app.config.get("SENTRY_DSN"):
|
2020-09-29 16:21:41 +00:00
|
|
|
sentry_sdk.capture_exception(exc)
|
|
|
|
raise
|