Service: set-password maintenance command for forgotten passwords
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
359000afa3
commit
f13ab1a629
2 files changed
+26
No files matched your search
@@ -115,6 +115,8 @@ A small Rust service (axum) in `server/` keeps the user accounts (Argon2 passwor
|
|||||||
| `GET /api/saves/{id}` | savegame JSON |
|
| `GET /api/saves/{id}` | savegame JSON |
|
||||||
| `DELETE /api/saves/{id}` | delete a savegame |
|
| `DELETE /api/saves/{id}` | delete a savegame |
|
||||||
|
|
||||||
|
A forgotten password is set again on the server with `docker compose stop saves && docker compose run --rm --no-deps saves dungeoncraft-saves set-password <name> '<password>' && docker compose up -d saves` (ends the user's sessions). To start over with no accounts at all, delete `users.json` and `sessions.json` in the `savegames` volume.
|
||||||
|
|
||||||
All requests except health and login need `Authorization: Bearer <token>`. Limits: 1 MB per savegame, 500 MB in total, 30 write requests and 20 login attempts per minute. Settings: `SAVE_DIR` (default `/data`), `LISTEN` (default `0.0.0.0:8080`).
|
All requests except health and login need `Authorization: Bearer <token>`. Limits: 1 MB per savegame, 500 MB in total, 30 write requests and 20 login attempts per minute. Settings: `SAVE_DIR` (default `/data`), `LISTEN` (default `0.0.0.0:8080`).
|
||||||
|
|
||||||
## Adding a language
|
## Adding a language
|
||||||
|
|||||||
@@ -470,9 +470,33 @@ async fn delete_save(State(st): State<AppState>, headers: HeaderMap, Path(id): P
|
|||||||
StatusCode::NO_CONTENT.into_response()
|
StatusCode::NO_CONTENT.into_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Maintenance: "dungeoncraft-saves set-password <name> <password>" sets a user's password
|
||||||
|
// (stop the service first or restart it afterwards; the user's sessions end).
|
||||||
|
fn set_password_cli(dir: &std::path::Path, name: &str, password: &str) {
|
||||||
|
let path = dir.join("users.json");
|
||||||
|
let mut users: Vec<User> = std::fs::read(&path).ok().and_then(|b| serde_json::from_slice(&b).ok()).unwrap_or_default();
|
||||||
|
let Some(u) = users.iter_mut().find(|u| u.name == name) else {
|
||||||
|
eprintln!("no user {}", name);
|
||||||
|
std::process::exit(1);
|
||||||
|
};
|
||||||
|
u.salt = random_hex(16);
|
||||||
|
u.hash = hash_password(password, &u.salt);
|
||||||
|
std::fs::write(&path, serde_json::to_vec_pretty(&users).unwrap_or_default()).expect("write users.json");
|
||||||
|
let spath = dir.join("sessions.json");
|
||||||
|
let mut sessions: HashMap<String, String> = std::fs::read(&spath).ok().and_then(|b| serde_json::from_slice(&b).ok()).unwrap_or_default();
|
||||||
|
sessions.retain(|_, n| n != name);
|
||||||
|
let _ = std::fs::write(&spath, serde_json::to_vec(&sessions).unwrap_or_default());
|
||||||
|
println!("password of {} set", name);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let dir = PathBuf::from(std::env::var("SAVE_DIR").unwrap_or_else(|_| "/data".into()));
|
let dir = PathBuf::from(std::env::var("SAVE_DIR").unwrap_or_else(|_| "/data".into()));
|
||||||
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
if args.get(1).map(|s| s.as_str()) == Some("set-password") && args.len() == 4 {
|
||||||
|
set_password_cli(&dir, &args[2], &args[3]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
tokio::fs::create_dir_all(dir.join("users")).await.expect("data directory");
|
tokio::fs::create_dir_all(dir.join("users")).await.expect("data directory");
|
||||||
let users: Vec<User> = tokio::fs::read(dir.join("users.json")).await.ok().and_then(|b| serde_json::from_slice(&b).ok()).unwrap_or_default();
|
let users: Vec<User> = tokio::fs::read(dir.join("users.json")).await.ok().and_then(|b| serde_json::from_slice(&b).ok()).unwrap_or_default();
|
||||||
let sessions: HashMap<String, String> = tokio::fs::read(dir.join("sessions.json")).await.ok().and_then(|b| serde_json::from_slice(&b).ok()).unwrap_or_default();
|
let sessions: HashMap<String, String> = tokio::fs::read(dir.join("sessions.json")).await.ok().and_then(|b| serde_json::from_slice(&b).ok()).unwrap_or_default();
|
||||||
|
|||||||
Reference in new issue
Block a user