commit 41e5f8af46091c8b3f2ecbef5b767dd3a74cf11d
parent fd9bc91a387b8779e64e0dc82a5ea39f82e227c9
Author: Zack Newman <zack@philomathiclife.com>
Date: Sun, 3 Nov 2024 14:03:50 -0700
merge upstream
Diffstat:
8 files changed, 75 insertions(+), 33 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -17,7 +17,7 @@ version = "3.0.0"
maintenance = { status = "actively-developed" }
[target.'cfg(target_os = "openbsd")'.dependencies]
-priv_sep = { version = "2.0.0", default-features = false, features = ["openbsd"], optional = true }
+priv_sep = { version = "2.1.0", default-features = false, features = ["openbsd"], optional = true }
[dependencies]
chrono = { version = "0.4.38", default-features = false, features = ["serde"] }
@@ -25,19 +25,19 @@ data-encoding = { version = "2.6.0", default-features = false }
diesel = { version = "2.2.4", default-features = false, features = ["32-column-tables", "chrono", "r2d2", "sqlite"] }
jsonwebtoken = { version = "9.3.0", default-features = false, features = ["use_pem"] }
libsqlite3-sys = { version = "0.30.1", default-features = false, features = ["bundled"] }
-openssl = { version = "0.10.66", default-features = false }
+openssl = { version = "0.10.68", default-features = false }
paste = { version = "1.0.15", default-features = false }
rand = { version = "0.8.5", default-features = false, features = ["small_rng"] }
ring = { version = "0.17.8", default-features = false }
rocket = { version = "0.5.1", default-features = false, features = ["json", "tls"] }
semver = { version = "1.0.23", default-features = false }
-serde = { version = "1.0.210", default-features = false }
-serde_json = { version = "1.0.128", default-features = false }
-tokio = { version = "1.40.0", default-features = false }
+serde = { version = "1.0.214", default-features = false }
+serde_json = { version = "1.0.132", default-features = false }
+tokio = { version = "1.41.0", default-features = false }
toml = { version = "0.8.19", default-features = false, features = ["parse"] }
totp-lite = { version = "2.0.1", default-features = false }
url = { version = "2.5.2", default-features = false }
-uuid = { version = "1.10.0", default-features = false, features = ["v4"] }
+uuid = { version = "1.11.0", default-features = false, features = ["v4"] }
webauthn-rs = { version = "0.4.8", default-features = false, features = ["danger-allow-state-serialisation", "danger-user-presence-only-security-keys"] }
[features]
diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs
@@ -146,7 +146,6 @@ async fn sync(data: SyncData, headers: Headers, conn: DbConn) -> Json<Value> {
"ciphers": ciphers_json,
"domains": domains_json,
"sends": Vec::<Value>::new(),
- "unofficialServer": true,
"object": "sync"
}))
}
diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs
@@ -298,11 +298,12 @@ async fn get_org_collections_details(
})
.collect();
let groups = Vec::<Value>::new();
- let mut json_object = col.to_json();
+ let mut json_object = col.to_json_details(&headers.user.uuid, None, &conn).await;
json_object["assigned"] = json!(assigned);
json_object["users"] = json!(users);
json_object["groups"] = json!(groups);
json_object["object"] = json!("collectionAccessDetails");
+ json_object["unmanaged"] = json!(false);
data.push(json_object);
}
Ok(Json(json!({
@@ -577,7 +578,9 @@ async fn get_org_collection_detail(
.collect();
let assigned =
Collection::can_access_collection(&user_org, &collection.uuid, &conn).await;
- let mut json_object = collection.to_json();
+ let mut json_object = collection
+ .to_json_details(&headers.user.uuid, None, &conn)
+ .await;
json_object["assigned"] = json!(assigned);
json_object["users"] = json!(users);
json_object["groups"] = json!(groups);
@@ -1130,9 +1133,7 @@ async fn post_org_import(
let mut ciphers = Vec::new();
for cipher_data in data.ciphers {
let mut cipher = Cipher::new(cipher_data.r#type, cipher_data.name.clone());
- update_cipher_from_data(&mut cipher, cipher_data, &headers, None, &conn, true)
- .await
- .ok();
+ drop(update_cipher_from_data(&mut cipher, cipher_data, &headers, None, &conn, true).await);
ciphers.push(cipher);
}
// Assign the collections
diff --git a/src/api/identity.rs b/src/api/identity.rs
@@ -71,16 +71,7 @@ async fn _refresh_login(data: ConnectData, conn: &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))
}
@@ -207,7 +198,6 @@ async fn _password_login(
"ForcePasswordReset": false,
"MasterPasswordPolicy": master_password_policy,
"scope": scope,
- "unofficialServer": true,
"UserDecryptionOptions": {
"HasMasterPassword": !user.password_hash.is_empty(),
"Object": "userDecryptionOptions"
diff --git a/src/db/models/cipher.rs b/src/db/models/cipher.rs
@@ -139,7 +139,27 @@ impl Cipher {
.inspect_err(|e| warn!("Error parsing fields {e:?} for {}", self.uuid))
.ok()
})
- .map_or(Value::Null, |d| d.into_iter().map(|da| da.data).collect());
+ .map_or(Value::Null, |d| {
+ d.into_iter()
+ .map(|mut da| match da.data.get("type") {
+ None => {
+ da.data["type"] = json!(1i32);
+ }
+ Some(x) => {
+ if x.is_string() {
+ let type_num = x
+ .as_str()
+ .unwrap_or_else(|| {
+ unreachable!("there is a bug in Value::is_string")
+ })
+ .parse::<u8>()
+ .unwrap_or(1);
+ da.data["type"] = json!(type_num);
+ }
+ }
+ })
+ .collect()
+ });
let password_history_json = self
.password_history
.as_ref()
diff --git a/src/db/models/collection.rs b/src/db/models/collection.rs
@@ -80,9 +80,9 @@ impl Collection {
{
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
+ // Owners and Admins always have true
Some(uo) if uo.has_full_access() => {
- (false, false, uo.atype == UserOrgType::Manager)
+ (false, false, uo.atype >= UserOrgType::Manager)
}
Some(uo) => {
// Only let a manager manage collections when the have full read/write access
@@ -105,7 +105,7 @@ impl Collection {
.await
{
Some(ou) if ou.has_full_access() => {
- (false, false, ou.atype == UserOrgType::Manager)
+ (false, false, ou.atype >= UserOrgType::Manager)
}
Some(ou) => {
let is_manager = ou.atype == UserOrgType::Manager;
diff --git a/src/db/models/organization.rs b/src/db/models/organization.rs
@@ -196,7 +196,6 @@ impl Organization {
"identifier": null, // not supported by us
"name": self.name,
"seats": null,
- "maxAutoscaleSeats": null,
"maxCollections": null,
"maxStorageGb": i16::MAX,
"use2fa": true,
@@ -269,6 +268,17 @@ impl UserOrganization {
}
}
+ /// Return the status of the user in an unrevoked state
+ pub fn get_unrevoked_status(&self) -> i32 {
+ if self.status <= i32::from(UserOrgStatus::Revoked) {
+ return self
+ .status
+ .checked_add(ACTIVATE_REVOKE_DIFF)
+ .unwrap_or_else(|| panic!("overflow"));
+ }
+ self.status
+ }
+
pub fn set_external_id(&mut self, external_id: Option<String>) -> bool {
//Check if external id is empty. We don't want to have
//empty strings in the database
@@ -366,7 +376,6 @@ impl UserOrganization {
"identifier": null, // Not supported
"name": org.name,
"seats": null,
- "maxAutoscaleSeats": null,
"maxCollections": null,
"usersGetPremium": true,
"use2fa": true,
@@ -402,7 +411,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,
"permissions": permissions,
@@ -446,7 +455,7 @@ impl UserOrganization {
.into_iter()
.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,
@@ -468,13 +477,30 @@ impl UserOrganization {
})
.collect()
} else {
- Vec::with_capacity(0)
+ Vec::new()
};
-
+ let permissions = json!({
+ // TODO: Add support for Custom User Roles
+ // See: https://bitwarden.com/help/article/user-types-access-control/#custom-role
+ "accessEventLogs": false,
+ "accessImportExport": false,
+ "accessReports": false,
+ "createNewCollections": false,
+ "editAnyCollection": false,
+ "deleteAnyCollection": false,
+ "editAssignedCollections": false,
+ "deleteAssignedCollections": false,
+ "manageGroups": false,
+ "managePolicies": false,
+ "manageSso": false, // Not supported
+ "manageUsers": false,
+ "manageResetPassword": false,
+ "manageScim": false // Not supported (Not AGPLv3 Licensed)
+ });
json!({
"id": self.uuid,
"userId": self.user_uuid,
- "name": user.name,
+ "name": if self.get_unrevoked_status() >= i32::from(UserOrgStatus::Accepted) { Some(user.name) } else { None },
"email": user.email,
"externalId": self.external_id,
"avatarColor": user.avatar_color,
@@ -485,6 +511,11 @@ impl UserOrganization {
"accessAll": self.access_all,
"twoFactorEnabled": twofactor_enabled,
"resetPasswordEnrolled": self.reset_password_key.is_some(),
+ "hasMasterPassword": !user.password_hash.is_empty(),
+ "permissions": permissions,
+ "ssoBound": false, // Not supported
+ "usesKeyConnector": false, // Not supported
+ "accessSecretsManager": false, // Not supported (Not AGPLv3 Licensed)
"object": "organizationUserUserDetails",
})
}
diff --git a/src/db/models/user.rs b/src/db/models/user.rs
@@ -292,6 +292,7 @@ impl User {
"forcePasswordReset": false,
"avatarColor": self.avatar_color,
"usesKeyConnector": false,
+ "creationDate": util::format_date(&self.created_at),
"object": "profile",
})
}