diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2a1a7910..c38cf407 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,3 +23,11 @@ repos: rev: 3.0.6 hooks: - id: djhtml + - repo: https://github.com/codespell-project/codespell + rev: v2.3.0 + hooks: + - id: codespell + additional_dependencies: + - tomli + exclude: ".*\\.min\\..*|translations|\\.css$|^tests|fomanticui" + args: [--write-changes] diff --git a/CHANGES.rst b/CHANGES.rst index 053532ed..bbaeb603 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -195,7 +195,7 @@ Fixed preferred user email address. - Password reset and initialization mails were not sent at all the user addresses if one email address could not be reached. -- Password comparision was too permissive on login. +- Password comparison was too permissive on login. - Encrypt passwords in the SQL backend. [0.0.35] - 2023-11-25 @@ -612,7 +612,7 @@ Changed Fixed ^^^^^ - ``HIDE_INVALID_LOGIN`` behavior and default value. -- mo files are not versionned anymore :pr:`49` :pr:`53` +- mo files are not versioned anymore :pr:`49` :pr:`53` [0.0.8] - 2022-03-15 -------------------- @@ -679,7 +679,7 @@ Added - Added an option to tune object IDs :pr:`26` - Avatar support :pr:`27` - Dynamical and configurable JWT claims :pr:`28` -- UI improvemnts :pr:`29` +- UI improvements :pr:`29` - Invitation links expiration :pr:`30` - Invitees can choose their IDs :pr:`31` - LDAP backend refactoring :pr:`35` @@ -705,7 +705,7 @@ Added - Two-steps sign-in :issue:`49` - Tokens can have several audiences. :issue:`62` :pr:`9` - Configuration check command. :issue:`66` :pr:`8` -- Groups managament. :issue:`12` :pr:`6` +- Groups management. :issue:`12` :pr:`6` Fixed ^^^^^ diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index a53ce1c9..1118f817 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -129,7 +129,7 @@ Code style ---------- We use `ruff `_ along with other tools to format our code. -Please run ``tox -e style`` on your patches before submiting them. +Please run ``tox -e style`` on your patches before submitting them. In order to perform a style check and correction at each commit you can use our `pre-commit `_ configuration with ``pre-commit install``. diff --git a/canaille/app/forms.py b/canaille/app/forms.py index 2095534d..e0959618 100644 --- a/canaille/app/forms.py +++ b/canaille/app/forms.py @@ -121,7 +121,7 @@ def render_field(self, field, *args, **kwargs): abort(response) def form_control(self): - """Check wether the current request is the result of the users adding + """Check whether the current request is the result of the users adding or removing a field from a FieldList.""" FIELDLIST_ADD_BUTTON = "fieldlist_add" FIELDLIST_REMOVE_BUTTON = "fieldlist_remove" diff --git a/canaille/app/mails.py b/canaille/app/mails.py index cdb34f09..7d766aea 100644 --- a/canaille/app/mails.py +++ b/canaille/app/mails.py @@ -50,7 +50,7 @@ def type_from_filename(filename): return maintype, subtype -def send_email(subject, recipient, text, html, attachements=None): +def send_email(subject, recipient, text, html, attachments=None): current_app.logger.debug(f"Sending a mail to {recipient}: {subject}") msg = email.message.EmailMessage() msg.set_content(text) @@ -68,8 +68,8 @@ def send_email(subject, recipient, text, html, attachements=None): msg["From"] = f'"{name}" <{address}>' - attachements = attachements or [] - for cid, filename, value in attachements: + attachments = attachments or [] + for cid, filename, value in attachments: maintype, subtype = type_from_filename(filename) msg.get_payload()[1].add_related( value, maintype=maintype, subtype=subtype, cid=cid diff --git a/canaille/backends/__init__.py b/canaille/backends/__init__.py index dba05c1e..07cef570 100644 --- a/canaille/backends/__init__.py +++ b/canaille/backends/__init__.py @@ -135,7 +135,7 @@ def set_user_password(self, user, password: str): raise NotImplementedError() def has_account_lockability(self): - """Indicate wether the backend supports locking user accounts.""" + """Indicate whether the backend supports locking user accounts.""" raise NotImplementedError() def register_models(self): diff --git a/canaille/backends/commands.py b/canaille/backends/commands.py index 0b12dbe0..84fad9e1 100644 --- a/canaille/backends/commands.py +++ b/canaille/backends/commands.py @@ -111,7 +111,7 @@ def serialize_attribute(attribute_name, value): def get_factory(model): - """Read informations about models. + """Read information about models. Options can be used to filter models:: diff --git a/canaille/backends/memory/backend.py b/canaille/backends/memory/backend.py index 1fda6bf7..ac73e82a 100644 --- a/canaille/backends/memory/backend.py +++ b/canaille/backends/memory/backend.py @@ -183,12 +183,12 @@ def index_save(self, instance): model, mirror_attribute ).setdefault(instance.id, set()) for subinstance_id in listify(instance._state.get(attribute, [])): - # add the current objet in the subinstance state + # add the current object in the subinstance state subinstance_state = self.index(model)[subinstance_id] subinstance_state.setdefault(mirror_attribute, []) subinstance_state[mirror_attribute].append(instance.id) - # add the current objet in the subinstance index + # add the current object in the subinstance index mirror_attribute_index.add(subinstance_id) def index_delete(self, instance): @@ -216,11 +216,11 @@ def index_delete(self, instance): for subinstance_id in self.index(instance.__class__)[instance.id].get( attribute, [] ): - # remove the current objet from the subinstance state + # remove the current object from the subinstance state subinstance_state = self.index(model)[subinstance_id] subinstance_state[mirror_attribute].remove(instance.id) - # remove the current objet from the subinstance index + # remove the current object from the subinstance index mirror_attribute_index.remove(subinstance_id) # update the index for each attribute diff --git a/canaille/config.sample.toml b/canaille/config.sample.toml index 89c9fae5..3d3cb4bb 100644 --- a/canaille/config.sample.toml +++ b/canaille/config.sample.toml @@ -40,7 +40,7 @@ SECRET_KEY = "change me before you go in production" # If EMAIL_CONFIRMATION is set to true, users will need to click on a # confirmation link sent by email when they want to add a new email. # By default, this is true if SMTP is configured, else this is false. -# If explicitely set to true and SMTP is disabled, the email field +# If explicitly set to true and SMTP is disabled, the email field # will be read-only. # EMAIL_CONFIRMATION = @@ -51,7 +51,7 @@ SECRET_KEY = "change me before you go in production" # If HIDE_INVALID_LOGINS is set to true (the default), when a user # tries to sign in with an invalid login, a message is shown indicating -# that the password is wrong, but does not give a clue wether the login +# that the password is wrong, but does not give a clue whether the login # exists or not. # If HIDE_INVALID_LOGINS is set to false, when a user tries to sign in with # an invalid login, a message is shown indicating that the login does not @@ -69,7 +69,7 @@ SECRET_KEY = "change me before you go in production" # LOGGING configures the logging output: # - if unset, everything is logged in the standard output # the log level is debug if DEBUG is True, else this is INFO -# - if this is a dictionnary, it is passed to the python dictConfig method: +# - if this is a dictionary, it is passed to the python dictConfig method: # https://docs.python.org/3/library/logging.config.html#logging.config.dictConfig # - if this is a string, it is passed to the python fileConfig method # https://docs.python.org/3/library/logging.config.html#logging.config.fileConfig @@ -133,7 +133,7 @@ SECRET_KEY = "change me before you go in production" # - {groups = 'moderators'} # # The 'PERMISSIONS' parameter that is an list of items the users in the access -# control will be able to manage. 'PERMISSIONS' is optionnal. Values can be: +# control will be able to manage. 'PERMISSIONS' is optional. Values can be: # - "edit_self" to allow users to edit their own profile # - "use_oidc" to allow OpenID Connect authentication # - "manage_oidc" to allow OpenID Connect client managements @@ -188,7 +188,7 @@ WRITE = [ ] [CANAILLE_OIDC] -# Wether a token is needed for the RFC7591 dynamical client registration. +# Whether a token is needed for the RFC7591 dynamical client registration. # If true, no token is needed to register a client. # If false, dynamical client registration needs a token defined # in DYNAMIC_CLIENT_REGISTRATION_TOKENS diff --git a/canaille/core/configuration.py b/canaille/core/configuration.py index ff16f5f6..d21ea947 100644 --- a/canaille/core/configuration.py +++ b/canaille/core/configuration.py @@ -25,10 +25,10 @@ class SMTPSettings(BaseModel): """The SMTP port.""" TLS: Optional[bool] = False - """Wether to use TLS to connect to the SMTP server.""" + """Whether to use TLS to connect to the SMTP server.""" SSL: Optional[bool] = False - """Wether to use SSL to connect to the SMTP server.""" + """Whether to use SSL to connect to the SMTP server.""" LOGIN: Optional[str] = None """The SMTP login.""" @@ -220,14 +220,14 @@ class CoreSettings(BaseModel): """Enables Javascript to smooth the user experience.""" HTMX: bool = True - """Accelerates webpages loading with asynchroneous requests.""" + """Accelerates webpages loading with asynchronous requests.""" EMAIL_CONFIRMATION: bool = True """If :py:data:`True`, users will need to click on a confirmation link sent by email when they want to add a new email. By default, this is true - if ``SMTP`` is configured, else this is false. If explicitely set to true + if ``SMTP`` is configured, else this is false. If explicitly set to true and ``SMTP`` is disabled, the email field will be read-only. """ @@ -242,7 +242,7 @@ class CoreSettings(BaseModel): HIDE_INVALID_LOGINS: bool = True """If :py:data:`True`, when users try to sign in with an invalid login, a message is shown indicating that the password is wrong, but does not give a - clue wether the login exists or not. + clue whether the login exists or not. If :py:data:`False`, when a user tries to sign in with an invalid login, a message is shown @@ -292,7 +292,7 @@ class = "logging.handlers.WatchedFileHandler" ACL: Optional[Dict[str, ACLSettings]] = {"DEFAULT": ACLSettings()} """Mapping of permission groups. See :class:`ACLSettings` for more details. - The ACL name can be freely choosed. For example:: + The ACL name can be freely chosen. For example:: [CANAILLE.ACL.DEFAULT] PERMISSIONS = ["edit_self", "use_oidc"] diff --git a/canaille/core/endpoints/account.py b/canaille/core/endpoints/account.py index 23afc315..637c99ce 100644 --- a/canaille/core/endpoints/account.py +++ b/canaille/core/endpoints/account.py @@ -804,7 +804,7 @@ def profile_delete(user, edited_user): flash( _( - "The user %(user)s has been sucessfuly deleted", + "The user %(user)s has been successfully deleted", user=edited_user.formatted_name, ), "success", diff --git a/canaille/core/endpoints/groups.py b/canaille/core/endpoints/groups.py index 5c1ace34..acaa3d80 100644 --- a/canaille/core/endpoints/groups.py +++ b/canaille/core/endpoints/groups.py @@ -46,7 +46,7 @@ def create_group(user): Backend.instance.save(group) flash( _( - "The group %(group)s has been sucessfully created", + "The group %(group)s has been successfully created", group=group.display_name, ), "success", @@ -106,7 +106,7 @@ def edit_group(group): Backend.instance.save(group) flash( _( - "The group %(group)s has been sucessfully edited.", + "The group %(group)s has been successfully edited.", group=group.display_name, ), "success", @@ -159,7 +159,10 @@ def delete_member(group): def delete_group(group): flash( - _("The group %(group)s has been sucessfully deleted", group=group.display_name), + _( + "The group %(group)s has been successfully deleted", + group=group.display_name, + ), "success", ) Backend.instance.delete(group) diff --git a/canaille/core/mails.py b/canaille/core/mails.py index b3ac5482..364b8694 100644 --- a/canaille/core/mails.py +++ b/canaille/core/mails.py @@ -33,7 +33,7 @@ def send_test_mail(email): recipient=email, text=text_body, html=html_body, - attachements=[(logo_cid, logo_filename, logo_raw)] if logo_filename else None, + attachments=[(logo_cid, logo_filename, logo_raw)] if logo_filename else None, ) @@ -74,7 +74,7 @@ def send_password_reset_mail(user, mail): recipient=mail, text=text_body, html=html_body, - attachements=[(logo_cid, logo_filename, logo_raw)] if logo_filename else None, + attachments=[(logo_cid, logo_filename, logo_raw)] if logo_filename else None, ) @@ -115,7 +115,7 @@ def send_password_initialization_mail(user, email): recipient=email, text=text_body, html=html_body, - attachements=[(logo_cid, logo_filename, logo_raw)] if logo_filename else None, + attachments=[(logo_cid, logo_filename, logo_raw)] if logo_filename else None, ) @@ -146,7 +146,7 @@ def send_invitation_mail(email, registration_url): recipient=email, text=text_body, html=html_body, - attachements=[(logo_cid, logo_filename, logo_raw)] if logo_filename else None, + attachments=[(logo_cid, logo_filename, logo_raw)] if logo_filename else None, ) @@ -177,7 +177,7 @@ def send_confirmation_email(email, confirmation_url): recipient=email, text=text_body, html=html_body, - attachements=[(logo_cid, logo_filename, logo_raw)] if logo_filename else None, + attachments=[(logo_cid, logo_filename, logo_raw)] if logo_filename else None, ) @@ -208,5 +208,5 @@ def send_registration_mail(email, registration_url): recipient=email, text=text_body, html=html_body, - attachements=[(logo_cid, logo_filename, logo_raw)] if logo_filename else None, + attachments=[(logo_cid, logo_filename, logo_raw)] if logo_filename else None, ) diff --git a/canaille/core/models.py b/canaille/core/models.py index ce1fb4ed..0e002f6b 100644 --- a/canaille/core/models.py +++ b/canaille/core/models.py @@ -248,7 +248,7 @@ class User(Model): _permissions = None def has_password(self) -> bool: - """Check wether a password has been set for the user.""" + """Check whether a password has been set for the user.""" return self.password is not None def can_read(self, field: str): @@ -266,7 +266,7 @@ def __getattribute__(self, name): return super().__getattribute__(name) def can(self, *permissions: Permission): - """Wether or not the user has the + """Whether or not the user has the :class:`~canaille.core.configuration.Permission` according to the :class:`configuration `.""" if self._permissions is None: @@ -276,7 +276,7 @@ def can(self, *permissions: Permission): @property def locked(self) -> bool: - """Wether the user account has been locked or has expired.""" + """Whether the user account has been locked or has expired.""" return bool(self.lock_date) and self.lock_date < datetime.datetime.now( datetime.timezone.utc ) diff --git a/canaille/core/templates/partial/group-members.html b/canaille/core/templates/partial/group-members.html index facc31f6..aa85369f 100644 --- a/canaille/core/templates/partial/group-members.html +++ b/canaille/core/templates/partial/group-members.html @@ -82,7 +82,7 @@
{% trans %}No item matches your request{% endtrans %}
-

{% trans %}Maybe try with different criterias?{% endtrans %}

+

{% trans %}Maybe try with different criteria?{% endtrans %}

{% else %}
{% trans %}There is nothing here{% endtrans %} diff --git a/canaille/core/templates/partial/groups.html b/canaille/core/templates/partial/groups.html index e8111184..3f9614f2 100644 --- a/canaille/core/templates/partial/groups.html +++ b/canaille/core/templates/partial/groups.html @@ -30,7 +30,7 @@
{% trans %}No item matches your request{% endtrans %}
-

{% trans %}Maybe try with different criterias?{% endtrans %}

+

{% trans %}Maybe try with different criteria?{% endtrans %}

{% else %}
{% trans %}There is nothing here{% endtrans %} diff --git a/canaille/core/templates/partial/users.html b/canaille/core/templates/partial/users.html index 97dde28c..25706ab0 100644 --- a/canaille/core/templates/partial/users.html +++ b/canaille/core/templates/partial/users.html @@ -72,7 +72,7 @@
{% trans %}No item matches your request{% endtrans %}
-

{% trans %}Maybe try with different criterias?{% endtrans %}

+

{% trans %}Maybe try with different criteria?{% endtrans %}

{% else %}
{% trans %}There is nothing here{% endtrans %} diff --git a/canaille/oidc/configuration.py b/canaille/oidc/configuration.py index 9489c955..09fafd09 100644 --- a/canaille/oidc/configuration.py +++ b/canaille/oidc/configuration.py @@ -7,7 +7,7 @@ class JWTMappingSettings(BaseModel): """Mapping between the user model and the JWT fields. - Fiels are evaluated with jinja. + Fields are evaluated with jinja. A ``user`` var is available. """ @@ -87,7 +87,7 @@ class OIDCSettings(BaseModel): """ DYNAMIC_CLIENT_REGISTRATION_OPEN: bool = False - """Wether a token is needed for the RFC7591 dynamical client registration. + """Whether a token is needed for the RFC7591 dynamical client registration. If :py:data:`True`, no token is needed to register a client. If :py:data:`False`, dynamical client registration needs a token defined in diff --git a/canaille/oidc/oauth.py b/canaille/oidc/oauth.py index ef3723be..18328375 100644 --- a/canaille/oidc/oauth.py +++ b/canaille/oidc/oauth.py @@ -432,7 +432,7 @@ def get_server_metadata(self): def resolve_public_key(self, request): # At the moment the only keypair accepted in software statement - # is the one used to isues JWTs. This might change somedays. + # is the one used to issues JWTs. This might change somedays. return current_app.config["CANAILLE_OIDC"]["JWT"]["PUBLIC_KEY"] def client_convert_data(self, **kwargs): diff --git a/canaille/oidc/templates/partial/authorization_list.html b/canaille/oidc/templates/partial/authorization_list.html index c8391d43..f54345e2 100644 --- a/canaille/oidc/templates/partial/authorization_list.html +++ b/canaille/oidc/templates/partial/authorization_list.html @@ -30,7 +30,7 @@
{% trans %}No item matches your request{% endtrans %}
-

{% trans %}Maybe try with different criterias?{% endtrans %}

+

{% trans %}Maybe try with different criteria?{% endtrans %}

{% else %}
{% trans %}There is nothing here{% endtrans %} diff --git a/canaille/oidc/templates/partial/client_list.html b/canaille/oidc/templates/partial/client_list.html index d114ec96..8fc1776d 100644 --- a/canaille/oidc/templates/partial/client_list.html +++ b/canaille/oidc/templates/partial/client_list.html @@ -34,7 +34,7 @@
{% trans %}No item matches your request{% endtrans %}
-

{% trans %}Maybe try with different criterias?{% endtrans %}

+

{% trans %}Maybe try with different criteria?{% endtrans %}

{% else %}
{% trans %}There is nothing here{% endtrans %} diff --git a/canaille/oidc/templates/partial/token_list.html b/canaille/oidc/templates/partial/token_list.html index 2fdf3b7f..7228786c 100644 --- a/canaille/oidc/templates/partial/token_list.html +++ b/canaille/oidc/templates/partial/token_list.html @@ -38,7 +38,7 @@
{% trans %}No item matches your request{% endtrans %}
-

{% trans %}Maybe try with different criterias?{% endtrans %}

+

{% trans %}Maybe try with different criteria?{% endtrans %}

{% else %}
{% trans %}There is nothing here{% endtrans %} diff --git a/doc/features.rst b/doc/features.rst index 16c43110..d819e9f3 100644 --- a/doc/features.rst +++ b/doc/features.rst @@ -32,7 +32,7 @@ Profile management Canaille provides an interface to manage user profiles. -The exact list of displayed fields, and wether they are :attr:`writable ` or :attr:`read-only ` depends on the user :class:`Access Control List settings (ACL) `. +The exact list of displayed fields, and whether they are :attr:`writable ` or :attr:`read-only ` depends on the user :class:`Access Control List settings (ACL) `. Depending on their ACL :class:`permissions `, users can either be allowed to edit their own profile, edit any user profile, or do nothing at all. @@ -272,7 +272,7 @@ Thanks to its lightweight :ref:`in-memory database ` It can also being launched in your development environment, if you find that launching a Keycloak in a Docker container is too heavy for your little web application. -It also fits well in continuous integration scenarios. Thanks to its :ref:`CLI `, you can prepare data in Canaille, let your application interract with it, and then check the side effects. +It also fits well in continuous integration scenarios. Thanks to its :ref:`CLI `, you can prepare data in Canaille, let your application interact with it, and then check the side effects. Roadmap ******* diff --git a/doc/index.rst b/doc/index.rst index c2e5a0be..95765ac4 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -12,7 +12,7 @@ .. rst-class:: lead - Lightweight Identity and Autorization Management + Lightweight Identity and Authorization Management ---- diff --git a/doc/tutorial/troubleshooting.rst b/doc/tutorial/troubleshooting.rst index 757ae028..fbb595e1 100644 --- a/doc/tutorial/troubleshooting.rst +++ b/doc/tutorial/troubleshooting.rst @@ -1,8 +1,8 @@ Troubleshooting ############### -The web interface throws unuseful error messages -================================================ +The web interface throws useless error messages +=============================================== Unless the current user has admin :class:`permissions `, or the installation is in :attr:`~canaille.app.configuration.RootSettings.DEBUG` mode, error messages won't be too technical. For instance, you can see *The request you made is invalid*. diff --git a/tests/app/test_i18n.py b/tests/app/test_i18n.py index 57651c47..082ed531 100644 --- a/tests/app/test_i18n.py +++ b/tests/app/test_i18n.py @@ -59,7 +59,7 @@ def test_form_translations(testclient, logged_user, backend): res = form.submit(name="action", value="edit-profile") res.mustcontain(no="Not a valid phone number") - res.mustcontain("N’est pas un numéro de téléphone valide") + res.mustcontain("N’est pas un numéro de téléphone valid") def test_language_config(testclient, logged_user, backend): diff --git a/tests/backends/ldap/test_utils.py b/tests/backends/ldap/test_utils.py index 6a2eee41..21a177ca 100644 --- a/tests/backends/ldap/test_utils.py +++ b/tests/backends/ldap/test_utils.py @@ -15,7 +15,7 @@ from canaille.backends.ldap.utils import python_to_ldap -# TODO: tester le changement de cardinalité des attributs +# TODO: tester le changement de cardinalité des attributes def test_object_creation(app, backend): user = models.User( formatted_name="Doe", # leading space diff --git a/tests/core/test_groups.py b/tests/core/test_groups.py index 93fa67da..3178d7a9 100644 --- a/tests/core/test_groups.py +++ b/tests/core/test_groups.py @@ -188,7 +188,7 @@ def test_moderator_can_create_edit_and_delete_group( form["description"] = "yolo2" res = form.submit(name="action", value="edit") - assert res.flashes == [("success", "The group bar has been sucessfully edited.")] + assert res.flashes == [("success", "The group bar has been successfully edited.")] res = res.follow() bar_group = backend.get(models.Group, display_name="bar") @@ -199,7 +199,7 @@ def test_moderator_can_create_edit_and_delete_group( res = res.forms["editgroupform"].submit(name="action", value="confirm-delete") res = res.form.submit(name="action", value="delete", status=302) assert backend.get(models.Group, display_name="bar") is None - assert ("success", "The group bar has been sucessfully deleted") in res.flashes + assert ("success", "The group bar has been successfully deleted") in res.flashes def test_cannot_create_already_existing_group(testclient, logged_moderator, foo_group): diff --git a/tests/oidc/conftest.py b/tests/oidc/conftest.py index 2f4c3168..1028d075 100644 --- a/tests/oidc/conftest.py +++ b/tests/oidc/conftest.py @@ -13,7 +13,7 @@ @pytest.fixture -# For some reason all the params from the overriden fixture must be present here +# For some reason all the params from the overridden fixture must be present here # https://github.com/pytest-dev/pytest/issues/11075 def app(app, configuration, backend): os.environ["AUTHLIB_INSECURE_TRANSPORT"] = "true"