2019-05-23 23:01:24 +00:00
|
|
|
use failure;
|
|
|
|
use failure::Fallible as Result;
|
|
|
|
|
|
|
|
use multipart::server::save::Entries;
|
|
|
|
use multipart::server::save::SaveResult::*;
|
|
|
|
use multipart::server::Multipart;
|
|
|
|
|
|
|
|
use rocket::http::ContentType;
|
|
|
|
use rocket::request::Form;
|
|
|
|
use rocket::Data;
|
2019-08-28 18:33:24 +00:00
|
|
|
use rocket_i18n::I18n;
|
2019-11-02 12:00:45 +00:00
|
|
|
use gettext_macros::i18n;
|
2019-05-23 23:01:24 +00:00
|
|
|
|
2019-09-02 20:49:02 +00:00
|
|
|
use crate::database::{KeyDatabase, StatefulTokens, Query, Database};
|
|
|
|
use crate::mail;
|
|
|
|
use crate::tokens;
|
|
|
|
use crate::web::{RequestOrigin, MyResponse};
|
|
|
|
use crate::rate_limiter::RateLimiter;
|
2019-05-23 23:01:24 +00:00
|
|
|
|
|
|
|
use std::io::Read;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
2019-09-02 20:49:02 +00:00
|
|
|
use crate::web::vks;
|
|
|
|
use crate::web::vks::response::*;
|
2019-05-23 23:01:24 +00:00
|
|
|
|
|
|
|
const UPLOAD_LIMIT: u64 = 1024 * 1024; // 1 MiB.
|
|
|
|
|
|
|
|
mod forms {
|
|
|
|
#[derive(FromForm,Deserialize)]
|
|
|
|
pub struct VerifyRequest {
|
|
|
|
pub token: String,
|
|
|
|
pub address: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
pub struct UploadRequest {
|
|
|
|
pub keytext: String,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mod template {
|
2019-09-26 19:04:48 +00:00
|
|
|
#[derive(Serialize)]
|
|
|
|
pub struct VerifyForm {
|
|
|
|
pub token: String,
|
|
|
|
}
|
|
|
|
|
2019-05-23 23:01:24 +00:00
|
|
|
#[derive(Serialize)]
|
|
|
|
pub struct Verify {
|
2019-06-12 14:56:21 +00:00
|
|
|
pub key_fpr: String,
|
2019-05-23 23:01:24 +00:00
|
|
|
pub userid: String,
|
2019-06-12 14:56:21 +00:00
|
|
|
pub userid_link: String,
|
2019-05-23 23:01:24 +00:00
|
|
|
}
|
|
|
|
|
2019-06-11 14:59:27 +00:00
|
|
|
#[derive(Serialize)]
|
|
|
|
pub struct Search {
|
|
|
|
pub query: String,
|
|
|
|
pub fpr: String,
|
2019-05-23 23:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize)]
|
|
|
|
pub struct VerificationSent {
|
|
|
|
pub key_fpr: String,
|
|
|
|
pub key_link: String,
|
|
|
|
pub is_revoked: bool,
|
|
|
|
pub token: String,
|
2019-06-05 11:21:37 +00:00
|
|
|
pub email_published: Vec<String>,
|
|
|
|
pub email_unpublished: Vec<UploadUidStatus>,
|
|
|
|
pub count_revoked_one: bool,
|
|
|
|
pub count_revoked: usize,
|
2019-06-08 15:15:46 +00:00
|
|
|
pub count_unparsed_one: bool,
|
|
|
|
pub count_unparsed: usize,
|
2019-05-23 23:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize)]
|
|
|
|
pub struct UploadOkKey {
|
|
|
|
pub key_fpr: String,
|
|
|
|
pub key_link: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize)]
|
|
|
|
pub struct UploadOkMultiple {
|
|
|
|
pub keys: Vec<UploadOkKey>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize)]
|
|
|
|
pub struct UploadUidStatus {
|
|
|
|
pub address: String,
|
|
|
|
pub requested: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MyResponse {
|
2019-06-05 12:33:02 +00:00
|
|
|
fn upload_response_quick(response: UploadResponse, base_uri: &str) -> Self {
|
|
|
|
match response {
|
2019-06-08 15:15:46 +00:00
|
|
|
UploadResponse::Ok { token, .. } => {
|
2019-06-05 12:33:02 +00:00
|
|
|
let uri = uri!(quick_upload_proceed: token);
|
|
|
|
let text = format!(
|
2019-08-28 21:51:21 +00:00
|
|
|
"Key successfully uploaded. Proceed with verification here:\n{}{}\n",
|
2019-06-05 12:33:02 +00:00
|
|
|
base_uri,
|
|
|
|
uri
|
|
|
|
);
|
|
|
|
MyResponse::plain(text)
|
|
|
|
},
|
|
|
|
UploadResponse::OkMulti { key_fprs } =>
|
2019-06-12 14:00:40 +00:00
|
|
|
MyResponse::plain(format!("Uploaded {} keys. For verification, please upload keys individually.\n", key_fprs.len())),
|
2019-06-05 12:33:02 +00:00
|
|
|
UploadResponse::Error(error) => MyResponse::bad_request(
|
2019-06-07 20:31:24 +00:00
|
|
|
"400-plain", failure::err_msg(error)),
|
2019-06-05 12:33:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-23 23:01:24 +00:00
|
|
|
fn upload_response(response: UploadResponse) -> Self {
|
|
|
|
match response {
|
2019-07-15 22:42:07 +00:00
|
|
|
UploadResponse::Ok { token, key_fpr, is_revoked, count_unparsed, status, .. } =>
|
2019-06-08 15:15:46 +00:00
|
|
|
Self::upload_ok(token, key_fpr, is_revoked, count_unparsed, status),
|
2019-06-05 12:33:02 +00:00
|
|
|
UploadResponse::OkMulti { key_fprs } =>
|
|
|
|
Self::upload_ok_multi(key_fprs),
|
2019-05-23 23:01:24 +00:00
|
|
|
UploadResponse::Error(error) => MyResponse::bad_request(
|
|
|
|
"upload/upload", failure::err_msg(error)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn upload_ok(
|
|
|
|
token: String,
|
|
|
|
key_fpr: String,
|
2019-06-05 11:21:37 +00:00
|
|
|
is_revoked: bool,
|
2019-06-08 15:15:46 +00:00
|
|
|
count_unparsed: usize,
|
2019-05-23 23:01:24 +00:00
|
|
|
uid_status: HashMap<String,EmailStatus>,
|
|
|
|
) -> Self {
|
2019-06-12 14:56:21 +00:00
|
|
|
let key_link = uri!(search: &key_fpr).to_string();
|
2019-05-23 23:01:24 +00:00
|
|
|
|
2019-06-05 11:21:37 +00:00
|
|
|
let count_revoked = uid_status.iter()
|
|
|
|
.filter(|(_,status)| **status == EmailStatus::Revoked)
|
|
|
|
.count();
|
|
|
|
|
|
|
|
let mut email_published: Vec<_> = uid_status.iter()
|
|
|
|
.filter(|(_,status)| **status == EmailStatus::Published)
|
|
|
|
.map(|(email,_)| email.to_string())
|
|
|
|
.collect();
|
|
|
|
email_published.sort_unstable();
|
|
|
|
|
|
|
|
let mut email_unpublished: Vec<_> = uid_status.into_iter()
|
|
|
|
.filter(|(_,status)| *status == EmailStatus::Unpublished ||
|
|
|
|
*status == EmailStatus::Pending)
|
|
|
|
.map(|(email,status)|
|
|
|
|
template::UploadUidStatus {
|
|
|
|
address: email.to_string(),
|
|
|
|
requested: status == EmailStatus::Pending,
|
|
|
|
})
|
2019-05-23 23:01:24 +00:00
|
|
|
.collect();
|
2019-06-05 11:21:37 +00:00
|
|
|
email_unpublished
|
|
|
|
.sort_unstable_by(|fst,snd| fst.address.cmp(&snd.address));
|
2019-05-23 23:01:24 +00:00
|
|
|
|
|
|
|
let context = template::VerificationSent {
|
2019-06-05 11:21:37 +00:00
|
|
|
is_revoked,
|
|
|
|
key_fpr,
|
|
|
|
key_link,
|
|
|
|
token,
|
|
|
|
email_published,
|
|
|
|
email_unpublished,
|
|
|
|
count_revoked_one: count_revoked == 1,
|
|
|
|
count_revoked,
|
2019-06-08 15:15:46 +00:00
|
|
|
count_unparsed_one: count_unparsed == 1,
|
|
|
|
count_unparsed,
|
2019-05-23 23:01:24 +00:00
|
|
|
};
|
|
|
|
MyResponse::ok("upload/upload-ok", context)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn upload_ok_multi(key_fprs: Vec<String>) -> Self {
|
|
|
|
let keys = key_fprs.into_iter()
|
2019-06-12 14:56:21 +00:00
|
|
|
.map(|fpr| {
|
|
|
|
let key_link = uri!(search: &fpr).to_string();
|
|
|
|
template::UploadOkKey {
|
|
|
|
key_fpr: fpr.to_owned(),
|
|
|
|
key_link,
|
|
|
|
}
|
2019-05-23 23:01:24 +00:00
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let context = template::UploadOkMultiple {
|
|
|
|
keys,
|
|
|
|
};
|
|
|
|
|
|
|
|
MyResponse::ok("upload/upload-ok-multiple", context)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/upload")]
|
|
|
|
pub fn upload() -> MyResponse {
|
2019-09-29 09:03:43 +00:00
|
|
|
MyResponse::ok_bare("upload/upload")
|
2019-05-23 23:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[post("/upload/submit", format = "multipart/form-data", data = "<data>")]
|
|
|
|
pub fn upload_post_form_data(
|
|
|
|
db: rocket::State<KeyDatabase>,
|
|
|
|
tokens_stateless: rocket::State<tokens::Service>,
|
|
|
|
rate_limiter: rocket::State<RateLimiter>,
|
2019-09-28 19:54:00 +00:00
|
|
|
i18n: I18n,
|
2019-05-23 23:01:24 +00:00
|
|
|
cont_type: &ContentType,
|
|
|
|
data: Data,
|
2019-06-05 13:42:35 +00:00
|
|
|
) -> MyResponse {
|
2019-09-28 19:54:00 +00:00
|
|
|
match process_post_form_data(db, tokens_stateless, rate_limiter, i18n, cont_type, data) {
|
2019-06-05 13:42:35 +00:00
|
|
|
Ok(response) => MyResponse::upload_response(response),
|
|
|
|
Err(err) => MyResponse::bad_request("upload/upload", err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn process_post_form_data(
|
|
|
|
db: rocket::State<KeyDatabase>,
|
|
|
|
tokens_stateless: rocket::State<tokens::Service>,
|
|
|
|
rate_limiter: rocket::State<RateLimiter>,
|
2019-09-28 19:54:00 +00:00
|
|
|
i18n: I18n,
|
2019-06-05 13:42:35 +00:00
|
|
|
cont_type: &ContentType,
|
|
|
|
data: Data,
|
|
|
|
) -> Result<UploadResponse> {
|
2019-05-23 23:01:24 +00:00
|
|
|
// multipart/form-data
|
2019-06-05 13:42:35 +00:00
|
|
|
let (_, boundary) = cont_type
|
|
|
|
.params()
|
|
|
|
.find(|&(k, _)| k == "boundary")
|
|
|
|
.ok_or_else(|| failure::err_msg("`Content-Type: multipart/form-data` \
|
|
|
|
boundary param not provided"))?;
|
2019-05-23 23:01:24 +00:00
|
|
|
|
2019-09-28 19:54:00 +00:00
|
|
|
process_upload(&db, &tokens_stateless, &rate_limiter, &i18n, data, boundary)
|
2019-05-23 23:01:24 +00:00
|
|
|
}
|
|
|
|
|
2019-06-11 14:59:27 +00:00
|
|
|
#[get("/search?<q>")]
|
|
|
|
pub fn search(
|
|
|
|
db: rocket::State<KeyDatabase>,
|
|
|
|
q: String,
|
|
|
|
) -> MyResponse {
|
|
|
|
match q.parse::<Query>() {
|
2019-09-27 14:21:10 +00:00
|
|
|
Ok(query) => key_to_response(db, q, query),
|
2019-06-11 14:59:27 +00:00
|
|
|
Err(e) => MyResponse::bad_request("index", e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-25 13:13:13 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct ParamEmail(crate::database::types::Email);
|
|
|
|
|
2020-08-06 08:23:02 +00:00
|
|
|
impl std::fmt::Display for ParamEmail {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
write!(f, "{}", self.0.as_str())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-25 13:13:13 +00:00
|
|
|
use rocket::request::FromParam;
|
|
|
|
use rocket::http::RawStr;
|
|
|
|
|
|
|
|
impl<'r> FromParam<'r> for ParamEmail {
|
|
|
|
type Error = &'r RawStr;
|
|
|
|
|
|
|
|
fn from_param(param: &'r RawStr) -> std::result::Result<Self, Self::Error> {
|
|
|
|
use std::str::FromStr;
|
|
|
|
if let Ok(email) = crate::database::types::Email::from_str(param) {
|
|
|
|
Ok(ParamEmail(email))
|
|
|
|
} else {
|
|
|
|
Err(param)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/<q>")]
|
|
|
|
pub fn render_profile(
|
|
|
|
db: rocket::State<KeyDatabase>,
|
|
|
|
q: ParamEmail,
|
|
|
|
) -> MyResponse {
|
|
|
|
if let Some(_) = db.by_email(&q.0) {
|
|
|
|
MyResponse::ok_bare("found-profile")
|
|
|
|
} else {
|
2020-08-06 08:23:02 +00:00
|
|
|
MyResponse::not_found(None, format!("No key found for given e-mail address: {}", q))
|
2020-05-25 13:13:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-11 14:59:27 +00:00
|
|
|
fn key_to_response(
|
|
|
|
db: rocket::State<KeyDatabase>,
|
|
|
|
query_string: String,
|
|
|
|
query: Query,
|
|
|
|
) -> MyResponse {
|
|
|
|
let fp = if let Some(fp) = db.lookup_primary_fingerprint(&query) {
|
|
|
|
fp
|
|
|
|
} else {
|
|
|
|
return MyResponse::not_found(None, query.describe_error());
|
|
|
|
};
|
|
|
|
|
|
|
|
let context = template::Search{
|
|
|
|
query: query_string,
|
|
|
|
fpr: fp.to_string(),
|
|
|
|
};
|
|
|
|
|
|
|
|
MyResponse::ok("found", context)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-06-05 12:33:02 +00:00
|
|
|
#[put("/", data = "<data>")]
|
|
|
|
pub fn quick_upload(
|
|
|
|
db: rocket::State<KeyDatabase>,
|
|
|
|
tokens_stateless: rocket::State<tokens::Service>,
|
|
|
|
rate_limiter: rocket::State<RateLimiter>,
|
2019-09-28 19:54:00 +00:00
|
|
|
i18n: I18n,
|
2019-06-22 21:25:49 +00:00
|
|
|
request_origin: RequestOrigin,
|
2019-06-05 12:33:02 +00:00
|
|
|
data: Data,
|
|
|
|
) -> MyResponse {
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
|
|
|
let mut buf = Vec::default();
|
|
|
|
if let Err(error) = std::io::copy(&mut data.open().take(UPLOAD_LIMIT), &mut buf) {
|
2019-06-06 14:33:16 +00:00
|
|
|
return MyResponse::bad_request("400-plain", failure::err_msg(error));
|
2019-06-05 12:33:02 +00:00
|
|
|
}
|
|
|
|
|
2019-09-28 19:54:00 +00:00
|
|
|
MyResponse::upload_response_quick(
|
|
|
|
vks::process_key(
|
|
|
|
&db,
|
|
|
|
&i18n,
|
|
|
|
&tokens_stateless,
|
|
|
|
&rate_limiter,
|
|
|
|
Cursor::new(buf)
|
|
|
|
), request_origin.get_base_uri())
|
2019-06-05 12:33:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/upload/<token>", rank = 2)]
|
|
|
|
pub fn quick_upload_proceed(
|
|
|
|
db: rocket::State<KeyDatabase>,
|
2019-06-22 23:39:36 +00:00
|
|
|
request_origin: RequestOrigin,
|
2019-06-05 12:33:02 +00:00
|
|
|
token_stateful: rocket::State<StatefulTokens>,
|
|
|
|
token_stateless: rocket::State<tokens::Service>,
|
|
|
|
mail_service: rocket::State<mail::Service>,
|
|
|
|
rate_limiter: rocket::State<RateLimiter>,
|
2019-08-28 18:33:24 +00:00
|
|
|
i18n: I18n,
|
2019-06-05 12:33:02 +00:00
|
|
|
token: String,
|
|
|
|
) -> MyResponse {
|
|
|
|
let result = vks::request_verify(
|
2019-06-22 23:39:36 +00:00
|
|
|
db, request_origin, token_stateful, token_stateless, mail_service,
|
2019-08-28 18:33:24 +00:00
|
|
|
rate_limiter, i18n, token, vec!());
|
2019-06-05 12:33:02 +00:00
|
|
|
MyResponse::upload_response(result)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-05-23 23:01:24 +00:00
|
|
|
#[post("/upload/submit", format = "application/x-www-form-urlencoded", data = "<data>")]
|
|
|
|
pub fn upload_post_form(
|
|
|
|
db: rocket::State<KeyDatabase>,
|
|
|
|
tokens_stateless: rocket::State<tokens::Service>,
|
|
|
|
rate_limiter: rocket::State<RateLimiter>,
|
2019-09-28 19:54:00 +00:00
|
|
|
i18n: I18n,
|
2019-05-23 23:01:24 +00:00
|
|
|
data: Data,
|
2019-06-05 13:42:35 +00:00
|
|
|
) -> MyResponse {
|
2019-09-28 19:54:00 +00:00
|
|
|
match process_post_form(&db, &tokens_stateless, &rate_limiter, &i18n, data) {
|
2019-06-05 13:42:35 +00:00
|
|
|
Ok(response) => MyResponse::upload_response(response),
|
|
|
|
Err(err) => MyResponse::bad_request("upload/upload", err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn process_post_form(
|
2019-09-28 19:54:00 +00:00
|
|
|
db: &KeyDatabase,
|
|
|
|
tokens_stateless: &tokens::Service,
|
|
|
|
rate_limiter: &RateLimiter,
|
|
|
|
i18n: &I18n,
|
2019-06-05 13:42:35 +00:00
|
|
|
data: Data,
|
|
|
|
) -> Result<UploadResponse> {
|
2019-05-23 23:01:24 +00:00
|
|
|
use rocket::request::FormItems;
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
|
|
|
// application/x-www-form-urlencoded
|
|
|
|
let mut buf = Vec::default();
|
|
|
|
|
|
|
|
std::io::copy(&mut data.open().take(UPLOAD_LIMIT), &mut buf)?;
|
|
|
|
|
|
|
|
for item in FormItems::from(&*String::from_utf8_lossy(&buf)) {
|
|
|
|
let (key, value) = item.key_value();
|
|
|
|
let decoded_value = value.url_decode().or_else(|_| {
|
|
|
|
Err(failure::err_msg(
|
|
|
|
"`Content-Type: application/x-www-form-urlencoded` \
|
|
|
|
not valid"))
|
|
|
|
})?;
|
|
|
|
|
|
|
|
match key.as_str() {
|
|
|
|
"keytext" => {
|
2019-06-05 13:42:35 +00:00
|
|
|
return Ok(vks::process_key(
|
2019-05-23 23:01:24 +00:00
|
|
|
&db,
|
2019-09-28 19:54:00 +00:00
|
|
|
&i18n,
|
2019-05-23 23:01:24 +00:00
|
|
|
&tokens_stateless,
|
|
|
|
&rate_limiter,
|
|
|
|
Cursor::new(decoded_value.as_bytes())
|
2019-06-05 13:42:35 +00:00
|
|
|
));
|
2019-05-23 23:01:24 +00:00
|
|
|
}
|
|
|
|
_ => { /* skip */ }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-05 13:42:35 +00:00
|
|
|
Err(failure::err_msg("No keytext found"))
|
2019-05-23 23:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn process_upload(
|
|
|
|
db: &KeyDatabase,
|
|
|
|
tokens_stateless: &tokens::Service,
|
|
|
|
rate_limiter: &RateLimiter,
|
2019-09-28 19:54:00 +00:00
|
|
|
i18n: &I18n,
|
2019-05-23 23:01:24 +00:00
|
|
|
data: Data,
|
|
|
|
boundary: &str,
|
2019-06-05 13:42:35 +00:00
|
|
|
) -> Result<UploadResponse> {
|
2019-05-23 23:01:24 +00:00
|
|
|
// saves all fields, any field longer than 10kB goes to a temporary directory
|
|
|
|
// Entries could implement FromData though that would give zero control over
|
|
|
|
// how the files are saved; Multipart would be a good impl candidate though
|
|
|
|
match Multipart::with_body(data.open().take(UPLOAD_LIMIT), boundary).save().temp() {
|
|
|
|
Full(entries) => {
|
2019-09-28 19:54:00 +00:00
|
|
|
process_multipart(db, tokens_stateless, rate_limiter, i18n, entries)
|
2019-05-23 23:01:24 +00:00
|
|
|
}
|
|
|
|
Partial(partial, _) => {
|
2019-09-28 19:54:00 +00:00
|
|
|
process_multipart(db, tokens_stateless, rate_limiter, i18n, partial.entries)
|
2019-05-23 23:01:24 +00:00
|
|
|
}
|
|
|
|
Error(err) => Err(err.into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn process_multipart(
|
|
|
|
db: &KeyDatabase,
|
|
|
|
tokens_stateless: &tokens::Service,
|
|
|
|
rate_limiter: &RateLimiter,
|
2019-09-28 19:54:00 +00:00
|
|
|
i18n: &I18n,
|
2019-05-23 23:01:24 +00:00
|
|
|
entries: Entries,
|
2019-06-05 13:42:35 +00:00
|
|
|
) -> Result<UploadResponse> {
|
2019-05-23 23:01:24 +00:00
|
|
|
match entries.fields.get("keytext") {
|
|
|
|
Some(ent) if ent.len() == 1 => {
|
|
|
|
let reader = ent[0].data.readable()?;
|
2019-09-28 19:54:00 +00:00
|
|
|
Ok(vks::process_key(db, i18n, tokens_stateless, rate_limiter, reader))
|
2019-05-23 23:01:24 +00:00
|
|
|
}
|
2019-06-05 13:42:35 +00:00
|
|
|
Some(_) => Err(failure::err_msg("Multiple keytexts found")),
|
|
|
|
None => Err(failure::err_msg("No keytext found")),
|
2019-05-23 23:01:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[post("/upload/request-verify", format = "application/x-www-form-urlencoded", data="<request>")]
|
|
|
|
pub fn request_verify_form(
|
|
|
|
db: rocket::State<KeyDatabase>,
|
2019-06-22 23:39:36 +00:00
|
|
|
request_origin: RequestOrigin,
|
2019-05-23 23:01:24 +00:00
|
|
|
token_stateful: rocket::State<StatefulTokens>,
|
|
|
|
token_stateless: rocket::State<tokens::Service>,
|
|
|
|
mail_service: rocket::State<mail::Service>,
|
|
|
|
rate_limiter: rocket::State<RateLimiter>,
|
2019-08-28 18:33:24 +00:00
|
|
|
i18n: I18n,
|
2019-05-23 23:01:24 +00:00
|
|
|
request: Form<forms::VerifyRequest>,
|
|
|
|
) -> MyResponse {
|
|
|
|
let forms::VerifyRequest { token, address } = request.into_inner();
|
|
|
|
let result = vks::request_verify(
|
2019-06-22 23:39:36 +00:00
|
|
|
db, request_origin, token_stateful, token_stateless, mail_service,
|
2019-08-28 18:33:24 +00:00
|
|
|
rate_limiter, i18n, token, vec!(address));
|
2019-05-23 23:01:24 +00:00
|
|
|
MyResponse::upload_response(result)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[post("/upload/request-verify", format = "multipart/form-data", data="<request>")]
|
|
|
|
pub fn request_verify_form_data(
|
|
|
|
db: rocket::State<KeyDatabase>,
|
2019-06-22 23:39:36 +00:00
|
|
|
request_origin: RequestOrigin,
|
2019-05-23 23:01:24 +00:00
|
|
|
token_stateful: rocket::State<StatefulTokens>,
|
|
|
|
token_stateless: rocket::State<tokens::Service>,
|
|
|
|
mail_service: rocket::State<mail::Service>,
|
|
|
|
rate_limiter: rocket::State<RateLimiter>,
|
2019-08-28 18:33:24 +00:00
|
|
|
i18n: I18n,
|
2019-05-23 23:01:24 +00:00
|
|
|
request: Form<forms::VerifyRequest>,
|
|
|
|
) -> MyResponse {
|
|
|
|
let forms::VerifyRequest { token, address } = request.into_inner();
|
|
|
|
let result = vks::request_verify(
|
2019-06-22 23:39:36 +00:00
|
|
|
db, request_origin, token_stateful, token_stateless, mail_service,
|
2019-08-28 18:33:24 +00:00
|
|
|
rate_limiter, i18n, token, vec!(address));
|
2019-05-23 23:01:24 +00:00
|
|
|
MyResponse::upload_response(result)
|
|
|
|
}
|
|
|
|
|
2019-09-26 19:04:48 +00:00
|
|
|
#[post("/verify/<token>")]
|
2019-05-23 23:01:24 +00:00
|
|
|
pub fn verify_confirm(
|
|
|
|
db: rocket::State<KeyDatabase>,
|
|
|
|
token_service: rocket::State<StatefulTokens>,
|
2019-09-26 20:58:52 +00:00
|
|
|
rate_limiter: rocket::State<RateLimiter>,
|
2019-09-28 19:54:00 +00:00
|
|
|
i18n: I18n,
|
2019-05-23 23:01:24 +00:00
|
|
|
token: String,
|
|
|
|
) -> MyResponse {
|
2019-09-26 20:58:52 +00:00
|
|
|
let rate_limit_id = format!("verify-token-{}", &token);
|
2019-09-28 19:54:00 +00:00
|
|
|
match vks::verify_confirm(db, &i18n, token_service, token) {
|
2019-06-12 14:56:21 +00:00
|
|
|
PublishResponse::Ok { fingerprint, email } => {
|
2019-09-26 20:58:52 +00:00
|
|
|
rate_limiter.action_perform(rate_limit_id);
|
2019-06-12 14:56:21 +00:00
|
|
|
let userid_link = uri!(search: &email).to_string();
|
2019-05-23 23:01:24 +00:00
|
|
|
let context = template::Verify {
|
|
|
|
userid: email,
|
2019-06-12 14:56:21 +00:00
|
|
|
key_fpr: fingerprint,
|
|
|
|
userid_link,
|
2019-05-23 23:01:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
MyResponse::ok("upload/publish-result", context)
|
|
|
|
},
|
2019-09-26 20:58:52 +00:00
|
|
|
PublishResponse::Error(error) => {
|
2019-11-02 12:00:45 +00:00
|
|
|
let error_msg = if rate_limiter.action_check(rate_limit_id) {
|
|
|
|
failure::err_msg(error)
|
2019-09-26 20:58:52 +00:00
|
|
|
} else {
|
2019-11-02 12:00:45 +00:00
|
|
|
failure::err_msg(i18n!(i18n.catalog, "This address has already been verified."))
|
|
|
|
};
|
|
|
|
MyResponse::bad_request("400", error_msg)
|
2019-09-26 20:58:52 +00:00
|
|
|
}
|
2019-05-23 23:01:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-26 19:04:48 +00:00
|
|
|
#[get("/verify/<token>")]
|
|
|
|
pub fn verify_confirm_form(
|
|
|
|
token: String,
|
|
|
|
) -> MyResponse {
|
|
|
|
MyResponse::ok("upload/verification-form", template::VerifyForm {
|
|
|
|
token
|
|
|
|
})
|
2019-09-27 14:21:10 +00:00
|
|
|
}
|