vw_small

Hardened fork of Vaultwarden (https://github.com/dani-garcia/vaultwarden) with fewer features.
git clone https://git.philomathiclife.com/repos/vw_small
Log | Files | Refs | README

device.rs (5872B)


      1 use crate::crypto;
      2 use crate::util;
      3 use chrono::{NaiveDateTime, Utc};
      4 
      5 db_object! {
      6     #[derive(Insertable, Queryable)]
      7     #[diesel(table_name = devices)]
      8     #[diesel(treat_none_as_null = true)]
      9     pub struct Device {
     10         pub uuid: String,
     11         created_at: NaiveDateTime,
     12         pub updated_at: NaiveDateTime,
     13         pub user_uuid: String,
     14         name: String,
     15         atype: i32,         // https://github.com/bitwarden/server/blob/master/src/Core/Enums/DeviceType.cs
     16         push_uuid: Option<String>,
     17         push_token: Option<String>,
     18         pub refresh_token: String,
     19         twofactor_remember: Option<String>,
     20     }
     21 }
     22 
     23 /// Local methods
     24 impl Device {
     25     pub fn new(uuid: String, user_uuid: String, name: String, atype: i32) -> Self {
     26         let now = Utc::now().naive_utc();
     27         Self {
     28             uuid,
     29             created_at: now,
     30             updated_at: now,
     31             user_uuid,
     32             name,
     33             atype,
     34             push_uuid: None,
     35             push_token: None,
     36             refresh_token: String::new(),
     37             twofactor_remember: None,
     38         }
     39     }
     40 
     41     pub fn delete_twofactor_remember(&mut self) {
     42         self.twofactor_remember = None;
     43     }
     44 
     45     pub fn refresh_tokens(&mut self, user: &super::User, scope: Vec<String>) -> (String, i64) {
     46         // If there is no refresh token, we create one
     47         if self.refresh_token.is_empty() {
     48             use data_encoding::BASE64URL;
     49             self.refresh_token = crypto::encode_random_bytes::<64>(&BASE64URL);
     50         }
     51         // Update the expiration of the device and the last update date
     52         let time_now = Utc::now();
     53         self.updated_at = time_now.naive_utc();
     54         // ---
     55         // Disabled these keys to be added to the JWT since they could cause the JWT to get too large
     56         // Also These key/value pairs are not used anywhere by either Vaultwarden or Bitwarden Clients
     57         // Because these might get used in the future, and they are added by the Bitwarden Server, lets keep it, but then commented out
     58         // ---
     59         // fn arg: orgs: Vec<super::UserOrganization>,
     60         // ---
     61         // let orgowner: Vec<_> = orgs.iter().filter(|o| o.atype == 0).map(|o| o.org_uuid.clone()).collect();
     62         // let orgadmin: Vec<_> = orgs.iter().filter(|o| o.atype == 1).map(|o| o.org_uuid.clone()).collect();
     63         // let orguser: Vec<_> = orgs.iter().filter(|o| o.atype == 2).map(|o| o.org_uuid.clone()).collect();
     64         // let orgmanager: Vec<_> = orgs.iter().filter(|o| o.atype == 3).map(|o| o.org_uuid.clone()).collect();
     65         // Create the JWT claims struct, to send to the client
     66         use crate::auth::{self, encode_jwt, LoginJwtClaims};
     67         let claims = LoginJwtClaims {
     68             nbf: time_now.timestamp(),
     69             exp: time_now
     70                 .checked_add_signed(*auth::get_default_validity())
     71                 .expect("overflow")
     72                 .timestamp(),
     73             iss: auth::get_jwt_login_issuer().to_owned(),
     74             sub: user.uuid.clone(),
     75             premium: true,
     76             name: user.name.clone(),
     77             email: user.email.clone(),
     78             email_verified: true,
     79             // ---
     80             // Disabled these keys to be added to the JWT since they could cause the JWT to get too large
     81             // Also These key/value pairs are not used anywhere by either Vaultwarden or Bitwarden Clients
     82             // Because these might get used in the future, and they are added by the Bitwarden Server, lets keep it, but then commented out
     83             // See: https://github.com/dani-garcia/vaultwarden/issues/4156
     84             // ---
     85             // orgowner,
     86             // orgadmin,
     87             // orguser,
     88             // orgmanager,
     89             sstamp: user.security_stamp.clone(),
     90             device: self.uuid.clone(),
     91             scope,
     92             amr: vec!["Application".into()],
     93         };
     94 
     95         (
     96             encode_jwt(&claims),
     97             auth::get_default_validity().num_seconds(),
     98         )
     99     }
    100 }
    101 
    102 use crate::api::EmptyResult;
    103 use crate::db::DbConn;
    104 use crate::error::MapResult;
    105 
    106 /// Database methods
    107 impl Device {
    108     pub async fn save(&mut self, conn: &DbConn) -> EmptyResult {
    109         self.updated_at = Utc::now().naive_utc();
    110         db_run! { conn:
    111             {
    112                 util::retry(
    113                     || diesel::replace_into(devices::table).values(DeviceDb::to_db(self)).execute(conn),
    114                     10,
    115                 ).map_res("Error saving device")
    116             }
    117         }
    118     }
    119 
    120     pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
    121         db_run! { conn: {
    122             diesel::delete(devices::table.filter(devices::user_uuid.eq(user_uuid)))
    123                 .execute(conn)
    124                 .map_res("Error removing devices for user")
    125         }}
    126     }
    127 
    128     pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &DbConn) -> Option<Self> {
    129         db_run! { conn: {
    130             devices::table
    131                 .filter(devices::uuid.eq(uuid))
    132                 .filter(devices::user_uuid.eq(user_uuid))
    133                 .first::<DeviceDb>(conn)
    134                 .ok()
    135                 .from_db()
    136         }}
    137     }
    138 
    139     pub async fn find_by_refresh_token(refresh_token: &str, conn: &DbConn) -> Option<Self> {
    140         db_run! { conn: {
    141             devices::table
    142                 .filter(devices::refresh_token.eq(refresh_token))
    143                 .first::<DeviceDb>(conn)
    144                 .ok()
    145                 .from_db()
    146         }}
    147     }
    148 
    149     pub async fn find_latest_active_by_user(user_uuid: &str, conn: &DbConn) -> Option<Self> {
    150         db_run! { conn: {
    151             devices::table
    152                 .filter(devices::user_uuid.eq(user_uuid))
    153                 .order(devices::updated_at.desc())
    154                 .first::<DeviceDb>(conn)
    155                 .ok()
    156                 .from_db()
    157         }}
    158     }
    159 }