1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
use base64::URL_SAFE_NO_PAD;
use rocket::{http::uri::Reference, serde::json::Json, State};
use serde::{Deserialize, Serialize};
use time::{Duration, OffsetDateTime};
use crate::{api::auth, auth::Authenticated, crypto::random_bytes, db::DbConn, Config};
use super::WithVerifiedFxaLogin;
pub(crate) async fn generate_invite_link(
db: &DbConn,
cfg: &Config,
ttl: Duration,
) -> anyhow::Result<Reference<'static>> {
let code = base64::encode_config(&random_bytes::<32>(), URL_SAFE_NO_PAD);
db.add_invite_code(&code, OffsetDateTime::now_utc() + ttl).await?;
Reference::parse_owned(format!("{}/#/register/{}", cfg.location, code))
.map_err(|e| anyhow!("url building failed at {e}"))
}
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct GenerateReq {
ttl_hours: u32,
}
#[derive(Debug, Serialize)]
pub(crate) struct GenerateResp {
url: Reference<'static>,
}
#[post("/generate", data = "<req>")]
pub(crate) async fn generate(
db: &DbConn,
cfg: &State<Config>,
req: Authenticated<GenerateReq, WithVerifiedFxaLogin>,
) -> auth::Result<GenerateResp> {
if !req.context.verified {
return Err(auth::Error::UnverifiedSession);
}
let user = db.get_user_by_id(&req.context.uid).await?;
if user.email != cfg.invite_admin_address {
return Err(auth::Error::InvalidAuthToken);
}
let url = generate_invite_link(db, cfg, Duration::hours(req.body.ttl_hours as i64)).await?;
Ok(Json(GenerateResp { url }))
}
|