authelia: allow password reset writes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
c57ca59f25
commit
8fe9e98300
3 changed files with 46 additions and 13 deletions
|
|
@ -1,5 +1,6 @@
|
|||
"""Self-check for guildhall-join.py: invites, the append-only users file, and
|
||||
that Authelia accepts the password hashes it writes.
|
||||
"""Self-check for guildhall-join.py: invites, the append-only users file (also
|
||||
after Authelia rewrote it for a password reset), and that Authelia accepts the
|
||||
password hashes it writes.
|
||||
|
||||
Run it against the same python the service uses, with authelia on PATH:
|
||||
|
||||
|
|
@ -78,6 +79,23 @@ check("display name survives quoting", users["alex"]["displayname"] == 'Alex "th
|
|||
check("invite is spent", j.open_invite(token) is None)
|
||||
check("spent invite refused", refused(j.sign_up, token, "sam", "", "longpassword", "longpassword"))
|
||||
|
||||
# What Authelia's yaml writer leaves behind after a password reset.
|
||||
REWRITTEN = """users:
|
||||
fred:
|
||||
password: $argon2id$v=19$m=65536,t=3,p=4$c2FsdA$aGFzaA
|
||||
displayname: Fred
|
||||
email: fred@example.org
|
||||
groups:
|
||||
- household
|
||||
address: null
|
||||
extra: {}
|
||||
"""
|
||||
j.USERS_FILE.write_text(REWRITTEN)
|
||||
second = j.mint("after a reset")
|
||||
j.sign_up(second, "sam", "", "correct horse battery", "correct horse battery")
|
||||
rewritten = yaml.safe_load(j.USERS_FILE.read_text())["users"]
|
||||
check("signs up after authelia rewrote the file", set(rewritten) == {"fred", "sam"} and rewritten["sam"]["groups"] == ["guildhall"])
|
||||
|
||||
expired = j.mint("old", now=1.0)
|
||||
check("expired invite is closed", j.open_invite(expired) is None)
|
||||
|
||||
|
|
|
|||
|
|
@ -113,18 +113,26 @@ def write_atomic(path: Path, text: str, mode: int, gid: int = -1) -> None:
|
|||
raise
|
||||
|
||||
|
||||
def user_indent(text: str) -> str:
|
||||
"""The indent of the existing user keys. Authelia rewrites the whole file
|
||||
with four spaces when someone resets their password; by hand it's two."""
|
||||
match = re.search(r"^users:[ \t]*\n(?:[ \t]*(?:#.*)?\n)*( +)\S", text, re.M)
|
||||
return match.group(1) if match else " "
|
||||
|
||||
|
||||
def add_user(path: Path, username: str, display: str, password_hash: str) -> None:
|
||||
"""Append one user; refuse unless every existing user parses back unchanged."""
|
||||
text = path.read_text()
|
||||
before = yaml.safe_load(text)["users"]
|
||||
if username.lower() in (name.lower() for name in before):
|
||||
raise Refused("That username is taken.")
|
||||
i = user_indent(text)
|
||||
block = (
|
||||
f" {username}:\n"
|
||||
f" displayname: {json.dumps(display)}\n"
|
||||
f" password: {json.dumps(password_hash)}\n"
|
||||
f" groups:\n"
|
||||
f" - {GROUP}\n"
|
||||
f"{i}{username}:\n"
|
||||
f"{i * 2}displayname: {json.dumps(display)}\n"
|
||||
f"{i * 2}password: {json.dumps(password_hash)}\n"
|
||||
f"{i * 2}groups:\n"
|
||||
f"{i * 3}- {GROUP}\n"
|
||||
)
|
||||
new_text = text.rstrip("\n") + "\n\n" + block
|
||||
after = yaml.safe_load(new_text)["users"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue