chore: replace passlib with bcrypt

passlib uses deprecated crypt module which is deprecated and scheduled
for removal in 3.13. Unfortunately, this module seems to be
unmaintained, so this commit replaces passlib with bcrypt, unfortunately
breaking current passwords
This commit is contained in:
Evgenii Alekseev 2024-10-05 15:59:51 +03:00
parent 910d178c71
commit cd0ac7a7bd
12 changed files with 66 additions and 37 deletions

View File

@ -10,7 +10,7 @@ echo -e '[arcanisrepo]\nServer = https://repo.arcanis.me/$arch\nSigLevel = Never
# refresh the image # refresh the image
pacman -Syyu --noconfirm pacman -Syyu --noconfirm
# main dependencies # 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 # make dependencies
pacman -S --noconfirm --asdeps base-devel python-build python-flit python-installer python-tox python-wheel pacman -S --noconfirm --asdeps base-devel python-build python-flit python-installer python-tox python-wheel
# optional dependencies # optional dependencies

View File

@ -35,8 +35,8 @@ RUN pacman -S --noconfirm --asdeps \
devtools \ devtools \
git \ git \
pyalpm \ pyalpm \
python-bcrypt \
python-inflection \ python-inflection \
python-passlib \
python-pyelftools \ python-pyelftools \
python-requests \ python-requests \
&& \ && \

View File

@ -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. * ``pacman/{repository}/{architecture}`` is the repository and architecture specific caches for pacman's databases.
* ``repository/{repository}/{architecture}`` is a repository packages directory. * ``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 <migration>`. Normally you should avoid direct interaction with the application tree. For tree migration process refer to the :doc:`migration notes <migrations/index>`.
Database Database
-------- --------

View File

@ -34,7 +34,7 @@ Contents
configuration configuration
command-line command-line
faq/index faq/index
migration migrations/index
architecture architecture
advanced-usage advanced-usage
triggers triggers

View File

@ -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 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: 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:

View File

@ -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 <https://foss.heptapod.net/python-libs/passlib/-/issues/187>`__), 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 <username> -R <role>``. Type password when it will be requested.

11
docs/migrations/2.9.0.rst Normal file
View File

@ -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.

14
docs/migrations/index.rst Normal file
View File

@ -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

View File

@ -7,7 +7,7 @@ pkgdesc="ArcH linux ReposItory MANager"
arch=('any') arch=('any')
url="https://github.com/arcan1s/ahriman" url="https://github.com/arcan1s/ahriman"
license=('GPL3') 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') makedepends=('python-build' 'python-flit' 'python-installer' 'python-wheel')
optdepends=('python-aioauth-client: web server with OAuth2 authorization' optdepends=('python-aioauth-client: web server with OAuth2 authorization'
'python-aiohttp: web server' 'python-aiohttp: web server'

View File

@ -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; * remove build chroot, e.g.: ahriman service-clean --chroot;
* update local databases: ahriman update --no-aur --no-local --no-manual -yy. * 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 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, * 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. 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 EOF
} }

View File

@ -17,8 +17,8 @@ authors = [
] ]
dependencies = [ dependencies = [
"bcrypt",
"inflection", "inflection",
"passlib",
"pyelftools", "pyelftools",
"requests", "requests",
] ]

View File

@ -17,8 +17,9 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
import bcrypt
from dataclasses import dataclass, replace from dataclasses import dataclass, replace
from passlib.hash import sha512_crypt
from secrets import token_urlsafe as generate_password from secrets import token_urlsafe as generate_password
from typing import Self from typing import Self
@ -67,8 +68,6 @@ class User:
packager_id: str | None = None packager_id: str | None = None
key: str | None = None key: str | None = None
_HASHER = sha512_crypt
def __post_init__(self) -> None: def __post_init__(self) -> None:
""" """
remove empty fields remove empty fields
@ -101,10 +100,9 @@ class User:
bool: ``True`` in case if password matches, ``False`` otherwise bool: ``True`` in case if password matches, ``False`` otherwise
""" """
try: try:
verified: bool = self._HASHER.verify(password + salt, self.password) return bcrypt.checkpw((password + salt).encode("utf8"), self.password.encode("utf8"))
except ValueError: except ValueError:
verified = False # the absence of evidence is not the evidence of absence (c) Gin Rummy return False # the absence of evidence is not the evidence of absence (c) Gin Rummy
return verified
def hash_password(self, salt: str) -> Self: 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 # 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 # when we do not store any password here
return self return self
password_hash: str = self._HASHER.hash(self.password + salt) password_hash = bcrypt.hashpw((self.password + salt).encode("utf8"), bcrypt.gensalt())
return replace(self, password=password_hash) return replace(self, password=password_hash.decode("utf8"))
def verify_access(self, required: UserAccess) -> bool: def verify_access(self, required: UserAccess) -> bool:
""" """