feat: hive matrix admin user + hivectl matrix promote-user/reset-password
- provision @hive:<server> as the first matrix account in ensure_all() (Conduit/tuwunel makes the first registered user admin automatically) - add hivectl matrix sync-admin — manual re-provision of the admin token - add hivectl matrix promote-user <name> --server <name> — promote via Synapse-compat admin API using the hive admin token - add hivectl matrix reset-password <name> --server <name> — reset an agent's password + persist it so ensure_user_for can re-login; follow with hivectl matrix create-user <name> to mint a fresh access token - both commands fall back to HYPERHIVE_MATRIX_SERVER_NAME env var for --server when omitted
This commit is contained in:
parent
47e9c1cc1b
commit
8757dc615d
2 changed files with 309 additions and 0 deletions
|
|
@ -150,6 +150,39 @@ enum MatrixCmd {
|
|||
#[arg(long, conflicts_with = "password")]
|
||||
password_stdin: bool,
|
||||
},
|
||||
/// Provision (or re-provision) the hive system admin matrix account
|
||||
/// (`@hive:<server>`). hive-c0re runs this automatically on startup
|
||||
/// before the agent sweep so the account is the first registered
|
||||
/// user — Conduit/tuwunel grants admin rights to the first user.
|
||||
/// Run manually to recover a missing admin token file.
|
||||
SyncAdmin,
|
||||
/// Promote a matrix user to homeserver admin via the admin API.
|
||||
/// Uses the hive system admin token at
|
||||
/// `/var/lib/hyperhive/matrix-admin-token`. Requires `--server` to
|
||||
/// be the matrix `server_name` (e.g. `pr1ma.darkest.space`).
|
||||
PromoteUser {
|
||||
/// Matrix localpart of the user to promote (e.g. `argus`).
|
||||
name: String,
|
||||
/// Matrix server_name embedded in user IDs (`@user:<server>`).
|
||||
/// Falls back to the `HYPERHIVE_MATRIX_SERVER_NAME` env var
|
||||
/// when omitted.
|
||||
#[arg(long)]
|
||||
server: Option<String>,
|
||||
},
|
||||
/// Reset a matrix user's password via the admin API and persist the
|
||||
/// new password to `/var/lib/hyperhive/matrix-creds/<name>-password`
|
||||
/// so the next `ensure_user_for` (or `create-user`) can re-login.
|
||||
///
|
||||
/// After this command succeeds, run `hivectl matrix create-user
|
||||
/// <name>` to mint a fresh access token for the agent.
|
||||
ResetPassword {
|
||||
/// Matrix localpart of the account to reset (e.g. `argus`).
|
||||
name: String,
|
||||
/// Matrix server_name embedded in user IDs. Falls back to the
|
||||
/// `HYPERHIVE_MATRIX_SERVER_NAME` env var when omitted.
|
||||
#[arg(long)]
|
||||
server: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Default htpasswd file path — the host-side location of the gateway's
|
||||
|
|
@ -250,6 +283,15 @@ async fn main() -> Result<()> {
|
|||
password,
|
||||
password_stdin,
|
||||
} => matrix_create_user(&name, password.as_deref(), password_stdin).await,
|
||||
MatrixCmd::SyncAdmin => matrix_sync_admin().await,
|
||||
MatrixCmd::PromoteUser { name, server } => {
|
||||
let server_name = resolve_server_name(server.as_deref())?;
|
||||
matrix_promote_user(&name, &server_name).await
|
||||
}
|
||||
MatrixCmd::ResetPassword { name, server } => {
|
||||
let server_name = resolve_server_name(server.as_deref())?;
|
||||
matrix_reset_password(&name, &server_name).await
|
||||
}
|
||||
},
|
||||
Cmd::Gateway { cmd } => match cmd {
|
||||
GatewayCmd::CreateUser {
|
||||
|
|
@ -399,6 +441,90 @@ async fn matrix_create_user(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Resolve matrix server_name from flag or env var.
|
||||
fn resolve_server_name(flag: Option<&str>) -> Result<String> {
|
||||
if let Some(s) = flag {
|
||||
return Ok(s.to_owned());
|
||||
}
|
||||
std::env::var("HYPERHIVE_MATRIX_SERVER_NAME")
|
||||
.map_err(|_| anyhow::anyhow!(
|
||||
"matrix server_name required — pass --server <name> or \
|
||||
set HYPERHIVE_MATRIX_SERVER_NAME"
|
||||
))
|
||||
}
|
||||
|
||||
async fn matrix_sync_admin() -> Result<()> {
|
||||
if !hive_c0re::matrix::is_present().await {
|
||||
bail!(
|
||||
"hive-matrix container not running — start it (services.hyperhive.matrix.enable = true) first"
|
||||
);
|
||||
}
|
||||
let register_token =
|
||||
hive_c0re::matrix::ensure_register_token().context("read matrix register token")?;
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(30))
|
||||
.build()
|
||||
.context("build reqwest client")?;
|
||||
hive_c0re::matrix::ensure_admin_user(&client, ®ister_token)
|
||||
.await
|
||||
.context("matrix sync-admin")?;
|
||||
let path = hive_c0re::matrix::admin_token_path();
|
||||
println!(
|
||||
"matrix: hive admin user '@{}' provisioned",
|
||||
hive_c0re::matrix::HIVE_ADMIN_LOCALPART
|
||||
);
|
||||
println!("token persisted at: {}", path.display());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn matrix_promote_user(name: &str, server_name: &str) -> Result<()> {
|
||||
if !hive_c0re::matrix::is_present().await {
|
||||
bail!(
|
||||
"hive-matrix container not running — start it (services.hyperhive.matrix.enable = true) first"
|
||||
);
|
||||
}
|
||||
let admin_token = hive_c0re::matrix::read_admin_token()?;
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(30))
|
||||
.build()
|
||||
.context("build reqwest client")?;
|
||||
hive_c0re::matrix::promote_user_to_admin(&client, &admin_token, name, server_name)
|
||||
.await
|
||||
.with_context(|| format!("matrix promote-user {name}"))?;
|
||||
println!("matrix: promoted @{name}:{server_name} to admin");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn matrix_reset_password(name: &str, server_name: &str) -> Result<()> {
|
||||
if !hive_c0re::matrix::is_present().await {
|
||||
bail!(
|
||||
"hive-matrix container not running — start it (services.hyperhive.matrix.enable = true) first"
|
||||
);
|
||||
}
|
||||
let admin_token = hive_c0re::matrix::read_admin_token()?;
|
||||
let new_password =
|
||||
hive_c0re::matrix::random_password().context("generate random password")?;
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(30))
|
||||
.build()
|
||||
.context("build reqwest client")?;
|
||||
hive_c0re::matrix::reset_user_password(
|
||||
&client,
|
||||
&admin_token,
|
||||
name,
|
||||
server_name,
|
||||
&new_password,
|
||||
)
|
||||
.await
|
||||
.with_context(|| format!("matrix reset-password {name}"))?;
|
||||
let pw_path = PathBuf::from("/var/lib/hyperhive/matrix-creds")
|
||||
.join(format!("{name}-password"));
|
||||
println!("matrix: password for @{name}:{server_name} reset");
|
||||
println!("password persisted at: {}", pw_path.display());
|
||||
println!("next: hivectl matrix create-user {name} # mints a fresh access token");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Gateway htpasswd helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Reference in a new issue