admin.rs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. use serde_json::Value;
  2. use rocket::http::{Cookie, Cookies, SameSite};
  3. use rocket::request::{self, FlashMessage, Form, FromRequest, Request};
  4. use rocket::response::{content::Html, Flash, Redirect};
  5. use rocket::{Outcome, Route};
  6. use rocket_contrib::json::Json;
  7. use crate::api::{ApiResult, EmptyResult};
  8. use crate::auth::{decode_admin, encode_jwt, generate_admin_claims, ClientIp};
  9. use crate::config::ConfigBuilder;
  10. use crate::db::{models::*, DbConn};
  11. use crate::error::Error;
  12. use crate::mail;
  13. use crate::CONFIG;
  14. pub fn routes() -> Vec<Route> {
  15. if CONFIG.admin_token().is_none() && !CONFIG.disable_admin_token() {
  16. return routes![admin_disabled];
  17. }
  18. routes![
  19. admin_login,
  20. post_admin_login,
  21. admin_page,
  22. invite_user,
  23. delete_user,
  24. deauth_user,
  25. post_config,
  26. delete_config,
  27. ]
  28. }
  29. #[get("/")]
  30. fn admin_disabled() -> &'static str {
  31. "The admin panel is disabled, please configure the 'ADMIN_TOKEN' variable to enable it"
  32. }
  33. const COOKIE_NAME: &str = "BWRS_ADMIN";
  34. const ADMIN_PATH: &str = "/admin";
  35. const BASE_TEMPLATE: &str = "admin/base";
  36. const VERSION: Option<&str> = option_env!("GIT_VERSION");
  37. #[get("/", rank = 2)]
  38. fn admin_login(flash: Option<FlashMessage>) -> ApiResult<Html<String>> {
  39. // If there is an error, show it
  40. let msg = flash.map(|msg| format!("{}: {}", msg.name(), msg.msg()));
  41. let json = json!({"page_content": "admin/login", "version": VERSION, "error": msg});
  42. // Return the page
  43. let text = CONFIG.render_template(BASE_TEMPLATE, &json)?;
  44. Ok(Html(text))
  45. }
  46. #[derive(FromForm)]
  47. struct LoginForm {
  48. token: String,
  49. }
  50. #[post("/", data = "<data>")]
  51. fn post_admin_login(data: Form<LoginForm>, mut cookies: Cookies, ip: ClientIp) -> Result<Redirect, Flash<Redirect>> {
  52. let data = data.into_inner();
  53. // If the token is invalid, redirect to login page
  54. if !_validate_token(&data.token) {
  55. error!("Invalid admin token. IP: {}", ip.ip);
  56. Err(Flash::error(
  57. Redirect::to(ADMIN_PATH),
  58. "Invalid admin token, please try again.",
  59. ))
  60. } else {
  61. // If the token received is valid, generate JWT and save it as a cookie
  62. let claims = generate_admin_claims();
  63. let jwt = encode_jwt(&claims);
  64. let cookie = Cookie::build(COOKIE_NAME, jwt)
  65. .path(ADMIN_PATH)
  66. .max_age(chrono::Duration::minutes(20))
  67. .same_site(SameSite::Strict)
  68. .http_only(true)
  69. .finish();
  70. cookies.add(cookie);
  71. Ok(Redirect::to(ADMIN_PATH))
  72. }
  73. }
  74. fn _validate_token(token: &str) -> bool {
  75. match CONFIG.admin_token().as_ref() {
  76. None => false,
  77. Some(t) => crate::crypto::ct_eq(t, token),
  78. }
  79. }
  80. #[derive(Serialize)]
  81. struct AdminTemplateData {
  82. page_content: String,
  83. version: Option<&'static str>,
  84. users: Vec<Value>,
  85. config: Value,
  86. }
  87. impl AdminTemplateData {
  88. fn new(users: Vec<Value>) -> Self {
  89. Self {
  90. page_content: String::from("admin/page"),
  91. version: VERSION,
  92. users,
  93. config: CONFIG.prepare_json(),
  94. }
  95. }
  96. fn render(self) -> Result<String, Error> {
  97. CONFIG.render_template(BASE_TEMPLATE, &self)
  98. }
  99. }
  100. #[get("/", rank = 1)]
  101. fn admin_page(_token: AdminToken, conn: DbConn) -> ApiResult<Html<String>> {
  102. let users = User::get_all(&conn);
  103. let users_json: Vec<Value> = users.iter().map(|u| u.to_json(&conn)).collect();
  104. let text = AdminTemplateData::new(users_json).render()?;
  105. Ok(Html(text))
  106. }
  107. #[derive(Deserialize, Debug)]
  108. #[allow(non_snake_case)]
  109. struct InviteData {
  110. email: String,
  111. }
  112. #[post("/invite", data = "<data>")]
  113. fn invite_user(data: Json<InviteData>, _token: AdminToken, conn: DbConn) -> EmptyResult {
  114. let data: InviteData = data.into_inner();
  115. let email = data.email.clone();
  116. if User::find_by_mail(&data.email, &conn).is_some() {
  117. err!("User already exists")
  118. }
  119. if !CONFIG.invitations_allowed() {
  120. err!("Invitations are not allowed")
  121. }
  122. if CONFIG.mail_enabled() {
  123. let mut user = User::new(email);
  124. user.save(&conn)?;
  125. let org_name = "bitwarden_rs";
  126. mail::send_invite(&user.email, &user.uuid, None, None, &org_name, None)
  127. } else {
  128. let mut invitation = Invitation::new(data.email);
  129. invitation.save(&conn)
  130. }
  131. }
  132. #[post("/users/<uuid>/delete")]
  133. fn delete_user(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult {
  134. let user = match User::find_by_uuid(&uuid, &conn) {
  135. Some(user) => user,
  136. None => err!("User doesn't exist"),
  137. };
  138. user.delete(&conn)
  139. }
  140. #[post("/users/<uuid>/deauth")]
  141. fn deauth_user(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult {
  142. let mut user = match User::find_by_uuid(&uuid, &conn) {
  143. Some(user) => user,
  144. None => err!("User doesn't exist"),
  145. };
  146. Device::delete_all_by_user(&user.uuid, &conn)?;
  147. user.reset_security_stamp();
  148. user.save(&conn)
  149. }
  150. #[post("/config", data = "<data>")]
  151. fn post_config(data: Json<ConfigBuilder>, _token: AdminToken) -> EmptyResult {
  152. let data: ConfigBuilder = data.into_inner();
  153. CONFIG.update_config(data)
  154. }
  155. #[post("/config/delete")]
  156. fn delete_config(_token: AdminToken) -> EmptyResult {
  157. CONFIG.delete_user_config()
  158. }
  159. pub struct AdminToken {}
  160. impl<'a, 'r> FromRequest<'a, 'r> for AdminToken {
  161. type Error = &'static str;
  162. fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
  163. if CONFIG.disable_admin_token() {
  164. Outcome::Success(AdminToken {})
  165. }
  166. else {
  167. let mut cookies = request.cookies();
  168. let access_token = match cookies.get(COOKIE_NAME) {
  169. Some(cookie) => cookie.value(),
  170. None => return Outcome::Forward(()), // If there is no cookie, redirect to login
  171. };
  172. let ip = match request.guard::<ClientIp>() {
  173. Outcome::Success(ip) => ip.ip,
  174. _ => err_handler!("Error getting Client IP"),
  175. };
  176. if decode_admin(access_token).is_err() {
  177. // Remove admin cookie
  178. cookies.remove(Cookie::named(COOKIE_NAME));
  179. error!("Invalid or expired admin JWT. IP: {}.", ip);
  180. return Outcome::Forward(());
  181. }
  182. Outcome::Success(AdminToken {})
  183. }
  184. }
  185. }