2019-04-05 15:07:40 +00:00
|
|
|
use rocket;
|
|
|
|
use rocket::State;
|
|
|
|
use rocket::request::Form;
|
2019-08-28 18:33:24 +00:00
|
|
|
use rocket_i18n::I18n;
|
2019-04-05 15:07:40 +00:00
|
|
|
|
2020-11-04 21:21:00 +00:00
|
|
|
use crate::Result;
|
2019-04-05 15:07:40 +00:00
|
|
|
|
2019-09-28 19:54:00 +00:00
|
|
|
use gettext_macros::i18n;
|
|
|
|
|
2019-09-27 14:21:10 +00:00
|
|
|
use crate::web::{RequestOrigin, MyResponse};
|
2019-09-02 20:49:02 +00:00
|
|
|
use crate::web::vks_web;
|
|
|
|
use crate::database::{Database, KeyDatabase, types::Email, types::Fingerprint};
|
|
|
|
use crate::mail;
|
|
|
|
use crate::counters;
|
|
|
|
use crate::rate_limiter::RateLimiter;
|
|
|
|
use crate::tokens::{self, StatelessSerializable};
|
2019-05-02 15:25:48 +00:00
|
|
|
|
|
|
|
#[derive(Debug,Serialize,Deserialize)]
|
|
|
|
struct StatelessVerifyToken {
|
|
|
|
fpr: Fingerprint,
|
|
|
|
}
|
|
|
|
impl StatelessSerializable for StatelessVerifyToken {
|
|
|
|
}
|
2019-04-05 15:07:40 +00:00
|
|
|
|
|
|
|
mod templates {
|
|
|
|
#[derive(Serialize)]
|
|
|
|
pub struct ManageKey {
|
2019-04-26 21:13:56 +00:00
|
|
|
pub key_fpr: String,
|
|
|
|
pub key_link: String,
|
2019-04-26 13:15:54 +00:00
|
|
|
pub base_uri: String,
|
2019-04-05 15:07:40 +00:00
|
|
|
pub uid_status: Vec<ManageKeyUidStatus>,
|
|
|
|
pub token: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize)]
|
|
|
|
pub struct ManageLinkSent {
|
|
|
|
pub address: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize)]
|
|
|
|
pub struct ManageKeyUidStatus {
|
|
|
|
pub address: String,
|
|
|
|
pub published: bool,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub mod forms {
|
|
|
|
#[derive(FromForm)]
|
|
|
|
pub struct ManageRequest {
|
|
|
|
pub search_term: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(FromForm)]
|
|
|
|
pub struct ManageDelete {
|
|
|
|
pub token: String,
|
|
|
|
pub address: String,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-25 18:04:20 +00:00
|
|
|
#[get("/manage")]
|
2019-04-05 15:07:40 +00:00
|
|
|
pub fn vks_manage() -> Result<MyResponse> {
|
2019-09-27 14:21:10 +00:00
|
|
|
Ok(MyResponse::ok_bare("manage/manage"))
|
2019-04-05 15:07:40 +00:00
|
|
|
}
|
|
|
|
|
2019-04-25 18:04:20 +00:00
|
|
|
#[get("/manage/<token>")]
|
2019-04-05 15:07:40 +00:00
|
|
|
pub fn vks_manage_key(
|
2019-06-22 21:25:49 +00:00
|
|
|
request_origin: RequestOrigin,
|
2019-04-26 22:21:30 +00:00
|
|
|
db: State<KeyDatabase>,
|
2019-09-28 19:54:00 +00:00
|
|
|
i18n: I18n,
|
2019-04-05 15:07:40 +00:00
|
|
|
token: String,
|
|
|
|
token_service: rocket::State<tokens::Service>,
|
|
|
|
) -> MyResponse {
|
2019-09-02 20:49:02 +00:00
|
|
|
use crate::database::types::Fingerprint;
|
2019-04-26 13:15:54 +00:00
|
|
|
use std::convert::TryFrom;
|
2019-05-02 15:25:48 +00:00
|
|
|
if let Ok(StatelessVerifyToken { fpr }) = token_service.check(&token) {
|
|
|
|
match db.lookup(&database::Query::ByFingerprint(fpr)) {
|
2019-04-05 15:07:40 +00:00
|
|
|
Ok(Some(tpk)) => {
|
2019-04-26 13:15:54 +00:00
|
|
|
let fp = Fingerprint::try_from(tpk.fingerprint()).unwrap();
|
2019-04-05 15:07:40 +00:00
|
|
|
let mut emails: Vec<Email> = tpk.userids()
|
|
|
|
.map(|u| u.userid().to_string().parse::<Email>())
|
|
|
|
.flatten()
|
|
|
|
.collect();
|
|
|
|
emails.sort_unstable();
|
|
|
|
emails.dedup();
|
|
|
|
let uid_status = emails.into_iter().map(|email|
|
|
|
|
templates::ManageKeyUidStatus {
|
|
|
|
address: email.to_string(),
|
|
|
|
published: true,
|
|
|
|
}
|
|
|
|
).collect();
|
2019-06-12 14:56:21 +00:00
|
|
|
let key_link = uri!(vks_web::search: fp.to_string()).to_string();
|
2019-04-05 15:07:40 +00:00
|
|
|
let context = templates::ManageKey {
|
2019-04-26 21:13:56 +00:00
|
|
|
key_fpr: fp.to_string(),
|
2019-06-12 14:56:21 +00:00
|
|
|
key_link,
|
2019-04-05 15:07:40 +00:00
|
|
|
uid_status,
|
|
|
|
token,
|
2019-06-22 21:25:49 +00:00
|
|
|
base_uri: request_origin.get_base_uri().to_owned(),
|
2019-04-05 15:07:40 +00:00
|
|
|
};
|
|
|
|
MyResponse::ok("manage/manage_key", context)
|
|
|
|
},
|
|
|
|
Ok(None) => MyResponse::not_found(
|
|
|
|
Some("manage/manage"),
|
2019-09-28 19:54:00 +00:00
|
|
|
Some(i18n!(i18n.catalog, "This link is invalid or expired"))),
|
2019-04-05 15:07:40 +00:00
|
|
|
Err(e) => MyResponse::ise(e),
|
|
|
|
}
|
|
|
|
} else {
|
2019-05-25 12:15:51 +00:00
|
|
|
MyResponse::not_found(
|
|
|
|
Some("manage/manage"),
|
2019-09-28 19:54:00 +00:00
|
|
|
Some(i18n!(i18n.catalog, "This link is invalid or expired")))
|
2019-04-05 15:07:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-25 18:04:20 +00:00
|
|
|
#[post("/manage", data="<request>")]
|
2019-04-05 15:07:40 +00:00
|
|
|
pub fn vks_manage_post(
|
2019-04-26 22:21:30 +00:00
|
|
|
db: State<KeyDatabase>,
|
2019-06-22 23:39:36 +00:00
|
|
|
request_origin: RequestOrigin,
|
2019-05-05 12:58:05 +00:00
|
|
|
mail_service: rocket::State<mail::Service>,
|
|
|
|
rate_limiter: rocket::State<RateLimiter>,
|
2019-08-28 18:33:24 +00:00
|
|
|
i18n: I18n,
|
2019-04-05 15:07:40 +00:00
|
|
|
request: Form<forms::ManageRequest>,
|
|
|
|
token_service: rocket::State<tokens::Service>,
|
|
|
|
) -> MyResponse {
|
|
|
|
use std::convert::TryInto;
|
|
|
|
|
|
|
|
let email = match request.search_term.parse::<Email>() {
|
|
|
|
Ok(email) => email,
|
|
|
|
Err(_) => return MyResponse::not_found(
|
|
|
|
Some("manage/manage"),
|
2022-01-04 10:56:59 +00:00
|
|
|
Some(i18n!(i18n.catalog, "Malformed address: {}"; &request.search_term)))
|
2019-04-05 15:07:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let tpk = match db.lookup(&database::Query::ByEmail(email.clone())) {
|
|
|
|
Ok(Some(tpk)) => tpk,
|
|
|
|
Ok(None) => return MyResponse::not_found(
|
|
|
|
Some("manage/manage"),
|
2022-01-04 10:56:59 +00:00
|
|
|
Some(i18n!(i18n.catalog, "No key for address: {}"; &request.search_term))),
|
2019-04-05 15:07:40 +00:00
|
|
|
Err(e) => return MyResponse::ise(e),
|
|
|
|
};
|
|
|
|
|
2019-05-05 12:58:05 +00:00
|
|
|
let email_exists = tpk.userids()
|
|
|
|
.flat_map(|binding| binding.userid().to_string().parse::<Email>())
|
|
|
|
.any(|candidate| candidate == email);
|
|
|
|
|
|
|
|
if !email_exists {
|
2019-09-28 19:54:00 +00:00
|
|
|
return MyResponse::ise(
|
2020-11-04 21:21:00 +00:00
|
|
|
anyhow!("Internal error: address check failed!"));
|
2019-05-05 12:58:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if !rate_limiter.action_perform(format!("manage-{}", &email)) {
|
|
|
|
return MyResponse::not_found(
|
|
|
|
Some("manage/manage"),
|
2019-10-31 13:02:41 +00:00
|
|
|
Some(i18n!(i18n.catalog, "A request has already been sent for this address recently.")));
|
2019-05-05 12:58:05 +00:00
|
|
|
}
|
|
|
|
|
2019-05-02 14:56:47 +00:00
|
|
|
let fpr: Fingerprint = tpk.fingerprint().try_into().unwrap();
|
2019-06-06 17:03:47 +00:00
|
|
|
let fpr_text = fpr.to_string();
|
2019-05-20 21:17:50 +00:00
|
|
|
let token = token_service.create(&StatelessVerifyToken { fpr });
|
2019-06-06 16:10:47 +00:00
|
|
|
let link_path = uri!(vks_manage_key: token).to_string();
|
2019-05-05 12:58:05 +00:00
|
|
|
|
2019-06-22 23:39:36 +00:00
|
|
|
let base_uri = request_origin.get_base_uri();
|
2019-08-28 18:33:24 +00:00
|
|
|
if let Err(e) = mail_service.send_manage_token(&i18n, base_uri, fpr_text, &email, &link_path) {
|
2019-05-05 12:58:05 +00:00
|
|
|
return MyResponse::ise(e);
|
2019-04-05 15:07:40 +00:00
|
|
|
}
|
2019-05-05 12:58:05 +00:00
|
|
|
|
2019-04-05 15:07:40 +00:00
|
|
|
let ctx = templates::ManageLinkSent {
|
|
|
|
address: email.to_string(),
|
|
|
|
};
|
|
|
|
MyResponse::ok("manage/manage_link_sent", ctx)
|
|
|
|
}
|
|
|
|
|
2019-04-25 18:04:20 +00:00
|
|
|
#[post("/manage/unpublish", data="<request>")]
|
2019-04-05 15:07:40 +00:00
|
|
|
pub fn vks_manage_unpublish(
|
2019-06-22 21:25:49 +00:00
|
|
|
request_origin: RequestOrigin,
|
2019-04-26 22:21:30 +00:00
|
|
|
db: rocket::State<KeyDatabase>,
|
2019-09-28 19:54:00 +00:00
|
|
|
i18n: I18n,
|
2019-04-05 15:07:40 +00:00
|
|
|
token_service: rocket::State<tokens::Service>,
|
|
|
|
request: Form<forms::ManageDelete>,
|
|
|
|
) -> MyResponse {
|
2019-09-28 19:54:00 +00:00
|
|
|
match vks_manage_unpublish_or_fail(request_origin, db, token_service, i18n, request) {
|
2019-04-05 15:07:40 +00:00
|
|
|
Ok(response) => response,
|
|
|
|
Err(e) => MyResponse::ise(e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn vks_manage_unpublish_or_fail(
|
2019-06-22 21:25:49 +00:00
|
|
|
request_origin: RequestOrigin,
|
2019-04-26 22:21:30 +00:00
|
|
|
db: rocket::State<KeyDatabase>,
|
2019-04-05 15:07:40 +00:00
|
|
|
token_service: rocket::State<tokens::Service>,
|
2019-09-28 19:54:00 +00:00
|
|
|
i18n: I18n,
|
2019-04-05 15:07:40 +00:00
|
|
|
request: Form<forms::ManageDelete>,
|
|
|
|
) -> Result<MyResponse> {
|
2019-05-02 15:25:48 +00:00
|
|
|
let verify_token = token_service.check::<StatelessVerifyToken>(&request.token)?;
|
2019-04-05 15:07:40 +00:00
|
|
|
let email = request.address.parse::<Email>()?;
|
2019-07-19 18:46:16 +00:00
|
|
|
|
2019-05-02 15:25:48 +00:00
|
|
|
db.set_email_unpublished(&verify_token.fpr, &email)?;
|
2019-07-20 15:00:02 +00:00
|
|
|
counters::inc_address_unpublished(&email);
|
2019-07-19 18:46:16 +00:00
|
|
|
|
2019-09-28 19:54:00 +00:00
|
|
|
Ok(vks_manage_key(request_origin, db, i18n, request.token.to_owned(), token_service))
|
2019-04-05 15:07:40 +00:00
|
|
|
}
|