rpcd-mod-rad3-enc: add radicale3 password hasher

There are a number of changes to radicale, passlib and bcrypt,
which means a new rpcd function is needed.

Signed-off-by: Daniel F. Dickinson <dfdpublic@wildtechgarden.ca>
This commit is contained in:
Daniel F. Dickinson
2026-01-09 19:07:34 -05:00
committed by Paul Donald
parent db37d7dd24
commit 7e25a5c32a
2 changed files with 115 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
# cspell:words TOPDIR INSTROOT IPKG Radicale passlib postinst rpcd
include $(TOPDIR)/rules.mk
PKG_NAME:=rpcd-mod-rad3-enc
PKG_VERSION:=20260109
PKG_LICENSE:=Apache-2.0
PKG_BUILD_PARALLEL:=1
include $(INCLUDE_DIR)/package.mk
define Build/Prepare
true
endef
define Build/Compile
true
endef
define Package/rpcd-mod-rad3-enc
SECTION:=libs
CATEGORY:=Libraries
TITLE:=Radicale v3 Hashing RPC module
DEPENDS:=+rpcd +python3 +python3-passlib +radicale3
PROVIDES:=rpcd-mod-rad2-enc
endef
define Package/rpcd-mod-rad32-enc/description
Python3 password hashing module for use with Radicale v3 LuCI app
endef
define Package/rpcd-mod-rad3-enc/install
$(INSTALL_DIR) $(1)/usr/libexec/rpcd
$(INSTALL_BIN) ./files/rad3-enc $(1)/usr/libexec/rpcd
endef
define Package/rpcd-mod-rad3-enc/postinst
#!/bin/sh
[ -n "$$IPKG_INSTROOT" ] || /etc/init.d/rpcd reload
endef
$(eval $(call BuildPackage,rpcd-mod-rad3-enc))

View File

@@ -0,0 +1,71 @@
#!/usr/bin/python3
# cspell:words encpass enctype hpasswd jsonin passlib plainpass radicale
import sys
import json
from passlib.hash import apr_md5_crypt, sha256_crypt, sha512_crypt # pyright: ignore[reportMissingModuleSource]
from radicale import utils # pyright: ignore[reportMissingImports]
def main():
if len(sys.argv) < 2:
return -1
if sys.argv[1] == 'list':
print('{ "encrypt": { "type": "str", "plainpass": "str" } }\n')
return 0
if sys.argv[1] == 'call':
if len(sys.argv) < 3:
return -1
if sys.argv[2] != 'encrypt':
return -1
encpass = ""
error = ""
try:
jsonin = json.loads(sys.stdin.readline())
enctype = jsonin['type'].strip()
plainpass = jsonin['plainpass']
if enctype == 'plain':
encpass = plainpass
elif enctype == 'md5':
encpass = apr_md5_crypt.hash(plainpass).strip()
elif enctype == 'sha256':
encpass = sha256_crypt.using(rounds=5000).hash(plainpass).strip()
elif enctype == 'sha512':
encpass = sha512_crypt.using(rounds=5000).hash(plainpass).strip()
elif enctype == 'bcrypt':
try:
from passlib.hash import bcrypt # pyright: ignore[reportMissingModuleSource]
except ImportError as e:
raise RuntimeError("hpasswd encryption method 'bcrypt' requires the bcrypt module, which is missing") from e
else:
encpass = bcrypt.hash(plainpass).strip()
elif enctype == 'argon2':
try:
import argon2 # pyright: ignore[reportMissingImports]
except ImportError as e:
raise RuntimeError("hpasswd encryption method 'argon2' requires the argon2 module, which is missing") from e
else:
encpass = argon2.using(type="ID").hash(plainpass).strip()
except Exception as e:
encpass = ""
error = str(e)
if ((encpass == "") and (error == "")):
error = "unable to encrypt password"
if error:
print(json.dumps({ "encrypted_password": encpass, "error": error}))
else:
print(json.dumps({ "encrypted_password": encpass}))
return 0
main()