summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorColin Reeder <vpzomtrrfrt@gmail.com>2020-09-26 14:20:05 -0600
committerColin Reeder <vpzomtrrfrt@gmail.com>2020-09-26 14:20:05 -0600
commit32fee9a2e36802f2ed56b8c905aef19c4cae4b2a (patch)
tree2fa91efd2e7d063f4b2619ff58c1c11034ee5afb
parentb6c67541127314c9d674083f595df059e8aa2c06 (diff)
Add endpoint for checking forgot password key validity
-rw-r--r--res/lang/en.ftl1
-rw-r--r--src/routes/api/forgot_password.rs54
2 files changed, 52 insertions, 3 deletions
diff --git a/res/lang/en.ftl b/res/lang/en.ftl
index 5c7828b..45ff178 100644
--- a/res/lang/en.ftl
+++ b/res/lang/en.ftl
@@ -10,6 +10,7 @@ name_in_use = That name is already in use
no_password = No password set for this user
no_such_comment = No such comment
no_such_community = No such community
+no_such_forgot_password_key = No such password reset key, or it has expired
no_such_local_user_by_email = No local user found by that email address
no_such_local_user_by_name = No local user found by that name
no_such_post = No such post
diff --git a/src/routes/api/forgot_password.rs b/src/routes/api/forgot_password.rs
index 8a8504e..b8124ce 100644
--- a/src/routes/api/forgot_password.rs
+++ b/src/routes/api/forgot_password.rs
@@ -19,12 +19,27 @@ impl ForgotPasswordKey {
pub fn as_int(&self) -> i32 {
self.value
}
+}
- pub fn to_str(&self) -> String {
+// implementing this trait is discouraged in favor of Display, but bs58 doesn't do streaming output
+impl std::string::ToString for ForgotPasswordKey {
+ fn to_string(&self) -> String {
bs58::encode(&self.value.to_be_bytes()).into_string()
}
}
+impl std::str::FromStr for ForgotPasswordKey {
+ type Err = bs58::decode::Error;
+
+ fn from_str(src: &str) -> Result<Self, Self::Err> {
+ let mut buf = [0; 4];
+ bs58::decode(src).into(&mut buf)?;
+ Ok(Self {
+ value: i32::from_be_bytes(buf),
+ })
+ }
+}
+
async fn route_unstable_forgot_password_keys_create(
_: (),
ctx: Arc<crate::RouteContext>,
@@ -73,7 +88,7 @@ async fn route_unstable_forgot_password_keys_create(
let msg_body = lang
.tr(
"email_content_forgot_password",
- Some(&fluent::fluent_args!["key" => key.to_str(), "username" => username]),
+ Some(&fluent::fluent_args!["key" => key.to_string(), "username" => username]),
)
.into_owned();
@@ -99,10 +114,43 @@ async fn route_unstable_forgot_password_keys_create(
.body("{}".into())?)
}
+async fn route_unstable_forgot_password_keys_get(
+ params: (ForgotPasswordKey,),
+ ctx: Arc<crate::RouteContext>,
+ req: hyper::Request<hyper::Body>,
+) -> Result<hyper::Response<hyper::Body>, crate::Error> {
+ let (key,) = params;
+
+ let lang = crate::get_lang_for_req(&req);
+ let db = ctx.db_pool.get().await?;
+
+ let row = db.query_opt("SELECT created < (current_timestamp - INTERVAL '1 HOUR') FROM forgot_password_key WHERE key=$1", &[&key.as_int()]).await?;
+
+ let found = match row {
+ None => false,
+ Some(row) => !row.get::<_, bool>(0),
+ };
+
+ if found {
+ Ok(hyper::Response::builder()
+ .header(hyper::header::CONTENT_TYPE, "application/json")
+ .body("{}".into())?)
+ } else {
+ Err(crate::Error::UserError(crate::simple_response(
+ hyper::StatusCode::NOT_FOUND,
+ lang.tr("no_such_forgot_password_key", None).into_owned(),
+ )))
+ }
+}
+
pub fn route_forgot_password() -> crate::RouteNode<()> {
crate::RouteNode::new().with_child(
"keys",
crate::RouteNode::new()
- .with_handler_async("POST", route_unstable_forgot_password_keys_create),
+ .with_handler_async("POST", route_unstable_forgot_password_keys_create)
+ .with_child_parse::<ForgotPasswordKey, _>(
+ crate::RouteNode::new()
+ .with_handler_async("GET", route_unstable_forgot_password_keys_get),
+ ),
)
}