From ae6ed0ece8bae45e8ff0e4a473004b54db348e25 Mon Sep 17 00:00:00 2001 From: Mathijs van Veluw Date: Fri, 18 Oct 2024 20:37:32 +0200 Subject: [PATCH] Fix collection management and match some json output (#5095) - Fixed collection management to be usable from the Password Manager UI - Checked and brought in-to-sync with upstream several json responses - Fixed a small issue with the `fields` response when it was empty Signed-off-by: BlackDex --- src/api/core/ciphers.rs | 1 - src/api/core/organizations.rs | 1 + src/api/identity.rs | 13 +------------ src/db/models/cipher.rs | 2 +- src/db/models/collection.rs | 6 +++--- src/db/models/group.rs | 2 +- src/db/models/organization.rs | 6 ++---- src/db/models/user.rs | 12 +++++++----- 8 files changed, 16 insertions(+), 27 deletions(-) diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs index da718942..94bdc485 100644 --- a/src/api/core/ciphers.rs +++ b/src/api/core/ciphers.rs @@ -150,7 +150,6 @@ async fn sync(data: SyncData, headers: Headers, mut conn: DbConn) -> Json "ciphers": ciphers_json, "domains": domains_json, "sends": sends_json, - "unofficialServer": true, "object": "sync" })) } diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index ffbf0812..402e7617 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -363,6 +363,7 @@ async fn get_org_collections_details(org_id: &str, headers: ManagerHeadersLoose, json_object["users"] = json!(users); json_object["groups"] = json!(groups); json_object["object"] = json!("collectionAccessDetails"); + json_object["unmanaged"] = json!(false); data.push(json_object) } diff --git a/src/api/identity.rs b/src/api/identity.rs index 4244d68d..672f128c 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -120,16 +120,8 @@ async fn _refresh_login(data: ConnectData, conn: &mut DbConn) -> JsonResult { "expires_in": expires_in, "token_type": "Bearer", "refresh_token": device.refresh_token, - "Key": user.akey, - "PrivateKey": user.private_key, - "Kdf": user.client_kdf_type, - "KdfIterations": user.client_kdf_iter, - "KdfMemory": user.client_kdf_memory, - "KdfParallelism": user.client_kdf_parallelism, - "ResetMasterPassword": false, // TODO: according to official server seems something like: user.password_hash.is_empty(), but would need testing "scope": scope, - "unofficialServer": true, }); Ok(Json(result)) @@ -342,7 +334,6 @@ async fn _password_login( "MasterPasswordPolicy": master_password_policy, "scope": scope, - "unofficialServer": true, "UserDecryptionOptions": { "HasMasterPassword": !user.password_hash.is_empty(), "Object": "userDecryptionOptions" @@ -461,9 +452,8 @@ async fn _user_api_key_login( "KdfIterations": user.client_kdf_iter, "KdfMemory": user.client_kdf_memory, "KdfParallelism": user.client_kdf_parallelism, - "ResetMasterPassword": false, // TODO: Same as above + "ResetMasterPassword": false, // TODO: according to official server seems something like: user.password_hash.is_empty(), but would need testing "scope": "api", - "unofficialServer": true, }); Ok(Json(result)) @@ -495,7 +485,6 @@ async fn _organization_api_key_login(data: ConnectData, conn: &mut DbConn, ip: & "expires_in": 3600, "token_type": "Bearer", "scope": "api.organization", - "unofficialServer": true, }))) } diff --git a/src/db/models/cipher.rs b/src/db/models/cipher.rs index 06aa69c5..fb2b5021 100644 --- a/src/db/models/cipher.rs +++ b/src/db/models/cipher.rs @@ -264,7 +264,7 @@ impl Cipher { // NOTE: This was marked as *Backwards Compatibility Code*, but as of January 2021 this is still being used by upstream // data_json should always contain the following keys with every atype - data_json["fields"] = json!([fields_json]); + data_json["fields"] = json!(fields_json); data_json["name"] = json!(self.name); data_json["notes"] = json!(self.notes); data_json["passwordHistory"] = Value::Array(password_history_json.clone()); diff --git a/src/db/models/collection.rs b/src/db/models/collection.rs index 7fb17c66..a26f22c7 100644 --- a/src/db/models/collection.rs +++ b/src/db/models/collection.rs @@ -81,8 +81,8 @@ impl Collection { let (read_only, hide_passwords, can_manage) = if let Some(cipher_sync_data) = cipher_sync_data { match cipher_sync_data.user_organizations.get(&self.org_uuid) { // Only for Manager types Bitwarden returns true for the can_manage option - // Owners and Admins always have false, but they can manage all collections anyway - Some(uo) if uo.has_full_access() => (false, false, uo.atype == UserOrgType::Manager), + // Owners and Admins always have true + Some(uo) if uo.has_full_access() => (false, false, uo.atype >= UserOrgType::Manager), Some(uo) => { // Only let a manager manage collections when the have full read/write access let is_manager = uo.atype == UserOrgType::Manager; @@ -98,7 +98,7 @@ impl Collection { } } else { match UserOrganization::find_confirmed_by_user_and_org(user_uuid, &self.org_uuid, conn).await { - Some(ou) if ou.has_full_access() => (false, false, ou.atype == UserOrgType::Manager), + Some(ou) if ou.has_full_access() => (false, false, ou.atype >= UserOrgType::Manager), Some(ou) => { let is_manager = ou.atype == UserOrgType::Manager; let read_only = !self.is_writable_by_user(user_uuid, conn).await; diff --git a/src/db/models/group.rs b/src/db/models/group.rs index 6e2db088..66ad338a 100644 --- a/src/db/models/group.rs +++ b/src/db/models/group.rs @@ -82,7 +82,7 @@ impl Group { "id": entry.collections_uuid, "readOnly": entry.read_only, "hidePasswords": entry.hide_passwords, - "manage": *user_org_type == UserOrgType::Manager && !entry.read_only && !entry.hide_passwords + "manage": *user_org_type >= UserOrgType::Admin || (*user_org_type == UserOrgType::Manager && !entry.read_only && !entry.hide_passwords) }) }) .collect(); diff --git a/src/db/models/organization.rs b/src/db/models/organization.rs index e59c1b05..21c241bf 100644 --- a/src/db/models/organization.rs +++ b/src/db/models/organization.rs @@ -161,7 +161,6 @@ impl Organization { "identifier": null, // not supported by us "name": self.name, "seats": null, - "maxAutoscaleSeats": null, "maxCollections": null, "maxStorageGb": i16::MAX, // The value doesn't matter, we don't check server-side "use2fa": true, @@ -374,7 +373,6 @@ impl UserOrganization { "identifier": null, // Not supported "name": org.name, "seats": null, - "maxAutoscaleSeats": null, "maxCollections": null, "usersGetPremium": true, "use2fa": true, @@ -411,7 +409,7 @@ impl UserOrganization { "familySponsorshipValidUntil": null, "familySponsorshipToDelete": null, "accessSecretsManager": false, - "limitCollectionCreationDeletion": true, + "limitCollectionCreationDeletion": false, // This should be set to true only when we can handle roles like createNewCollections "allowAdminAccessToAllCollectionItems": true, "flexibleCollections": false, @@ -477,7 +475,7 @@ impl UserOrganization { .into_iter() .filter_map(|c| { let (read_only, hide_passwords, can_manage) = if self.has_full_access() { - (false, false, self.atype == UserOrgType::Manager) + (false, false, self.atype >= UserOrgType::Manager) } else if let Some(cu) = cu.get(&c.uuid) { ( cu.read_only, diff --git a/src/db/models/user.rs b/src/db/models/user.rs index d91c91c1..94f42c84 100644 --- a/src/db/models/user.rs +++ b/src/db/models/user.rs @@ -1,3 +1,4 @@ +use crate::util::{format_date, get_uuid, retry}; use chrono::{NaiveDateTime, TimeDelta, Utc}; use serde_json::Value; @@ -90,7 +91,7 @@ impl User { let email = email.to_lowercase(); Self { - uuid: crate::util::get_uuid(), + uuid: get_uuid(), enabled: true, created_at: now, updated_at: now, @@ -107,7 +108,7 @@ impl User { salt: crypto::get_random_bytes::<64>().to_vec(), password_iterations: CONFIG.password_iterations(), - security_stamp: crate::util::get_uuid(), + security_stamp: get_uuid(), stamp_exception: None, password_hint: None, @@ -188,7 +189,7 @@ impl User { } pub fn reset_security_stamp(&mut self) { - self.security_stamp = crate::util::get_uuid(); + self.security_stamp = get_uuid(); } /// Set the stamp_exception to only allow a subsequent request matching a specific route using the current security-stamp. @@ -259,6 +260,7 @@ impl User { "forcePasswordReset": false, "avatarColor": self.avatar_color, "usesKeyConnector": false, + "creationDate": format_date(&self.created_at), "object": "profile", }) } @@ -340,7 +342,7 @@ impl User { let updated_at = Utc::now().naive_utc(); db_run! {conn: { - crate::util::retry(|| { + retry(|| { diesel::update(users::table) .set(users::updated_at.eq(updated_at)) .execute(conn) @@ -357,7 +359,7 @@ impl User { async fn _update_revision(uuid: &str, date: &NaiveDateTime, conn: &mut DbConn) -> EmptyResult { db_run! {conn: { - crate::util::retry(|| { + retry(|| { diesel::update(users::table.filter(users::uuid.eq(uuid))) .set(users::updated_at.eq(date)) .execute(conn)