2019-04-05 15:07:40 +00:00
|
|
|
use rocket;
|
|
|
|
use rocket::State;
|
|
|
|
use rocket::request::Form;
|
|
|
|
|
|
|
|
use failure::Fallible as Result;
|
|
|
|
|
2019-04-26 13:15:54 +00:00
|
|
|
use web::{HagridState, MyResponse, templates::General};
|
2019-05-02 14:56:47 +00:00
|
|
|
use database::{Database, KeyDatabase, types::Email, types::Fingerprint};
|
2019-04-05 15:07:40 +00:00
|
|
|
use mail;
|
2019-05-05 12:58:05 +00:00
|
|
|
use rate_limiter::RateLimiter;
|
2019-05-02 15:25:48 +00:00
|
|
|
use tokens::{self, StatelessSerializable};
|
|
|
|
|
|
|
|
#[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,
|
|
|
|
pub commit: String,
|
|
|
|
pub version: 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> {
|
|
|
|
Ok(MyResponse::ok("manage/manage", General::default()))
|
|
|
|
}
|
|
|
|
|
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-04-26 13:15:54 +00:00
|
|
|
state: rocket::State<HagridState>,
|
2019-04-26 22:21:30 +00:00
|
|
|
db: State<KeyDatabase>,
|
2019-04-05 15:07:40 +00:00
|
|
|
token: String,
|
|
|
|
token_service: rocket::State<tokens::Service>,
|
|
|
|
) -> MyResponse {
|
2019-04-26 13:15:54 +00:00
|
|
|
use database::types::Fingerprint;
|
|
|
|
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();
|
|
|
|
let context = templates::ManageKey {
|
2019-04-26 21:13:56 +00:00
|
|
|
key_fpr: fp.to_string(),
|
2019-04-28 21:15:25 +00:00
|
|
|
key_link: format!("/pks/lookup?op=get&search={}", &fp),
|
2019-04-05 15:07:40 +00:00
|
|
|
uid_status,
|
|
|
|
token,
|
2019-04-26 13:15:54 +00:00
|
|
|
base_uri: state.base_uri.clone(),
|
2019-04-05 15:07:40 +00:00
|
|
|
version: env!("VERGEN_SEMVER").to_string(),
|
|
|
|
commit: env!("VERGEN_SHA_SHORT").to_string(),
|
|
|
|
};
|
|
|
|
MyResponse::ok("manage/manage_key", context)
|
|
|
|
},
|
|
|
|
Ok(None) => MyResponse::not_found(
|
|
|
|
Some("manage/manage"),
|
|
|
|
Some("This link is invalid or expired".to_owned())),
|
|
|
|
Err(e) => MyResponse::ise(e),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
MyResponse::ok("manage/manage_expired", General::default())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-05-05 12:58:05 +00:00
|
|
|
mail_service: rocket::State<mail::Service>,
|
|
|
|
rate_limiter: rocket::State<RateLimiter>,
|
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"),
|
2019-05-05 12:58:05 +00:00
|
|
|
Some(format!("Malformed email 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"),
|
2019-05-05 12:58:05 +00:00
|
|
|
Some(format!("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 {
|
|
|
|
return MyResponse::ise(failure::err_msg("Address check failed!"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if !rate_limiter.action_perform(format!("manage-{}", &email)) {
|
|
|
|
return MyResponse::not_found(
|
|
|
|
Some("manage/manage"),
|
|
|
|
Some("A request was already sent for this address recently.".to_owned()));
|
|
|
|
}
|
|
|
|
|
2019-05-02 14:56:47 +00:00
|
|
|
let fpr: Fingerprint = tpk.fingerprint().try_into().unwrap();
|
2019-05-20 21:17:50 +00:00
|
|
|
let token = token_service.create(&StatelessVerifyToken { fpr });
|
2019-04-05 15:07:40 +00:00
|
|
|
let token_uri = uri!(vks_manage_key: token).to_string();
|
2019-05-05 12:58:05 +00:00
|
|
|
|
|
|
|
if let Err(e) = mail_service.send_manage_token(&email, &token_uri) {
|
|
|
|
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-04-26 13:15:54 +00:00
|
|
|
state: rocket::State<HagridState>,
|
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>,
|
|
|
|
request: Form<forms::ManageDelete>,
|
|
|
|
) -> MyResponse {
|
2019-04-26 13:15:54 +00:00
|
|
|
match vks_manage_unpublish_or_fail(state, db, token_service, 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-04-26 13:15:54 +00:00
|
|
|
state: rocket::State<HagridState>,
|
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>,
|
|
|
|
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-05-02 15:25:48 +00:00
|
|
|
db.set_email_unpublished(&verify_token.fpr, &email)?;
|
2019-04-26 13:15:54 +00:00
|
|
|
Ok(vks_manage_key(state, db, request.token.to_owned(), token_service))
|
2019-04-05 15:07:40 +00:00
|
|
|
}
|