canaille-globuzma/canaille/oidc/clients.py

282 lines
8.9 KiB
Python
Raw Normal View History

2020-08-17 13:49:48 +00:00
import datetime
2021-12-20 22:57:27 +00:00
2020-08-17 13:49:48 +00:00
import wtforms
2021-12-20 22:57:27 +00:00
from canaille.flaskutils import permissions_needed
2022-01-11 18:49:06 +00:00
from canaille.oidc.models import Client
2021-12-20 22:57:27 +00:00
from flask import abort
from flask import Blueprint
from flask import flash
from flask import redirect
from flask import request
from flask import url_for
from flask_babel import lazy_gettext as _
from flask_themer import render_template
2020-08-17 13:49:48 +00:00
from flask_wtf import FlaskForm
from werkzeug.security import gen_salt
2022-01-11 18:49:06 +00:00
bp = Blueprint("clients", __name__, url_prefix="/admin/client")
2020-08-17 13:49:48 +00:00
@bp.route("/")
2021-12-02 17:23:14 +00:00
@permissions_needed("manage_oidc")
2020-10-29 10:09:31 +00:00
def index(user):
clients = Client.all()
2022-01-11 18:49:06 +00:00
return render_template(
"oidc/admin/client_list.html", clients=clients, menuitem="admin"
)
2020-08-17 13:49:48 +00:00
2021-10-13 09:52:02 +00:00
def client_audiences():
2022-10-17 15:49:52 +00:00
return [(client.dn, client.client_name) for client in Client.all()]
2021-10-13 09:52:02 +00:00
2020-08-17 13:49:48 +00:00
class ClientAdd(FlaskForm):
2022-10-17 15:49:52 +00:00
client_name = wtforms.StringField(
2020-10-28 14:49:14 +00:00
_("Name"),
2020-08-17 13:49:48 +00:00
validators=[wtforms.validators.DataRequired()],
render_kw={"placeholder": "Client Name"},
)
2022-10-17 15:49:52 +00:00
contacts = wtforms.EmailField(
2020-10-28 14:49:14 +00:00
_("Contact"),
2020-08-17 13:49:48 +00:00
validators=[wtforms.validators.Optional()],
render_kw={"placeholder": "admin@mydomain.tld"},
)
2022-10-17 15:49:52 +00:00
client_uri = wtforms.URLField(
2020-10-28 14:49:14 +00:00
_("URI"),
2020-08-17 13:49:48 +00:00
validators=[wtforms.validators.DataRequired()],
render_kw={"placeholder": "https://mydomain.tld"},
)
redirect_uris = wtforms.URLField(
2020-10-28 14:49:14 +00:00
_("Redirect URIs"),
2020-08-17 13:49:48 +00:00
validators=[wtforms.validators.DataRequired()],
render_kw={"placeholder": "https://mydomain.tld/callback"},
)
2022-05-20 12:07:56 +00:00
post_logout_redirect_uris = wtforms.URLField(
_("Post logout redirect URIs"),
validators=[wtforms.validators.Optional()],
render_kw={"placeholder": "https://mydomain.tld/you-have-been-disconnected"},
)
2022-10-17 15:49:52 +00:00
grant_types = wtforms.SelectMultipleField(
2020-10-28 14:49:14 +00:00
_("Grant types"),
2020-08-17 13:49:48 +00:00
validators=[wtforms.validators.DataRequired()],
choices=[
("password", "password"),
("authorization_code", "authorization_code"),
2020-08-20 12:30:42 +00:00
("implicit", "implicit"),
("hybrid", "hybrid"),
2020-08-24 08:52:21 +00:00
("refresh_token", "refresh_token"),
2020-08-17 13:49:48 +00:00
],
2020-08-24 08:52:21 +00:00
default=["authorization_code", "refresh_token"],
2020-08-17 13:49:48 +00:00
)
scope = wtforms.StringField(
2020-10-28 14:49:14 +00:00
_("Scope"),
2020-08-17 13:49:48 +00:00
validators=[wtforms.validators.Optional()],
2020-09-23 15:15:25 +00:00
default="openid profile email",
2020-08-17 13:49:48 +00:00
render_kw={"placeholder": "openid profile"},
)
2022-10-17 15:49:52 +00:00
response_types = wtforms.SelectMultipleField(
2020-10-28 14:49:14 +00:00
_("Response types"),
2020-08-17 13:49:48 +00:00
validators=[wtforms.validators.DataRequired()],
2020-08-20 12:30:42 +00:00
choices=[("code", "code"), ("token", "token"), ("id_token", "id_token")],
2020-08-17 13:49:48 +00:00
default=["code"],
)
token_endpoint_auth_method = wtforms.SelectField(
2020-10-28 14:49:14 +00:00
_("Token Endpoint Auth Method"),
2020-08-17 13:49:48 +00:00
validators=[wtforms.validators.DataRequired()],
choices=[
("client_secret_basic", "client_secret_basic"),
("client_secret_post", "client_secret_post"),
("none", "none"),
],
default="client_secret_basic",
)
audience = wtforms.SelectMultipleField(
2021-10-13 09:52:02 +00:00
_("Token audiences"),
validators=[wtforms.validators.Optional()],
choices=client_audiences,
validate_choice=False,
)
logo_uri = wtforms.URLField(
2020-10-28 14:49:14 +00:00
_("Logo URI"),
2020-08-17 13:49:48 +00:00
validators=[wtforms.validators.Optional()],
render_kw={"placeholder": "https://mydomain.tld/logo.png"},
)
tos_uri = wtforms.URLField(
2020-10-28 14:49:14 +00:00
_("Terms of service URI"),
2020-08-17 13:49:48 +00:00
validators=[wtforms.validators.Optional()],
render_kw={"placeholder": "https://mydomain.tld/tos.html"},
)
policy_uri = wtforms.URLField(
2020-10-28 14:49:14 +00:00
_("Policy URI"),
2020-08-17 13:49:48 +00:00
validators=[wtforms.validators.Optional()],
render_kw={"placeholder": "https://mydomain.tld/policy.html"},
)
software_id = wtforms.StringField(
2020-10-28 14:49:14 +00:00
_("Software ID"),
2020-08-17 13:49:48 +00:00
validators=[wtforms.validators.Optional()],
render_kw={"placeholder": "xyz"},
)
software_version = wtforms.StringField(
2020-10-28 14:49:14 +00:00
_("Software Version"),
2020-08-17 13:49:48 +00:00
validators=[wtforms.validators.Optional()],
render_kw={"placeholder": "1.0"},
)
jwk = wtforms.StringField(
2020-10-28 14:49:14 +00:00
_("JWK"),
2020-08-17 13:49:48 +00:00
validators=[wtforms.validators.Optional()],
render_kw={"placeholder": ""},
)
2022-10-17 15:49:52 +00:00
jwks_uri = wtforms.URLField(
2020-10-28 14:49:14 +00:00
_("JKW URI"),
2020-08-17 13:49:48 +00:00
validators=[wtforms.validators.Optional()],
render_kw={"placeholder": ""},
)
preconsent = wtforms.BooleanField(
2021-10-20 10:05:08 +00:00
_("Pre-consent"),
validators=[wtforms.validators.Optional()],
default=False,
)
2020-08-17 13:49:48 +00:00
@bp.route("/add", methods=["GET", "POST"])
2021-12-02 17:23:14 +00:00
@permissions_needed("manage_oidc")
2020-10-29 10:09:31 +00:00
def add(user):
2020-08-17 13:49:48 +00:00
form = ClientAdd(request.form or None)
if not request.form:
2022-01-11 18:49:06 +00:00
return render_template(
"oidc/admin/client_add.html", form=form, menuitem="admin"
)
2020-08-17 13:49:48 +00:00
if not form.validate():
flash(
2021-10-13 09:52:02 +00:00
_("The client has not been added. Please check your information."),
"error",
2020-08-17 13:49:48 +00:00
)
2022-01-11 18:49:06 +00:00
return render_template(
"oidc/admin/client_add.html", form=form, menuitem="admin"
)
2020-08-17 13:49:48 +00:00
client_id = gen_salt(24)
2021-12-08 14:53:20 +00:00
client_id_issued_at = datetime.datetime.now()
2020-08-17 13:49:48 +00:00
client = Client(
client_id=client_id,
2022-10-17 15:49:52 +00:00
client_id_issued_at=client_id_issued_at,
client_name=form["client_name"].data,
contacts=[form["contacts"].data],
client_uri=form["client_uri"].data,
grant_types=form["grant_types"].data,
redirect_uris=[form["redirect_uris"].data],
2022-05-20 12:07:56 +00:00
post_logout_redirect_uris=[form["post_logout_redirect_uris"].data],
2022-10-17 15:49:52 +00:00
response_types=form["response_types"].data,
scope=form["scope"].data.split(" "),
token_endpoint_auth_method=form["token_endpoint_auth_method"].data,
logo_uri=form["logo_uri"].data,
tos_uri=form["tos_uri"].data,
policy_uri=form["policy_uri"].data,
software_id=form["software_id"].data,
software_version=form["software_version"].data,
jwk=form["jwk"].data,
2022-10-17 15:49:52 +00:00
jwks_uri=form["jwks_uri"].data,
preconsent=form["preconsent"].data,
2022-10-17 15:49:52 +00:00
client_secret=""
if form["token_endpoint_auth_method"].data == "none"
2020-08-17 13:49:48 +00:00
else gen_salt(48),
)
client.audience = [client.dn]
2020-08-17 13:49:48 +00:00
client.save()
flash(
2021-10-13 09:52:02 +00:00
_("The client has been created."),
"success",
2020-08-17 13:49:48 +00:00
)
2022-01-11 18:49:06 +00:00
return redirect(url_for("oidc.clients.edit", client_id=client_id))
2020-08-17 13:49:48 +00:00
@bp.route("/edit/<client_id>", methods=["GET", "POST"])
2021-12-02 17:23:14 +00:00
@permissions_needed("manage_oidc")
2020-10-29 10:09:31 +00:00
def edit(user, client_id):
2020-11-23 16:32:40 +00:00
if request.method == "GET" or request.form.get("action") == "edit":
return client_edit(client_id)
if request.form.get("action") == "delete":
return client_delete(client_id)
abort(400)
def client_edit(client_id):
client = Client.get(client_id)
if not client:
abort(404)
2020-08-17 13:49:48 +00:00
data = dict(client)
data["scope"] = " ".join(data["scope"])
data["redirect_uris"] = data["redirect_uris"][0]
data["contacts"] = data["contacts"][0]
2022-05-20 12:07:56 +00:00
data["post_logout_redirect_uris"] = (
data["post_logout_redirect_uris"][0]
if data["post_logout_redirect_uris"]
else ""
)
data["preconsent"] = client.preconsent
2020-08-17 13:49:48 +00:00
form = ClientAdd(request.form or None, data=data, client=client)
if not request.form:
2020-10-21 10:14:35 +00:00
return render_template(
2022-01-11 18:49:06 +00:00
"oidc/admin/client_edit.html", form=form, client=client, menuitem="admin"
2020-10-21 10:14:35 +00:00
)
2020-08-17 13:49:48 +00:00
if not form.validate():
flash(
2020-10-28 14:49:14 +00:00
_("The client has not been edited. Please check your information."),
2020-08-17 13:49:48 +00:00
"error",
)
else:
client.update(
2022-10-17 15:49:52 +00:00
client_name=form["client_name"].data,
contacts=[form["contacts"].data],
client_uri=form["client_uri"].data,
grant_types=form["grant_types"].data,
redirect_uris=[form["redirect_uris"].data],
2022-05-20 12:07:56 +00:00
post_logout_redirect_uris=[form["post_logout_redirect_uris"].data],
2022-10-17 15:49:52 +00:00
response_types=form["response_types"].data,
scope=form["scope"].data.split(" "),
token_endpoint_auth_method=form["token_endpoint_auth_method"].data,
logo_uri=form["logo_uri"].data,
tos_uri=form["tos_uri"].data,
policy_uri=form["policy_uri"].data,
software_id=form["software_id"].data,
software_version=form["software_version"].data,
jwk=form["jwk"].data,
2022-10-17 15:49:52 +00:00
jwks_uri=form["jwks_uri"].data,
audience=form["audience"].data,
preconsent=form["preconsent"].data,
2020-08-17 13:49:48 +00:00
)
client.save()
flash(
2021-10-13 09:52:02 +00:00
_("The client has been edited."),
"success",
2020-08-17 13:49:48 +00:00
)
2020-10-21 10:14:35 +00:00
return render_template(
2022-01-11 18:49:06 +00:00
"oidc/admin/client_edit.html", form=form, client=client, menuitem="admin"
2020-10-21 10:14:35 +00:00
)
2020-11-23 16:32:40 +00:00
def client_delete(client_id):
client = Client.get(client_id)
if not client:
abort(404)
2020-11-23 16:32:40 +00:00
flash(
2021-10-13 09:52:02 +00:00
_("The client has been deleted."),
"success",
2020-11-23 16:32:40 +00:00
)
client.delete()
2022-01-11 18:49:06 +00:00
return redirect(url_for("oidc.clients.index"))