forked from Github-Mirrors/canaille
modals are HTML pages instead of JS elements
This will help providing the very same user experience for users with and without javascript. We will still be able to re-enable javascript modals in the future, but this should be done from the ground up, HTML first and javascript after.
This commit is contained in:
parent
54abdaea3b
commit
b4908d5e57
24 changed files with 313 additions and 238 deletions
|
@ -13,6 +13,11 @@ Fixed
|
||||||
|
|
||||||
- The `check` command uses the default configuration values.
|
- The `check` command uses the default configuration values.
|
||||||
|
|
||||||
|
Changed
|
||||||
|
*******
|
||||||
|
|
||||||
|
- Modals do not need use javascript at the moment. :issue:`158` :pr:`144`
|
||||||
|
|
||||||
[0.0.30] - 2023-07-06
|
[0.0.30] - 2023-07-06
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,7 @@ class HTMXFormMixin:
|
||||||
fieldlist, context = self.field_from_name(fieldlist_name)
|
fieldlist, context = self.field_from_name(fieldlist_name)
|
||||||
|
|
||||||
if not fieldlist or not isinstance(fieldlist, wtforms.FieldList):
|
if not fieldlist or not isinstance(fieldlist, wtforms.FieldList):
|
||||||
abort(400)
|
abort(400, f"{field_name} is not a valid field list")
|
||||||
|
|
||||||
if fieldlist.render_kw and (
|
if fieldlist.render_kw and (
|
||||||
"readonly" in fieldlist.render_kw or "disabled" in fieldlist.render_kw
|
"readonly" in fieldlist.render_kw or "disabled" in fieldlist.render_kw
|
||||||
|
@ -137,7 +137,7 @@ class HTMXFormMixin:
|
||||||
fieldlist, context = self.field_from_name(fieldlist_name)
|
fieldlist, context = self.field_from_name(fieldlist_name)
|
||||||
|
|
||||||
if not fieldlist or not isinstance(fieldlist, wtforms.FieldList):
|
if not fieldlist or not isinstance(fieldlist, wtforms.FieldList):
|
||||||
abort(400)
|
abort(400, f"{field_name} is not a valid field list")
|
||||||
|
|
||||||
if fieldlist.render_kw and (
|
if fieldlist.render_kw and (
|
||||||
"readonly" in fieldlist.render_kw or "disabled" in fieldlist.render_kw
|
"readonly" in fieldlist.render_kw or "disabled" in fieldlist.render_kw
|
||||||
|
|
|
@ -529,6 +529,13 @@ def profile_settings(user, edited_user):
|
||||||
):
|
):
|
||||||
return profile_settings_edit(user, edited_user)
|
return profile_settings_edit(user, edited_user)
|
||||||
|
|
||||||
|
if (
|
||||||
|
request.form.get("action") == "confirm-delete"
|
||||||
|
and BaseBackend.get().has_account_lockability()
|
||||||
|
and not edited_user.locked
|
||||||
|
):
|
||||||
|
return render_template("modals/delete-account.html", edited_user=edited_user)
|
||||||
|
|
||||||
if request.form.get("action") == "delete":
|
if request.form.get("action") == "delete":
|
||||||
return profile_delete(user, edited_user)
|
return profile_delete(user, edited_user)
|
||||||
|
|
||||||
|
@ -567,6 +574,13 @@ def profile_settings(user, edited_user):
|
||||||
|
|
||||||
return profile_settings_edit(user, edited_user)
|
return profile_settings_edit(user, edited_user)
|
||||||
|
|
||||||
|
if (
|
||||||
|
request.form.get("action") == "confirm-lock"
|
||||||
|
and BaseBackend.get().has_account_lockability()
|
||||||
|
and not edited_user.locked
|
||||||
|
):
|
||||||
|
return render_template("modals/lock-account.html", edited_user=edited_user)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
request.form.get("action") == "lock"
|
request.form.get("action") == "lock"
|
||||||
and BaseBackend.get().has_account_lockability()
|
and BaseBackend.get().has_account_lockability()
|
||||||
|
@ -589,7 +603,7 @@ def profile_settings(user, edited_user):
|
||||||
|
|
||||||
return profile_settings_edit(user, edited_user)
|
return profile_settings_edit(user, edited_user)
|
||||||
|
|
||||||
abort(400)
|
abort(400, f"bad form action: {request.form.get('action')}")
|
||||||
|
|
||||||
|
|
||||||
def profile_settings_edit(editor, edited_user):
|
def profile_settings_edit(editor, edited_user):
|
||||||
|
|
|
@ -65,10 +65,13 @@ def group(user, group):
|
||||||
):
|
):
|
||||||
return edit_group(group)
|
return edit_group(group)
|
||||||
|
|
||||||
|
if request.form.get("action") == "confirm-delete":
|
||||||
|
return render_template("modals/delete-group.html", group=group)
|
||||||
|
|
||||||
if request.form.get("action") == "delete":
|
if request.form.get("action") == "delete":
|
||||||
return delete_group(group)
|
return delete_group(group)
|
||||||
|
|
||||||
abort(400)
|
abort(400, f"bad form action: {request.form.get('action')}")
|
||||||
|
|
||||||
|
|
||||||
def edit_group(group):
|
def edit_group(group):
|
||||||
|
|
|
@ -89,8 +89,12 @@ def add(user):
|
||||||
@bp.route("/edit/<client:client>", methods=["GET", "POST"])
|
@bp.route("/edit/<client:client>", methods=["GET", "POST"])
|
||||||
@permissions_needed("manage_oidc")
|
@permissions_needed("manage_oidc")
|
||||||
def edit(user, client):
|
def edit(user, client):
|
||||||
|
if request.form.get("action") == "confirm-delete":
|
||||||
|
return render_template("modals/delete-client.html", client=client)
|
||||||
|
|
||||||
if request.form and request.form.get("action") == "delete":
|
if request.form and request.form.get("action") == "delete":
|
||||||
return client_delete(client)
|
return client_delete(client)
|
||||||
|
|
||||||
return client_edit(client)
|
return client_edit(client)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -159,3 +159,7 @@ class ClientAddForm(HTMXForm):
|
||||||
validators=[wtforms.validators.Optional()],
|
validators=[wtforms.validators.Optional()],
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TokenRevokationForm(HTMXForm):
|
||||||
|
pass
|
||||||
|
|
|
@ -4,12 +4,11 @@ from canaille.app import models
|
||||||
from canaille.app.flask import permissions_needed
|
from canaille.app.flask import permissions_needed
|
||||||
from canaille.app.flask import render_htmx_template
|
from canaille.app.flask import render_htmx_template
|
||||||
from canaille.app.forms import TableForm
|
from canaille.app.forms import TableForm
|
||||||
|
from canaille.oidc.forms import TokenRevokationForm
|
||||||
from flask import abort
|
from flask import abort
|
||||||
from flask import Blueprint
|
from flask import Blueprint
|
||||||
from flask import flash
|
from flask import flash
|
||||||
from flask import redirect
|
|
||||||
from flask import request
|
from flask import request
|
||||||
from flask import url_for
|
|
||||||
from flask_babel import gettext as _
|
from flask_babel import gettext as _
|
||||||
from flask_themer import render_template
|
from flask_themer import render_template
|
||||||
|
|
||||||
|
@ -31,18 +30,23 @@ def index(user):
|
||||||
@bp.route("/<token:token>", methods=["GET", "POST"])
|
@bp.route("/<token:token>", methods=["GET", "POST"])
|
||||||
@permissions_needed("manage_oidc")
|
@permissions_needed("manage_oidc")
|
||||||
def view(user, token):
|
def view(user, token):
|
||||||
|
form = TokenRevokationForm(request.form or None)
|
||||||
|
|
||||||
|
if request.form and form.validate():
|
||||||
|
if request.form.get("action") == "confirm-revoke":
|
||||||
|
return render_template("modals/revoke-token.html", token=token)
|
||||||
|
|
||||||
|
elif request.form.get("action") == "revoke":
|
||||||
|
token.revokation_date = datetime.datetime.now(datetime.timezone.utc)
|
||||||
|
token.save()
|
||||||
|
flash(_("The token has successfully been revoked."), "success")
|
||||||
|
|
||||||
|
else:
|
||||||
|
abort(400, f"bad form action: {request.form.get('action')}")
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"oidc/admin/token_view.html",
|
"oidc/admin/token_view.html",
|
||||||
token=token,
|
token=token,
|
||||||
menuitem="admin",
|
menuitem="admin",
|
||||||
|
form=form,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/<token:token>/revoke", methods=["GET", "POST"])
|
|
||||||
@permissions_needed("manage_oidc")
|
|
||||||
def revoke(user, token):
|
|
||||||
token.revokation_date = datetime.datetime.now(datetime.timezone.utc)
|
|
||||||
token.save()
|
|
||||||
flash(_("The token has successfully been revoked."), "success")
|
|
||||||
|
|
||||||
return redirect(url_for("oidc.tokens.view", token=token))
|
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
$('.confirm').click(function(e){
|
|
||||||
e.preventDefault();
|
|
||||||
$('.ui.modal')
|
|
||||||
.modal({
|
|
||||||
onApprove : function() {
|
|
||||||
$('.confirm').unbind('click').click();
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.modal('show');
|
|
||||||
});
|
|
|
@ -1,10 +0,0 @@
|
||||||
$('.confirm').click(function(e){
|
|
||||||
e.preventDefault();
|
|
||||||
$('#modal-' + e.target.id + '.ui.modal')
|
|
||||||
.modal({
|
|
||||||
onApprove : function() {
|
|
||||||
$('.confirm').unbind('click').click();
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.modal('show');
|
|
||||||
});
|
|
|
@ -10,10 +10,6 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- endblock -%}
|
{%- endblock -%}
|
||||||
|
|
||||||
{% block script %}
|
|
||||||
<script src="/static/js/confirm.js" defer></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block submenu %}
|
{% block submenu %}
|
||||||
<nav class="ui bottom attached two item borderless menu">
|
<nav class="ui bottom attached two item borderless menu">
|
||||||
<a class="{% if edited_group %}active {% endif %}item" href="{{ url_for('groups.groups') }}">
|
<a class="{% if edited_group %}active {% endif %}item" href="{{ url_for('groups.groups') }}">
|
||||||
|
@ -28,24 +24,6 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% if edited_group %}
|
|
||||||
<div class="ui basic modal" id="modal-delete">
|
|
||||||
<div class="ui icon header">
|
|
||||||
<i class="trash alternate icon"></i>
|
|
||||||
{% trans %}Group deletion{% endtrans %}
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<p>
|
|
||||||
{% trans %}Are you sure you want to delete this group? This action is unrevokable and all the data about this group will be removed.{% endtrans %}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="actions">
|
|
||||||
<div class="ui inverted cancel button">{% trans %}Cancel{% endtrans %}</div>
|
|
||||||
<div class="ui inverted red approve button">{% trans %}Delete{% endtrans %}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="ui clearing segment">
|
<div class="ui clearing segment">
|
||||||
<h2 class="ui center aligned header">
|
<h2 class="ui center aligned header">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
@ -78,7 +56,7 @@
|
||||||
<div class="ui right aligned container">
|
<div class="ui right aligned container">
|
||||||
<div class="ui stackable buttons">
|
<div class="ui stackable buttons">
|
||||||
{% if edited_group %}
|
{% if edited_group %}
|
||||||
<button type="submit" class="ui right floated basic negative button confirm" name="action" value="delete" id="delete">
|
<button type="submit" class="ui right floated basic negative button confirm" name="action" value="confirm-delete" id="confirm-delete">
|
||||||
{% trans %}Delete group{% endtrans %}
|
{% trans %}Delete group{% endtrans %}
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
32
canaille/templates/modals/delete-account.html
Normal file
32
canaille/templates/modals/delete-account.html
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{% extends theme('base.html') %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div id="modal-delete" class="ui warning message">
|
||||||
|
<form method="post" action="{{ request.url }}">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{ request.form.get("csrf_token") }}">
|
||||||
|
<div class="ui icon header">
|
||||||
|
<i class="user minus icon"></i>
|
||||||
|
{% trans %}Account deletion{% endtrans %}
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<p>
|
||||||
|
{% if user != edited_user %}
|
||||||
|
{% trans user_name=(edited_user.formatted_name[0] or edited_user.identifier) %}
|
||||||
|
Are you sure you want to delete the account of {{ user_name }}? This action is unrevokable and all the data about this user will be removed.
|
||||||
|
{% endtrans %}
|
||||||
|
{% else %}
|
||||||
|
{% trans %}
|
||||||
|
Are you sure you want to delete your account? This action is unrevokable and all your data will be removed forever.
|
||||||
|
{% endtrans %}
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="ui center aligned container">
|
||||||
|
<div class="ui stackable buttons">
|
||||||
|
<a class="ui cancel button" href="{{ request.url }}">{% trans %}Cancel{% endtrans %}</a>
|
||||||
|
<button name="action" value="delete" class="ui red approve button">{% trans %}Delete{% endtrans %}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
26
canaille/templates/modals/delete-client.html
Normal file
26
canaille/templates/modals/delete-client.html
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{% extends theme('base.html') %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="ui warning message">
|
||||||
|
<form method="post" action="{{ request.url }}">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{ request.form.get("csrf_token") }}">
|
||||||
|
<div class="ui icon header">
|
||||||
|
<i class="trash icon"></i>
|
||||||
|
{% trans %}Client deletion{% endtrans %}
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<p>
|
||||||
|
{% trans client_name=client.client_name %}
|
||||||
|
Are you sure you want to delete the client {{ client_name }}? This action is unrevokable and all the data about this client will be removed.
|
||||||
|
{% endtrans %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="ui center aligned container">
|
||||||
|
<div class="ui stackable buttons">
|
||||||
|
<a href="{{ request.url }}" class="ui cancel button">{% trans %}Cancel{% endtrans %}</a>
|
||||||
|
<button name="action" value="delete" class="ui red approve button">{% trans %}Delete{% endtrans %}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
31
canaille/templates/modals/delete-group.html
Normal file
31
canaille/templates/modals/delete-group.html
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
{% extends theme('base.html') %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="ui warning message" id="modal-delete">
|
||||||
|
<form method="post" action="{{ request.url }}">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{ request.form.get("csrf_token") }}">
|
||||||
|
<div class="ui icon header">
|
||||||
|
<i class="trash alternate icon"></i>
|
||||||
|
{% trans %}Group deletion{% endtrans %}
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<p>
|
||||||
|
{% trans group_name=group.display_name %}
|
||||||
|
Are you sure you want to delete the group "{{ group_name }}"?
|
||||||
|
This action is unrevokable and all the data about this group will be removed.
|
||||||
|
{% endtrans %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="ui center aligned container">
|
||||||
|
<div class="ui stackable buttons">
|
||||||
|
<a href="{{ request.url }}" class="ui cancel button">
|
||||||
|
{% trans %}Cancel{% endtrans %}
|
||||||
|
</a>
|
||||||
|
<button name="action" value="delete" id="delete" class="ui red approve button">
|
||||||
|
{% trans %}Delete{% endtrans %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
32
canaille/templates/modals/lock-account.html
Normal file
32
canaille/templates/modals/lock-account.html
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{% extends theme('base.html') %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div id="modal-lock" class="ui warning message">
|
||||||
|
<form method="post" action="{{ request.url }}">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{ request.form.get("csrf_token") }}">
|
||||||
|
<div class="ui icon header">
|
||||||
|
<i class="lock icon"></i>
|
||||||
|
{% trans %}Account locking{% endtrans %}
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<p>
|
||||||
|
{% if user != edited_user %}
|
||||||
|
{% trans user_name=(edited_user.formatted_name[0] or edited_user.identifier) %}
|
||||||
|
Are you sure you want to lock the account of {{ user_name }} ? The user won't be able to login until their account is unlocked.
|
||||||
|
{% endtrans %}
|
||||||
|
{% else %}
|
||||||
|
{% trans %}
|
||||||
|
Are you sure you want to lock your account? You won't be able to login until your account is unlocked.
|
||||||
|
{% endtrans %}
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="ui center aligned container">
|
||||||
|
<div class="ui stackable buttons">
|
||||||
|
<a class="ui cancel button" href="{{ request.url }}">{% trans %}Cancel{% endtrans %}</a>
|
||||||
|
<button type="submit" name="action" value="lock" class="ui red approve button">{% trans %}Lock{% endtrans %}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
26
canaille/templates/modals/revoke-token.html
Normal file
26
canaille/templates/modals/revoke-token.html
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{% extends theme('base.html') %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="ui warning message">
|
||||||
|
<form method="post" action="{{ request.url }}">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{ request.form.get("csrf_token") }}">
|
||||||
|
<div class="ui icon header">
|
||||||
|
<i class="trash icon"></i>
|
||||||
|
{% trans %}Token revokation{% endtrans %}
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<p>
|
||||||
|
{% trans %}
|
||||||
|
Are you sure you want to revoke this token? This action is unrevokable.
|
||||||
|
{% endtrans %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="ui center aligned container">
|
||||||
|
<div class="ui stackable buttons">
|
||||||
|
<a href="{{ request.url }}" class="ui cancel button">{% trans %}Cancel{% endtrans %}</a>
|
||||||
|
<button name="action" value="revoke" class="ui red approve button">{% trans %}Revoke{% endtrans %}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -5,10 +5,6 @@
|
||||||
{%- trans %}Edit a client{% endtrans -%}
|
{%- trans %}Edit a client{% endtrans -%}
|
||||||
{%- endblock -%}
|
{%- endblock -%}
|
||||||
|
|
||||||
{% block script %}
|
|
||||||
<script src="/static/js/admin/client_edit.js" defer></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block submenu %}
|
{% block submenu %}
|
||||||
<nav class="ui bottom attached five item borderless menu">
|
<nav class="ui bottom attached five item borderless menu">
|
||||||
<a class="item" href="{{ url_for('admin.mail_index') }}">
|
<a class="item" href="{{ url_for('admin.mail_index') }}">
|
||||||
|
@ -35,20 +31,6 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="ui basic modal">
|
|
||||||
<div class="ui icon header">
|
|
||||||
<i class="trash icon"></i>
|
|
||||||
{% trans %}Client deletion{% endtrans %}
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<p>{{ _("Are you sure you want to delete this client? This action is unrevokable and all the data about this client will be removed.") }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="actions">
|
|
||||||
<div class="ui inverted cancel button">{% trans %}Cancel{% endtrans %}</div>
|
|
||||||
<div class="ui inverted red approve button">{% trans %}Delete{% endtrans %}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="ui top attached segment">
|
<div class="ui top attached segment">
|
||||||
<h2 class="ui center aligned header">
|
<h2 class="ui center aligned header">
|
||||||
{% trans %}Edit a client{% endtrans %}
|
{% trans %}Edit a client{% endtrans %}
|
||||||
|
@ -91,7 +73,7 @@
|
||||||
|
|
||||||
<div class="ui right aligned container">
|
<div class="ui right aligned container">
|
||||||
<div class="ui stackable buttons">
|
<div class="ui stackable buttons">
|
||||||
<button type="submit" class="ui right floated negative basic button confirm" name="action" value="delete" id="delete" formnovalidate>
|
<button type="submit" class="ui right floated negative basic button confirm" name="action" value="confirm-delete" id="confirm-delete" formnovalidate>
|
||||||
{{ _("Delete the client") }}
|
{{ _("Delete the client") }}
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" class="ui right floated primary button" name="action" value="edit" id="edit">
|
<button type="submit" class="ui right floated primary button" name="action" value="edit" id="edit">
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
{%- endblock -%}
|
{%- endblock -%}
|
||||||
|
|
||||||
{% block script %}
|
{% block script %}
|
||||||
<script src="/static/js/confirm.js" defer></script>
|
|
||||||
<script src="/static/js/copy.js" defer></script>
|
<script src="/static/js/copy.js" defer></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -36,118 +35,104 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="modal-delete" class="ui basic modal">
|
|
||||||
<div class="ui icon header">
|
|
||||||
<i class="trash icon"></i>
|
|
||||||
{% trans %}Token deletion{% endtrans %}
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<p>
|
|
||||||
{{ _("Are you sure you want to revoke this token? This action is unrevokable.") }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="actions">
|
|
||||||
<div class="ui inverted cancel button">{% trans %}Cancel{% endtrans %}</div>
|
|
||||||
<div class="ui inverted red approve button">{% trans %}Delete{% endtrans %}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="loginform">
|
<div class="loginform">
|
||||||
<h3 class="ui top attached header">
|
<h3 class="ui top attached header">
|
||||||
{% trans %}Token details{% endtrans %}
|
{% trans %}Token details{% endtrans %}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div class="ui attached clearing segment">
|
<div class="ui attached clearing segment">
|
||||||
<table class="ui celled striped table">
|
{% call fui.render_form(form, class_="token-form info") %}
|
||||||
<tr>
|
<table class="ui celled striped table">
|
||||||
<td>{{ _("Client") }}</td>
|
<tr>
|
||||||
<td>
|
<td>{{ _("Client") }}</td>
|
||||||
<a href="{{ url_for("oidc.clients.edit", client=token.client) }}">
|
<td>
|
||||||
{{ token.client.client_name }}
|
<a href="{{ url_for("oidc.clients.edit", client=token.client) }}">
|
||||||
</a>
|
{{ token.client.client_name }}
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ _("Subject") }}</td>
|
|
||||||
<td>
|
|
||||||
<a href="{{ url_for("account.profile_edition", edited_user=token.subject) }}">
|
|
||||||
{{ token.subject.identifier }}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ _("Scope") }}</td>
|
|
||||||
<td>
|
|
||||||
<ul class="ui list">
|
|
||||||
{% for scope in token.scope %}
|
|
||||||
<li class="item">{{ scope }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ _("Audience") }}</td>
|
|
||||||
<td>
|
|
||||||
<ul class="ui list">
|
|
||||||
{% for client in token.audience %}
|
|
||||||
<li class="item">
|
|
||||||
<a href="{{ url_for("oidc.clients.edit", client=client) }}">
|
|
||||||
{{ client.client_name }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ _("Issue date") }}</td>
|
|
||||||
<td>{{ token.issue_date }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ _("Expiration date") }}</td>
|
|
||||||
<td>{{ token.expire_date }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ _("Revokation date") }}</td>
|
|
||||||
<td>
|
|
||||||
{% if token.revokation_date %}
|
|
||||||
{{ token.revokation_date }}
|
|
||||||
{% else %}
|
|
||||||
<a href="{{ url_for("oidc.tokens.revoke", token=token) }}" class="ui negative right floated button confirm" name="action" value="delete" id="delete">
|
|
||||||
{% trans %}Revoke token{% endtrans %}
|
|
||||||
</a>
|
</a>
|
||||||
<p>
|
</td>
|
||||||
{% trans %}This token has not been revoked{% endtrans %}
|
</tr>
|
||||||
</p>
|
<tr>
|
||||||
{% endif %}
|
<td>{{ _("Subject") }}</td>
|
||||||
</td>
|
<td>
|
||||||
</tr>
|
<a href="{{ url_for("account.profile_edition", edited_user=token.subject) }}">
|
||||||
<tr>
|
{{ token.subject.identifier }}
|
||||||
<td>{{ _("Token type") }}</td>
|
</a>
|
||||||
<td>{{ token.type }}</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ _("Access token") }}</td>
|
<td>{{ _("Scope") }}</td>
|
||||||
<td class="ui fluid action input">
|
<td>
|
||||||
<input type="text" value="{{ token.access_token }}" readonly class="copy-text" id="access-token" data-copy="access-token">
|
<ul class="ui list">
|
||||||
<button class="ui primary right labeled icon button copy-button" data-copy="access-token">
|
{% for scope in token.scope %}
|
||||||
<i class="copy icon"></i>
|
<li class="item">{{ scope }}</li>
|
||||||
{% trans %}Copy{% endtrans %}
|
{% endfor %}
|
||||||
</button>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ _("Audience") }}</td>
|
||||||
|
<td>
|
||||||
|
<ul class="ui list">
|
||||||
|
{% for client in token.audience %}
|
||||||
|
<li class="item">
|
||||||
|
<a href="{{ url_for("oidc.clients.edit", client=client) }}">
|
||||||
|
{{ client.client_name }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ _("Issue date") }}</td>
|
||||||
|
<td>{{ token.issue_date }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ _("Expiration date") }}</td>
|
||||||
|
<td>{{ token.expire_date }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ _("Revokation date") }}</td>
|
||||||
|
<td>
|
||||||
|
{% if token.revokation_date %}
|
||||||
|
{{ token.revokation_date }}
|
||||||
|
{% else %}
|
||||||
|
<button type="submit" class="ui negative right floated button confirm" name="action" value="confirm-revoke" id="confirm-revoke">
|
||||||
|
{% trans %}Revoke token{% endtrans %}
|
||||||
|
</button>
|
||||||
|
<p>
|
||||||
|
{% trans %}This token has not been revoked{% endtrans %}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ _("Token type") }}</td>
|
||||||
|
<td>{{ token.type }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ _("Access token") }}</td>
|
||||||
|
<td class="ui fluid action input">
|
||||||
|
<input type="text" value="{{ token.access_token }}" readonly class="copy-text" id="access-token" data-copy="access-token">
|
||||||
|
<button class="ui primary right labeled icon button copy-button" data-copy="access-token">
|
||||||
|
<i class="copy icon"></i>
|
||||||
|
{% trans %}Copy{% endtrans %}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ _("Refresh token") }}</td>
|
<td>{{ _("Refresh token") }}</td>
|
||||||
<td class="ui fluid action input">
|
<td class="ui fluid action input">
|
||||||
<input type="text" value="{{ token.refresh_token }}" readonly class="copy-text" id="refresh-token" data-copy="refresh-token">
|
<input type="text" value="{{ token.refresh_token }}" readonly class="copy-text" id="refresh-token" data-copy="refresh-token">
|
||||||
<button class="ui primary right labeled icon button copy-button" data-copy="refresh-token">
|
<button class="ui primary right labeled icon button copy-button" data-copy="refresh-token">
|
||||||
<i class="copy icon"></i>
|
<i class="copy icon"></i>
|
||||||
{% trans %}Copy{% endtrans %}
|
{% trans %}Copy{% endtrans %}
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
{% endcall %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -9,10 +9,6 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- endblock -%}
|
{%- endblock -%}
|
||||||
|
|
||||||
{% block script %}
|
|
||||||
<script src="/static/js/confirm.js" defer></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block submenu %}
|
{% block submenu %}
|
||||||
<nav class="ui bottom attached two item borderless menu">
|
<nav class="ui bottom attached two item borderless menu">
|
||||||
<a class="item" href="{{ url_for('account.profile_edition', edited_user=edited_user) }}">
|
<a class="item" href="{{ url_for('account.profile_edition', edited_user=edited_user) }}">
|
||||||
|
@ -40,50 +36,6 @@
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% if self_deletion or user.can_manage_users %}
|
|
||||||
<div id="modal-delete" class="ui basic modal">
|
|
||||||
<div class="ui icon header">
|
|
||||||
<i class="user minus icon"></i>
|
|
||||||
{% trans %}Account deletion{% endtrans %}
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<p>
|
|
||||||
{% if user.user_name != edited_user.user_name %}
|
|
||||||
{{ _("Are you sure you want to delete this user? This action is unrevokable and all the data about this user will be removed.") }}
|
|
||||||
{% else %}
|
|
||||||
{{ _("Are you sure you want to delete your account? This action is unrevokable and all your data will be removed forever.") }}
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="actions">
|
|
||||||
<div class="ui inverted cancel button">{% trans %}Cancel{% endtrans %}</div>
|
|
||||||
<div class="ui inverted red approve button">{% trans %}Delete{% endtrans %}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if has_account_lockability and user.can_manage_users and not edited_user.locked %}
|
|
||||||
<div id="modal-lock" class="ui basic modal">
|
|
||||||
<div class="ui icon header">
|
|
||||||
<i class="lock icon"></i>
|
|
||||||
{% trans %}Account locking{% endtrans %}
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<p>
|
|
||||||
{% if user.user_name != edited_user.user_name %}
|
|
||||||
{{ _("Are you sure you want to lock this account? The user won't be able to login until their account is unlocked.") }}
|
|
||||||
{% else %}
|
|
||||||
{{ _("Are you sure you want to lock your account? You won't be abel to login until your account is unlocked.") }}
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="actions">
|
|
||||||
<div class="ui inverted cancel button">{% trans %}Cancel{% endtrans %}</div>
|
|
||||||
<div class="ui inverted red approve button">{% trans %}Lock{% endtrans %}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="ui clearing segment">
|
<div class="ui clearing segment">
|
||||||
<h2 class="ui center aligned header">
|
<h2 class="ui center aligned header">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
@ -173,13 +125,13 @@
|
||||||
<div class="ui right aligned container">
|
<div class="ui right aligned container">
|
||||||
<div class="ui stackable buttons">
|
<div class="ui stackable buttons">
|
||||||
{% if has_account_lockability and "lock_date" in user.write and not edited_user.locked %}
|
{% if has_account_lockability and "lock_date" in user.write and not edited_user.locked %}
|
||||||
<button type="submit" class="ui right floated basic negative button confirm" name="action" value="lock" id="lock" formnovalidate>
|
<button type="submit" class="ui right floated basic negative button confirm" name="action" value="confirm-lock" id="lock" formnovalidate>
|
||||||
{% trans %}Lock the account{% endtrans %}
|
{% trans %}Lock the account{% endtrans %}
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if user.can_manage_users or self_deletion %}
|
{% if user.can_manage_users or self_deletion %}
|
||||||
<button type="submit" class="ui right floated basic negative button confirm" name="action" value="delete" id="delete" formnovalidate>
|
<button type="submit" class="ui right floated basic negative button confirm" name="action" value="confirm-delete" id="delete" formnovalidate>
|
||||||
{% if user.user_name != edited_user.user_name %}
|
{% if user.user_name != edited_user.user_name %}
|
||||||
{{ _("Delete the user") }}
|
{{ _("Delete the user") }}
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
|
@ -302,6 +302,7 @@ def test_admin_self_deletion(testclient, backend):
|
||||||
sess["user_id"] = [admin.id]
|
sess["user_id"] = [admin.id]
|
||||||
|
|
||||||
res = testclient.get("/profile/temp/settings")
|
res = testclient.get("/profile/temp/settings")
|
||||||
|
res = res.form.submit(name="action", value="confirm-delete")
|
||||||
res = (
|
res = (
|
||||||
res.form.submit(name="action", value="delete", status=302)
|
res.form.submit(name="action", value="delete", status=302)
|
||||||
.follow(status=302)
|
.follow(status=302)
|
||||||
|
@ -336,6 +337,7 @@ def test_user_self_deletion(testclient, backend):
|
||||||
]
|
]
|
||||||
res = testclient.get("/profile/temp/settings")
|
res = testclient.get("/profile/temp/settings")
|
||||||
res.mustcontain("Delete my account")
|
res.mustcontain("Delete my account")
|
||||||
|
res = res.form.submit(name="action", value="confirm-delete")
|
||||||
res = (
|
res = (
|
||||||
res.form.submit(name="action", value="delete", status=302)
|
res.form.submit(name="action", value="delete", status=302)
|
||||||
.follow(status=302)
|
.follow(status=302)
|
||||||
|
|
|
@ -176,7 +176,8 @@ def test_moderator_can_create_edit_and_delete_group(
|
||||||
res.mustcontain(member.formatted_name[0])
|
res.mustcontain(member.formatted_name[0])
|
||||||
|
|
||||||
# Group is deleted
|
# Group is deleted
|
||||||
res = form.submit(name="action", value="delete", status=302)
|
res = res.forms["editgroupform"].submit(name="action", value="confirm-delete")
|
||||||
|
res = res.form.submit(name="action", value="delete", status=302)
|
||||||
assert models.Group.get(display_name="bar") is None
|
assert models.Group.get(display_name="bar") is None
|
||||||
assert ("success", "The group bar has been sucessfully deleted") in res.flashes
|
assert ("success", "The group bar has been sucessfully deleted") in res.flashes
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,9 @@ def test_user_creation_edition_and_deletion(
|
||||||
|
|
||||||
# User have been deleted.
|
# User have been deleted.
|
||||||
res = testclient.get("/profile/george/settings", status=200)
|
res = testclient.get("/profile/george/settings", status=200)
|
||||||
res = res.form.submit(name="action", value="delete", status=302).follow(status=200)
|
res = res.form.submit(name="action", value="confirm-delete", status=200)
|
||||||
|
res = res.form.submit(name="action", value="delete", status=302)
|
||||||
|
res = res.follow(status=200)
|
||||||
assert models.User.get_from_login("george") is None
|
assert models.User.get_from_login("george") is None
|
||||||
res.mustcontain(no="george")
|
res.mustcontain(no="george")
|
||||||
|
|
||||||
|
|
|
@ -317,6 +317,7 @@ def test_account_locking(
|
||||||
res.mustcontain("Lock the account")
|
res.mustcontain("Lock the account")
|
||||||
res.mustcontain(no="Unlock")
|
res.mustcontain(no="Unlock")
|
||||||
|
|
||||||
|
res = res.form.submit(name="action", value="confirm-lock")
|
||||||
res = res.form.submit(name="action", value="lock")
|
res = res.form.submit(name="action", value="lock")
|
||||||
user = models.User.get(id=user.id)
|
user = models.User.get(id=user.id)
|
||||||
assert user.lock_date <= datetime.datetime.now(datetime.timezone.utc)
|
assert user.lock_date <= datetime.datetime.now(datetime.timezone.utc)
|
||||||
|
@ -329,7 +330,7 @@ def test_account_locking(
|
||||||
user = models.User.get(id=user.id)
|
user = models.User.get(id=user.id)
|
||||||
assert not user.lock_date
|
assert not user.lock_date
|
||||||
assert not user.locked
|
assert not user.locked
|
||||||
assert "The account has been unlocked"
|
res.mustcontain("The account has been unlocked")
|
||||||
res.mustcontain("Lock the account")
|
res.mustcontain("Lock the account")
|
||||||
res.mustcontain(no="Unlock")
|
res.mustcontain(no="Unlock")
|
||||||
|
|
||||||
|
|
|
@ -230,7 +230,9 @@ def test_client_delete(testclient, logged_admin):
|
||||||
models.AuthorizationCode(authorization_code_id="id", client=client, subject=client)
|
models.AuthorizationCode(authorization_code_id="id", client=client, subject=client)
|
||||||
|
|
||||||
res = testclient.get("/admin/client/edit/" + client.client_id)
|
res = testclient.get("/admin/client/edit/" + client.client_id)
|
||||||
res = res.forms["clientaddform"].submit(name="action", value="delete").follow()
|
res = res.forms["clientaddform"].submit(name="action", value="confirm-delete")
|
||||||
|
res = res.form.submit(name="action", value="delete")
|
||||||
|
res = res.follow()
|
||||||
|
|
||||||
assert not models.Client.get()
|
assert not models.Client.get()
|
||||||
assert not models.Token.get()
|
assert not models.Token.get()
|
||||||
|
|
|
@ -127,10 +127,19 @@ def test_token_not_found(testclient, logged_admin):
|
||||||
testclient.get("/admin/token/" + "yolo", status=404)
|
testclient.get("/admin/token/" + "yolo", status=404)
|
||||||
|
|
||||||
|
|
||||||
|
def test_revoke_bad_request(testclient, token, logged_admin):
|
||||||
|
assert not token.revoked
|
||||||
|
|
||||||
|
res = testclient.get(f"/admin/token/{token.token_id}")
|
||||||
|
res = res.form.submit(name="action", value="invalid", status=400)
|
||||||
|
|
||||||
|
|
||||||
def test_revoke_token(testclient, token, logged_admin):
|
def test_revoke_token(testclient, token, logged_admin):
|
||||||
assert not token.revoked
|
assert not token.revoked
|
||||||
|
|
||||||
res = testclient.get(f"/admin/token/{token.token_id}/revoke")
|
res = testclient.get(f"/admin/token/{token.token_id}")
|
||||||
|
res = res.form.submit(name="action", value="confirm-revoke")
|
||||||
|
res = res.form.submit(name="action", value="revoke")
|
||||||
assert ("success", "The token has successfully been revoked.") in res.flashes
|
assert ("success", "The token has successfully been revoked.") in res.flashes
|
||||||
|
|
||||||
token.reload()
|
token.reload()
|
||||||
|
|
Loading…
Reference in a new issue