feat: got started on allowing old members to set an email for password recovery

This commit is contained in:
silver 2023-08-16 00:02:44 +01:00
parent edb511b094
commit f41e0caa74
5 changed files with 588 additions and 13 deletions

View file

@ -42,6 +42,13 @@ pub struct AccountsReset {
pub date_expiry: String,
}
#[derive(Debug, Clone, Deserialize, Serialize, sqlx::FromRow)]
pub struct AccountsSSH {
pub user: String,
pub auth_code: String,
pub email: String,
}
#[derive(Debug, Clone, Deserialize, Serialize, sqlx::FromRow)]
pub struct Accounts {
pub user: String,
@ -95,6 +102,16 @@ pub async fn db_init(config: &Config) -> Result<Pool<Sqlite>, Error> {
.execute(&pool)
.await?;
sqlx::query(
"CREATE TABLE IF NOT EXISTS accounts_ssh (
user text primary key,
auth_code text not null,
email text not null
)",
)
.execute(&pool)
.await?;
sqlx::query(
"CREATE TABLE IF NOT EXISTS accounts_reset (
user text primary key,

View file

@ -31,6 +31,7 @@ async fn main() -> tide::Result<()> {
app.at("/ldap/recover/password").post(account_recover::password::reset);
app.at("/ldap/recover/password/auth").post(account_recover::password::auth);
app.at("/ldap/recover/username").post(account_recover::username::submit);
app.at("/ldap/recover/ssh/request").post(account_recover::ssh::request);
app.listen(host_port).await?;
Ok(())

View file

@ -422,3 +422,96 @@ pub mod username {
mailer.send(&email)
}
}
pub mod ssh {
use super::*;
use crate::AccountsSSH;
use ssh_key::AuthorizedKeys;
use std::fs;
// this is for a legacy member who has forgotten their account password to be able to set an email.
// With an email they can do a password recovery
#[derive(Debug, Deserialize)]
struct RequestChallenge {
user: String,
email: String,
}
#[derive(Debug, Deserialize)]
struct RequestResult {
auth: String,
keys: Vec<String>,
}
pub async fn request(mut req: Request<State>) -> tide::Result {
let RequestChallenge {
user,
email,
} = req.body_json().await?;
// check that any mail is not using @skynet.ie
if email.trim().ends_with("@skynet.ie") {
// all responses from this are a success
return Ok(json!({"result": "error", "error": "Skynet email not permitted."}).into());
}
// check if <root>/<user>/.ssh/authorized_keys exists
//let root = "/skynet_old";
let root = ".";
let path = format!("{}/{}/.ssh/authorized_keys", root, user);
let mut keys = vec![];
if fs::read_to_string(&path).is_ok() {
if let Ok(x) = AuthorizedKeys::read_file(path) {
for entry in x {
if let Ok(y) = entry.public_key().to_openssh() {
keys.push(y);
}
}
}
}
if keys.is_empty() {
return Ok(json!({ "result": "success", "success": { "auth": "", "keys": keys }}).into());
}
let db = &req.state().db;
// check if there is ane listing entry, use that auth if exists
if let Ok(result) = sqlx::query_as::<_, AccountsSSH>(
r#"
SELECT *
FROM accounts_ssh
WHERE user == ?
"#,
)
.bind(&user)
.fetch_one(db)
.await
{
return Ok(json!({ "result": "success", "success": { "auth": result.auth_code, "keys": keys }}).into());
}
// not in db, generate auth and save
let auth = random_string(50);
if sqlx::query_as::<_, AccountsSSH>(
"
INSERT OR REPLACE INTO accounts_ssh (user, auth_code, email)
VALUES (?1, ?2, ?3)
",
)
.bind(&user)
.bind(&auth)
.bind(&email)
.fetch_optional(db)
.await
.is_err()
{
// dont return any keys
return Ok(json!({ "result": "success", "success": { "auth": "", "keys": [] }}).into());
}
// return the full thing
Ok(json!({"result": "success","success": { "auth": auth,"keys": keys }}).into())
}
}