From 3181e4e96e9e952b92b79ed07653fe6b6a46c322 Mon Sep 17 00:00:00 2001 From: BlackDex Date: Wed, 11 Jan 2023 20:23:53 +0100 Subject: [PATCH 1/7] Optimize CipherSyncData for very large vaults As mentioned in #3111, using a very very large vault causes some issues. Mainly because of a SQLite limit, but, it could also cause issue on MariaDB/MySQL or PostgreSQL. It also uses a lot of memory, and memory allocations. This PR solves this by removing the need of all the cipher_uuid's just to gather the correct attachments. It will use the user_uuid and org_uuid's to get all attachments linked to both, weither the user has access to them or not. This isn't an issue, since the matching is done per cipher and the attachment data is only returned if there is a matching cipher to where the user has access to. I also modified some code to be able to use `::with_capacity(n)` where possible. This prevents re-allocations if the `Vec` increases size, which will happen a lot if there are a lot of ciphers. According to my tests measuring the time it takes to sync, it seems to have lowered the duration a bit more. Fixes #3111 --- src/api/core/ciphers.rs | 43 ++++++++++++++++++-------------- src/api/core/emergency_access.rs | 5 ++-- src/api/core/organizations.rs | 4 +-- src/db/models/attachment.rs | 9 +++++-- 4 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs index f97403a0..0b0f1080 100644 --- a/src/api/core/ciphers.rs +++ b/src/api/core/ciphers.rs @@ -104,16 +104,17 @@ async fn sync(data: SyncData, headers: Headers, mut conn: DbConn) -> Json // Get all ciphers which are visible by the user let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &mut conn).await; - let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, &ciphers, CipherSyncType::User, &mut conn).await; + let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, CipherSyncType::User, &mut conn).await; // Lets generate the ciphers_json using all the gathered info - let mut ciphers_json = Vec::new(); + let mut ciphers_json = Vec::with_capacity(ciphers.len()); for c in ciphers { ciphers_json.push(c.to_json(&headers.host, &headers.user.uuid, Some(&cipher_sync_data), &mut conn).await); } - let mut collections_json = Vec::new(); - for c in Collection::find_by_user_uuid(headers.user.uuid.clone(), &mut conn).await { + let collections = Collection::find_by_user_uuid(headers.user.uuid.clone(), &mut conn).await; + let mut collections_json = Vec::with_capacity(collections.len()); + for c in collections { collections_json.push(c.to_json_details(&headers.user.uuid, Some(&cipher_sync_data), &mut conn).await); } @@ -148,9 +149,9 @@ async fn sync(data: SyncData, headers: Headers, mut conn: DbConn) -> Json #[get("/ciphers")] async fn get_ciphers(headers: Headers, mut conn: DbConn) -> Json { let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &mut conn).await; - let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, &ciphers, CipherSyncType::User, &mut conn).await; + let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, CipherSyncType::User, &mut conn).await; - let mut ciphers_json = Vec::new(); + let mut ciphers_json = Vec::with_capacity(ciphers.len()); for c in ciphers { ciphers_json.push(c.to_json(&headers.host, &headers.user.uuid, Some(&cipher_sync_data), &mut conn).await); } @@ -1721,12 +1722,9 @@ pub enum CipherSyncType { } impl CipherSyncData { - pub async fn new(user_uuid: &str, ciphers: &[Cipher], sync_type: CipherSyncType, conn: &mut DbConn) -> Self { - // Generate a list of Cipher UUID's to be used during a query filter with an eq_any. - let cipher_uuids = ciphers.iter().map(|c| c.uuid.clone()).collect(); - - let mut cipher_folders: HashMap = HashMap::new(); - let mut cipher_favorites: HashSet = HashSet::new(); + pub async fn new(user_uuid: &str, sync_type: CipherSyncType, conn: &mut DbConn) -> Self { + let cipher_folders: HashMap; + let cipher_favorites: HashSet; match sync_type { // User Sync supports Folders and Favorits CipherSyncType::User => { @@ -1738,18 +1736,25 @@ impl CipherSyncData { } // Organization Sync does not support Folders and Favorits. // If these are set, it will cause issues in the web-vault. - CipherSyncType::Organization => {} + CipherSyncType::Organization => { + cipher_folders = HashMap::with_capacity(0); + cipher_favorites = HashSet::with_capacity(0); + } } // Generate a list of Cipher UUID's containing a Vec with one or more Attachment records - let mut cipher_attachments: HashMap> = HashMap::new(); - for attachment in Attachment::find_all_by_ciphers(&cipher_uuids, conn).await { + let user_org_uuids = UserOrganization::get_org_uuid_by_user(user_uuid, conn).await; + let attachments = Attachment::find_all_by_user_and_orgs(user_uuid, &user_org_uuids, conn).await; + let mut cipher_attachments: HashMap> = HashMap::with_capacity(attachments.len()); + for attachment in attachments { cipher_attachments.entry(attachment.cipher_uuid.clone()).or_default().push(attachment); } // Generate a HashMap with the Cipher UUID as key and one or more Collection UUID's - let mut cipher_collections: HashMap> = HashMap::new(); - for (cipher, collection) in Cipher::get_collections_with_cipher_by_user(user_uuid.to_string(), conn).await { + let user_cipher_collections = Cipher::get_collections_with_cipher_by_user(user_uuid.to_string(), conn).await; + let mut cipher_collections: HashMap> = + HashMap::with_capacity(user_cipher_collections.len()); + for (cipher, collection) in user_cipher_collections { cipher_collections.entry(cipher).or_default().push(collection); } @@ -1768,14 +1773,14 @@ impl CipherSyncData { .collect(); // Generate a HashMap with the collections_uuid as key and the CollectionGroup record - let user_collections_groups = CollectionGroup::find_by_user(user_uuid, conn) + let user_collections_groups: HashMap = CollectionGroup::find_by_user(user_uuid, conn) .await .into_iter() .map(|collection_group| (collection_group.collections_uuid.clone(), collection_group)) .collect(); // Get all organizations that the user has full access to via group assignement - let user_group_full_access_for_organizations = + let user_group_full_access_for_organizations: HashSet = Group::gather_user_organizations_full_access(user_uuid, conn).await.into_iter().collect(); Self { diff --git a/src/api/core/emergency_access.rs b/src/api/core/emergency_access.rs index 7a683ea4..f15c1b8e 100644 --- a/src/api/core/emergency_access.rs +++ b/src/api/core/emergency_access.rs @@ -584,10 +584,9 @@ async fn view_emergency_access(emer_id: String, headers: Headers, mut conn: DbCo } let ciphers = Cipher::find_owned_by_user(&emergency_access.grantor_uuid, &mut conn).await; - let cipher_sync_data = - CipherSyncData::new(&emergency_access.grantor_uuid, &ciphers, CipherSyncType::User, &mut conn).await; + let cipher_sync_data = CipherSyncData::new(&emergency_access.grantor_uuid, CipherSyncType::User, &mut conn).await; - let mut ciphers_json = Vec::new(); + let mut ciphers_json = Vec::with_capacity(ciphers.len()); for c in ciphers { ciphers_json .push(c.to_json(&headers.host, &emergency_access.grantor_uuid, Some(&cipher_sync_data), &mut conn).await); diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index 3fb83ae2..c0af8f6e 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -617,9 +617,9 @@ async fn get_org_details(data: OrgIdData, headers: Headers, mut conn: DbConn) -> async fn _get_org_details(org_id: &str, host: &str, user_uuid: &str, conn: &mut DbConn) -> Value { let ciphers = Cipher::find_by_org(org_id, conn).await; - let cipher_sync_data = CipherSyncData::new(user_uuid, &ciphers, CipherSyncType::Organization, conn).await; + let cipher_sync_data = CipherSyncData::new(user_uuid, CipherSyncType::Organization, conn).await; - let mut ciphers_json = Vec::new(); + let mut ciphers_json = Vec::with_capacity(ciphers.len()); for c in ciphers { ciphers_json.push(c.to_json(host, user_uuid, Some(&cipher_sync_data), conn).await); } diff --git a/src/db/models/attachment.rs b/src/db/models/attachment.rs index 325b9a27..e850537d 100644 --- a/src/db/models/attachment.rs +++ b/src/db/models/attachment.rs @@ -187,10 +187,15 @@ impl Attachment { }} } - pub async fn find_all_by_ciphers(cipher_uuids: &Vec, conn: &mut DbConn) -> Vec { + // This will return all attachments linked to the user or org + // 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, org_uuids: &Vec, conn: &mut DbConn) -> Vec { db_run! { conn: { attachments::table - .filter(attachments::cipher_uuid.eq_any(cipher_uuids)) + .left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid))) + .filter(ciphers::user_uuid.eq(user_uuid)) + .or_filter(ciphers::organization_uuid.eq_any(org_uuids)) .select(attachments::all_columns) .load::(conn) .expect("Error loading attachments") From 42acb2ebb601bf74246d91842c285f8a375120d9 Mon Sep 17 00:00:00 2001 From: Rychart Redwerkz Date: Tue, 10 Jan 2023 00:24:37 +0100 Subject: [PATCH 2/7] Use more modern meta tag for charset encoding --- src/static/templates/admin/base.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/templates/admin/base.hbs b/src/static/templates/admin/base.hbs index e296b114..2fe1ee54 100644 --- a/src/static/templates/admin/base.hbs +++ b/src/static/templates/admin/base.hbs @@ -1,7 +1,7 @@ - + From fa70b440d0ec02f9947d1cbdacac81ec3318b8c2 Mon Sep 17 00:00:00 2001 From: BlackDex Date: Mon, 9 Jan 2023 20:32:56 +0100 Subject: [PATCH 3/7] Fix remaning inline format --- src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index fa8bea66..f8990dc0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1222,7 +1222,7 @@ fn to_json<'reg, 'rc>( ) -> HelperResult { let param = h.param(0).ok_or_else(|| RenderError::new("Expected 1 parameter for \"to_json\""))?.value(); let json = serde_json::to_string(param) - .map_err(|e| RenderError::new(format!("Can't serialize parameter to JSON: {}", e)))?; + .map_err(|e| RenderError::new(format!("Can't serialize parameter to JSON: {e}")))?; out.write(&json)?; Ok(()) } From e0e95e95e469fa0341a8339a9c9869525a32e978 Mon Sep 17 00:00:00 2001 From: GeekCorner <45696571+GeekCornerGH@users.noreply.github.com> Date: Tue, 10 Jan 2023 09:41:35 +0100 Subject: [PATCH 4/7] fix (2fa.directory): Allow api.2fa.directory, and remove 2fa.directory --- src/util.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util.rs b/src/util.rs index 3297fad5..fe99e2d3 100644 --- a/src/util.rs +++ b/src/util.rs @@ -50,7 +50,7 @@ impl Fairing for AppHeaders { // Have I Been Pwned and Gravator to allow those calls to work. // # Connect src: // Leaked Passwords check: api.pwnedpasswords.com - // 2FA/MFA Site check: 2fa.directory + // 2FA/MFA Site check: api.2fa.directory // # Mail Relay: https://bitwarden.com/blog/add-privacy-and-security-using-email-aliases-with-bitwarden/ // app.simplelogin.io, app.anonaddy.com, api.fastmail.com, quack.duckduckgo.com let csp = format!( @@ -73,7 +73,7 @@ impl Fairing for AppHeaders { {icon_service_csp}; \ connect-src 'self' \ https://api.pwnedpasswords.com \ - https://2fa.directory \ + https://api.2fa.directory \ https://app.simplelogin.io/api/ \ https://app.anonaddy.com/api/ \ https://api.fastmail.com/ \ From aaffb2e0079b364f70ba49f753385dc4e912ba19 Mon Sep 17 00:00:00 2001 From: BlackDex Date: Wed, 11 Jan 2023 22:13:20 +0100 Subject: [PATCH 5/7] Add MFA icon to org member overview The Organization member overview supports showing an icon if the user has MFA enabled or not. This PR adds this feature. This is very useful if you want to enable force mfa for example. --- src/db/models/organization.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/db/models/organization.rs b/src/db/models/organization.rs index a2ab75a7..331e1007 100644 --- a/src/db/models/organization.rs +++ b/src/db/models/organization.rs @@ -2,7 +2,7 @@ use num_traits::FromPrimitive; use serde_json::Value; use std::cmp::Ordering; -use super::{CollectionUser, GroupUser, OrgPolicy, OrgPolicyType, User}; +use super::{CollectionUser, GroupUser, OrgPolicy, OrgPolicyType, TwoFactor, User}; use crate::CONFIG; db_object! { @@ -365,6 +365,8 @@ impl UserOrganization { self.status }; + let twofactor_enabled = !TwoFactor::find_by_user(&user.uuid, conn).await.is_empty(); + json!({ "Id": self.uuid, "UserId": self.user_uuid, @@ -374,6 +376,7 @@ impl UserOrganization { "Status": status, "Type": self.atype, "AccessAll": self.access_all, + "TwoFactorEnabled": twofactor_enabled, "Object": "organizationUserUserDetails", }) From c90b3031a6eb57c3a3c1326d2a8ac2b80ceca26a Mon Sep 17 00:00:00 2001 From: BlackDex Date: Thu, 12 Jan 2023 09:45:52 +0100 Subject: [PATCH 6/7] Update Rust to v1.66.1 to patch CVE This PR sets Rust to v1.66.1 to fix a CVE. https://blog.rust-lang.org/2023/01/10/cve-2022-46176.html https://blog.rust-lang.org/2023/01/10/Rust-1.66.1.html Also updated some packages while at it. --- Cargo.lock | 48 +++++++++++++++------------ docker/Dockerfile.j2 | 8 ++--- docker/amd64/Dockerfile.alpine | 2 +- docker/amd64/Dockerfile.buildx.alpine | 2 +- docker/arm64/Dockerfile.alpine | 2 +- docker/arm64/Dockerfile.buildx.alpine | 2 +- docker/armv6/Dockerfile.alpine | 2 +- docker/armv6/Dockerfile.buildx.alpine | 2 +- docker/armv7/Dockerfile.alpine | 2 +- docker/armv7/Dockerfile.buildx.alpine | 2 +- rust-toolchain | 2 +- 11 files changed, 40 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 665367cf..21a4bf72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -190,6 +190,12 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + [[package]] name = "binascii" version = "0.1.4" @@ -261,7 +267,7 @@ dependencies = [ "cached_proc_macro", "cached_proc_macro_types", "futures", - "hashbrown 0.13.1", + "hashbrown 0.13.2", "instant", "lazy_static", "once_cell", @@ -1030,9 +1036,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" [[package]] name = "heck" @@ -1677,9 +1683,9 @@ dependencies = [ [[package]] name = "object" -version = "0.30.1" +version = "0.30.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d864c91689fdc196779b98dba0aceac6118594c2df6ee5d943eb6a8df4d107a" +checksum = "2b8c786513eb403643f2a88c244c2aaa270ef2153f55094587d0c48a3cf22a83" dependencies = [ "memchr", ] @@ -1769,9 +1775,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" dependencies = [ "cfg-if", "libc", @@ -1820,9 +1826,9 @@ dependencies = [ [[package]] name = "pem" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c64931a1a212348ec4f3b4362585eca7159d0d09cbdf4a7f74f02173596fd4" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" dependencies = [ "base64 0.13.1", ] @@ -1835,9 +1841,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f6e86fb9e7026527a0d46bc308b841d73170ef8f443e1807f6ef88526a816d4" +checksum = "4257b4a04d91f7e9e6290be5d3da4804dd5784fafde3a497d73eb2b4a158c30a" dependencies = [ "thiserror", "ucd-trie", @@ -1845,9 +1851,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96504449aa860c8dcde14f9fba5c58dc6658688ca1fe363589d6327b8662c603" +checksum = "241cda393b0cdd65e62e07e12454f1f25d57017dcc514b1514cd3c4645e3a0a6" dependencies = [ "pest", "pest_generator", @@ -1855,9 +1861,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "798e0220d1111ae63d66cb66a5dcb3fc2d986d520b98e49e1852bfdb11d7c5e7" +checksum = "46b53634d8c8196302953c74d5352f33d0c512a9499bd2ce468fc9f4128fa27c" dependencies = [ "pest", "pest_meta", @@ -1868,13 +1874,13 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "984298b75898e30a843e278a9f2452c31e349a073a0ce6fd950a12a74464e065" +checksum = "0ef4f1332a8d4678b41966bb4cc1d0676880e84183a1ecc3f4b69f03e99c7a51" dependencies = [ "once_cell", "pest", - "sha1", + "sha2", ] [[package]] @@ -2380,11 +2386,11 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64 0.13.1", + "base64 0.21.0", ] [[package]] diff --git a/docker/Dockerfile.j2 b/docker/Dockerfile.j2 index f0f4b2c9..ae823567 100644 --- a/docker/Dockerfile.j2 +++ b/docker/Dockerfile.j2 @@ -6,19 +6,19 @@ {% set build_stage_base_image = "rust:1.66-bullseye" %} {% if "alpine" in target_file %} {% if "amd64" in target_file %} -{% set build_stage_base_image = "blackdex/rust-musl:x86_64-musl-stable-1.66.0" %} +{% set build_stage_base_image = "blackdex/rust-musl:x86_64-musl-stable-1.66.1" %} {% set runtime_stage_base_image = "alpine:3.17" %} {% set package_arch_target = "x86_64-unknown-linux-musl" %} {% elif "armv7" in target_file %} -{% set build_stage_base_image = "blackdex/rust-musl:armv7-musleabihf-stable-1.66.0" %} +{% set build_stage_base_image = "blackdex/rust-musl:armv7-musleabihf-stable-1.66.1" %} {% set runtime_stage_base_image = "balenalib/armv7hf-alpine:3.17" %} {% set package_arch_target = "armv7-unknown-linux-musleabihf" %} {% elif "armv6" in target_file %} -{% set build_stage_base_image = "blackdex/rust-musl:arm-musleabi-stable-1.66.0" %} +{% set build_stage_base_image = "blackdex/rust-musl:arm-musleabi-stable-1.66.1" %} {% set runtime_stage_base_image = "balenalib/rpi-alpine:3.17" %} {% set package_arch_target = "arm-unknown-linux-musleabi" %} {% elif "arm64" in target_file %} -{% set build_stage_base_image = "blackdex/rust-musl:aarch64-musl-stable-1.66.0" %} +{% set build_stage_base_image = "blackdex/rust-musl:aarch64-musl-stable-1.66.1" %} {% set runtime_stage_base_image = "balenalib/aarch64-alpine:3.17" %} {% set package_arch_target = "aarch64-unknown-linux-musl" %} {% endif %} diff --git a/docker/amd64/Dockerfile.alpine b/docker/amd64/Dockerfile.alpine index 07db4098..791f355f 100644 --- a/docker/amd64/Dockerfile.alpine +++ b/docker/amd64/Dockerfile.alpine @@ -27,7 +27,7 @@ FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM blackdex/rust-musl:x86_64-musl-stable-1.66.0 as build +FROM blackdex/rust-musl:x86_64-musl-stable-1.66.1 as build diff --git a/docker/amd64/Dockerfile.buildx.alpine b/docker/amd64/Dockerfile.buildx.alpine index 8ee0bb1c..55cf0e57 100644 --- a/docker/amd64/Dockerfile.buildx.alpine +++ b/docker/amd64/Dockerfile.buildx.alpine @@ -27,7 +27,7 @@ FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM blackdex/rust-musl:x86_64-musl-stable-1.66.0 as build +FROM blackdex/rust-musl:x86_64-musl-stable-1.66.1 as build diff --git a/docker/arm64/Dockerfile.alpine b/docker/arm64/Dockerfile.alpine index d6509ff5..cdf0b5ee 100644 --- a/docker/arm64/Dockerfile.alpine +++ b/docker/arm64/Dockerfile.alpine @@ -27,7 +27,7 @@ FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM blackdex/rust-musl:aarch64-musl-stable-1.66.0 as build +FROM blackdex/rust-musl:aarch64-musl-stable-1.66.1 as build diff --git a/docker/arm64/Dockerfile.buildx.alpine b/docker/arm64/Dockerfile.buildx.alpine index b002cee9..cc1b8a06 100644 --- a/docker/arm64/Dockerfile.buildx.alpine +++ b/docker/arm64/Dockerfile.buildx.alpine @@ -27,7 +27,7 @@ FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM blackdex/rust-musl:aarch64-musl-stable-1.66.0 as build +FROM blackdex/rust-musl:aarch64-musl-stable-1.66.1 as build diff --git a/docker/armv6/Dockerfile.alpine b/docker/armv6/Dockerfile.alpine index 5bc379c2..f2d2741e 100644 --- a/docker/armv6/Dockerfile.alpine +++ b/docker/armv6/Dockerfile.alpine @@ -27,7 +27,7 @@ FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM blackdex/rust-musl:arm-musleabi-stable-1.66.0 as build +FROM blackdex/rust-musl:arm-musleabi-stable-1.66.1 as build diff --git a/docker/armv6/Dockerfile.buildx.alpine b/docker/armv6/Dockerfile.buildx.alpine index 7455205a..95781007 100644 --- a/docker/armv6/Dockerfile.buildx.alpine +++ b/docker/armv6/Dockerfile.buildx.alpine @@ -27,7 +27,7 @@ FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM blackdex/rust-musl:arm-musleabi-stable-1.66.0 as build +FROM blackdex/rust-musl:arm-musleabi-stable-1.66.1 as build diff --git a/docker/armv7/Dockerfile.alpine b/docker/armv7/Dockerfile.alpine index ec26d0fe..b5e08689 100644 --- a/docker/armv7/Dockerfile.alpine +++ b/docker/armv7/Dockerfile.alpine @@ -27,7 +27,7 @@ FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM blackdex/rust-musl:armv7-musleabihf-stable-1.66.0 as build +FROM blackdex/rust-musl:armv7-musleabihf-stable-1.66.1 as build diff --git a/docker/armv7/Dockerfile.buildx.alpine b/docker/armv7/Dockerfile.buildx.alpine index fcb8f8a1..cf3f16fb 100644 --- a/docker/armv7/Dockerfile.buildx.alpine +++ b/docker/armv7/Dockerfile.buildx.alpine @@ -27,7 +27,7 @@ FROM vaultwarden/web-vault@sha256:068ac863d52a5626568ae3c7f93a509f87c76b1b15821b101f2707724df9da3e as vault ########################## BUILD IMAGE ########################## -FROM blackdex/rust-musl:armv7-musleabihf-stable-1.66.0 as build +FROM blackdex/rust-musl:armv7-musleabihf-stable-1.66.1 as build diff --git a/rust-toolchain b/rust-toolchain index b6148bc0..0403bed1 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.66.0 +1.66.1 From acc1474394e430a45ab6d3cde2455c6165b02b30 Mon Sep 17 00:00:00 2001 From: BlackDex Date: Wed, 11 Jan 2023 21:45:11 +0100 Subject: [PATCH 7/7] Add avatar color support The new web-vault v2023.1.0 supports a custom color for the avatar. https://github.com/bitwarden/server/pull/2330 This PR adds this feature. --- .../down.sql | 0 .../2023-01-11-205851_add_avatar_color/up.sql | 2 ++ .../down.sql | 0 .../2023-01-11-205851_add_avatar_color/up.sql | 2 ++ .../down.sql | 0 .../2023-01-11-205851_add_avatar_color/up.sql | 2 ++ src/api/core/accounts.rs | 27 +++++++++++++++++++ src/db/models/user.rs | 5 ++++ src/db/schemas/mysql/schema.rs | 1 + src/db/schemas/postgresql/schema.rs | 1 + src/db/schemas/sqlite/schema.rs | 1 + src/main.rs | 2 +- 12 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 migrations/mysql/2023-01-11-205851_add_avatar_color/down.sql create mode 100644 migrations/mysql/2023-01-11-205851_add_avatar_color/up.sql create mode 100644 migrations/postgresql/2023-01-11-205851_add_avatar_color/down.sql create mode 100644 migrations/postgresql/2023-01-11-205851_add_avatar_color/up.sql create mode 100644 migrations/sqlite/2023-01-11-205851_add_avatar_color/down.sql create mode 100644 migrations/sqlite/2023-01-11-205851_add_avatar_color/up.sql diff --git a/migrations/mysql/2023-01-11-205851_add_avatar_color/down.sql b/migrations/mysql/2023-01-11-205851_add_avatar_color/down.sql new file mode 100644 index 00000000..e69de29b diff --git a/migrations/mysql/2023-01-11-205851_add_avatar_color/up.sql b/migrations/mysql/2023-01-11-205851_add_avatar_color/up.sql new file mode 100644 index 00000000..8f8e7205 --- /dev/null +++ b/migrations/mysql/2023-01-11-205851_add_avatar_color/up.sql @@ -0,0 +1,2 @@ +ALTER TABLE users +ADD COLUMN avatar_color VARCHAR(7); diff --git a/migrations/postgresql/2023-01-11-205851_add_avatar_color/down.sql b/migrations/postgresql/2023-01-11-205851_add_avatar_color/down.sql new file mode 100644 index 00000000..e69de29b diff --git a/migrations/postgresql/2023-01-11-205851_add_avatar_color/up.sql b/migrations/postgresql/2023-01-11-205851_add_avatar_color/up.sql new file mode 100644 index 00000000..cf3ef9f7 --- /dev/null +++ b/migrations/postgresql/2023-01-11-205851_add_avatar_color/up.sql @@ -0,0 +1,2 @@ +ALTER TABLE users +ADD COLUMN avatar_color TEXT; diff --git a/migrations/sqlite/2023-01-11-205851_add_avatar_color/down.sql b/migrations/sqlite/2023-01-11-205851_add_avatar_color/down.sql new file mode 100644 index 00000000..e69de29b diff --git a/migrations/sqlite/2023-01-11-205851_add_avatar_color/up.sql b/migrations/sqlite/2023-01-11-205851_add_avatar_color/up.sql new file mode 100644 index 00000000..cf3ef9f7 --- /dev/null +++ b/migrations/sqlite/2023-01-11-205851_add_avatar_color/up.sql @@ -0,0 +1,2 @@ +ALTER TABLE users +ADD COLUMN avatar_color TEXT; diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index 1f932f69..758d9028 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -39,6 +39,7 @@ pub fn routes() -> Vec { api_key, rotate_api_key, get_known_device, + put_avatar, ] } @@ -228,6 +229,32 @@ async fn post_profile(data: JsonUpcase, headers: Headers, mut conn: Ok(Json(user.to_json(&mut conn).await)) } +#[derive(Deserialize)] +#[allow(non_snake_case)] +struct AvatarData { + AvatarColor: Option, +} + +#[put("/accounts/avatar", data = "")] +async fn put_avatar(data: JsonUpcase, headers: Headers, mut conn: DbConn) -> JsonResult { + let data: AvatarData = data.into_inner().data; + + // It looks like it only supports the 6 hex color format. + // If you try to add the short value it will not show that color. + // Check and force 7 chars, including the #. + if let Some(color) = &data.AvatarColor { + if color.len() != 7 { + err!("The field AvatarColor must be a HTML/Hex color code with a length of 7 characters") + } + } + + let mut user = headers.user; + user.avatar_color = data.AvatarColor; + + user.save(&mut conn).await?; + Ok(Json(user.to_json(&mut conn).await)) +} + #[get("/users//public-key")] async fn get_public_keys(uuid: String, _headers: Headers, mut conn: DbConn) -> JsonResult { let user = match User::find_by_uuid(&uuid, &mut conn).await { diff --git a/src/db/models/user.rs b/src/db/models/user.rs index b59f10b8..611c4ebb 100644 --- a/src/db/models/user.rs +++ b/src/db/models/user.rs @@ -46,6 +46,8 @@ db_object! { pub client_kdf_iter: i32, pub api_key: Option, + + pub avatar_color: Option, } #[derive(Identifiable, Queryable, Insertable)] @@ -113,6 +115,8 @@ impl User { client_kdf_iter: Self::CLIENT_KDF_ITER_DEFAULT, api_key: None, + + avatar_color: None, } } @@ -226,6 +230,7 @@ impl User { "Providers": [], "ProviderOrganizations": [], "ForcePasswordReset": false, + "AvatarColor": self.avatar_color, "Object": "profile", }) } diff --git a/src/db/schemas/mysql/schema.rs b/src/db/schemas/mysql/schema.rs index 0073a9d5..27cd24c3 100644 --- a/src/db/schemas/mysql/schema.rs +++ b/src/db/schemas/mysql/schema.rs @@ -200,6 +200,7 @@ table! { client_kdf_type -> Integer, client_kdf_iter -> Integer, api_key -> Nullable, + avatar_color -> Nullable, } } diff --git a/src/db/schemas/postgresql/schema.rs b/src/db/schemas/postgresql/schema.rs index 1421513c..0233e0c9 100644 --- a/src/db/schemas/postgresql/schema.rs +++ b/src/db/schemas/postgresql/schema.rs @@ -200,6 +200,7 @@ table! { client_kdf_type -> Integer, client_kdf_iter -> Integer, api_key -> Nullable, + avatar_color -> Nullable, } } diff --git a/src/db/schemas/sqlite/schema.rs b/src/db/schemas/sqlite/schema.rs index 0fedcf1d..391e6700 100644 --- a/src/db/schemas/sqlite/schema.rs +++ b/src/db/schemas/sqlite/schema.rs @@ -200,6 +200,7 @@ table! { client_kdf_type -> Integer, client_kdf_iter -> Integer, api_key -> Nullable, + avatar_color -> Nullable, } } diff --git a/src/main.rs b/src/main.rs index 8a5f53da..3c648231 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,7 +34,7 @@ // The more key/value pairs there are the more recursion occurs. // We want to keep this as low as possible, but not higher then 128. // If you go above 128 it will cause rust-analyzer to fail, -#![recursion_limit = "94"] +#![recursion_limit = "97"] // When enabled use MiMalloc as malloc instead of the default malloc #[cfg(feature = "enable_mimalloc")]