canaille-globuzma/oidc_ldap_bridge/__init__.py

175 lines
5.2 KiB
Python
Raw Normal View History

2020-08-17 13:49:48 +00:00
import ldap
import os
import toml
2020-08-31 09:23:50 +00:00
import oidc_ldap_bridge.admin
import oidc_ldap_bridge.admin.tokens
import oidc_ldap_bridge.admin.authorizations
import oidc_ldap_bridge.admin.clients
import oidc_ldap_bridge.oauth
import oidc_ldap_bridge.routes
import oidc_ldap_bridge.tokens
import oidc_ldap_bridge.well_known
2020-09-01 15:11:30 +00:00
2020-08-28 14:08:29 +00:00
from cryptography.hazmat.primitives import serialization as crypto_serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend as crypto_default_backend
2020-09-01 15:11:30 +00:00
from flask import Flask, g, request, render_template
from flask_babel import Babel
2020-08-19 14:20:57 +00:00
from .flaskutils import current_user
2020-08-18 15:39:34 +00:00
from .ldaputils import LDAPObjectHelper
2020-08-19 14:20:57 +00:00
from .oauth2utils import config_oauth
2020-09-01 15:11:30 +00:00
from .models import User
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(
{
"SESSION_COOKIE_NAME": "oidc-ldap-bridge",
"OAUTH2_REFRESH_TOKEN_GENERATOR": True,
}
)
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
2020-08-28 14:08:29 +00:00
setup_dev_keypair(app)
if not os.path.exists(app.config["JWT"]["PUBLIC_KEY"]) or not os.path.exists(
app.config["JWT"]["PRIVATE_KEY"]
):
raise Exception("Invalid keypair")
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-08-28 14:08:29 +00:00
def setup_dev_keypair(app):
if not os.environ.get("FLASK_ENV") == "development":
return
if os.path.exists(app.config["JWT"]["PUBLIC_KEY"]) or os.path.exists(
app.config["JWT"]["PRIVATE_KEY"]
):
return
key = rsa.generate_private_key(
backend=crypto_default_backend(), public_exponent=65537, key_size=2048
)
private_key = key.private_bytes(
crypto_serialization.Encoding.PEM,
crypto_serialization.PrivateFormat.PKCS8,
crypto_serialization.NoEncryption(),
)
public_key = key.public_key().public_bytes(
crypto_serialization.Encoding.OpenSSH, crypto_serialization.PublicFormat.OpenSSH
)
with open(app.config["JWT"]["PUBLIC_KEY"], "wb") as fd:
fd.write(public_key)
with open(app.config["JWT"]["PRIVATE_KEY"], "wb") as fd:
fd.write(private_key)
2020-08-17 13:49:48 +00:00
def setup_app(app):
2020-08-18 15:39:34 +00:00
app.url_map.strict_slashes = False
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-01 15:11:30 +00:00
base = app.config["LDAP"]["USER_BASE"]
if base.endswith(app.config["LDAP"]["ROOT_DN"]):
base = base[: -len(app.config["LDAP"]["ROOT_DN"]) - 1]
User.base = base
2020-08-18 15:39:34 +00:00
config_oauth(app)
2020-08-31 09:23:50 +00:00
app.register_blueprint(oidc_ldap_bridge.routes.bp)
app.register_blueprint(oidc_ldap_bridge.oauth.bp, url_prefix="/oauth")
app.register_blueprint(oidc_ldap_bridge.tokens.bp, url_prefix="/token")
app.register_blueprint(oidc_ldap_bridge.well_known.bp, url_prefix="/.well-known")
app.register_blueprint(oidc_ldap_bridge.admin.tokens.bp, url_prefix="/admin/token")
2020-08-26 15:23:53 +00:00
app.register_blueprint(
2020-08-31 09:23:50 +00:00
oidc_ldap_bridge.admin.authorizations.bp, url_prefix="/admin/authorization"
2020-08-26 15:23:53 +00:00
)
2020-08-31 11:55:45 +00:00
app.register_blueprint(
oidc_ldap_bridge.admin.clients.bp, url_prefix="/admin/client"
)
2020-08-18 15:39:34 +00:00
babel = Babel(app)
2020-08-17 13:49:48 +00:00
@app.before_request
def before_request():
2020-08-18 15:39:34 +00:00
LDAPObjectHelper.root_dn = app.config["LDAP"]["ROOT_DN"]
2020-08-17 13:49:48 +00:00
g.ldap = ldap.initialize(app.config["LDAP"]["URI"])
g.ldap.simple_bind_s(
2020-08-18 15:39:34 +00:00
app.config["LDAP"]["BIND_DN"], app.config["LDAP"]["BIND_PW"]
2020-08-17 13:49:48 +00:00
)
@app.after_request
def after_request(response):
if "ldap" in g:
g.ldap.unbind_s()
return response
@app.context_processor
def global_processor():
return {
"logo_url": app.config.get("LOGO"),
"website_name": app.config.get("NAME"),
2020-08-19 14:20:57 +00:00
"user": current_user(),
"menu": True,
2020-08-17 13:49:48 +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")
return request.accept_languages.best_match(["fr", "en"])
@babel.timezoneselector
def get_timezone():
user = getattr(g, "user", None)
if user is not None:
return user.timezone
2020-08-19 14:20:57 +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