fix: route forge_admin through hive-priv; auto-recover matrix passwords
forge_admin() spawned nixos-container run hive-forge directly from the
hive-core process. nixos-container run uses nsenter to enter the container
namespaces, which requires root. hive-core is unprivileged, so every call
failed with: nsenter: stat of /proc/<pid>/ns/user failed: Permission denied
Fix: add RunForgeAdmin { args } to PrivRequest. hive-priv (root) handles
it by spawning nixos-container run hive-forge -- runuser -u forgejo --
forgejo --work-path /var/lib/forgejo admin <args>. forge_admin() now calls
priv_client::run_forge_admin().
matrix: ensure_user_for hit M_USER_IN_USE then failed when the stored
password file was missing (state dirs wiped but homeserver kept accounts).
Previously required manual hivectl matrix reset-password <name>.
Fix: add auto_reset_password() — calls the admin API (PUT
/_synapse/admin/v2/users/@<name>:<server> with the hive admin token) to
set a new random password, then proceeds with login. Falls back to the
existing manual-recovery error if the admin token is unavailable.
Closes #1234
This commit is contained in:
parent
3830b13b03
commit
34bc4c0b06
5 changed files with 147 additions and 47 deletions
|
|
@ -285,6 +285,30 @@ async fn login_user(client: &reqwest::Client, agent: &str, password: &str) -> Re
|
|||
extract_access_token(&json)
|
||||
}
|
||||
|
||||
/// Auto-recovery helper: reset a user's matrix password via the admin API
|
||||
/// when the locally stored password is missing. Requires a valid hive admin
|
||||
/// token at [`admin_token_path()`]. Returns the new password (already
|
||||
/// persisted to [`password_path(name)`]) on success.
|
||||
///
|
||||
/// Called by [`ensure_user_for`] when registration returns `M_USER_IN_USE`
|
||||
/// but the password file is absent — covers the case where agent state dirs
|
||||
/// were wiped but the homeserver still has the accounts.
|
||||
async fn auto_reset_password(client: &reqwest::Client, name: &str) -> anyhow::Result<String> {
|
||||
let admin_token = read_admin_token()
|
||||
.context("matrix: admin token unavailable for auto-recovery; provision hive admin first")?;
|
||||
let server_name = discover_server_name(client)
|
||||
.await
|
||||
.context("matrix: discover_server_name for auto-recovery")?;
|
||||
let new_password = random_password()?;
|
||||
reset_user_password(client, &admin_token, name, &server_name, &new_password)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("matrix: admin API password reset for {name} (auto-recovery)")
|
||||
})?;
|
||||
tracing::info!(%name, "matrix: auto-recovered password via admin API reset");
|
||||
Ok(new_password)
|
||||
}
|
||||
|
||||
/// Ensure `name` has a matrix user + token file on the local
|
||||
/// homeserver. Skips provisioning entirely if the token file already
|
||||
/// exists (treating a present token as proof the account is good).
|
||||
|
|
@ -356,18 +380,34 @@ pub async fn ensure_user_for(
|
|||
// Account already exists — try to re-login with the stored password.
|
||||
tracing::info!(%name, "matrix: user already exists, attempting login with stored password");
|
||||
let pw_path = password_path(name);
|
||||
let stored = std::fs::read_to_string(&pw_path)
|
||||
let stored = match std::fs::read_to_string(&pw_path)
|
||||
.ok()
|
||||
.map(|s| s.trim().to_owned())
|
||||
.filter(|s| !s.is_empty())
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"matrix: user {name} already exists in homeserver but the stored \
|
||||
password is missing — run:\n\
|
||||
hivectl matrix reset-password {name}\n\
|
||||
hivectl matrix create-user {name}"
|
||||
)
|
||||
})?;
|
||||
{
|
||||
Some(pw) => pw,
|
||||
None => {
|
||||
// Password file missing — attempt auto-recovery via admin API.
|
||||
// This covers the case where agent state dirs were wiped but the
|
||||
// homeserver still has the accounts. Requires the hive admin
|
||||
// token at /var/lib/hyperhive/matrix-admin-token.
|
||||
tracing::info!(
|
||||
%name,
|
||||
"matrix: stored password missing, attempting admin-API auto-recovery"
|
||||
);
|
||||
match auto_reset_password(client, name).await {
|
||||
Ok(new_pw) => new_pw,
|
||||
Err(e) => {
|
||||
anyhow::bail!(
|
||||
"matrix: user {name} already exists but password is missing \
|
||||
and admin auto-recovery failed ({e:#}) — run:\n\
|
||||
hivectl matrix reset-password {name}\n\
|
||||
hivectl matrix create-user {name}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
login_user(client, name, &stored).await.with_context(|| {
|
||||
format!(
|
||||
"matrix: login fallback for {name} failed — if homeserver was wiped, delete \
|
||||
|
|
|
|||
Loading…
Reference in a new issue