1
0
Fork 1
Spiegel von https://github.com/dani-garcia/vaultwarden.git synchronisiert 2025-01-07 11:45:40 +01:00

introduce user_id newtype

Dieser Commit ist enthalten in:
Stefan Melmuk 2024-12-21 11:41:24 +01:00
Ursprung 64ae0aa386
Commit 16afe10898
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 817020C608FE9C09
34 geänderte Dateien mit 351 neuen und 257 gelöschten Zeilen

Datei anzeigen

@ -280,7 +280,7 @@ struct InviteData {
email: String,
}
async fn get_user_or_404(uuid: &str, conn: &mut DbConn) -> ApiResult<User> {
async fn get_user_or_404(uuid: &UserId, conn: &mut DbConn) -> ApiResult<User> {
if let Some(user) = User::find_by_uuid(uuid, conn).await {
Ok(user)
} else {
@ -382,8 +382,8 @@ async fn get_user_by_mail_json(mail: &str, _token: AdminToken, mut conn: DbConn)
}
#[get("/users/<uuid>")]
async fn get_user_json(uuid: &str, _token: AdminToken, mut conn: DbConn) -> JsonResult {
let u = get_user_or_404(uuid, &mut conn).await?;
async fn get_user_json(uuid: UserId, _token: AdminToken, mut conn: DbConn) -> JsonResult {
let u = get_user_or_404(&uuid, &mut conn).await?;
let mut usr = u.to_json(&mut conn).await;
usr["userEnabled"] = json!(u.enabled);
usr["createdAt"] = json!(format_naive_datetime_local(&u.created_at, DT_FMT));
@ -391,11 +391,11 @@ async fn get_user_json(uuid: &str, _token: AdminToken, mut conn: DbConn) -> Json
}
#[post("/users/<uuid>/delete")]
async fn delete_user(uuid: &str, token: AdminToken, mut conn: DbConn) -> EmptyResult {
let user = get_user_or_404(uuid, &mut conn).await?;
async fn delete_user(uuid: UserId, token: AdminToken, mut conn: DbConn) -> EmptyResult {
let user = get_user_or_404(&uuid, &mut conn).await?;
// Get the membership records before deleting the actual user
let memberships = Membership::find_any_state_by_user(uuid, &mut conn).await;
let memberships = Membership::find_any_state_by_user(&uuid, &mut conn).await;
let res = user.delete(&mut conn).await;
for membership in memberships {
@ -415,8 +415,8 @@ async fn delete_user(uuid: &str, token: AdminToken, mut conn: DbConn) -> EmptyRe
}
#[post("/users/<uuid>/deauth")]
async fn deauth_user(uuid: &str, _token: AdminToken, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
let mut user = get_user_or_404(uuid, &mut conn).await?;
async fn deauth_user(uuid: UserId, _token: AdminToken, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
let mut user = get_user_or_404(&uuid, &mut conn).await?;
nt.send_logout(&user, None).await;
@ -436,8 +436,8 @@ async fn deauth_user(uuid: &str, _token: AdminToken, mut conn: DbConn, nt: Notif
}
#[post("/users/<uuid>/disable")]
async fn disable_user(uuid: &str, _token: AdminToken, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
let mut user = get_user_or_404(uuid, &mut conn).await?;
async fn disable_user(uuid: UserId, _token: AdminToken, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
let mut user = get_user_or_404(&uuid, &mut conn).await?;
Device::delete_all_by_user(&user.uuid, &mut conn).await?;
user.reset_security_stamp();
user.enabled = false;
@ -450,16 +450,16 @@ async fn disable_user(uuid: &str, _token: AdminToken, mut conn: DbConn, nt: Noti
}
#[post("/users/<uuid>/enable")]
async fn enable_user(uuid: &str, _token: AdminToken, mut conn: DbConn) -> EmptyResult {
let mut user = get_user_or_404(uuid, &mut conn).await?;
async fn enable_user(uuid: UserId, _token: AdminToken, mut conn: DbConn) -> EmptyResult {
let mut user = get_user_or_404(&uuid, &mut conn).await?;
user.enabled = true;
user.save(&mut conn).await
}
#[post("/users/<uuid>/remove-2fa")]
async fn remove_2fa(uuid: &str, token: AdminToken, mut conn: DbConn) -> EmptyResult {
let mut user = get_user_or_404(uuid, &mut conn).await?;
async fn remove_2fa(uuid: UserId, token: AdminToken, mut conn: DbConn) -> EmptyResult {
let mut user = get_user_or_404(&uuid, &mut conn).await?;
TwoFactor::delete_all_by_user(&user.uuid, &mut conn).await?;
two_factor::enforce_2fa_policy(&user, ACTING_ADMIN_USER, 14, &token.ip.ip, &mut conn).await?;
user.totp_recover = None;
@ -467,8 +467,8 @@ async fn remove_2fa(uuid: &str, token: AdminToken, mut conn: DbConn) -> EmptyRes
}
#[post("/users/<uuid>/invite/resend")]
async fn resend_user_invite(uuid: &str, _token: AdminToken, mut conn: DbConn) -> EmptyResult {
if let Some(user) = User::find_by_uuid(uuid, &mut conn).await {
async fn resend_user_invite(uuid: UserId, _token: AdminToken, mut conn: DbConn) -> EmptyResult {
if let Some(user) = User::find_by_uuid(&uuid, &mut conn).await {
//TODO: replace this with user.status check when it will be available (PR#3397)
if !user.password_hash.is_empty() {
err_code!("User already accepted invitation", Status::BadRequest.code);
@ -487,7 +487,7 @@ async fn resend_user_invite(uuid: &str, _token: AdminToken, mut conn: DbConn) ->
#[derive(Debug, Deserialize)]
struct MembershipTypeData {
user_type: NumberOrString,
user_uuid: String,
user_uuid: UserId,
org_uuid: OrganizationId,
}

Datei anzeigen

@ -306,8 +306,8 @@ async fn put_avatar(data: Json<AvatarData>, headers: Headers, mut conn: DbConn)
}
#[get("/users/<uuid>/public-key")]
async fn get_public_keys(uuid: &str, _headers: Headers, mut conn: DbConn) -> JsonResult {
let user = match User::find_by_uuid(uuid, &mut conn).await {
async fn get_public_keys(uuid: UserId, _headers: Headers, mut conn: DbConn) -> JsonResult {
let user = match User::find_by_uuid(&uuid, &mut conn).await {
Some(user) if user.public_key.is_some() => user,
Some(_) => err_code!("User has no public_key", Status::NotFound.code),
None => err_code!("User doesn't exist", Status::NotFound.code),
@ -793,7 +793,7 @@ async fn post_verify_email(headers: Headers) -> EmptyResult {
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct VerifyEmailTokenData {
user_id: String,
user_id: UserId,
token: String,
}
@ -808,7 +808,7 @@ async fn post_verify_email_token(data: Json<VerifyEmailTokenData>, mut conn: DbC
let Ok(claims) = decode_verify_email(&data.token) else {
err!("Invalid claim")
};
if claims.sub != user.uuid {
if claims.sub != *user.uuid {
err!("Invalid claim");
}
user.verified_at = Some(Utc::now().naive_utc());
@ -850,7 +850,7 @@ async fn post_delete_recover(data: Json<DeleteRecoverData>, mut conn: DbConn) ->
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct DeleteRecoverTokenData {
user_id: String,
user_id: UserId,
token: String,
}
@ -866,7 +866,7 @@ async fn post_delete_recover_token(data: Json<DeleteRecoverTokenData>, mut conn:
err!("User doesn't exist")
};
if claims.sub != user.uuid {
if claims.sub != *user.uuid {
err!("Invalid claim");
}
user.delete(&mut conn).await

Datei anzeigen

@ -771,7 +771,7 @@ async fn post_collections_update(
let posted_collections = HashSet::<String>::from_iter(data.collection_ids);
let current_collections =
HashSet::<String>::from_iter(cipher.get_collections(headers.user.uuid.clone(), &mut conn).await);
HashSet::<String>::from_iter(cipher.get_collections(headers.user.uuid.to_string(), &mut conn).await);
for collection in posted_collections.symmetric_difference(&current_collections) {
match Collection::find_by_uuid_and_org(collection, cipher.organization_uuid.as_ref().unwrap(), &mut conn).await
@ -848,7 +848,7 @@ async fn post_collections_admin(
let posted_collections = HashSet::<String>::from_iter(data.collection_ids);
let current_collections =
HashSet::<String>::from_iter(cipher.get_admin_collections(headers.user.uuid.clone(), &mut conn).await);
HashSet::<String>::from_iter(cipher.get_admin_collections(headers.user.uuid.to_string(), &mut conn).await);
for collection in posted_collections.symmetric_difference(&current_collections) {
match Collection::find_by_uuid_and_org(collection, cipher.organization_uuid.as_ref().unwrap(), &mut conn).await
@ -1848,7 +1848,7 @@ pub enum CipherSyncType {
}
impl CipherSyncData {
pub async fn new(user_uuid: &str, sync_type: CipherSyncType, conn: &mut DbConn) -> Self {
pub async fn new(user_uuid: &UserId, sync_type: CipherSyncType, conn: &mut DbConn) -> Self {
let cipher_folders: HashMap<String, String>;
let cipher_favorites: HashSet<String>;
match sync_type {

Datei anzeigen

@ -701,7 +701,7 @@ async fn policies_emergency_access(emer_id: &str, headers: Headers, mut conn: Db
fn is_valid_request(
emergency_access: &EmergencyAccess,
requesting_user_uuid: &str,
requesting_user_uuid: &UserId,
requested_access_type: EmergencyAccessType,
) -> bool {
emergency_access.grantee_uuid.is_some()

Datei anzeigen

@ -8,7 +8,7 @@ use crate::{
api::{EmptyResult, JsonResult},
auth::{AdminHeaders, Headers},
db::{
models::{Cipher, Event, Membership, MembershipId, OrganizationId},
models::{Cipher, Event, Membership, MembershipId, OrganizationId, UserId},
DbConn, DbPool,
},
util::parse_date,
@ -218,7 +218,7 @@ async fn post_events_collect(data: Json<Vec<EventCollection>>, headers: Headers,
Ok(())
}
pub async fn log_user_event(event_type: i32, user_uuid: &str, device_type: i32, ip: &IpAddr, conn: &mut DbConn) {
pub async fn log_user_event(event_type: i32, user_uuid: &UserId, device_type: i32, ip: &IpAddr, conn: &mut DbConn) {
if !CONFIG.org_events_enabled() {
return;
}
@ -227,7 +227,7 @@ pub async fn log_user_event(event_type: i32, user_uuid: &str, device_type: i32,
async fn _log_user_event(
event_type: i32,
user_uuid: &str,
user_uuid: &UserId,
device_type: i32,
event_date: Option<NaiveDateTime>,
ip: &IpAddr,
@ -238,8 +238,8 @@ async fn _log_user_event(
// Upstream saves the event also without any org_uuid.
let mut event = Event::new(event_type, event_date);
event.user_uuid = Some(String::from(user_uuid));
event.act_user_uuid = Some(String::from(user_uuid));
event.user_uuid = Some(user_uuid.clone());
event.act_user_uuid = Some(user_uuid.to_string());
event.device_type = Some(device_type);
event.ip_address = Some(ip.to_string());
events.push(event);
@ -247,9 +247,9 @@ async fn _log_user_event(
// For each org a user is a member of store these events per org
for org_uuid in orgs {
let mut event = Event::new(event_type, event_date);
event.user_uuid = Some(String::from(user_uuid));
event.user_uuid = Some(user_uuid.clone());
event.org_uuid = Some(org_uuid);
event.act_user_uuid = Some(String::from(user_uuid));
event.act_user_uuid = Some(user_uuid.to_string());
event.device_type = Some(device_type);
event.ip_address = Some(ip.to_string());
events.push(event);

Datei anzeigen

@ -358,7 +358,7 @@ async fn get_org_collections_details(
let users: Vec<Value> = coll_users
.iter()
.filter(|collection_user| collection_user.collection_uuid == col.uuid)
.map(|collection_user| SelectionReadOnly::to_collection_user_details_read_only(collection_user).to_json())
.map(|collection_user| UserSelection::to_collection_user_details_read_only(collection_user).to_json())
.collect();
// get the group details for the given collection
@ -667,7 +667,7 @@ async fn get_org_collection_detail(
.await
.iter()
.map(|collection_user| {
SelectionReadOnly::to_collection_user_details_read_only(collection_user).to_json()
UserSelection::to_collection_user_details_read_only(collection_user).to_json()
})
.collect();
@ -761,7 +761,7 @@ async fn get_org_details(data: OrgIdData, headers: Headers, mut conn: DbConn) ->
})))
}
async fn _get_org_details(org_id: &OrganizationId, host: &str, user_uuid: &str, conn: &mut DbConn) -> Value {
async fn _get_org_details(org_id: &OrganizationId, host: &str, user_uuid: &UserId, conn: &mut DbConn) -> Value {
let ciphers = Cipher::find_by_org(org_id, conn).await;
let cipher_sync_data = CipherSyncData::new(user_uuid, CipherSyncType::Organization, conn).await;
@ -2384,16 +2384,30 @@ impl SelectionReadOnly {
CollectionGroup::new(self.id.clone(), groups_uuid, self.read_only, self.hide_passwords)
}
pub fn to_collection_group_details_read_only(collection_group: &CollectionGroup) -> SelectionReadOnly {
SelectionReadOnly {
pub fn to_collection_group_details_read_only(collection_group: &CollectionGroup) -> Self {
Self {
id: collection_group.groups_uuid.clone(),
read_only: collection_group.read_only,
hide_passwords: collection_group.hide_passwords,
}
}
pub fn to_collection_user_details_read_only(collection_user: &CollectionUser) -> SelectionReadOnly {
SelectionReadOnly {
pub fn to_json(&self) -> Value {
json!(self)
}
}
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
struct UserSelection {
id: UserId,
read_only: bool,
hide_passwords: bool,
}
impl UserSelection {
pub fn to_collection_user_details_read_only(collection_user: &CollectionUser) -> Self {
Self {
id: collection_user.user_uuid.clone(),
read_only: collection_user.read_only,
hide_passwords: collection_user.hide_passwords,

Datei anzeigen

@ -106,7 +106,7 @@ async fn enforce_disable_hide_email_policy(data: &SendData, headers: &Headers, c
Ok(())
}
fn create_send(data: SendData, user_uuid: String) -> ApiResult<Send> {
fn create_send(data: SendData, user_uuid: UserId) -> ApiResult<Send> {
let data_val = if data.r#type == SendType::Text as i32 {
data.text
} else if data.r#type == SendType::File as i32 {

Datei anzeigen

@ -7,7 +7,7 @@ use crate::{
auth::{ClientIp, Headers},
crypto,
db::{
models::{EventType, TwoFactor, TwoFactorType},
models::{EventType, TwoFactor, TwoFactorType, UserId},
DbConn,
},
util::NumberOrString,
@ -95,7 +95,7 @@ async fn activate_authenticator_put(data: Json<EnableAuthenticatorData>, headers
}
pub async fn validate_totp_code_str(
user_uuid: &str,
user_uuid: &UserId,
totp_code: &str,
secret: &str,
ip: &ClientIp,
@ -109,7 +109,7 @@ pub async fn validate_totp_code_str(
}
pub async fn validate_totp_code(
user_uuid: &str,
user_uuid: &UserId,
totp_code: &str,
secret: &str,
ip: &ClientIp,
@ -124,7 +124,7 @@ pub async fn validate_totp_code(
let mut twofactor =
match TwoFactor::find_by_user_and_type(user_uuid, TwoFactorType::Authenticator as i32, conn).await {
Some(tf) => tf,
_ => TwoFactor::new(user_uuid.to_string(), TwoFactorType::Authenticator, secret.to_string()),
_ => TwoFactor::new(user_uuid.clone(), TwoFactorType::Authenticator, secret.to_string()),
};
// The amount of steps back and forward in time

Datei anzeigen

@ -11,7 +11,7 @@ use crate::{
auth::Headers,
crypto,
db::{
models::{EventType, TwoFactor, TwoFactorType, User},
models::{EventType, TwoFactor, TwoFactorType, User, UserId},
DbConn,
},
error::MapResult,
@ -228,11 +228,11 @@ const AUTH_PREFIX: &str = "AUTH";
const DUO_PREFIX: &str = "TX";
const APP_PREFIX: &str = "APP";
async fn get_user_duo_data(uuid: &str, conn: &mut DbConn) -> DuoStatus {
async fn get_user_duo_data(user_uuid: &UserId, conn: &mut DbConn) -> DuoStatus {
let type_ = TwoFactorType::Duo as i32;
// If the user doesn't have an entry, disabled
let Some(twofactor) = TwoFactor::find_by_user_and_type(uuid, type_, conn).await else {
let Some(twofactor) = TwoFactor::find_by_user_and_type(user_uuid, type_, conn).await else {
return DuoStatus::Disabled(DuoData::global().is_some());
};

Datei anzeigen

@ -10,7 +10,7 @@ use crate::{
auth::Headers,
crypto,
db::{
models::{EventType, TwoFactor, TwoFactorType, User},
models::{EventType, TwoFactor, TwoFactorType, User, UserId},
DbConn,
},
error::{Error, MapResult},
@ -59,7 +59,7 @@ async fn send_email_login(data: Json<SendEmailLoginData>, mut conn: DbConn) -> E
}
/// Generate the token, save the data for later verification and send email to user
pub async fn send_token(user_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn send_token(user_uuid: &UserId, conn: &mut DbConn) -> EmptyResult {
let type_ = TwoFactorType::Email as i32;
let mut twofactor =
TwoFactor::find_by_user_and_type(user_uuid, type_, conn).await.map_res("Two factor not found")?;
@ -198,7 +198,7 @@ async fn email(data: Json<EmailData>, headers: Headers, mut conn: DbConn) -> Jso
}
/// Validate the email code when used as TwoFactor token mechanism
pub async fn validate_email_code_str(user_uuid: &str, token: &str, data: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn validate_email_code_str(user_uuid: &UserId, token: &str, data: &str, conn: &mut DbConn) -> EmptyResult {
let mut email_data = EmailTokenData::from_json(data)?;
let mut twofactor = TwoFactor::find_by_user_and_type(user_uuid, TwoFactorType::Email as i32, conn)
.await
@ -327,7 +327,7 @@ pub fn obscure_email(email: &str) -> String {
format!("{}@{}", new_name, &domain)
}
pub async fn find_and_activate_email_2fa(user_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn find_and_activate_email_2fa(user_uuid: &UserId, conn: &mut DbConn) -> EmptyResult {
if let Some(user) = User::find_by_uuid(user_uuid, conn).await {
activate_email_2fa(&user, conn).await
} else {

Datei anzeigen

@ -6,7 +6,7 @@ use crate::{
auth::Headers,
crypto,
db::{
models::{TwoFactor, TwoFactorType},
models::{TwoFactor, TwoFactorType, UserId},
DbConn,
},
error::{Error, MapResult},
@ -104,7 +104,7 @@ async fn verify_otp(data: Json<ProtectedActionVerify>, headers: Headers, mut con
pub async fn validate_protected_action_otp(
otp: &str,
user_uuid: &str,
user_uuid: &UserId,
delete_if_valid: bool,
conn: &mut DbConn,
) -> EmptyResult {

Datei anzeigen

@ -11,7 +11,7 @@ use crate::{
},
auth::Headers,
db::{
models::{EventType, TwoFactor, TwoFactorType},
models::{EventType, TwoFactor, TwoFactorType, UserId},
DbConn,
},
error::Error,
@ -148,7 +148,7 @@ async fn generate_webauthn_challenge(data: Json<PasswordOrOtpData>, headers: Hea
)?;
let type_ = TwoFactorType::WebauthnRegisterChallenge;
TwoFactor::new(user.uuid, type_, serde_json::to_string(&state)?).save(&mut conn).await?;
TwoFactor::new(user.uuid.clone(), type_, serde_json::to_string(&state)?).save(&mut conn).await?;
let mut challenge_value = serde_json::to_value(challenge.public_key)?;
challenge_value["status"] = "ok".into();
@ -352,7 +352,7 @@ async fn delete_webauthn(data: Json<DeleteU2FData>, headers: Headers, mut conn:
}
pub async fn get_webauthn_registrations(
user_uuid: &str,
user_uuid: &UserId,
conn: &mut DbConn,
) -> Result<(bool, Vec<WebauthnRegistration>), Error> {
let type_ = TwoFactorType::Webauthn as i32;
@ -362,7 +362,7 @@ pub async fn get_webauthn_registrations(
}
}
pub async fn generate_webauthn_login(user_uuid: &str, conn: &mut DbConn) -> JsonResult {
pub async fn generate_webauthn_login(user_uuid: &UserId, conn: &mut DbConn) -> JsonResult {
// Load saved credentials
let creds: Vec<Credential> =
get_webauthn_registrations(user_uuid, conn).await?.1.into_iter().map(|r| r.credential).collect();
@ -376,7 +376,7 @@ pub async fn generate_webauthn_login(user_uuid: &str, conn: &mut DbConn) -> Json
let (response, state) = WebauthnConfig::load().generate_challenge_authenticate_options(creds, Some(ext))?;
// Save the challenge state for later validation
TwoFactor::new(user_uuid.into(), TwoFactorType::WebauthnLoginChallenge, serde_json::to_string(&state)?)
TwoFactor::new(user_uuid.clone(), TwoFactorType::WebauthnLoginChallenge, serde_json::to_string(&state)?)
.save(conn)
.await?;
@ -384,7 +384,7 @@ pub async fn generate_webauthn_login(user_uuid: &str, conn: &mut DbConn) -> Json
Ok(Json(serde_json::to_value(response.public_key)?))
}
pub async fn validate_webauthn_login(user_uuid: &str, response: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn validate_webauthn_login(user_uuid: &UserId, response: &str, conn: &mut DbConn) -> EmptyResult {
let type_ = TwoFactorType::WebauthnLoginChallenge as i32;
let state = match TwoFactor::find_by_user_and_type(user_uuid, type_, conn).await {
Some(tf) => {
@ -413,7 +413,7 @@ pub async fn validate_webauthn_login(user_uuid: &str, response: &str, conn: &mut
if &reg.credential.cred_id == cred_id {
reg.credential.counter = auth_data.counter;
TwoFactor::new(user_uuid.to_string(), TwoFactorType::Webauthn, serde_json::to_string(&registrations)?)
TwoFactor::new(user_uuid.clone(), TwoFactorType::Webauthn, serde_json::to_string(&registrations)?)
.save(conn)
.await?;
return Ok(());

Datei anzeigen

@ -31,7 +31,7 @@ pub fn routes() -> Vec<Route> {
async fn login(data: Form<ConnectData>, client_header: ClientHeaders, mut conn: DbConn) -> JsonResult {
let data: ConnectData = data.into_inner();
let mut user_uuid: Option<String> = None;
let mut user_uuid: Option<UserId> = None;
let login_result = match data.grant_type.as_ref() {
"refresh_token" => {
@ -141,7 +141,7 @@ struct MasterPasswordPolicy {
async fn _password_login(
data: ConnectData,
user_uuid: &mut Option<String>,
user_uuid: &mut Option<UserId>,
conn: &mut DbConn,
ip: &ClientIp,
) -> JsonResult {
@ -359,7 +359,7 @@ async fn _password_login(
async fn _api_key_login(
data: ConnectData,
user_uuid: &mut Option<String>,
user_uuid: &mut Option<UserId>,
conn: &mut DbConn,
ip: &ClientIp,
) -> JsonResult {
@ -376,7 +376,7 @@ async fn _api_key_login(
async fn _user_api_key_login(
data: ConnectData,
user_uuid: &mut Option<String>,
user_uuid: &mut Option<UserId>,
conn: &mut DbConn,
ip: &ClientIp,
) -> JsonResult {
@ -385,7 +385,8 @@ async fn _user_api_key_login(
let Some(client_user_uuid) = client_id.strip_prefix("user.") else {
err!("Malformed client_id", format!("IP: {}.", ip.ip))
};
let Some(user) = User::find_by_uuid(client_user_uuid, conn).await else {
let client_user_uuid: UserId = client_user_uuid.to_string().into();
let Some(user) = User::find_by_uuid(&client_user_uuid, conn).await else {
err!("Invalid client_id", format!("IP: {}.", ip.ip))
};
@ -615,7 +616,7 @@ fn _selected_data(tf: Option<TwoFactor>) -> ApiResult<String> {
async fn _json_err_twofactor(
providers: &[i32],
user_uuid: &str,
user_uuid: &UserId,
data: &ConnectData,
conn: &mut DbConn,
) -> ApiResult<Value> {

Datei anzeigen

@ -10,7 +10,7 @@ use rocket_ws::{Message, WebSocket};
use crate::{
auth::{ClientIp, WsAccessTokenHeader},
db::{
models::{Cipher, Folder, Send as DbSend, User},
models::{Cipher, Folder, Send as DbSend, User, UserId},
DbConn,
},
Error, CONFIG,
@ -53,13 +53,13 @@ struct WsAccessToken {
struct WSEntryMapGuard {
users: Arc<WebSocketUsers>,
user_uuid: String,
user_uuid: UserId,
entry_uuid: uuid::Uuid,
addr: IpAddr,
}
impl WSEntryMapGuard {
fn new(users: Arc<WebSocketUsers>, user_uuid: String, entry_uuid: uuid::Uuid, addr: IpAddr) -> Self {
fn new(users: Arc<WebSocketUsers>, user_uuid: UserId, entry_uuid: uuid::Uuid, addr: IpAddr) -> Self {
Self {
users,
user_uuid,
@ -72,7 +72,7 @@ impl WSEntryMapGuard {
impl Drop for WSEntryMapGuard {
fn drop(&mut self) {
info!("Closing WS connection from {}", self.addr);
if let Some(mut entry) = self.users.map.get_mut(&self.user_uuid) {
if let Some(mut entry) = self.users.map.get_mut(&self.user_uuid.to_string()) {
entry.retain(|(uuid, _)| uuid != &self.entry_uuid);
}
}
@ -129,7 +129,7 @@ fn websockets_hub<'r>(
// Add a channel to send messages to this client to the map
let entry_uuid = uuid::Uuid::new_v4();
let (tx, rx) = tokio::sync::mpsc::channel::<Message>(100);
users.map.entry(claims.sub.clone()).or_default().push((entry_uuid, tx));
users.map.entry(claims.sub.to_string()).or_default().push((entry_uuid, tx));
// Once the guard goes out of scope, the connection will have been closed and the entry will be deleted from the map
(rx, WSEntryMapGuard::new(users, claims.sub, entry_uuid, addr))
@ -328,8 +328,8 @@ pub struct WebSocketUsers {
}
impl WebSocketUsers {
async fn send_update(&self, user_uuid: &str, data: &[u8]) {
if let Some(user) = self.map.get(user_uuid).map(|v| v.clone()) {
async fn send_update(&self, user_uuid: &UserId, data: &[u8]) {
if let Some(user) = self.map.get(user_uuid.as_ref()).map(|v| v.clone()) {
for (_, sender) in user.iter() {
if let Err(e) = sender.send(Message::binary(data)).await {
error!("Error sending WS update {e}");
@ -345,7 +345,7 @@ impl WebSocketUsers {
return;
}
let data = create_update(
vec![("UserId".into(), user.uuid.clone().into()), ("Date".into(), serialize_date(user.updated_at))],
vec![("UserId".into(), user.uuid.to_string().into()), ("Date".into(), serialize_date(user.updated_at))],
ut,
None,
);
@ -365,7 +365,7 @@ impl WebSocketUsers {
return;
}
let data = create_update(
vec![("UserId".into(), user.uuid.clone().into()), ("Date".into(), serialize_date(user.updated_at))],
vec![("UserId".into(), user.uuid.to_string().into()), ("Date".into(), serialize_date(user.updated_at))],
UpdateType::LogOut,
acting_device_uuid.clone(),
);
@ -393,7 +393,7 @@ impl WebSocketUsers {
let data = create_update(
vec![
("Id".into(), folder.uuid.clone().into()),
("UserId".into(), folder.user_uuid.clone().into()),
("UserId".into(), folder.user_uuid.to_string().into()),
("RevisionDate".into(), serialize_date(folder.updated_at)),
],
ut,
@ -413,7 +413,7 @@ impl WebSocketUsers {
&self,
ut: UpdateType,
cipher: &Cipher,
user_uuids: &[String],
user_uuids: &[UserId],
acting_device_uuid: &String,
collection_uuids: Option<Vec<String>>,
conn: &mut DbConn,
@ -432,7 +432,7 @@ impl WebSocketUsers {
serialize_date(Utc::now().naive_utc()),
)
} else {
(convert_option(cipher.user_uuid.clone()), Value::Nil, serialize_date(cipher.updated_at))
(convert_option(cipher.user_uuid.as_deref()), Value::Nil, serialize_date(cipher.updated_at))
};
let data = create_update(
@ -462,7 +462,7 @@ impl WebSocketUsers {
&self,
ut: UpdateType,
send: &DbSend,
user_uuids: &[String],
user_uuids: &[UserId],
acting_device_uuid: &String,
conn: &mut DbConn,
) {
@ -470,7 +470,7 @@ impl WebSocketUsers {
if *NOTIFICATIONS_DISABLED {
return;
}
let user_uuid = convert_option(send.user_uuid.clone());
let user_uuid = convert_option(send.user_uuid.as_deref());
let data = create_update(
vec![
@ -494,7 +494,7 @@ impl WebSocketUsers {
pub async fn send_auth_request(
&self,
user_uuid: &String,
user_uuid: &UserId,
auth_request_uuid: &String,
acting_device_uuid: &String,
conn: &mut DbConn,
@ -504,7 +504,7 @@ impl WebSocketUsers {
return;
}
let data = create_update(
vec![("Id".into(), auth_request_uuid.clone().into()), ("UserId".into(), user_uuid.clone().into())],
vec![("Id".into(), auth_request_uuid.clone().into()), ("UserId".into(), user_uuid.to_string().into())],
UpdateType::AuthRequest,
Some(acting_device_uuid.to_string()),
);
@ -513,13 +513,13 @@ impl WebSocketUsers {
}
if CONFIG.push_enabled() {
push_auth_request(user_uuid.to_string(), auth_request_uuid.to_string(), conn).await;
push_auth_request(user_uuid.clone(), auth_request_uuid.to_string(), conn).await;
}
}
pub async fn send_auth_response(
&self,
user_uuid: &String,
user_uuid: &UserId,
auth_response_uuid: &str,
approving_device_uuid: String,
conn: &mut DbConn,
@ -529,17 +529,16 @@ impl WebSocketUsers {
return;
}
let data = create_update(
vec![("Id".into(), auth_response_uuid.to_owned().into()), ("UserId".into(), user_uuid.clone().into())],
vec![("Id".into(), auth_response_uuid.to_owned().into()), ("UserId".into(), user_uuid.to_string().into())],
UpdateType::AuthRequestResponse,
approving_device_uuid.clone().into(),
);
if CONFIG.enable_websocket() {
self.send_update(auth_response_uuid, &data).await;
self.send_update(user_uuid, &data).await;
}
if CONFIG.push_enabled() {
push_auth_response(user_uuid.to_string(), auth_response_uuid.to_string(), approving_device_uuid, conn)
.await;
push_auth_response(user_uuid.clone(), auth_response_uuid.to_string(), approving_device_uuid, conn).await;
}
}
}
@ -558,16 +557,16 @@ impl AnonymousWebSocketSubscriptions {
}
}
pub async fn send_auth_response(&self, user_uuid: &String, auth_response_uuid: &str) {
pub async fn send_auth_response(&self, user_uuid: &UserId, auth_response_uuid: &str) {
if !CONFIG.enable_websocket() {
return;
}
let data = create_anonymous_update(
vec![("Id".into(), auth_response_uuid.to_owned().into()), ("UserId".into(), user_uuid.clone().into())],
vec![("Id".into(), auth_response_uuid.to_owned().into()), ("UserId".into(), user_uuid.to_string().into())],
UpdateType::AuthRequestResponse,
user_uuid.to_string(),
user_uuid.clone(),
);
self.send_update(auth_response_uuid, &data).await;
self.send_update(user_uuid, &data).await;
}
}
@ -604,7 +603,7 @@ fn create_update(payload: Vec<(Value, Value)>, ut: UpdateType, acting_device_uui
serialize(value)
}
fn create_anonymous_update(payload: Vec<(Value, Value)>, ut: UpdateType, user_id: String) -> Vec<u8> {
fn create_anonymous_update(payload: Vec<(Value, Value)>, ut: UpdateType, user_id: UserId) -> Vec<u8> {
use rmpv::Value as V;
let value = V::Array(vec![
@ -615,7 +614,7 @@ fn create_anonymous_update(payload: Vec<(Value, Value)>, ut: UpdateType, user_id
V::Array(vec![V::Map(vec![
("Type".into(), (ut as i32).into()),
("Payload".into(), payload.into()),
("UserId".into(), user_id.into()),
("UserId".into(), user_id.to_string().into()),
])]),
]);

Datei anzeigen

@ -7,7 +7,7 @@ use tokio::sync::RwLock;
use crate::{
api::{ApiResult, EmptyResult, UpdateType},
db::models::{Cipher, Device, Folder, Send, User},
db::models::{Cipher, Device, Folder, Send, User, UserId},
http_client::make_http_request,
util::format_date,
CONFIG,
@ -284,8 +284,8 @@ async fn send_to_push_relay(notification_data: Value) {
};
}
pub async fn push_auth_request(user_uuid: String, auth_request_uuid: String, conn: &mut crate::db::DbConn) {
if Device::check_user_has_push_device(user_uuid.as_str(), conn).await {
pub async fn push_auth_request(user_uuid: UserId, auth_request_uuid: String, conn: &mut crate::db::DbConn) {
if Device::check_user_has_push_device(&user_uuid, conn).await {
tokio::task::spawn(send_to_push_relay(json!({
"userId": user_uuid,
"organizationId": (),
@ -301,12 +301,12 @@ pub async fn push_auth_request(user_uuid: String, auth_request_uuid: String, con
}
pub async fn push_auth_response(
user_uuid: String,
user_uuid: UserId,
auth_request_uuid: String,
approving_device_uuid: String,
conn: &mut crate::db::DbConn,
) {
if Device::check_user_has_push_device(user_uuid.as_str(), conn).await {
if Device::check_user_has_push_device(&user_uuid, conn).await {
tokio::task::spawn(send_to_push_relay(json!({
"userId": user_uuid,
"organizationId": (),

Datei anzeigen

@ -14,7 +14,7 @@ use std::{
net::IpAddr,
};
use crate::db::models::{MembershipId, OrganizationId};
use crate::db::models::{MembershipId, OrganizationId, UserId};
use crate::{error::Error, CONFIG};
const JWT_ALGORITHM: Algorithm = Algorithm::RS256;
@ -151,7 +151,7 @@ pub struct LoginJwtClaims {
// Issuer
pub iss: String,
// Subject
pub sub: String,
pub sub: UserId,
pub premium: bool,
pub name: String,

Datei anzeigen

@ -3,7 +3,7 @@ use std::io::ErrorKind;
use bigdecimal::{BigDecimal, ToPrimitive};
use serde_json::Value;
use super::OrganizationId;
use super::{OrganizationId, UserId};
use crate::CONFIG;
db_object! {
@ -145,7 +145,7 @@ impl Attachment {
}}
}
pub async fn size_by_user(user_uuid: &str, conn: &mut DbConn) -> i64 {
pub async fn size_by_user(user_uuid: &UserId, conn: &mut DbConn) -> i64 {
db_run! { conn: {
let result: Option<BigDecimal> = attachments::table
.left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid)))
@ -162,7 +162,7 @@ impl Attachment {
}}
}
pub async fn count_by_user(user_uuid: &str, conn: &mut DbConn) -> i64 {
pub async fn count_by_user(user_uuid: &UserId, conn: &mut DbConn) -> i64 {
db_run! { conn: {
attachments::table
.left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid)))
@ -205,7 +205,7 @@ impl Attachment {
// There is no filtering done here if the user actually has access!
// It is used to speed up the sync process, and the matching is done in a different part.
pub async fn find_all_by_user_and_orgs(
user_uuid: &str,
user_uuid: &UserId,
org_uuids: &Vec<OrganizationId>,
conn: &mut DbConn,
) -> Vec<Self> {

Datei anzeigen

@ -1,4 +1,4 @@
use super::OrganizationId;
use super::{OrganizationId, UserId};
use crate::crypto::ct_eq;
use chrono::{NaiveDateTime, Utc};
@ -9,7 +9,7 @@ db_object! {
#[diesel(primary_key(uuid))]
pub struct AuthRequest {
pub uuid: String,
pub user_uuid: String,
pub user_uuid: UserId,
pub organization_uuid: Option<OrganizationId>,
pub request_device_identifier: String,
@ -34,7 +34,7 @@ db_object! {
impl AuthRequest {
pub fn new(
user_uuid: String,
user_uuid: UserId,
request_device_identifier: String,
device_type: i32,
request_ip: String,
@ -112,7 +112,7 @@ impl AuthRequest {
}}
}
pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &mut DbConn) -> Option<Self> {
pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &UserId, conn: &mut DbConn) -> Option<Self> {
db_run! {conn: {
auth_requests::table
.filter(auth_requests::uuid.eq(uuid))
@ -123,7 +123,7 @@ impl AuthRequest {
}}
}
pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<Self> {
db_run! {conn: {
auth_requests::table
.filter(auth_requests::user_uuid.eq(user_uuid))

Datei anzeigen

@ -5,7 +5,7 @@ use serde_json::Value;
use super::{
Attachment, CollectionCipher, Favorite, FolderCipher, Group, Membership, MembershipStatus, MembershipType,
OrganizationId, User,
OrganizationId, User, UserId,
};
use crate::api::core::{CipherData, CipherSyncData, CipherSyncType};
@ -22,7 +22,7 @@ db_object! {
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
pub user_uuid: Option<String>,
pub user_uuid: Option<UserId>,
pub organization_uuid: Option<OrganizationId>,
pub key: Option<String>,
@ -136,7 +136,7 @@ impl Cipher {
pub async fn to_json(
&self,
host: &str,
user_uuid: &str,
user_uuid: &UserId,
cipher_sync_data: Option<&CipherSyncData>,
sync_type: CipherSyncType,
conn: &mut DbConn,
@ -357,7 +357,7 @@ impl Cipher {
json_object
}
pub async fn update_users_revision(&self, conn: &mut DbConn) -> Vec<String> {
pub async fn update_users_revision(&self, conn: &mut DbConn) -> Vec<UserId> {
let mut user_uuids = Vec::new();
match self.user_uuid {
Some(ref user_uuid) => {
@ -443,7 +443,7 @@ impl Cipher {
Ok(())
}
pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn delete_all_by_user(user_uuid: &UserId, conn: &mut DbConn) -> EmptyResult {
for cipher in Self::find_owned_by_user(user_uuid, conn).await {
cipher.delete(conn).await?;
}
@ -461,7 +461,12 @@ impl Cipher {
}
}
pub async fn move_to_folder(&self, folder_uuid: Option<String>, user_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn move_to_folder(
&self,
folder_uuid: Option<String>,
user_uuid: &UserId,
conn: &mut DbConn,
) -> EmptyResult {
User::update_uuid_revision(user_uuid, conn).await;
match (self.get_folder_uuid(user_uuid, conn).await, folder_uuid) {
@ -489,14 +494,14 @@ impl Cipher {
}
/// Returns whether this cipher is directly owned by the user.
pub fn is_owned_by_user(&self, user_uuid: &str) -> bool {
pub fn is_owned_by_user(&self, user_uuid: &UserId) -> bool {
self.user_uuid.is_some() && self.user_uuid.as_ref().unwrap() == user_uuid
}
/// Returns whether this cipher is owned by an org in which the user has full access.
async fn is_in_full_access_org(
&self,
user_uuid: &str,
user_uuid: &UserId,
cipher_sync_data: Option<&CipherSyncData>,
conn: &mut DbConn,
) -> bool {
@ -515,7 +520,7 @@ impl Cipher {
/// Returns whether this cipher is owned by an group in which the user has full access.
async fn is_in_full_access_group(
&self,
user_uuid: &str,
user_uuid: &UserId,
cipher_sync_data: Option<&CipherSyncData>,
conn: &mut DbConn,
) -> bool {
@ -539,7 +544,7 @@ impl Cipher {
/// the access restrictions.
pub async fn get_access_restrictions(
&self,
user_uuid: &str,
user_uuid: &UserId,
cipher_sync_data: Option<&CipherSyncData>,
conn: &mut DbConn,
) -> Option<(bool, bool)> {
@ -599,7 +604,7 @@ impl Cipher {
Some((read_only, hide_passwords))
}
async fn get_user_collections_access_flags(&self, user_uuid: &str, conn: &mut DbConn) -> Vec<(bool, bool)> {
async fn get_user_collections_access_flags(&self, user_uuid: &UserId, conn: &mut DbConn) -> Vec<(bool, bool)> {
db_run! {conn: {
// Check whether this cipher is in any collections accessible to the
// user. If so, retrieve the access flags for each collection.
@ -616,7 +621,7 @@ impl Cipher {
}}
}
async fn get_group_collections_access_flags(&self, user_uuid: &str, conn: &mut DbConn) -> Vec<(bool, bool)> {
async fn get_group_collections_access_flags(&self, user_uuid: &UserId, conn: &mut DbConn) -> Vec<(bool, bool)> {
if !CONFIG.org_groups_enabled() {
return Vec::new();
}
@ -642,31 +647,31 @@ impl Cipher {
}}
}
pub async fn is_write_accessible_to_user(&self, user_uuid: &str, conn: &mut DbConn) -> bool {
pub async fn is_write_accessible_to_user(&self, user_uuid: &UserId, conn: &mut DbConn) -> bool {
match self.get_access_restrictions(user_uuid, None, conn).await {
Some((read_only, _hide_passwords)) => !read_only,
None => false,
}
}
pub async fn is_accessible_to_user(&self, user_uuid: &str, conn: &mut DbConn) -> bool {
pub async fn is_accessible_to_user(&self, user_uuid: &UserId, conn: &mut DbConn) -> bool {
self.get_access_restrictions(user_uuid, None, conn).await.is_some()
}
// Returns whether this cipher is a favorite of the specified user.
pub async fn is_favorite(&self, user_uuid: &str, conn: &mut DbConn) -> bool {
pub async fn is_favorite(&self, user_uuid: &UserId, conn: &mut DbConn) -> bool {
Favorite::is_favorite(&self.uuid, user_uuid, conn).await
}
// Sets whether this cipher is a favorite of the specified user.
pub async fn set_favorite(&self, favorite: Option<bool>, user_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn set_favorite(&self, favorite: Option<bool>, user_uuid: &UserId, conn: &mut DbConn) -> EmptyResult {
match favorite {
None => Ok(()), // No change requested.
Some(status) => Favorite::set_favorite(status, &self.uuid, user_uuid, conn).await,
}
}
pub async fn get_folder_uuid(&self, user_uuid: &str, conn: &mut DbConn) -> Option<String> {
pub async fn get_folder_uuid(&self, user_uuid: &UserId, conn: &mut DbConn) -> Option<String> {
db_run! {conn: {
folders_ciphers::table
.inner_join(folders::table)
@ -711,7 +716,7 @@ impl Cipher {
// true, then the non-interesting ciphers will not be returned. As a
// result, those ciphers will not appear in "My Vault" for the org
// owner/admin, but they can still be accessed via the org vault view.
pub async fn find_by_user(user_uuid: &str, visible_only: bool, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_user(user_uuid: &UserId, visible_only: bool, conn: &mut DbConn) -> Vec<Self> {
if CONFIG.org_groups_enabled() {
db_run! {conn: {
let mut query = ciphers::table
@ -793,12 +798,12 @@ impl Cipher {
}
// Find all ciphers visible to the specified user.
pub async fn find_by_user_visible(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_user_visible(user_uuid: &UserId, conn: &mut DbConn) -> Vec<Self> {
Self::find_by_user(user_uuid, true, conn).await
}
// Find all ciphers directly owned by the specified user.
pub async fn find_owned_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_owned_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<Self> {
db_run! {conn: {
ciphers::table
.filter(
@ -809,7 +814,7 @@ impl Cipher {
}}
}
pub async fn count_owned_by_user(user_uuid: &str, conn: &mut DbConn) -> i64 {
pub async fn count_owned_by_user(user_uuid: &UserId, conn: &mut DbConn) -> i64 {
db_run! {conn: {
ciphers::table
.filter(ciphers::user_uuid.eq(user_uuid))

Datei anzeigen

@ -1,6 +1,6 @@
use serde_json::Value;
use super::{CollectionGroup, GroupUser, Membership, MembershipStatus, MembershipType, OrganizationId, User};
use super::{CollectionGroup, GroupUser, Membership, MembershipStatus, MembershipType, OrganizationId, User, UserId};
use crate::CONFIG;
db_object! {
@ -18,7 +18,7 @@ db_object! {
#[diesel(table_name = users_collections)]
#[diesel(primary_key(user_uuid, collection_uuid))]
pub struct CollectionUser {
pub user_uuid: String,
pub user_uuid: UserId,
pub collection_uuid: String,
pub read_only: bool,
pub hide_passwords: bool,
@ -74,7 +74,7 @@ impl Collection {
pub async fn to_json_details(
&self,
user_uuid: &str,
user_uuid: &UserId,
cipher_sync_data: Option<&crate::api::core::CipherSyncData>,
conn: &mut DbConn,
) -> Value {
@ -208,7 +208,7 @@ impl Collection {
}}
}
pub async fn find_by_user_uuid(user_uuid: String, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_user_uuid(user_uuid: UserId, conn: &mut DbConn) -> Vec<Self> {
if CONFIG.org_groups_enabled() {
db_run! { conn: {
collections::table
@ -281,7 +281,7 @@ impl Collection {
pub async fn find_by_organization_and_user_uuid(
org_uuid: &OrganizationId,
user_uuid: &str,
user_uuid: &UserId,
conn: &mut DbConn,
) -> Vec<Self> {
Self::find_by_user_uuid(user_uuid.to_owned(), conn)
@ -324,7 +324,7 @@ impl Collection {
}}
}
pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: String, conn: &mut DbConn) -> Option<Self> {
pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: UserId, conn: &mut DbConn) -> Option<Self> {
if CONFIG.org_groups_enabled() {
db_run! { conn: {
collections::table
@ -391,7 +391,7 @@ impl Collection {
}
}
pub async fn is_writable_by_user(&self, user_uuid: &str, conn: &mut DbConn) -> bool {
pub async fn is_writable_by_user(&self, user_uuid: &UserId, conn: &mut DbConn) -> bool {
let user_uuid = user_uuid.to_string();
if CONFIG.org_groups_enabled() {
db_run! { conn: {
@ -453,7 +453,7 @@ impl Collection {
}
}
pub async fn hide_passwords_for_user(&self, user_uuid: &str, conn: &mut DbConn) -> bool {
pub async fn hide_passwords_for_user(&self, user_uuid: &UserId, conn: &mut DbConn) -> bool {
let user_uuid = user_uuid.to_string();
db_run! { conn: {
collections::table
@ -504,7 +504,7 @@ impl Collection {
impl CollectionUser {
pub async fn find_by_organization_and_user_uuid(
org_uuid: &OrganizationId,
user_uuid: &str,
user_uuid: &UserId,
conn: &mut DbConn,
) -> Vec<Self> {
db_run! { conn: {
@ -533,7 +533,7 @@ impl CollectionUser {
}
pub async fn save(
user_uuid: &str,
user_uuid: &UserId,
collection_uuid: &str,
read_only: bool,
hide_passwords: bool,
@ -632,7 +632,7 @@ impl CollectionUser {
pub async fn find_by_collection_and_user(
collection_uuid: &str,
user_uuid: &str,
user_uuid: &UserId,
conn: &mut DbConn,
) -> Option<Self> {
db_run! { conn: {
@ -646,7 +646,7 @@ impl CollectionUser {
}}
}
pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
users_collections::table
.filter(users_collections::user_uuid.eq(user_uuid))
@ -670,7 +670,7 @@ impl CollectionUser {
}
pub async fn delete_all_by_user_and_org(
user_uuid: &str,
user_uuid: &UserId,
org_uuid: &OrganizationId,
conn: &mut DbConn,
) -> EmptyResult {
@ -689,7 +689,7 @@ impl CollectionUser {
}}
}
pub async fn has_access_to_collection_by_user(col_id: &str, user_uuid: &str, conn: &mut DbConn) -> bool {
pub async fn has_access_to_collection_by_user(col_id: &str, user_uuid: &UserId, conn: &mut DbConn) -> bool {
Self::find_by_collection_and_user(col_id, user_uuid, conn).await.is_some()
}
}

Datei anzeigen

@ -1,5 +1,6 @@
use chrono::{NaiveDateTime, Utc};
use super::UserId;
use crate::{crypto, CONFIG};
use core::fmt;
@ -13,7 +14,7 @@ db_object! {
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
pub user_uuid: String,
pub user_uuid: UserId,
pub name: String,
pub atype: i32, // https://github.com/bitwarden/server/blob/dcc199bcce4aa2d5621f6fab80f1b49d8b143418/src/Core/Enums/DeviceType.cs
@ -28,7 +29,7 @@ db_object! {
/// Local methods
impl Device {
pub fn new(uuid: String, user_uuid: String, name: String, atype: i32) -> Self {
pub fn new(uuid: String, user_uuid: UserId, name: String, atype: i32) -> Self {
let now = Utc::now().naive_utc();
Self {
@ -150,7 +151,7 @@ impl Device {
}
}
pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn delete_all_by_user(user_uuid: &UserId, conn: &mut DbConn) -> EmptyResult {
db_run! { conn: {
diesel::delete(devices::table.filter(devices::user_uuid.eq(user_uuid)))
.execute(conn)
@ -158,7 +159,7 @@ impl Device {
}}
}
pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &mut DbConn) -> Option<Self> {
pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &UserId, conn: &mut DbConn) -> Option<Self> {
db_run! { conn: {
devices::table
.filter(devices::uuid.eq(uuid))
@ -169,7 +170,7 @@ impl Device {
}}
}
pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
devices::table
.filter(devices::user_uuid.eq(user_uuid))
@ -208,7 +209,7 @@ impl Device {
}}
}
pub async fn find_latest_active_by_user(user_uuid: &str, conn: &mut DbConn) -> Option<Self> {
pub async fn find_latest_active_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Option<Self> {
db_run! { conn: {
devices::table
.filter(devices::user_uuid.eq(user_uuid))
@ -219,7 +220,7 @@ impl Device {
}}
}
pub async fn find_push_devices_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_push_devices_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
devices::table
.filter(devices::user_uuid.eq(user_uuid))
@ -230,7 +231,7 @@ impl Device {
}}
}
pub async fn check_user_has_push_device(user_uuid: &str, conn: &mut DbConn) -> bool {
pub async fn check_user_has_push_device(user_uuid: &UserId, conn: &mut DbConn) -> bool {
db_run! { conn: {
devices::table
.filter(devices::user_uuid.eq(user_uuid))

Datei anzeigen

@ -3,7 +3,7 @@ use serde_json::Value;
use crate::{api::EmptyResult, db::DbConn, error::MapResult};
use super::User;
use super::{User, UserId};
db_object! {
#[derive(Identifiable, Queryable, Insertable, AsChangeset)]
@ -12,8 +12,8 @@ db_object! {
#[diesel(primary_key(uuid))]
pub struct EmergencyAccess {
pub uuid: String,
pub grantor_uuid: String,
pub grantee_uuid: Option<String>,
pub grantor_uuid: UserId,
pub grantee_uuid: Option<UserId>,
pub email: Option<String>,
pub key_encrypted: Option<String>,
pub atype: i32, //EmergencyAccessType
@ -29,7 +29,7 @@ db_object! {
// Local methods
impl EmergencyAccess {
pub fn new(grantor_uuid: String, email: String, status: i32, atype: i32, wait_time_days: i32) -> Self {
pub fn new(grantor_uuid: UserId, email: String, status: i32, atype: i32, wait_time_days: i32) -> Self {
let now = Utc::now().naive_utc();
Self {
@ -82,7 +82,7 @@ impl EmergencyAccess {
}
pub async fn to_json_grantee_details(&self, conn: &mut DbConn) -> Option<Value> {
let grantee_user = if let Some(grantee_uuid) = self.grantee_uuid.as_deref() {
let grantee_user = if let Some(grantee_uuid) = &self.grantee_uuid {
User::find_by_uuid(grantee_uuid, conn).await.expect("Grantee user not found.")
} else if let Some(email) = self.email.as_deref() {
match User::find_by_mail(email, conn).await {
@ -211,7 +211,7 @@ impl EmergencyAccess {
}}
}
pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn delete_all_by_user(user_uuid: &UserId, conn: &mut DbConn) -> EmptyResult {
for ea in Self::find_all_by_grantor_uuid(user_uuid, conn).await {
ea.delete(conn).await?;
}
@ -239,8 +239,8 @@ impl EmergencyAccess {
}
pub async fn find_by_grantor_uuid_and_grantee_uuid_or_email(
grantor_uuid: &str,
grantee_uuid: &str,
grantor_uuid: &UserId,
grantee_uuid: &UserId,
email: &str,
conn: &mut DbConn,
) -> Option<Self> {
@ -262,7 +262,7 @@ impl EmergencyAccess {
}}
}
pub async fn find_by_uuid_and_grantor_uuid(uuid: &str, grantor_uuid: &str, conn: &mut DbConn) -> Option<Self> {
pub async fn find_by_uuid_and_grantor_uuid(uuid: &str, grantor_uuid: &UserId, conn: &mut DbConn) -> Option<Self> {
db_run! { conn: {
emergency_access::table
.filter(emergency_access::uuid.eq(uuid))
@ -272,7 +272,7 @@ impl EmergencyAccess {
}}
}
pub async fn find_by_uuid_and_grantee_uuid(uuid: &str, grantee_uuid: &str, conn: &mut DbConn) -> Option<Self> {
pub async fn find_by_uuid_and_grantee_uuid(uuid: &str, grantee_uuid: &UserId, conn: &mut DbConn) -> Option<Self> {
db_run! { conn: {
emergency_access::table
.filter(emergency_access::uuid.eq(uuid))
@ -292,7 +292,7 @@ impl EmergencyAccess {
}}
}
pub async fn find_all_by_grantee_uuid(grantee_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_all_by_grantee_uuid(grantee_uuid: &UserId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
emergency_access::table
.filter(emergency_access::grantee_uuid.eq(grantee_uuid))
@ -319,7 +319,7 @@ impl EmergencyAccess {
}}
}
pub async fn find_all_by_grantor_uuid(grantor_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_all_by_grantor_uuid(grantor_uuid: &UserId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
emergency_access::table
.filter(emergency_access::grantor_uuid.eq(grantor_uuid))
@ -327,7 +327,12 @@ impl EmergencyAccess {
}}
}
pub async fn accept_invite(&mut self, grantee_uuid: &str, grantee_email: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn accept_invite(
&mut self,
grantee_uuid: &UserId,
grantee_email: &str,
conn: &mut DbConn,
) -> EmptyResult {
if self.email.is_none() || self.email.as_ref().unwrap() != grantee_email {
err!("User email does not match invite.");
}
@ -337,7 +342,7 @@ impl EmergencyAccess {
}
self.status = EmergencyAccessStatus::Accepted as i32;
self.grantee_uuid = Some(String::from(grantee_uuid));
self.grantee_uuid = Some(grantee_uuid.clone());
self.email = None;
self.save(conn).await
}

Datei anzeigen

@ -1,7 +1,7 @@
use crate::db::DbConn;
use serde_json::Value;
use super::OrganizationId;
use super::{OrganizationId, UserId};
use crate::{api::EmptyResult, error::MapResult, CONFIG};
use chrono::{NaiveDateTime, TimeDelta, Utc};
@ -18,7 +18,7 @@ db_object! {
pub struct Event {
pub uuid: String,
pub event_type: i32, // EventType
pub user_uuid: Option<String>,
pub user_uuid: Option<UserId>,
pub org_uuid: Option<OrganizationId>,
pub cipher_uuid: Option<String>,
pub collection_uuid: Option<String>,

Datei anzeigen

@ -1,11 +1,11 @@
use super::User;
use super::{User, UserId};
db_object! {
#[derive(Identifiable, Queryable, Insertable)]
#[diesel(table_name = favorites)]
#[diesel(primary_key(user_uuid, cipher_uuid))]
pub struct Favorite {
pub user_uuid: String,
pub user_uuid: UserId,
pub cipher_uuid: String,
}
}
@ -17,7 +17,7 @@ use crate::error::MapResult;
impl Favorite {
// Returns whether the specified cipher is a favorite of the specified user.
pub async fn is_favorite(cipher_uuid: &str, user_uuid: &str, conn: &mut DbConn) -> bool {
pub async fn is_favorite(cipher_uuid: &str, user_uuid: &UserId, conn: &mut DbConn) -> bool {
db_run! { conn: {
let query = favorites::table
.filter(favorites::cipher_uuid.eq(cipher_uuid))
@ -29,7 +29,7 @@ impl Favorite {
}
// Sets whether the specified cipher is a favorite of the specified user.
pub async fn set_favorite(favorite: bool, cipher_uuid: &str, user_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn set_favorite(favorite: bool, cipher_uuid: &str, user_uuid: &UserId, conn: &mut DbConn) -> EmptyResult {
let (old, new) = (Self::is_favorite(cipher_uuid, user_uuid, conn).await, favorite);
match (old, new) {
(false, true) => {
@ -71,7 +71,7 @@ impl Favorite {
}
// Delete all favorite entries associated with the specified user.
pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn delete_all_by_user(user_uuid: &UserId, conn: &mut DbConn) -> EmptyResult {
db_run! { conn: {
diesel::delete(favorites::table.filter(favorites::user_uuid.eq(user_uuid)))
.execute(conn)
@ -81,7 +81,7 @@ impl Favorite {
/// Return a vec with (cipher_uuid) this will only contain favorite flagged ciphers
/// This is used during a full sync so we only need one query for all favorite cipher matches.
pub async fn get_all_cipher_uuid_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<String> {
pub async fn get_all_cipher_uuid_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<String> {
db_run! { conn: {
favorites::table
.filter(favorites::user_uuid.eq(user_uuid))

Datei anzeigen

@ -1,7 +1,7 @@
use chrono::{NaiveDateTime, Utc};
use serde_json::Value;
use super::User;
use super::{User, UserId};
db_object! {
#[derive(Identifiable, Queryable, Insertable, AsChangeset)]
@ -11,7 +11,7 @@ db_object! {
pub uuid: String,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
pub user_uuid: String,
pub user_uuid: UserId,
pub name: String,
}
@ -26,7 +26,7 @@ db_object! {
/// Local methods
impl Folder {
pub fn new(user_uuid: String, name: String) -> Self {
pub fn new(user_uuid: UserId, name: String) -> Self {
let now = Utc::now().naive_utc();
Self {
@ -113,14 +113,14 @@ impl Folder {
}}
}
pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn delete_all_by_user(user_uuid: &UserId, conn: &mut DbConn) -> EmptyResult {
for folder in Self::find_by_user(user_uuid, conn).await {
folder.delete(conn).await?;
}
Ok(())
}
pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &mut DbConn) -> Option<Self> {
pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &UserId, conn: &mut DbConn) -> Option<Self> {
db_run! { conn: {
folders::table
.filter(folders::uuid.eq(uuid))
@ -131,7 +131,7 @@ impl Folder {
}}
}
pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
folders::table
.filter(folders::user_uuid.eq(user_uuid))
@ -216,7 +216,7 @@ impl FolderCipher {
/// Return a vec with (cipher_uuid, folder_uuid)
/// This is used during a full sync so we only need one query for all folder matches.
pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<(String, String)> {
pub async fn find_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<(String, String)> {
db_run! { conn: {
folders_ciphers::table
.inner_join(folders::table)

Datei anzeigen

@ -1,4 +1,4 @@
use super::{Membership, MembershipId, OrganizationId, User};
use super::{Membership, MembershipId, OrganizationId, User, UserId};
use crate::api::EmptyResult;
use crate::db::DbConn;
use crate::error::MapResult;
@ -222,7 +222,7 @@ impl Group {
}}
}
//Returns all organizations the user has full access to
pub async fn get_orgs_by_user_with_full_access(user_uuid: &str, conn: &mut DbConn) -> Vec<OrganizationId> {
pub async fn get_orgs_by_user_with_full_access(user_uuid: &UserId, conn: &mut DbConn) -> Vec<OrganizationId> {
db_run! { conn: {
groups_users::table
.inner_join(users_organizations::table.on(
@ -240,7 +240,7 @@ impl Group {
}}
}
pub async fn is_in_full_access_group(user_uuid: &str, org_uuid: &OrganizationId, conn: &mut DbConn) -> bool {
pub async fn is_in_full_access_group(user_uuid: &UserId, org_uuid: &OrganizationId, conn: &mut DbConn) -> bool {
db_run! { conn: {
groups::table
.inner_join(groups_users::table.on(
@ -353,7 +353,7 @@ impl CollectionGroup {
}}
}
pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
collections_groups::table
.inner_join(groups_users::table.on(

Datei anzeigen

@ -34,4 +34,4 @@ pub use self::send::{Send, SendType};
pub use self::two_factor::{TwoFactor, TwoFactorType};
pub use self::two_factor_duo_context::TwoFactorDuoContext;
pub use self::two_factor_incomplete::TwoFactorIncomplete;
pub use self::user::{Invitation, User, UserKdfType, UserStampException};
pub use self::user::{Invitation, User, UserId, UserKdfType, UserStampException};

Datei anzeigen

@ -5,7 +5,7 @@ use crate::api::EmptyResult;
use crate::db::DbConn;
use crate::error::MapResult;
use super::{Membership, MembershipId, MembershipStatus, MembershipType, OrganizationId, TwoFactor};
use super::{Membership, MembershipId, MembershipStatus, MembershipType, OrganizationId, TwoFactor, UserId};
db_object! {
#[derive(Identifiable, Queryable, Insertable, AsChangeset)]
@ -152,7 +152,7 @@ impl OrgPolicy {
}}
}
pub async fn find_confirmed_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_confirmed_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
org_policies::table
.inner_join(
@ -194,7 +194,7 @@ impl OrgPolicy {
}
pub async fn find_accepted_and_confirmed_by_user_and_active_policy(
user_uuid: &str,
user_uuid: &UserId,
policy_type: OrgPolicyType,
conn: &mut DbConn,
) -> Vec<Self> {
@ -221,7 +221,7 @@ impl OrgPolicy {
}
pub async fn find_confirmed_by_user_and_active_policy(
user_uuid: &str,
user_uuid: &UserId,
policy_type: OrgPolicyType,
conn: &mut DbConn,
) -> Vec<Self> {
@ -248,7 +248,7 @@ impl OrgPolicy {
/// and the user is not an owner or admin of that org. This is only useful for checking
/// applicability of policy types that have these particular semantics.
pub async fn is_applicable_to_user(
user_uuid: &str,
user_uuid: &UserId,
policy_type: OrgPolicyType,
exclude_org_uuid: Option<&OrganizationId>,
conn: &mut DbConn,
@ -271,7 +271,7 @@ impl OrgPolicy {
}
pub async fn is_user_allowed(
user_uuid: &str,
user_uuid: &UserId,
org_uuid: &OrganizationId,
exclude_current_org: bool,
conn: &mut DbConn,
@ -316,7 +316,7 @@ impl OrgPolicy {
/// Returns true if the user belongs to an org that has enabled the `DisableHideEmail`
/// option of the `Send Options` policy, and the user is not an owner or admin of that org.
pub async fn is_hide_email_disabled(user_uuid: &str, conn: &mut DbConn) -> bool {
pub async fn is_hide_email_disabled(user_uuid: &UserId, conn: &mut DbConn) -> bool {
for policy in
OrgPolicy::find_confirmed_by_user_and_active_policy(user_uuid, OrgPolicyType::SendOptions, conn).await
{

Datei anzeigen

@ -10,7 +10,7 @@ use std::{
ops::Deref,
};
use super::{CollectionUser, Group, GroupUser, OrgPolicy, OrgPolicyType, TwoFactor, User};
use super::{CollectionUser, Group, GroupUser, OrgPolicy, OrgPolicyType, TwoFactor, User, UserId};
use crate::db::models::{Collection, CollectionGroup};
use crate::CONFIG;
@ -31,7 +31,7 @@ db_object! {
#[diesel(primary_key(uuid))]
pub struct Membership {
pub uuid: MembershipId,
pub user_uuid: String,
pub user_uuid: UserId,
pub org_uuid: OrganizationId,
pub access_all: bool,
@ -204,7 +204,7 @@ impl Organization {
static ACTIVATE_REVOKE_DIFF: i32 = 128;
impl Membership {
pub fn new(user_uuid: String, org_uuid: OrganizationId) -> Self {
pub fn new(user_uuid: UserId, org_uuid: OrganizationId) -> Self {
Self {
uuid: MembershipId(crate::util::get_uuid()),
@ -666,7 +666,7 @@ impl Membership {
Ok(())
}
pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn delete_all_by_user(user_uuid: &UserId, conn: &mut DbConn) -> EmptyResult {
for member in Self::find_any_state_by_user(user_uuid, conn).await {
member.delete(conn).await?;
}
@ -722,7 +722,7 @@ impl Membership {
}}
}
pub async fn find_confirmed_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_confirmed_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid))
@ -732,7 +732,7 @@ impl Membership {
}}
}
pub async fn find_invited_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_invited_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid))
@ -742,7 +742,7 @@ impl Membership {
}}
}
pub async fn find_any_state_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_any_state_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid))
@ -751,7 +751,7 @@ impl Membership {
}}
}
pub async fn count_accepted_and_confirmed_by_user(user_uuid: &str, conn: &mut DbConn) -> i64 {
pub async fn count_accepted_and_confirmed_by_user(user_uuid: &UserId, conn: &mut DbConn) -> i64 {
db_run! { conn: {
users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid))
@ -822,7 +822,11 @@ impl Membership {
}}
}
pub async fn find_by_user_and_org(user_uuid: &str, org_uuid: &OrganizationId, conn: &mut DbConn) -> Option<Self> {
pub async fn find_by_user_and_org(
user_uuid: &UserId,
org_uuid: &OrganizationId,
conn: &mut DbConn,
) -> Option<Self> {
db_run! { conn: {
users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid))
@ -833,7 +837,7 @@ impl Membership {
}
pub async fn find_confirmed_by_user_and_org(
user_uuid: &str,
user_uuid: &UserId,
org_uuid: &OrganizationId,
conn: &mut DbConn,
) -> Option<Self> {
@ -849,7 +853,7 @@ impl Membership {
}}
}
pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid))
@ -858,7 +862,7 @@ impl Membership {
}}
}
pub async fn get_orgs_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<OrganizationId> {
pub async fn get_orgs_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<OrganizationId> {
db_run! { conn: {
users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid))
@ -868,7 +872,11 @@ impl Membership {
}}
}
pub async fn find_by_user_and_policy(user_uuid: &str, policy_type: OrgPolicyType, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_user_and_policy(
user_uuid: &UserId,
policy_type: OrgPolicyType,
conn: &mut DbConn,
) -> Vec<Self> {
db_run! { conn: {
users_organizations::table
.inner_join(
@ -940,7 +948,7 @@ impl Membership {
}}
}
pub async fn user_has_ge_admin_access_to_cipher(user_uuid: &str, cipher_uuid: &str, conn: &mut DbConn) -> bool {
pub async fn user_has_ge_admin_access_to_cipher(user_uuid: &UserId, cipher_uuid: &str, conn: &mut DbConn) -> bool {
db_run! { conn: {
users_organizations::table
.inner_join(ciphers::table.on(ciphers::uuid.eq(cipher_uuid).and(ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable()))))

Datei anzeigen

@ -3,7 +3,7 @@ use serde_json::Value;
use crate::util::LowerCase;
use super::{OrganizationId, User};
use super::{OrganizationId, User, UserId};
db_object! {
#[derive(Identifiable, Queryable, Insertable, AsChangeset)]
@ -13,7 +13,7 @@ db_object! {
pub struct Send {
pub uuid: String,
pub user_uuid: Option<String>,
pub user_uuid: Option<UserId>,
pub organization_uuid: Option<OrganizationId>,
pub name: String,
@ -242,7 +242,7 @@ impl Send {
}
}
pub async fn update_users_revision(&self, conn: &mut DbConn) -> Vec<String> {
pub async fn update_users_revision(&self, conn: &mut DbConn) -> Vec<UserId> {
let mut user_uuids = Vec::new();
match &self.user_uuid {
Some(user_uuid) => {
@ -256,7 +256,7 @@ impl Send {
user_uuids
}
pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn delete_all_by_user(user_uuid: &UserId, conn: &mut DbConn) -> EmptyResult {
for send in Self::find_by_user(user_uuid, conn).await {
send.delete(conn).await?;
}
@ -289,7 +289,7 @@ impl Send {
}}
}
pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &str, conn: &mut DbConn) -> Option<Self> {
pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &UserId, conn: &mut DbConn) -> Option<Self> {
db_run! {conn: {
sends::table
.filter(sends::uuid.eq(uuid))
@ -300,7 +300,7 @@ impl Send {
}}
}
pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<Self> {
db_run! {conn: {
sends::table
.filter(sends::user_uuid.eq(user_uuid))
@ -308,7 +308,7 @@ impl Send {
}}
}
pub async fn size_by_user(user_uuid: &str, conn: &mut DbConn) -> Option<i64> {
pub async fn size_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Option<i64> {
let sends = Self::find_by_user(user_uuid, conn).await;
#[derive(serde::Deserialize)]

Datei anzeigen

@ -1,5 +1,6 @@
use serde_json::Value;
use super::UserId;
use crate::{api::EmptyResult, db::DbConn, error::MapResult};
db_object! {
@ -8,7 +9,7 @@ db_object! {
#[diesel(primary_key(uuid))]
pub struct TwoFactor {
pub uuid: String,
pub user_uuid: String,
pub user_uuid: UserId,
pub atype: i32,
pub enabled: bool,
pub data: String,
@ -41,7 +42,7 @@ pub enum TwoFactorType {
/// Local methods
impl TwoFactor {
pub fn new(user_uuid: String, atype: TwoFactorType, data: String) -> Self {
pub fn new(user_uuid: UserId, atype: TwoFactorType, data: String) -> Self {
Self {
uuid: crate::util::get_uuid(),
user_uuid,
@ -118,7 +119,7 @@ impl TwoFactor {
}}
}
pub async fn find_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_user(user_uuid: &UserId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
twofactor::table
.filter(twofactor::user_uuid.eq(user_uuid))
@ -129,7 +130,7 @@ impl TwoFactor {
}}
}
pub async fn find_by_user_and_type(user_uuid: &str, atype: i32, conn: &mut DbConn) -> Option<Self> {
pub async fn find_by_user_and_type(user_uuid: &UserId, atype: i32, conn: &mut DbConn) -> Option<Self> {
db_run! { conn: {
twofactor::table
.filter(twofactor::user_uuid.eq(user_uuid))
@ -140,7 +141,7 @@ impl TwoFactor {
}}
}
pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn delete_all_by_user(user_uuid: &UserId, conn: &mut DbConn) -> EmptyResult {
db_run! { conn: {
diesel::delete(twofactor::table.filter(twofactor::user_uuid.eq(user_uuid)))
.execute(conn)

Datei anzeigen

@ -1,13 +1,19 @@
use chrono::{NaiveDateTime, Utc};
use crate::{api::EmptyResult, auth::ClientIp, db::DbConn, error::MapResult, CONFIG};
use crate::{
api::EmptyResult,
auth::ClientIp,
db::{models::UserId, DbConn},
error::MapResult,
CONFIG,
};
db_object! {
#[derive(Identifiable, Queryable, Insertable, AsChangeset)]
#[diesel(table_name = twofactor_incomplete)]
#[diesel(primary_key(user_uuid, device_uuid))]
pub struct TwoFactorIncomplete {
pub user_uuid: String,
pub user_uuid: UserId,
// This device UUID is simply what's claimed by the device. It doesn't
// necessarily correspond to any UUID in the devices table, since a device
// must complete 2FA login before being added into the devices table.
@ -21,7 +27,7 @@ db_object! {
impl TwoFactorIncomplete {
pub async fn mark_incomplete(
user_uuid: &str,
user_uuid: &UserId,
device_uuid: &str,
device_name: &str,
device_type: i32,
@ -55,7 +61,7 @@ impl TwoFactorIncomplete {
}}
}
pub async fn mark_complete(user_uuid: &str, device_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn mark_complete(user_uuid: &UserId, device_uuid: &str, conn: &mut DbConn) -> EmptyResult {
if CONFIG.incomplete_2fa_time_limit() <= 0 || !CONFIG.mail_enabled() {
return Ok(());
}
@ -63,7 +69,7 @@ impl TwoFactorIncomplete {
Self::delete_by_user_and_device(user_uuid, device_uuid, conn).await
}
pub async fn find_by_user_and_device(user_uuid: &str, device_uuid: &str, conn: &mut DbConn) -> Option<Self> {
pub async fn find_by_user_and_device(user_uuid: &UserId, device_uuid: &str, conn: &mut DbConn) -> Option<Self> {
db_run! { conn: {
twofactor_incomplete::table
.filter(twofactor_incomplete::user_uuid.eq(user_uuid))
@ -88,7 +94,7 @@ impl TwoFactorIncomplete {
Self::delete_by_user_and_device(&self.user_uuid, &self.device_uuid, conn).await
}
pub async fn delete_by_user_and_device(user_uuid: &str, device_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn delete_by_user_and_device(user_uuid: &UserId, device_uuid: &str, conn: &mut DbConn) -> EmptyResult {
db_run! { conn: {
diesel::delete(twofactor_incomplete::table
.filter(twofactor_incomplete::user_uuid.eq(user_uuid))
@ -98,7 +104,7 @@ impl TwoFactorIncomplete {
}}
}
pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn delete_all_by_user(user_uuid: &UserId, conn: &mut DbConn) -> EmptyResult {
db_run! { conn: {
diesel::delete(twofactor_incomplete::table.filter(twofactor_incomplete::user_uuid.eq(user_uuid)))
.execute(conn)

Datei anzeigen

@ -1,9 +1,23 @@
use crate::util::{format_date, get_uuid, retry};
use chrono::{NaiveDateTime, TimeDelta, Utc};
use rocket::request::FromParam;
use serde_json::Value;
use std::{
borrow::Borrow,
fmt::{Display, Formatter},
ops::Deref,
};
use crate::crypto;
use crate::CONFIG;
use super::{
Cipher, Device, EmergencyAccess, Favorite, Folder, Membership, MembershipType, TwoFactor, TwoFactorIncomplete,
};
use crate::{
api::EmptyResult,
crypto,
db::DbConn,
error::MapResult,
util::{format_date, get_uuid, retry},
CONFIG,
};
db_object! {
#[derive(Identifiable, Queryable, Insertable, AsChangeset)]
@ -11,7 +25,7 @@ db_object! {
#[diesel(treat_none_as_null = true)]
#[diesel(primary_key(uuid))]
pub struct User {
pub uuid: String,
pub uuid: UserId,
pub enabled: bool,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
@ -91,7 +105,7 @@ impl User {
let email = email.to_lowercase();
Self {
uuid: get_uuid(),
uuid: UserId(get_uuid()),
enabled: true,
created_at: now,
updated_at: now,
@ -214,14 +228,6 @@ impl User {
}
}
use super::{
Cipher, Device, EmergencyAccess, Favorite, Folder, Membership, MembershipType, Send, TwoFactor, TwoFactorIncomplete,
};
use crate::db::DbConn;
use crate::api::EmptyResult;
use crate::error::MapResult;
/// Database methods
impl User {
pub async fn to_json(&self, conn: &mut DbConn) -> Value {
@ -311,7 +317,7 @@ impl User {
}
}
Send::delete_all_by_user(&self.uuid, conn).await?;
super::Send::delete_all_by_user(&self.uuid, conn).await?;
EmergencyAccess::delete_all_by_user(&self.uuid, conn).await?;
EmergencyAccess::delete_all_by_grantee_email(&self.email, conn).await?;
Membership::delete_all_by_user(&self.uuid, conn).await?;
@ -330,7 +336,7 @@ impl User {
}}
}
pub async fn update_uuid_revision(uuid: &str, conn: &mut DbConn) {
pub async fn update_uuid_revision(uuid: &UserId, conn: &mut DbConn) {
if let Err(e) = Self::_update_revision(uuid, &Utc::now().naive_utc(), conn).await {
warn!("Failed to update revision for {}: {:#?}", uuid, e);
}
@ -355,7 +361,7 @@ impl User {
Self::_update_revision(&self.uuid, &self.updated_at, conn).await
}
async fn _update_revision(uuid: &str, date: &NaiveDateTime, conn: &mut DbConn) -> EmptyResult {
async fn _update_revision(uuid: &UserId, date: &NaiveDateTime, conn: &mut DbConn) -> EmptyResult {
db_run! {conn: {
retry(|| {
diesel::update(users::table.filter(users::uuid.eq(uuid)))
@ -377,7 +383,7 @@ impl User {
}}
}
pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option<Self> {
pub async fn find_by_uuid(uuid: &UserId, conn: &mut DbConn) -> Option<Self> {
db_run! {conn: {
users::table.filter(users::uuid.eq(uuid)).first::<UserDb>(conn).ok().from_db()
}}
@ -456,3 +462,51 @@ impl Invitation {
}
}
}
#[derive(DieselNewType, FromForm, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct UserId(String);
impl AsRef<str> for UserId {
fn as_ref(&self) -> &str {
&self.0
}
}
impl Deref for UserId {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Borrow<str> for UserId {
fn borrow(&self) -> &str {
&self.0
}
}
impl Display for UserId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<String> for UserId {
fn from(raw: String) -> Self {
Self(raw)
}
}
impl<'r> FromParam<'r> for UserId {
type Error = ();
#[inline(always)]
fn from_param(param: &'r str) -> Result<Self, Self::Error> {
if param.chars().all(|c| matches!(c, 'a'..='z' | 'A'..='Z' |'0'..='9' | '-')) {
Ok(Self(param.to_string()))
} else {
Err(())
}
}
}

Datei anzeigen

@ -17,7 +17,7 @@ use crate::{
encode_jwt, generate_delete_claims, generate_emergency_access_invite_claims, generate_invite_claims,
generate_verify_email_claims,
},
db::models::{Device, DeviceType, MembershipId, OrganizationId, User},
db::models::{Device, DeviceType, MembershipId, OrganizationId, User, UserId},
error::Error,
CONFIG,
};
@ -166,8 +166,8 @@ pub async fn send_password_hint(address: &str, hint: Option<String>) -> EmptyRes
send_email(address, &subject, body_html, body_text).await
}
pub async fn send_delete_account(address: &str, uuid: &str) -> EmptyResult {
let claims = generate_delete_claims(uuid.to_string());
pub async fn send_delete_account(address: &str, user_id: &UserId) -> EmptyResult {
let claims = generate_delete_claims(user_id.to_string());
let delete_token = encode_jwt(&claims);
let (subject, body_html, body_text) = get_text(
@ -175,7 +175,7 @@ pub async fn send_delete_account(address: &str, uuid: &str) -> EmptyResult {
json!({
"url": CONFIG.domain(),
"img_src": CONFIG._smtp_img_src(),
"user_id": uuid,
"user_id": user_id,
"email": percent_encode(address.as_bytes(), NON_ALPHANUMERIC).to_string(),
"token": delete_token,
}),
@ -184,8 +184,8 @@ pub async fn send_delete_account(address: &str, uuid: &str) -> EmptyResult {
send_email(address, &subject, body_html, body_text).await
}
pub async fn send_verify_email(address: &str, uuid: &str) -> EmptyResult {
let claims = generate_verify_email_claims(uuid.to_string());
pub async fn send_verify_email(address: &str, user_id: &UserId) -> EmptyResult {
let claims = generate_verify_email_claims(user_id.to_string());
let verify_email_token = encode_jwt(&claims);
let (subject, body_html, body_text) = get_text(
@ -193,7 +193,7 @@ pub async fn send_verify_email(address: &str, uuid: &str) -> EmptyResult {
json!({
"url": CONFIG.domain(),
"img_src": CONFIG._smtp_img_src(),
"user_id": uuid,
"user_id": user_id,
"email": percent_encode(address.as_bytes(), NON_ALPHANUMERIC).to_string(),
"token": verify_email_token,
}),
@ -214,8 +214,8 @@ pub async fn send_welcome(address: &str) -> EmptyResult {
send_email(address, &subject, body_html, body_text).await
}
pub async fn send_welcome_must_verify(address: &str, uuid: &str) -> EmptyResult {
let claims = generate_verify_email_claims(uuid.to_string());
pub async fn send_welcome_must_verify(address: &str, user_id: &UserId) -> EmptyResult {
let claims = generate_verify_email_claims(user_id.to_string());
let verify_email_token = encode_jwt(&claims);
let (subject, body_html, body_text) = get_text(
@ -223,7 +223,7 @@ pub async fn send_welcome_must_verify(address: &str, uuid: &str) -> EmptyResult
json!({
"url": CONFIG.domain(),
"img_src": CONFIG._smtp_img_src(),
"user_id": uuid,
"user_id": user_id,
"token": verify_email_token,
}),
)?;
@ -265,7 +265,7 @@ pub async fn send_invite(
invited_by_email: Option<String>,
) -> EmptyResult {
let claims = generate_invite_claims(
user.uuid.clone(),
user.uuid.to_string(),
user.email.clone(),
org_id.clone(),
member_id.clone(),