diff --git a/.github/workflows/setup.sh b/.github/workflows/setup.sh index bd3e8fe8..0d3cc807 100755 --- a/.github/workflows/setup.sh +++ b/.github/workflows/setup.sh @@ -10,7 +10,7 @@ echo -e '[arcanisrepo]\nServer = https://repo.arcanis.me/$arch\nSigLevel = Never # refresh the image pacman -Syyu --noconfirm # main dependencies -pacman -S --noconfirm devtools git pyalpm python-inflection python-passlib python-pyelftools python-requests python-systemd sudo +pacman -S --noconfirm devtools git pyalpm python-bcrypt python-inflection python-pyelftools python-requests python-systemd sudo # make dependencies pacman -S --noconfirm --asdeps base-devel python-build python-flit python-installer python-tox python-wheel # optional dependencies diff --git a/Dockerfile b/Dockerfile index a58229f5..54b84656 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,8 +35,8 @@ RUN pacman -S --noconfirm --asdeps \ devtools \ git \ pyalpm \ + python-bcrypt \ python-inflection \ - python-passlib \ python-pyelftools \ python-requests \ && \ diff --git a/docs/architecture.rst b/docs/architecture.rst index cab793ed..780d10ef 100644 --- a/docs/architecture.rst +++ b/docs/architecture.rst @@ -147,7 +147,7 @@ There are multiple subdirectories, some of them are commons for any repository, * ``pacman/{repository}/{architecture}`` is the repository and architecture specific caches for pacman's databases. * ``repository/{repository}/{architecture}`` is a repository packages directory. -Normally you should avoid direct interaction with the application tree. For tree migration process refer to the :doc:`migration notes `. +Normally you should avoid direct interaction with the application tree. For tree migration process refer to the :doc:`migration notes `. Database -------- diff --git a/docs/index.rst b/docs/index.rst index 7c1625d0..977647bd 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -34,7 +34,7 @@ Contents configuration command-line faq/index - migration + migrations/index architecture advanced-usage triggers diff --git a/docs/migration.rst b/docs/migrations/2.12.0.rst similarity index 65% rename from docs/migration.rst rename to docs/migrations/2.12.0.rst index 9d1a5c5c..47f9e6d4 100644 --- a/docs/migration.rst +++ b/docs/migrations/2.12.0.rst @@ -1,25 +1,5 @@ -Manual migrations -================= - -Normally the most of migrations are handled automatically after application start, however, some upgrades require manual interventions; this document describes them. - -Upgrades to breakpoints ------------------------ - -To 2.9.0 -^^^^^^^^ - -This release includes major upgrade for the newest devtools and archlinux repository structure. In order to upgrade package need to: - -#. Upgrade to the latest major release of python (3.11) (required by other changes). -#. Upgrade devtools to the latest release. -#. Backup local settings, ``/etc/ahriman.ini.d/00-setup-overrides.ini`` by default. -#. Run setup command (i.e. ``ahriman service-setup``) again with the same arguments as used before. This step can be done manually by moving ``devtools`` configuration (something like ``/usr/share/devtools/pacman-ahriman*.conf``) to new location ``/usr/share/devtools/pacman.conf.d/`` under name ``ahriman.conf``. After that make sure to remove any ``community`` mentions from configurations (e.g. ``/usr/share/devtools/pacman.conf.d/ahriman.conf``, ``/etc/ahriman.ini``) if there were any. The only thing which will change is ``devtools`` configuration. -#. Remove build chroot as it is incompatible, e.g. ``sudo ahriman service-clean --chroot``. -#. Run ``sudo -u ahriman ahriman update --no-aur --no-local --no-manual -yy`` in order to update local databases. - To 2.12.0 -^^^^^^^^^ +--------- This release includes paths migration. Unlike usual case, no automatic migration is performed because it might break user configuration. The following noticeable changes have been made: diff --git a/docs/migrations/2.16.0.rst b/docs/migrations/2.16.0.rst new file mode 100644 index 00000000..14de9b53 --- /dev/null +++ b/docs/migrations/2.16.0.rst @@ -0,0 +1,16 @@ +To 2.16.0 +--------- + +This release replaces ``passlib`` dependency with ``bcrypt``. + +The reason behind this change is that python developers have deprecated and scheduled for removal ``crypt`` module, which is used by ``passlib``. (By the way, they recommend to use ``passlib`` as a replacement.) Unfortunately, it appears that ``passlib`` is unmaintained (see `the issue `__), so the only solution is to migrate to anoher library. + +Because passwords are stored as hashes, it is near to impossible to shadow change passwords in database, the manual intervention is required if: + +#. Authentication is used. +#. Notification provider is ``configuration`` or a user with explicitly set password exists. + +Manual steps might look as: + +#. Get list of users with their roles ``ahriman user-list``. +#. For each user run update command, i.e. ``ahriman user-add -R ``. Type password when it will be requested. diff --git a/docs/migrations/2.9.0.rst b/docs/migrations/2.9.0.rst new file mode 100644 index 00000000..31d842f3 --- /dev/null +++ b/docs/migrations/2.9.0.rst @@ -0,0 +1,11 @@ +To 2.9.0 +-------- + +This release includes major upgrade for the newest devtools and archlinux repository structure. In order to upgrade package need to: + +#. Upgrade to the latest major release of python (3.11) (required by other changes). +#. Upgrade devtools to the latest release. +#. Backup local settings, ``/etc/ahriman.ini.d/00-setup-overrides.ini`` by default. +#. Run setup command (i.e. ``ahriman service-setup``) again with the same arguments as used before. This step can be done manually by moving ``devtools`` configuration (something like ``/usr/share/devtools/pacman-ahriman*.conf``) to new location ``/usr/share/devtools/pacman.conf.d/`` under name ``ahriman.conf``. After that make sure to remove any ``community`` mentions from configurations (e.g. ``/usr/share/devtools/pacman.conf.d/ahriman.conf``, ``/etc/ahriman.ini``) if there were any. The only thing which will change is ``devtools`` configuration. +#. Remove build chroot as it is incompatible, e.g. ``sudo ahriman service-clean --chroot``. +#. Run ``sudo -u ahriman ahriman update --no-aur --no-local --no-manual -yy`` in order to update local databases. diff --git a/docs/migrations/index.rst b/docs/migrations/index.rst new file mode 100644 index 00000000..64dafb50 --- /dev/null +++ b/docs/migrations/index.rst @@ -0,0 +1,14 @@ +Manual migrations +================= + +Normally the most of migrations are handled automatically after application start, however, some upgrades require manual interventions; this document describes them. + +Upgrades to breakpoints +----------------------- + +.. toctree:: + :maxdepth: 2 + + 2.9.0 + 2.12.0 + 2.16.0 diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD index 1617a83f..2834b3af 100644 --- a/package/archlinux/PKGBUILD +++ b/package/archlinux/PKGBUILD @@ -7,7 +7,7 @@ pkgdesc="ArcH linux ReposItory MANager" arch=('any') url="https://github.com/arcan1s/ahriman" license=('GPL3') -depends=('devtools>=1:1.0.0' 'git' 'pyalpm' 'python-inflection' 'python-passlib' 'python-pyelftools' 'python-requests') +depends=('devtools>=1:1.0.0' 'git' 'pyalpm' 'python-bcrypt' 'python-inflection' 'python-pyelftools' 'python-requests') makedepends=('python-build' 'python-flit' 'python-installer' 'python-wheel') optdepends=('python-aioauth-client: web server with OAuth2 authorization' 'python-aiohttp: web server' diff --git a/package/archlinux/ahriman.install b/package/archlinux/ahriman.install index a8ec42d7..da9a0d70 100644 --- a/package/archlinux/ahriman.install +++ b/package/archlinux/ahriman.install @@ -21,7 +21,7 @@ It was found that there was an upgrade from old devtools package to the new one, * remove build chroot, e.g.: ahriman service-clean --chroot; * update local databases: ahriman update --no-aur --no-local --no-manual -yy. -For more information kindly refer to migration notes https://ahriman.readthedocs.io/en/stable/migration.html. +For more information kindly refer to migration notes https://ahriman.readthedocs.io/en/stable/migrations/2.9.0.html. EOF } @@ -37,6 +37,16 @@ Whereas old local tree is still supported it is highly recommended to migrate to * enable web and timer services again by using x86_64-aur suffix, where x86_64 is the repository architecture and aur is the repository name. -For more information kindly refer to migration notes https://ahriman.readthedocs.io/en/stable/migration.html. +For more information kindly refer to migration notes https://ahriman.readthedocs.io/en/stable/migrations/2.12.0.html. +EOF +} + +_2_16_0_1_changes() { + cat << EOF +In order to prepare to python 3.13 the project now uses bcrypt instead of passlib for generating and validating +passwords, because the passlib seems to be unmaintained and will be broken since then. If you are using password +authentication, you'd need to generate passwords again. + +For more information kindly refer to migration notes https://ahriman.readthedocs.io/en/stable/migrations/2.16.0.html. EOF } diff --git a/pyproject.toml b/pyproject.toml index c72d137c..d0c65831 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,8 +17,8 @@ authors = [ ] dependencies = [ + "bcrypt", "inflection", - "passlib", "pyelftools", "requests", ] diff --git a/src/ahriman/models/user.py b/src/ahriman/models/user.py index e83deac3..4e3daa5f 100644 --- a/src/ahriman/models/user.py +++ b/src/ahriman/models/user.py @@ -17,8 +17,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +import bcrypt + from dataclasses import dataclass, replace -from passlib.hash import sha512_crypt from secrets import token_urlsafe as generate_password from typing import Self @@ -67,8 +68,6 @@ class User: packager_id: str | None = None key: str | None = None - _HASHER = sha512_crypt - def __post_init__(self) -> None: """ remove empty fields @@ -101,10 +100,9 @@ class User: bool: ``True`` in case if password matches, ``False`` otherwise """ try: - verified: bool = self._HASHER.verify(password + salt, self.password) + return bcrypt.checkpw((password + salt).encode("utf8"), self.password.encode("utf8")) except ValueError: - verified = False # the absence of evidence is not the evidence of absence (c) Gin Rummy - return verified + return False # the absence of evidence is not the evidence of absence (c) Gin Rummy def hash_password(self, salt: str) -> Self: """ @@ -120,8 +118,8 @@ class User: # in case of empty password we leave it empty. This feature is used by any external (like OAuth) provider # when we do not store any password here return self - password_hash: str = self._HASHER.hash(self.password + salt) - return replace(self, password=password_hash) + password_hash = bcrypt.hashpw((self.password + salt).encode("utf8"), bcrypt.gensalt()) + return replace(self, password=password_hash.decode("utf8")) def verify_access(self, required: UserAccess) -> bool: """