um.rs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. use microrm::prelude::*;
  2. use tide::http::mime;
  3. use crate::{schema, user::UserExt, UIDCError};
  4. type Request = tide::Request<super::ServerStateWrapper>;
  5. fn generate_template_data(
  6. lease: &mut microrm::ConnectionLease,
  7. realm: &microrm::Stored<schema::Realm>,
  8. user: &microrm::Stored<schema::User>,
  9. ) -> Result<serde_json::Value, UIDCError> {
  10. let has_totp = user
  11. .auth
  12. .with(
  13. schema::AuthChallenge::ChallengeType,
  14. schema::AuthChallengeType::Totp.into_serialized(),
  15. )
  16. .count(lease)?
  17. > 0;
  18. let template_data = serde_json::json!({
  19. "username": user.username,
  20. "realm": realm.shortname,
  21. "totp_control": if has_totp {
  22. serde_json::json!([{ "value": "keep", "text": "Keep as-is"}, { "value": "remove", "text": "Remove" }, { "value": "reset", "text": "Reset" }])
  23. } else {
  24. serde_json::json!([{ "value": "keep", "text": "Leave disabled" }, { "value": "reset", "text": "Start setup" }])
  25. },
  26. });
  27. Ok(template_data)
  28. }
  29. async fn um_index(req: Request) -> tide::Result<tide::Response> {
  30. let shelper = super::session::SessionHelper::new(&req);
  31. let mut lease = req.state().lease().await?;
  32. let (realm, user_id) = match shelper.verify_session(&mut lease, &req) {
  33. Some(v) => v,
  34. None => {
  35. return Ok(tide::Redirect::temporary(format!(
  36. "../v1/session/login?redirect={}",
  37. req.url()
  38. ))
  39. .into())
  40. }
  41. };
  42. let user = realm
  43. .users
  44. .with_id(user_id)
  45. .first()
  46. .get(&mut lease)?
  47. .unwrap();
  48. let template_data = generate_template_data(&mut lease, &realm, &user)?;
  49. Ok(tide::Response::builder(200)
  50. .content_type(mime::HTML)
  51. .body(
  52. req.state()
  53. .core
  54. .templates
  55. .render("um_index", &template_data)
  56. .map_err(|_| tide::Error::from_str(500, "error rendering template"))?,
  57. )
  58. .build())
  59. }
  60. #[derive(serde::Deserialize)]
  61. struct UpdateForm {
  62. current_password: String,
  63. new_password: Option<String>,
  64. new_password_repeated: Option<String>,
  65. totp_control: Option<String>,
  66. }
  67. async fn um_update(mut req: Request) -> tide::Result<tide::Response> {
  68. let update_form: UpdateForm = req.body_form().await?;
  69. let shelper = super::session::SessionHelper::new(&req);
  70. let mut lease = req.state().lease().await?;
  71. let (realm, user_id) = match shelper.verify_session(&mut lease, &req) {
  72. Some(v) => v,
  73. None => {
  74. return Ok(tide::Redirect::temporary("../v1/session/login?redirect=../../um/").into())
  75. }
  76. };
  77. let user = realm
  78. .users
  79. .with_id(user_id)
  80. .first()
  81. .get(&mut lease)?
  82. .unwrap();
  83. log::info!("processing update request...");
  84. let progress: Result<Vec<String>, UIDCError> = (|| {
  85. let mut info_msgs = vec![];
  86. let challenge = user.verify_challenge_by_type(
  87. &mut lease,
  88. schema::AuthChallengeType::Password,
  89. update_form.current_password.as_bytes(),
  90. )?;
  91. if !challenge {
  92. Err(UIDCError::Abort("password verification failed"))?
  93. }
  94. if let Some((new_pass, new_pass_repeated)) = update_form
  95. .new_password
  96. .as_ref()
  97. .zip(update_form.new_password_repeated.as_ref())
  98. {
  99. if new_pass != new_pass_repeated {
  100. Err(UIDCError::Abort("entered passwords do not match"))?
  101. }
  102. if !new_pass.is_empty() {
  103. user.set_new_password(&mut lease, new_pass.as_bytes())?;
  104. info_msgs.push("Updated password!".into());
  105. }
  106. } else if update_form.new_password.is_some() || update_form.new_password_repeated.is_some()
  107. {
  108. Err(UIDCError::Abort("must enter new password twice"))?
  109. }
  110. if let Some(totp) = update_form.totp_control.as_ref() {
  111. if totp == "remove" {
  112. user.clear_totp(&mut lease)?;
  113. info_msgs.push("Cleared TOTP setup".into());
  114. } else if totp == "reset" {
  115. let (_secret, _uri) = user.generate_totp_with_uri()?;
  116. Err(UIDCError::Abort(
  117. "totp setup outside of cli not (yet) supported",
  118. ))?
  119. }
  120. }
  121. Ok(info_msgs)
  122. })();
  123. let mut template_data = generate_template_data(&mut lease, &realm, &user)?;
  124. match progress {
  125. Ok(info_msgs) => {
  126. template_data
  127. .as_object_mut()
  128. .and_then(|o| o.insert("info_msg".into(), serde_json::json!(info_msgs)));
  129. }
  130. Err(UIDCError::Abort(msg)) => {
  131. template_data
  132. .as_object_mut()
  133. .and_then(|o| o.insert("error_msg".into(), serde_json::json!([msg])));
  134. }
  135. Err(e) => {
  136. template_data
  137. .as_object_mut()
  138. .and_then(|o| o.insert("error_msg".into(), serde_json::json!([e.to_string()])));
  139. }
  140. }
  141. Ok(tide::Response::builder(200)
  142. .content_type(mime::HTML)
  143. .body(
  144. req.state()
  145. .core
  146. .templates
  147. .render("um_index", &template_data)
  148. .map_err(|_| tide::Error::from_str(500, "error rendering template"))?,
  149. )
  150. .build())
  151. }
  152. pub(super) fn um_server(mut route: tide::Route<super::ServerStateWrapper>) {
  153. route
  154. .at("um")
  155. .get(|_req| async { Ok(tide::Redirect::permanent("um/")) });
  156. route.at("um/").get(um_index);
  157. route.at("um/update").post(um_update);
  158. // route.at("/change_password").get(um_change_password).post(um_change_password_post);
  159. }