From 46a346a0d0a72cf89e1ccf0c9468ca4d250035e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Tue, 7 Mar 2023 18:29:18 +0100 Subject: [PATCH] Table search implementation --- canaille/forms.py | 7 +++- canaille/templates/groups.html | 4 ++ canaille/templates/macro/table.html | 14 +++++++ .../oidc/admin/authorization_list.html | 4 ++ .../templates/oidc/admin/client_list.html | 4 ++ canaille/templates/oidc/admin/token_list.html | 4 ++ canaille/templates/partial/groups.html | 2 +- .../oidc/admin/authorization_list.html | 2 +- .../partial/oidc/admin/client_list.html | 2 +- .../partial/oidc/admin/token_list.html | 2 +- canaille/templates/partial/users.html | 2 +- canaille/templates/users.html | 4 ++ tests/oidc/test_client_admin.py | 15 +++++++ tests/oidc/test_code_admin.py | 23 +++++++++++ tests/oidc/test_token_admin.py | 40 +++++++++++++++++++ tests/test_groups.py | 35 ++++++++++++++++ tests/test_profile.py | 15 +++++++ 17 files changed, 173 insertions(+), 6 deletions(-) diff --git a/canaille/forms.py b/canaille/forms.py index e35b1900..5befbbd1 100644 --- a/canaille/forms.py +++ b/canaille/forms.py @@ -47,7 +47,11 @@ class TableForm(FlaskForm): def __init__(self, cls=None, page_size=25, filter=None, **kwargs): filter = filter or {} super().__init__(**kwargs) - self.items = cls.query(**filter) + if self.query.data: + self.items = cls.fuzzy(self.query.data, **filter) + else: + self.items = cls.query(**filter) + self.page_size = page_size self.nb_items = len(self.items) self.page_max = max(1, math.ceil(self.nb_items / self.page_size)) @@ -56,6 +60,7 @@ class TableForm(FlaskForm): self.items_slice = self.items[first_item:last_item] page = wtforms.IntegerField(default=1) + query = wtforms.StringField(default="") def validate_page(self, field): if field.data < 1 or field.data > self.page_max: diff --git a/canaille/templates/groups.html b/canaille/templates/groups.html index 1507f999..83e1caf5 100644 --- a/canaille/templates/groups.html +++ b/canaille/templates/groups.html @@ -1,4 +1,5 @@ {% extends theme('base.html') %} +{% import "macro/table.html" as table %} {% block content %}
@@ -11,6 +12,9 @@ {% trans %}Groups{% endtrans %}
+
+ {{ table.search(table_form) }} +
{% include "partial/groups.html" %} {% endblock %} diff --git a/canaille/templates/macro/table.html b/canaille/templates/macro/table.html index 1ee13aa4..5bb22428 100644 --- a/canaille/templates/macro/table.html +++ b/canaille/templates/macro/table.html @@ -1,6 +1,20 @@ +{% macro search(form) %} + +{% endmacro %} + {% macro pagination(form) %}
{{ form.hidden_tag() if form.hidden_tag }} +
{% trans %}Page{% endtrans %} diff --git a/canaille/templates/oidc/admin/authorization_list.html b/canaille/templates/oidc/admin/authorization_list.html index 8d89f37b..048621d3 100644 --- a/canaille/templates/oidc/admin/authorization_list.html +++ b/canaille/templates/oidc/admin/authorization_list.html @@ -1,9 +1,13 @@ {% extends theme('base.html') %} +{% import "macro/table.html" as table %} {% block script %} {% endblock %} {% block content %} +
+ {{ table.search(table_form) }} +
{% include "partial/oidc/admin/authorization_list.html" %} {% endblock %} diff --git a/canaille/templates/oidc/admin/client_list.html b/canaille/templates/oidc/admin/client_list.html index 99ff4d2b..17af90ec 100644 --- a/canaille/templates/oidc/admin/client_list.html +++ b/canaille/templates/oidc/admin/client_list.html @@ -1,4 +1,5 @@ {% extends theme('base.html') %} +{% import "macro/table.html" as table %} {% block script %} @@ -10,5 +11,8 @@ {% trans %}Add client{% endtrans %}
+
+ {{ table.search(table_form) }} +
{% include "partial/oidc/admin/client_list.html" %} {% endblock %} diff --git a/canaille/templates/oidc/admin/token_list.html b/canaille/templates/oidc/admin/token_list.html index 6cf464e4..2062d5a5 100644 --- a/canaille/templates/oidc/admin/token_list.html +++ b/canaille/templates/oidc/admin/token_list.html @@ -1,9 +1,13 @@ {% extends theme('base.html') %} +{% import "macro/table.html" as table %} {% block script %} {% endblock %} {% block content %} +
+ {{ table.search(table_form) }} +
{% include "partial/oidc/admin/token_list.html" %} {% endblock %} diff --git a/canaille/templates/partial/groups.html b/canaille/templates/partial/groups.html index e7168735..b350a7ab 100644 --- a/canaille/templates/partial/groups.html +++ b/canaille/templates/partial/groups.html @@ -1,5 +1,5 @@ {% import "macro/table.html" as table %} - +
diff --git a/canaille/templates/partial/oidc/admin/authorization_list.html b/canaille/templates/partial/oidc/admin/authorization_list.html index 008238c0..c03e76a5 100644 --- a/canaille/templates/partial/oidc/admin/authorization_list.html +++ b/canaille/templates/partial/oidc/admin/authorization_list.html @@ -1,5 +1,5 @@ {% import "macro/table.html" as table %} -
+
diff --git a/canaille/templates/partial/oidc/admin/client_list.html b/canaille/templates/partial/oidc/admin/client_list.html index de88b5ee..99b300c9 100644 --- a/canaille/templates/partial/oidc/admin/client_list.html +++ b/canaille/templates/partial/oidc/admin/client_list.html @@ -1,5 +1,5 @@ {% import "macro/table.html" as table %} -
{% trans %}Code{% endtrans %}
+
diff --git a/canaille/templates/partial/oidc/admin/token_list.html b/canaille/templates/partial/oidc/admin/token_list.html index f8541d0c..f07693d8 100644 --- a/canaille/templates/partial/oidc/admin/token_list.html +++ b/canaille/templates/partial/oidc/admin/token_list.html @@ -1,5 +1,5 @@ {% import "macro/table.html" as table %} -
+
diff --git a/canaille/templates/partial/users.html b/canaille/templates/partial/users.html index f0be706c..9553fd16 100644 --- a/canaille/templates/partial/users.html +++ b/canaille/templates/partial/users.html @@ -1,5 +1,5 @@ {% import "macro/table.html" as table %} -
{% trans %}Token{% endtrans %}
+
{% if user.can_read("jpegPhoto") %} diff --git a/canaille/templates/users.html b/canaille/templates/users.html index 8fe76acb..f3caefdc 100644 --- a/canaille/templates/users.html +++ b/canaille/templates/users.html @@ -1,4 +1,5 @@ {% extends theme('base.html') %} +{% import "macro/table.html" as table %} {% block script %} @@ -13,5 +14,8 @@ {% endif %} +
+ {{ table.search(table_form) }} +
{% include "partial/users.html" %} {% endblock %} diff --git a/tests/oidc/test_client_admin.py b/tests/oidc/test_client_admin.py index 8d2f9517..2574aa7d 100644 --- a/tests/oidc/test_client_admin.py +++ b/tests/oidc/test_client_admin.py @@ -66,6 +66,21 @@ def test_client_list_bad_pages(testclient, logged_admin): ) +def test_client_list_search(testclient, logged_admin, client, other_client): + res = testclient.get("/admin/client") + assert "2 items" in res + assert client.client_name in res + assert other_client.client_name in res + + form = res.forms["search"] + form["query"] = "other" + res = form.submit() + + assert "1 items" in res + assert other_client.client_name in res + assert client.client_name not in res + + def test_client_add(testclient, logged_admin): res = testclient.get("/admin/client/add") data = { diff --git a/tests/oidc/test_code_admin.py b/tests/oidc/test_code_admin.py index adc4c858..a655b846 100644 --- a/tests/oidc/test_code_admin.py +++ b/tests/oidc/test_code_admin.py @@ -64,6 +64,29 @@ def test_authorization_list_bad_pages(testclient, logged_admin): ) +def test_authorization_list_search(testclient, logged_admin, client): + id1 = gen_salt(48) + auth1 = AuthorizationCode(authorization_code_id=id1, client=client, subject=client) + auth1.save() + + id2 = gen_salt(48) + auth2 = AuthorizationCode(authorization_code_id=id2, client=client, subject=client) + auth2.save() + + res = testclient.get("/admin/authorization") + assert "2 items" in res + assert id1 in res + assert id2 in res + + form = res.forms["search"] + form["query"] = id1 + res = form.submit() + + assert "1 items" in res + assert id1 in res + assert id2 not in res + + def test_authorizaton_view(testclient, authorization, logged_admin): res = testclient.get("/admin/authorization/" + authorization.authorization_code_id) for attr in authorization.may() + authorization.must(): diff --git a/tests/oidc/test_token_admin.py b/tests/oidc/test_token_admin.py index c0bfeaae..11281a97 100644 --- a/tests/oidc/test_token_admin.py +++ b/tests/oidc/test_token_admin.py @@ -68,6 +68,46 @@ def test_token_list_bad_pages(testclient, logged_admin): ) +def test_token_list_search(testclient, logged_admin, client): + token1 = Token( + token_id=gen_salt(48), + access_token="this-token-is-ok", + client=client, + subject=logged_admin, + type=None, + refresh_token=gen_salt(48), + scope="openid profile", + issue_date=(datetime.datetime.now().replace(microsecond=0)), + lifetime=3600, + ) + token1.save() + token2 = Token( + token_id=gen_salt(48), + access_token="this-token-is-valid", + client=client, + subject=logged_admin, + type=None, + refresh_token=gen_salt(48), + scope="openid profile", + issue_date=(datetime.datetime.now().replace(microsecond=0)), + lifetime=3600, + ) + token2.save() + + res = testclient.get("/admin/token") + assert "2 items" in res + assert token1.token_id in res + assert token2.token_id in res + + form = res.forms["search"] + form["query"] = "valid" + res = form.submit() + + assert "1 items" in res + assert token2.token_id in res + assert token1.token_id not in res + + def test_token_view(testclient, token, logged_admin): res = testclient.get("/admin/token/" + token.token_id) assert token.access_token in res.text diff --git a/tests/test_groups.py b/tests/test_groups.py index 5ceb7575..7aa924bf 100644 --- a/tests/test_groups.py +++ b/tests/test_groups.py @@ -45,6 +45,21 @@ def test_group_list_bad_pages(testclient, logged_admin): ) +def test_group_list_search(testclient, logged_admin, foo_group, bar_group): + res = testclient.get("/groups") + assert "2 items" in res + assert foo_group.name in res + assert bar_group.name in res + + form = res.forms["search"] + form["query"] = "oo" + res = form.submit() + + assert "1 items" in res, res.text + assert foo_group.name in res + assert bar_group.name not in res + + def test_set_groups(app, user, foo_group, bar_group): foo_dns = {m.dn for m in foo_group.get_members()} assert user.dn in foo_dns @@ -227,3 +242,23 @@ def test_user_list_bad_pages(testclient, logged_admin, foo_group): testclient.post( "/groups/foo", {"csrf_token": form["csrf_token"], "page": "-1"}, status=404 ) + + +def test_user_list_search(testclient, logged_admin, foo_group, user, moderator): + foo_group.add_member(logged_admin) + foo_group.add_member(moderator) + foo_group.save() + + res = testclient.get("/groups/foo") + assert "3 items" in res + assert user.name in res + assert moderator.name in res + + form = res.forms["search"] + form["query"] = "ohn" + res = form.submit() + + assert "1 items" in res + assert user.name in res + assert logged_admin.name not in res + assert moderator.name not in res diff --git a/tests/test_profile.py b/tests/test_profile.py index aecfd369..493f5919 100644 --- a/tests/test_profile.py +++ b/tests/test_profile.py @@ -40,6 +40,21 @@ def test_user_list_bad_pages(testclient, logged_admin): ) +def test_user_list_search(testclient, logged_admin, user, moderator): + res = testclient.get("/users") + assert "3 items" in res + assert moderator.name in res + assert user.name in res + + form = res.forms["search"] + form["query"] = "Jack" + res = form.submit() + + assert "1 items" in res, res.text + assert moderator.name in res + assert user.name not in res + + def test_edition_permission( testclient, slapd_server,