Spiegel von
https://github.com/dani-garcia/vaultwarden.git
synchronisiert 2025-01-08 11:55:42 +01:00
add device_id newtype
Dieser Commit ist enthalten in:
Ursprung
53b1c36085
Commit
423a08e749
13 geänderte Dateien mit 142 neuen und 66 gelöschten Zeilen
|
@ -418,7 +418,7 @@ async fn delete_user(uuid: UserId, token: AdminToken, mut conn: DbConn) -> Empty
|
||||||
async fn deauth_user(uuid: UserId, _token: AdminToken, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
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?;
|
let mut user = get_user_or_404(&uuid, &mut conn).await?;
|
||||||
|
|
||||||
nt.send_logout(&user, None).await;
|
nt.send_logout(&user, &DeviceId::empty()).await;
|
||||||
|
|
||||||
if CONFIG.push_enabled() {
|
if CONFIG.push_enabled() {
|
||||||
for device in Device::find_push_devices_by_user(&user.uuid, &mut conn).await {
|
for device in Device::find_push_devices_by_user(&user.uuid, &mut conn).await {
|
||||||
|
@ -444,7 +444,7 @@ async fn disable_user(uuid: UserId, _token: AdminToken, mut conn: DbConn, nt: No
|
||||||
|
|
||||||
let save_result = user.save(&mut conn).await;
|
let save_result = user.save(&mut conn).await;
|
||||||
|
|
||||||
nt.send_logout(&user, None).await;
|
nt.send_logout(&user, &DeviceId::empty()).await;
|
||||||
|
|
||||||
save_result
|
save_result
|
||||||
}
|
}
|
||||||
|
|
|
@ -374,7 +374,7 @@ async fn post_password(data: Json<ChangePassData>, headers: Headers, mut conn: D
|
||||||
// Prevent logging out the client where the user requested this endpoint from.
|
// Prevent logging out the client where the user requested this endpoint from.
|
||||||
// If you do logout the user it will causes issues at the client side.
|
// If you do logout the user it will causes issues at the client side.
|
||||||
// Adding the device uuid will prevent this.
|
// Adding the device uuid will prevent this.
|
||||||
nt.send_logout(&user, Some(headers.device.uuid)).await;
|
nt.send_logout(&user, &headers.device.uuid).await;
|
||||||
|
|
||||||
save_result
|
save_result
|
||||||
}
|
}
|
||||||
|
@ -434,7 +434,7 @@ async fn post_kdf(data: Json<ChangeKdfData>, headers: Headers, mut conn: DbConn,
|
||||||
user.set_password(&data.new_master_password_hash, Some(data.key), true, None);
|
user.set_password(&data.new_master_password_hash, Some(data.key), true, None);
|
||||||
let save_result = user.save(&mut conn).await;
|
let save_result = user.save(&mut conn).await;
|
||||||
|
|
||||||
nt.send_logout(&user, Some(headers.device.uuid)).await;
|
nt.send_logout(&user, &headers.device.uuid).await;
|
||||||
|
|
||||||
save_result
|
save_result
|
||||||
}
|
}
|
||||||
|
@ -646,7 +646,7 @@ async fn post_rotatekey(data: Json<KeyData>, headers: Headers, mut conn: DbConn,
|
||||||
// Prevent logging out the client where the user requested this endpoint from.
|
// Prevent logging out the client where the user requested this endpoint from.
|
||||||
// If you do logout the user it will causes issues at the client side.
|
// If you do logout the user it will causes issues at the client side.
|
||||||
// Adding the device uuid will prevent this.
|
// Adding the device uuid will prevent this.
|
||||||
nt.send_logout(&user, Some(headers.device.uuid)).await;
|
nt.send_logout(&user, &headers.device.uuid).await;
|
||||||
|
|
||||||
save_result
|
save_result
|
||||||
}
|
}
|
||||||
|
@ -662,7 +662,7 @@ async fn post_sstamp(data: Json<PasswordOrOtpData>, headers: Headers, mut conn:
|
||||||
user.reset_security_stamp();
|
user.reset_security_stamp();
|
||||||
let save_result = user.save(&mut conn).await;
|
let save_result = user.save(&mut conn).await;
|
||||||
|
|
||||||
nt.send_logout(&user, None).await;
|
nt.send_logout(&user, &DeviceId::empty()).await;
|
||||||
|
|
||||||
save_result
|
save_result
|
||||||
}
|
}
|
||||||
|
@ -770,7 +770,7 @@ async fn post_email(data: Json<ChangeEmailData>, headers: Headers, mut conn: DbC
|
||||||
|
|
||||||
let save_result = user.save(&mut conn).await;
|
let save_result = user.save(&mut conn).await;
|
||||||
|
|
||||||
nt.send_logout(&user, None).await;
|
nt.send_logout(&user, &DeviceId::empty()).await;
|
||||||
|
|
||||||
save_result
|
save_result
|
||||||
}
|
}
|
||||||
|
@ -1028,7 +1028,7 @@ async fn get_known_device(device: KnownDevice, mut conn: DbConn) -> JsonResult {
|
||||||
|
|
||||||
struct KnownDevice {
|
struct KnownDevice {
|
||||||
email: String,
|
email: String,
|
||||||
uuid: String,
|
uuid: DeviceId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rocket::async_trait]
|
#[rocket::async_trait]
|
||||||
|
@ -1051,7 +1051,7 @@ impl<'r> FromRequest<'r> for KnownDevice {
|
||||||
};
|
};
|
||||||
|
|
||||||
let uuid = if let Some(uuid) = req.headers().get_one("X-Device-Identifier") {
|
let uuid = if let Some(uuid) = req.headers().get_one("X-Device-Identifier") {
|
||||||
uuid.to_string()
|
uuid.to_string().into()
|
||||||
} else {
|
} else {
|
||||||
return Outcome::Error((Status::BadRequest, "X-Device-Identifier value is required"));
|
return Outcome::Error((Status::BadRequest, "X-Device-Identifier value is required"));
|
||||||
};
|
};
|
||||||
|
@ -1108,7 +1108,7 @@ async fn put_device_token(uuid: &str, data: Json<PushToken>, headers: Headers, m
|
||||||
}
|
}
|
||||||
|
|
||||||
#[put("/devices/identifier/<uuid>/clear-token")]
|
#[put("/devices/identifier/<uuid>/clear-token")]
|
||||||
async fn put_clear_device_token(uuid: &str, mut conn: DbConn) -> EmptyResult {
|
async fn put_clear_device_token(uuid: DeviceId, mut conn: DbConn) -> EmptyResult {
|
||||||
// This only clears push token
|
// This only clears push token
|
||||||
// https://github.com/bitwarden/core/blob/master/src/Api/Controllers/DevicesController.cs#L109
|
// https://github.com/bitwarden/core/blob/master/src/Api/Controllers/DevicesController.cs#L109
|
||||||
// https://github.com/bitwarden/core/blob/master/src/Core/Services/Implementations/DeviceService.cs#L37
|
// https://github.com/bitwarden/core/blob/master/src/Core/Services/Implementations/DeviceService.cs#L37
|
||||||
|
@ -1117,8 +1117,8 @@ async fn put_clear_device_token(uuid: &str, mut conn: DbConn) -> EmptyResult {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(device) = Device::find_by_uuid(uuid, &mut conn).await {
|
if let Some(device) = Device::find_by_uuid(&uuid, &mut conn).await {
|
||||||
Device::clear_push_token_by_uuid(uuid, &mut conn).await?;
|
Device::clear_push_token_by_uuid(&uuid, &mut conn).await?;
|
||||||
unregister_push_device(device.push_uuid).await?;
|
unregister_push_device(device.push_uuid).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1127,7 +1127,7 @@ async fn put_clear_device_token(uuid: &str, mut conn: DbConn) -> EmptyResult {
|
||||||
|
|
||||||
// On upstream server, both PUT and POST are declared. Implementing the POST method in case it would be useful somewhere
|
// On upstream server, both PUT and POST are declared. Implementing the POST method in case it would be useful somewhere
|
||||||
#[post("/devices/identifier/<uuid>/clear-token")]
|
#[post("/devices/identifier/<uuid>/clear-token")]
|
||||||
async fn post_clear_device_token(uuid: &str, conn: DbConn) -> EmptyResult {
|
async fn post_clear_device_token(uuid: DeviceId, conn: DbConn) -> EmptyResult {
|
||||||
put_clear_device_token(uuid, conn).await
|
put_clear_device_token(uuid, conn).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1135,7 +1135,7 @@ async fn post_clear_device_token(uuid: &str, conn: DbConn) -> EmptyResult {
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct AuthRequestRequest {
|
struct AuthRequestRequest {
|
||||||
access_code: String,
|
access_code: String,
|
||||||
device_identifier: String,
|
device_identifier: DeviceId,
|
||||||
email: String,
|
email: String,
|
||||||
public_key: String,
|
public_key: String,
|
||||||
// Not used for now
|
// Not used for now
|
||||||
|
@ -1215,7 +1215,7 @@ async fn get_auth_request(uuid: &str, headers: Headers, mut conn: DbConn) -> Jso
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct AuthResponseRequest {
|
struct AuthResponseRequest {
|
||||||
device_identifier: String,
|
device_identifier: DeviceId,
|
||||||
key: String,
|
key: String,
|
||||||
master_password_hash: Option<String>,
|
master_password_hash: Option<String>,
|
||||||
request_approved: bool,
|
request_approved: bool,
|
||||||
|
|
|
@ -2910,7 +2910,7 @@ async fn put_reset_password(
|
||||||
user.set_password(reset_request.new_master_password_hash.as_str(), Some(reset_request.key), true, None);
|
user.set_password(reset_request.new_master_password_hash.as_str(), Some(reset_request.key), true, None);
|
||||||
user.save(&mut conn).await?;
|
user.save(&mut conn).await?;
|
||||||
|
|
||||||
nt.send_logout(&user, None).await;
|
nt.send_logout(&user, &DeviceId::empty()).await;
|
||||||
|
|
||||||
log_event(
|
log_event(
|
||||||
EventType::OrganizationUserAdminResetPassword as i32,
|
EventType::OrganizationUserAdminResetPassword as i32,
|
||||||
|
|
|
@ -485,7 +485,7 @@ async fn post_access(
|
||||||
UpdateType::SyncSendUpdate,
|
UpdateType::SyncSendUpdate,
|
||||||
&send,
|
&send,
|
||||||
&send.update_users_revision(&mut conn).await,
|
&send.update_users_revision(&mut conn).await,
|
||||||
&String::from("00000000-0000-0000-0000-000000000000"),
|
&DeviceId::empty(),
|
||||||
&mut conn,
|
&mut conn,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
@ -542,7 +542,7 @@ async fn post_access_file(
|
||||||
UpdateType::SyncSendUpdate,
|
UpdateType::SyncSendUpdate,
|
||||||
&send,
|
&send,
|
||||||
&send.update_users_revision(&mut conn).await,
|
&send.update_users_revision(&mut conn).await,
|
||||||
&String::from("00000000-0000-0000-0000-000000000000"),
|
&DeviceId::empty(),
|
||||||
&mut conn,
|
&mut conn,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
@ -635,7 +635,7 @@ pub async fn update_send_from_data(
|
||||||
|
|
||||||
send.save(conn).await?;
|
send.save(conn).await?;
|
||||||
if ut != UpdateType::None {
|
if ut != UpdateType::None {
|
||||||
nt.send_send_update(ut, send, &send.update_users_revision(conn).await, &headers.device.uuid, conn).await;
|
nt.send_send_update(ut, send, &send.update_users_revision(conn).await, &headers.device.uuid, conn).await
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
||||||
api::{core::two_factor::duo::get_duo_keys_email, EmptyResult},
|
api::{core::two_factor::duo::get_duo_keys_email, EmptyResult},
|
||||||
crypto,
|
crypto,
|
||||||
db::{
|
db::{
|
||||||
models::{EventType, TwoFactorDuoContext},
|
models::{DeviceId, EventType, TwoFactorDuoContext},
|
||||||
DbConn, DbPool,
|
DbConn, DbPool,
|
||||||
},
|
},
|
||||||
error::Error,
|
error::Error,
|
||||||
|
@ -379,7 +379,7 @@ fn make_callback_url(client_name: &str) -> Result<String, Error> {
|
||||||
pub async fn get_duo_auth_url(
|
pub async fn get_duo_auth_url(
|
||||||
email: &str,
|
email: &str,
|
||||||
client_id: &str,
|
client_id: &str,
|
||||||
device_identifier: &String,
|
device_identifier: &DeviceId,
|
||||||
conn: &mut DbConn,
|
conn: &mut DbConn,
|
||||||
) -> Result<String, Error> {
|
) -> Result<String, Error> {
|
||||||
let (ik, sk, _, host) = get_duo_keys_email(email, conn).await?;
|
let (ik, sk, _, host) = get_duo_keys_email(email, conn).await?;
|
||||||
|
@ -417,7 +417,7 @@ pub async fn validate_duo_login(
|
||||||
email: &str,
|
email: &str,
|
||||||
two_factor_token: &str,
|
two_factor_token: &str,
|
||||||
client_id: &str,
|
client_id: &str,
|
||||||
device_identifier: &str,
|
device_identifier: &DeviceId,
|
||||||
conn: &mut DbConn,
|
conn: &mut DbConn,
|
||||||
) -> EmptyResult {
|
) -> EmptyResult {
|
||||||
// Result supplied to us by clients in the form "<authz code>|<state>"
|
// Result supplied to us by clients in the form "<authz code>|<state>"
|
||||||
|
|
|
@ -747,7 +747,7 @@ struct ConnectData {
|
||||||
|
|
||||||
#[field(name = uncased("device_identifier"))]
|
#[field(name = uncased("device_identifier"))]
|
||||||
#[field(name = uncased("deviceidentifier"))]
|
#[field(name = uncased("deviceidentifier"))]
|
||||||
device_identifier: Option<String>,
|
device_identifier: Option<DeviceId>,
|
||||||
#[field(name = uncased("device_name"))]
|
#[field(name = uncased("device_name"))]
|
||||||
#[field(name = uncased("devicename"))]
|
#[field(name = uncased("devicename"))]
|
||||||
device_name: Option<String>,
|
device_name: Option<String>,
|
||||||
|
|
|
@ -10,7 +10,7 @@ use rocket_ws::{Message, WebSocket};
|
||||||
use crate::{
|
use crate::{
|
||||||
auth::{ClientIp, WsAccessTokenHeader},
|
auth::{ClientIp, WsAccessTokenHeader},
|
||||||
db::{
|
db::{
|
||||||
models::{Cipher, CollectionId, Folder, Send as DbSend, User, UserId},
|
models::{Cipher, CollectionId, DeviceId, Folder, Send as DbSend, User, UserId},
|
||||||
DbConn,
|
DbConn,
|
||||||
},
|
},
|
||||||
Error, CONFIG,
|
Error, CONFIG,
|
||||||
|
@ -347,7 +347,7 @@ impl WebSocketUsers {
|
||||||
let data = create_update(
|
let data = create_update(
|
||||||
vec![("UserId".into(), user.uuid.to_string().into()), ("Date".into(), serialize_date(user.updated_at))],
|
vec![("UserId".into(), user.uuid.to_string().into()), ("Date".into(), serialize_date(user.updated_at))],
|
||||||
ut,
|
ut,
|
||||||
None,
|
DeviceId::empty(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if CONFIG.enable_websocket() {
|
if CONFIG.enable_websocket() {
|
||||||
|
@ -359,7 +359,7 @@ impl WebSocketUsers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_logout(&self, user: &User, acting_device_uuid: Option<String>) {
|
pub async fn send_logout(&self, user: &User, acting_device_uuid: &DeviceId) {
|
||||||
// Skip any processing if both WebSockets and Push are not active
|
// Skip any processing if both WebSockets and Push are not active
|
||||||
if *NOTIFICATIONS_DISABLED {
|
if *NOTIFICATIONS_DISABLED {
|
||||||
return;
|
return;
|
||||||
|
@ -375,7 +375,7 @@ impl WebSocketUsers {
|
||||||
}
|
}
|
||||||
|
|
||||||
if CONFIG.push_enabled() {
|
if CONFIG.push_enabled() {
|
||||||
push_logout(user, acting_device_uuid);
|
push_logout(user, acting_device_uuid.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,7 +383,7 @@ impl WebSocketUsers {
|
||||||
&self,
|
&self,
|
||||||
ut: UpdateType,
|
ut: UpdateType,
|
||||||
folder: &Folder,
|
folder: &Folder,
|
||||||
acting_device_uuid: &String,
|
acting_device_uuid: &DeviceId,
|
||||||
conn: &mut DbConn,
|
conn: &mut DbConn,
|
||||||
) {
|
) {
|
||||||
// Skip any processing if both WebSockets and Push are not active
|
// Skip any processing if both WebSockets and Push are not active
|
||||||
|
@ -397,7 +397,7 @@ impl WebSocketUsers {
|
||||||
("RevisionDate".into(), serialize_date(folder.updated_at)),
|
("RevisionDate".into(), serialize_date(folder.updated_at)),
|
||||||
],
|
],
|
||||||
ut,
|
ut,
|
||||||
Some(acting_device_uuid.into()),
|
acting_device_uuid.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if CONFIG.enable_websocket() {
|
if CONFIG.enable_websocket() {
|
||||||
|
@ -414,7 +414,7 @@ impl WebSocketUsers {
|
||||||
ut: UpdateType,
|
ut: UpdateType,
|
||||||
cipher: &Cipher,
|
cipher: &Cipher,
|
||||||
user_uuids: &[UserId],
|
user_uuids: &[UserId],
|
||||||
acting_device_uuid: &String,
|
acting_device_uuid: &DeviceId,
|
||||||
collection_uuids: Option<Vec<CollectionId>>,
|
collection_uuids: Option<Vec<CollectionId>>,
|
||||||
conn: &mut DbConn,
|
conn: &mut DbConn,
|
||||||
) {
|
) {
|
||||||
|
@ -444,7 +444,7 @@ impl WebSocketUsers {
|
||||||
("RevisionDate".into(), revision_date),
|
("RevisionDate".into(), revision_date),
|
||||||
],
|
],
|
||||||
ut,
|
ut,
|
||||||
Some(acting_device_uuid.into()),
|
acting_device_uuid.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if CONFIG.enable_websocket() {
|
if CONFIG.enable_websocket() {
|
||||||
|
@ -463,7 +463,7 @@ impl WebSocketUsers {
|
||||||
ut: UpdateType,
|
ut: UpdateType,
|
||||||
send: &DbSend,
|
send: &DbSend,
|
||||||
user_uuids: &[UserId],
|
user_uuids: &[UserId],
|
||||||
acting_device_uuid: &String,
|
acting_device_uuid: &DeviceId,
|
||||||
conn: &mut DbConn,
|
conn: &mut DbConn,
|
||||||
) {
|
) {
|
||||||
// Skip any processing if both WebSockets and Push are not active
|
// Skip any processing if both WebSockets and Push are not active
|
||||||
|
@ -479,7 +479,7 @@ impl WebSocketUsers {
|
||||||
("RevisionDate".into(), serialize_date(send.revision_date)),
|
("RevisionDate".into(), serialize_date(send.revision_date)),
|
||||||
],
|
],
|
||||||
ut,
|
ut,
|
||||||
None,
|
acting_device_uuid.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if CONFIG.enable_websocket() {
|
if CONFIG.enable_websocket() {
|
||||||
|
@ -496,7 +496,7 @@ impl WebSocketUsers {
|
||||||
&self,
|
&self,
|
||||||
user_uuid: &UserId,
|
user_uuid: &UserId,
|
||||||
auth_request_uuid: &String,
|
auth_request_uuid: &String,
|
||||||
acting_device_uuid: &String,
|
acting_device_uuid: &DeviceId,
|
||||||
conn: &mut DbConn,
|
conn: &mut DbConn,
|
||||||
) {
|
) {
|
||||||
// Skip any processing if both WebSockets and Push are not active
|
// Skip any processing if both WebSockets and Push are not active
|
||||||
|
@ -506,7 +506,7 @@ impl WebSocketUsers {
|
||||||
let data = create_update(
|
let data = create_update(
|
||||||
vec![("Id".into(), auth_request_uuid.clone().into()), ("UserId".into(), user_uuid.to_string().into())],
|
vec![("Id".into(), auth_request_uuid.clone().into()), ("UserId".into(), user_uuid.to_string().into())],
|
||||||
UpdateType::AuthRequest,
|
UpdateType::AuthRequest,
|
||||||
Some(acting_device_uuid.to_string()),
|
acting_device_uuid.clone(),
|
||||||
);
|
);
|
||||||
if CONFIG.enable_websocket() {
|
if CONFIG.enable_websocket() {
|
||||||
self.send_update(user_uuid, &data).await;
|
self.send_update(user_uuid, &data).await;
|
||||||
|
@ -521,7 +521,7 @@ impl WebSocketUsers {
|
||||||
&self,
|
&self,
|
||||||
user_uuid: &UserId,
|
user_uuid: &UserId,
|
||||||
auth_response_uuid: &str,
|
auth_response_uuid: &str,
|
||||||
approving_device_uuid: String,
|
approving_device_uuid: DeviceId,
|
||||||
conn: &mut DbConn,
|
conn: &mut DbConn,
|
||||||
) {
|
) {
|
||||||
// Skip any processing if both WebSockets and Push are not active
|
// Skip any processing if both WebSockets and Push are not active
|
||||||
|
@ -531,7 +531,7 @@ impl WebSocketUsers {
|
||||||
let data = create_update(
|
let data = create_update(
|
||||||
vec![("Id".into(), auth_response_uuid.to_owned().into()), ("UserId".into(), user_uuid.to_string().into())],
|
vec![("Id".into(), auth_response_uuid.to_owned().into()), ("UserId".into(), user_uuid.to_string().into())],
|
||||||
UpdateType::AuthRequestResponse,
|
UpdateType::AuthRequestResponse,
|
||||||
approving_device_uuid.clone().into(),
|
approving_device_uuid.clone(),
|
||||||
);
|
);
|
||||||
if CONFIG.enable_websocket() {
|
if CONFIG.enable_websocket() {
|
||||||
self.send_update(user_uuid, &data).await;
|
self.send_update(user_uuid, &data).await;
|
||||||
|
@ -585,7 +585,7 @@ impl AnonymousWebSocketSubscriptions {
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
*/
|
*/
|
||||||
fn create_update(payload: Vec<(Value, Value)>, ut: UpdateType, acting_device_uuid: Option<String>) -> Vec<u8> {
|
fn create_update(payload: Vec<(Value, Value)>, ut: UpdateType, acting_device_uuid: DeviceId) -> Vec<u8> {
|
||||||
use rmpv::Value as V;
|
use rmpv::Value as V;
|
||||||
|
|
||||||
let value = V::Array(vec![
|
let value = V::Array(vec![
|
||||||
|
@ -594,7 +594,7 @@ fn create_update(payload: Vec<(Value, Value)>, ut: UpdateType, acting_device_uui
|
||||||
V::Nil,
|
V::Nil,
|
||||||
"ReceiveMessage".into(),
|
"ReceiveMessage".into(),
|
||||||
V::Array(vec![V::Map(vec![
|
V::Array(vec![V::Map(vec![
|
||||||
("ContextId".into(), acting_device_uuid.map(|v| v.into()).unwrap_or_else(|| V::Nil)),
|
("ContextId".into(), acting_device_uuid.to_string().into()),
|
||||||
("Type".into(), (ut as i32).into()),
|
("Type".into(), (ut as i32).into()),
|
||||||
("Payload".into(), payload.into()),
|
("Payload".into(), payload.into()),
|
||||||
])]),
|
])]),
|
||||||
|
|
|
@ -7,7 +7,7 @@ use tokio::sync::RwLock;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{ApiResult, EmptyResult, UpdateType},
|
api::{ApiResult, EmptyResult, UpdateType},
|
||||||
db::models::{Cipher, Device, Folder, Send, User, UserId},
|
db::models::{Cipher, Device, DeviceId, Folder, Send, User, UserId},
|
||||||
http_client::make_http_request,
|
http_client::make_http_request,
|
||||||
util::format_date,
|
util::format_date,
|
||||||
CONFIG,
|
CONFIG,
|
||||||
|
@ -148,7 +148,7 @@ pub async fn unregister_push_device(push_uuid: Option<String>) -> EmptyResult {
|
||||||
pub async fn push_cipher_update(
|
pub async fn push_cipher_update(
|
||||||
ut: UpdateType,
|
ut: UpdateType,
|
||||||
cipher: &Cipher,
|
cipher: &Cipher,
|
||||||
acting_device_uuid: &String,
|
acting_device_uuid: &DeviceId,
|
||||||
conn: &mut crate::db::DbConn,
|
conn: &mut crate::db::DbConn,
|
||||||
) {
|
) {
|
||||||
// We shouldn't send a push notification on cipher update if the cipher belongs to an organization, this isn't implemented in the upstream server too.
|
// We shouldn't send a push notification on cipher update if the cipher belongs to an organization, this isn't implemented in the upstream server too.
|
||||||
|
@ -178,8 +178,8 @@ pub async fn push_cipher_update(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_logout(user: &User, acting_device_uuid: Option<String>) {
|
pub fn push_logout(user: &User, acting_device_uuid: DeviceId) {
|
||||||
let acting_device_uuid: Value = acting_device_uuid.map(|v| v.into()).unwrap_or_else(|| Value::Null);
|
let acting_device_uuid: Value = acting_device_uuid.to_string().into();
|
||||||
|
|
||||||
tokio::task::spawn(send_to_push_relay(json!({
|
tokio::task::spawn(send_to_push_relay(json!({
|
||||||
"userId": user.uuid,
|
"userId": user.uuid,
|
||||||
|
@ -211,7 +211,7 @@ pub fn push_user_update(ut: UpdateType, user: &User) {
|
||||||
pub async fn push_folder_update(
|
pub async fn push_folder_update(
|
||||||
ut: UpdateType,
|
ut: UpdateType,
|
||||||
folder: &Folder,
|
folder: &Folder,
|
||||||
acting_device_uuid: &String,
|
acting_device_uuid: &DeviceId,
|
||||||
conn: &mut crate::db::DbConn,
|
conn: &mut crate::db::DbConn,
|
||||||
) {
|
) {
|
||||||
if Device::check_user_has_push_device(&folder.user_uuid, conn).await {
|
if Device::check_user_has_push_device(&folder.user_uuid, conn).await {
|
||||||
|
@ -230,7 +230,12 @@ pub async fn push_folder_update(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn push_send_update(ut: UpdateType, send: &Send, acting_device_uuid: &String, conn: &mut crate::db::DbConn) {
|
pub async fn push_send_update(
|
||||||
|
ut: UpdateType,
|
||||||
|
send: &Send,
|
||||||
|
acting_device_uuid: &DeviceId,
|
||||||
|
conn: &mut crate::db::DbConn,
|
||||||
|
) {
|
||||||
if let Some(s) = &send.user_uuid {
|
if let Some(s) = &send.user_uuid {
|
||||||
if Device::check_user_has_push_device(s, conn).await {
|
if Device::check_user_has_push_device(s, conn).await {
|
||||||
tokio::task::spawn(send_to_push_relay(json!({
|
tokio::task::spawn(send_to_push_relay(json!({
|
||||||
|
@ -303,7 +308,7 @@ pub async fn push_auth_request(user_uuid: UserId, auth_request_uuid: String, con
|
||||||
pub async fn push_auth_response(
|
pub async fn push_auth_response(
|
||||||
user_uuid: UserId,
|
user_uuid: UserId,
|
||||||
auth_request_uuid: String,
|
auth_request_uuid: String,
|
||||||
approving_device_uuid: String,
|
approving_device_uuid: DeviceId,
|
||||||
conn: &mut crate::db::DbConn,
|
conn: &mut crate::db::DbConn,
|
||||||
) {
|
) {
|
||||||
if Device::check_user_has_push_device(&user_uuid, conn).await {
|
if Device::check_user_has_push_device(&user_uuid, conn).await {
|
||||||
|
|
|
@ -14,7 +14,7 @@ use std::{
|
||||||
net::IpAddr,
|
net::IpAddr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::db::models::{AttachmentId, CipherId, CollectionId, MembershipId, OrganizationId, UserId};
|
use crate::db::models::{AttachmentId, CipherId, CollectionId, DeviceId, MembershipId, OrganizationId, UserId};
|
||||||
use crate::{error::Error, CONFIG};
|
use crate::{error::Error, CONFIG};
|
||||||
|
|
||||||
const JWT_ALGORITHM: Algorithm = Algorithm::RS256;
|
const JWT_ALGORITHM: Algorithm = Algorithm::RS256;
|
||||||
|
@ -172,7 +172,7 @@ pub struct LoginJwtClaims {
|
||||||
// user security_stamp
|
// user security_stamp
|
||||||
pub sstamp: String,
|
pub sstamp: String,
|
||||||
// device uuid
|
// device uuid
|
||||||
pub device: String,
|
pub device: DeviceId,
|
||||||
// [ "api", "offline_access" ]
|
// [ "api", "offline_access" ]
|
||||||
pub scope: Vec<String>,
|
pub scope: Vec<String>,
|
||||||
// [ "Application" ]
|
// [ "Application" ]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use super::{OrganizationId, UserId};
|
use super::{DeviceId, OrganizationId, UserId};
|
||||||
use crate::crypto::ct_eq;
|
use crate::crypto::ct_eq;
|
||||||
use chrono::{NaiveDateTime, Utc};
|
use chrono::{NaiveDateTime, Utc};
|
||||||
|
|
||||||
|
@ -12,11 +12,11 @@ db_object! {
|
||||||
pub user_uuid: UserId,
|
pub user_uuid: UserId,
|
||||||
pub organization_uuid: Option<OrganizationId>,
|
pub organization_uuid: Option<OrganizationId>,
|
||||||
|
|
||||||
pub request_device_identifier: String,
|
pub request_device_identifier: DeviceId,
|
||||||
pub device_type: i32, // https://github.com/bitwarden/server/blob/master/src/Core/Enums/DeviceType.cs
|
pub device_type: i32, // https://github.com/bitwarden/server/blob/master/src/Core/Enums/DeviceType.cs
|
||||||
|
|
||||||
pub request_ip: String,
|
pub request_ip: String,
|
||||||
pub response_device_id: Option<String>,
|
pub response_device_id: Option<DeviceId>,
|
||||||
|
|
||||||
pub access_code: String,
|
pub access_code: String,
|
||||||
pub public_key: String,
|
pub public_key: String,
|
||||||
|
@ -35,7 +35,7 @@ db_object! {
|
||||||
impl AuthRequest {
|
impl AuthRequest {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
user_uuid: UserId,
|
user_uuid: UserId,
|
||||||
request_device_identifier: String,
|
request_device_identifier: DeviceId,
|
||||||
device_type: i32,
|
device_type: i32,
|
||||||
request_ip: String,
|
request_ip: String,
|
||||||
access_code: String,
|
access_code: String,
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
use chrono::{NaiveDateTime, Utc};
|
use chrono::{NaiveDateTime, Utc};
|
||||||
|
use rocket::request::FromParam;
|
||||||
|
use std::{
|
||||||
|
borrow::Borrow,
|
||||||
|
fmt::{Display, Formatter},
|
||||||
|
ops::Deref,
|
||||||
|
};
|
||||||
|
|
||||||
use super::UserId;
|
use super::UserId;
|
||||||
use crate::{crypto, CONFIG};
|
use crate::{crypto, CONFIG};
|
||||||
|
@ -10,7 +16,7 @@ db_object! {
|
||||||
#[diesel(treat_none_as_null = true)]
|
#[diesel(treat_none_as_null = true)]
|
||||||
#[diesel(primary_key(uuid, user_uuid))]
|
#[diesel(primary_key(uuid, user_uuid))]
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
pub uuid: String,
|
pub uuid: DeviceId,
|
||||||
pub created_at: NaiveDateTime,
|
pub created_at: NaiveDateTime,
|
||||||
pub updated_at: NaiveDateTime,
|
pub updated_at: NaiveDateTime,
|
||||||
|
|
||||||
|
@ -29,7 +35,7 @@ db_object! {
|
||||||
|
|
||||||
/// Local methods
|
/// Local methods
|
||||||
impl Device {
|
impl Device {
|
||||||
pub fn new(uuid: String, user_uuid: UserId, name: String, atype: i32) -> Self {
|
pub fn new(uuid: DeviceId, user_uuid: UserId, name: String, atype: i32) -> Self {
|
||||||
let now = Utc::now().naive_utc();
|
let now = Utc::now().naive_utc();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -159,7 +165,7 @@ impl Device {
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn find_by_uuid_and_user(uuid: &str, user_uuid: &UserId, conn: &mut DbConn) -> Option<Self> {
|
pub async fn find_by_uuid_and_user(uuid: &DeviceId, user_uuid: &UserId, conn: &mut DbConn) -> Option<Self> {
|
||||||
db_run! { conn: {
|
db_run! { conn: {
|
||||||
devices::table
|
devices::table
|
||||||
.filter(devices::uuid.eq(uuid))
|
.filter(devices::uuid.eq(uuid))
|
||||||
|
@ -180,7 +186,7 @@ impl Device {
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option<Self> {
|
pub async fn find_by_uuid(uuid: &DeviceId, conn: &mut DbConn) -> Option<Self> {
|
||||||
db_run! { conn: {
|
db_run! { conn: {
|
||||||
devices::table
|
devices::table
|
||||||
.filter(devices::uuid.eq(uuid))
|
.filter(devices::uuid.eq(uuid))
|
||||||
|
@ -190,7 +196,7 @@ impl Device {
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn clear_push_token_by_uuid(uuid: &str, conn: &mut DbConn) -> EmptyResult {
|
pub async fn clear_push_token_by_uuid(uuid: &DeviceId, conn: &mut DbConn) -> EmptyResult {
|
||||||
db_run! { conn: {
|
db_run! { conn: {
|
||||||
diesel::update(devices::table)
|
diesel::update(devices::table)
|
||||||
.filter(devices::uuid.eq(uuid))
|
.filter(devices::uuid.eq(uuid))
|
||||||
|
@ -273,8 +279,8 @@ pub enum DeviceType {
|
||||||
LinuxCLI = 25,
|
LinuxCLI = 25,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for DeviceType {
|
impl Display for DeviceType {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
DeviceType::Android => write!(f, "Android"),
|
DeviceType::Android => write!(f, "Android"),
|
||||||
DeviceType::Ios => write!(f, "iOS"),
|
DeviceType::Ios => write!(f, "iOS"),
|
||||||
|
@ -339,3 +345,57 @@ impl DeviceType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(DieselNewType, FromForm, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct DeviceId(String);
|
||||||
|
|
||||||
|
impl DeviceId {
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
Self(String::from("00000000-0000-0000-0000-000000000000"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for DeviceId {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for DeviceId {
|
||||||
|
type Target = str;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Borrow<str> for DeviceId {
|
||||||
|
fn borrow(&self) -> &str {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for DeviceId {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for DeviceId {
|
||||||
|
fn from(raw: String) -> Self {
|
||||||
|
Self(raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'r> FromParam<'r> for DeviceId {
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub use self::attachment::{Attachment, AttachmentId};
|
||||||
pub use self::auth_request::AuthRequest;
|
pub use self::auth_request::AuthRequest;
|
||||||
pub use self::cipher::{Cipher, CipherId, RepromptType};
|
pub use self::cipher::{Cipher, CipherId, RepromptType};
|
||||||
pub use self::collection::{Collection, CollectionCipher, CollectionId, CollectionUser};
|
pub use self::collection::{Collection, CollectionCipher, CollectionId, CollectionUser};
|
||||||
pub use self::device::{Device, DeviceType};
|
pub use self::device::{Device, DeviceId, DeviceType};
|
||||||
pub use self::emergency_access::{EmergencyAccess, EmergencyAccessStatus, EmergencyAccessType};
|
pub use self::emergency_access::{EmergencyAccess, EmergencyAccessStatus, EmergencyAccessType};
|
||||||
pub use self::event::{Event, EventType};
|
pub use self::event::{Event, EventType};
|
||||||
pub use self::favorite::Favorite;
|
pub use self::favorite::Favorite;
|
||||||
|
|
|
@ -3,7 +3,10 @@ use chrono::{NaiveDateTime, Utc};
|
||||||
use crate::{
|
use crate::{
|
||||||
api::EmptyResult,
|
api::EmptyResult,
|
||||||
auth::ClientIp,
|
auth::ClientIp,
|
||||||
db::{models::UserId, DbConn},
|
db::{
|
||||||
|
models::{DeviceId, UserId},
|
||||||
|
DbConn,
|
||||||
|
},
|
||||||
error::MapResult,
|
error::MapResult,
|
||||||
CONFIG,
|
CONFIG,
|
||||||
};
|
};
|
||||||
|
@ -17,7 +20,7 @@ db_object! {
|
||||||
// This device UUID is simply what's claimed by the device. It doesn't
|
// 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
|
// necessarily correspond to any UUID in the devices table, since a device
|
||||||
// must complete 2FA login before being added into the devices table.
|
// must complete 2FA login before being added into the devices table.
|
||||||
pub device_uuid: String,
|
pub device_uuid: DeviceId,
|
||||||
pub device_name: String,
|
pub device_name: String,
|
||||||
pub device_type: i32,
|
pub device_type: i32,
|
||||||
pub login_time: NaiveDateTime,
|
pub login_time: NaiveDateTime,
|
||||||
|
@ -28,7 +31,7 @@ db_object! {
|
||||||
impl TwoFactorIncomplete {
|
impl TwoFactorIncomplete {
|
||||||
pub async fn mark_incomplete(
|
pub async fn mark_incomplete(
|
||||||
user_uuid: &UserId,
|
user_uuid: &UserId,
|
||||||
device_uuid: &str,
|
device_uuid: &DeviceId,
|
||||||
device_name: &str,
|
device_name: &str,
|
||||||
device_type: i32,
|
device_type: i32,
|
||||||
ip: &ClientIp,
|
ip: &ClientIp,
|
||||||
|
@ -61,7 +64,7 @@ impl TwoFactorIncomplete {
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn mark_complete(user_uuid: &UserId, device_uuid: &str, conn: &mut DbConn) -> EmptyResult {
|
pub async fn mark_complete(user_uuid: &UserId, device_uuid: &DeviceId, conn: &mut DbConn) -> EmptyResult {
|
||||||
if CONFIG.incomplete_2fa_time_limit() <= 0 || !CONFIG.mail_enabled() {
|
if CONFIG.incomplete_2fa_time_limit() <= 0 || !CONFIG.mail_enabled() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -69,7 +72,11 @@ impl TwoFactorIncomplete {
|
||||||
Self::delete_by_user_and_device(user_uuid, device_uuid, conn).await
|
Self::delete_by_user_and_device(user_uuid, device_uuid, conn).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn find_by_user_and_device(user_uuid: &UserId, device_uuid: &str, conn: &mut DbConn) -> Option<Self> {
|
pub async fn find_by_user_and_device(
|
||||||
|
user_uuid: &UserId,
|
||||||
|
device_uuid: &DeviceId,
|
||||||
|
conn: &mut DbConn,
|
||||||
|
) -> Option<Self> {
|
||||||
db_run! { conn: {
|
db_run! { conn: {
|
||||||
twofactor_incomplete::table
|
twofactor_incomplete::table
|
||||||
.filter(twofactor_incomplete::user_uuid.eq(user_uuid))
|
.filter(twofactor_incomplete::user_uuid.eq(user_uuid))
|
||||||
|
@ -94,7 +101,11 @@ impl TwoFactorIncomplete {
|
||||||
Self::delete_by_user_and_device(&self.user_uuid, &self.device_uuid, conn).await
|
Self::delete_by_user_and_device(&self.user_uuid, &self.device_uuid, conn).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_by_user_and_device(user_uuid: &UserId, device_uuid: &str, conn: &mut DbConn) -> EmptyResult {
|
pub async fn delete_by_user_and_device(
|
||||||
|
user_uuid: &UserId,
|
||||||
|
device_uuid: &DeviceId,
|
||||||
|
conn: &mut DbConn,
|
||||||
|
) -> EmptyResult {
|
||||||
db_run! { conn: {
|
db_run! { conn: {
|
||||||
diesel::delete(twofactor_incomplete::table
|
diesel::delete(twofactor_incomplete::table
|
||||||
.filter(twofactor_incomplete::user_uuid.eq(user_uuid))
|
.filter(twofactor_incomplete::user_uuid.eq(user_uuid))
|
||||||
|
|
Laden …
In neuem Issue referenzieren