diff options
author | Colin Reeder <vpzomtrrfrt@gmail.com> | 2020-09-26 14:20:05 -0600 |
---|---|---|
committer | Colin Reeder <vpzomtrrfrt@gmail.com> | 2020-09-26 14:20:05 -0600 |
commit | 32fee9a2e36802f2ed56b8c905aef19c4cae4b2a (patch) | |
tree | 2fa91efd2e7d063f4b2619ff58c1c11034ee5afb | |
parent | b6c67541127314c9d674083f595df059e8aa2c06 (diff) |
Add endpoint for checking forgot password key validity
-rw-r--r-- | res/lang/en.ftl | 1 | ||||
-rw-r--r-- | src/routes/api/forgot_password.rs | 54 |
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), + ), ) } |