From 73db51845eadcd55da7b9e8ddce5eb75592046f7 Mon Sep 17 00:00:00 2001 From: Will Lotto Date: Mon, 10 Aug 2026 09:01:31 +1000 Subject: [PATCH] Add revision_date to org policies Org policy JSON responses never included a revisionDate field, unlike every other synced entity. The official Bitwarden server always sets it, and current official clients (recently migrated to the WASM SDK's typed PolicyView) now assume it is always a valid date string. Its absence deserializes to undefined client-side, which then throws (RangeError: Invalid time value) when the client formats it, breaking the whole policy sync pipeline and silently emptying the vault view in the browser and desktop apps. Adds a revision_date column (mirroring the existing pattern used by Send), stamped on creation and bumped on every save. --- .../down.sql | 0 .../up.sql | 2 ++ .../down.sql | 0 .../up.sql | 2 ++ .../down.sql | 0 .../up.sql | 2 ++ src/db/models/org_policy.rs | 17 ++++++++++++----- src/db/schema.rs | 1 + 8 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 migrations/mysql/2026-08-10-120000_add_org_policy_revision_date/down.sql create mode 100644 migrations/mysql/2026-08-10-120000_add_org_policy_revision_date/up.sql create mode 100644 migrations/postgresql/2026-08-10-120000_add_org_policy_revision_date/down.sql create mode 100644 migrations/postgresql/2026-08-10-120000_add_org_policy_revision_date/up.sql create mode 100644 migrations/sqlite/2026-08-10-120000_add_org_policy_revision_date/down.sql create mode 100644 migrations/sqlite/2026-08-10-120000_add_org_policy_revision_date/up.sql diff --git a/migrations/mysql/2026-08-10-120000_add_org_policy_revision_date/down.sql b/migrations/mysql/2026-08-10-120000_add_org_policy_revision_date/down.sql new file mode 100644 index 00000000..e69de29b diff --git a/migrations/mysql/2026-08-10-120000_add_org_policy_revision_date/up.sql b/migrations/mysql/2026-08-10-120000_add_org_policy_revision_date/up.sql new file mode 100644 index 00000000..fc5a5f44 --- /dev/null +++ b/migrations/mysql/2026-08-10-120000_add_org_policy_revision_date/up.sql @@ -0,0 +1,2 @@ +ALTER TABLE org_policies +ADD COLUMN revision_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP; diff --git a/migrations/postgresql/2026-08-10-120000_add_org_policy_revision_date/down.sql b/migrations/postgresql/2026-08-10-120000_add_org_policy_revision_date/down.sql new file mode 100644 index 00000000..e69de29b diff --git a/migrations/postgresql/2026-08-10-120000_add_org_policy_revision_date/up.sql b/migrations/postgresql/2026-08-10-120000_add_org_policy_revision_date/up.sql new file mode 100644 index 00000000..ed39a1d9 --- /dev/null +++ b/migrations/postgresql/2026-08-10-120000_add_org_policy_revision_date/up.sql @@ -0,0 +1,2 @@ +ALTER TABLE org_policies +ADD COLUMN revision_date TIMESTAMP NOT NULL DEFAULT now(); diff --git a/migrations/sqlite/2026-08-10-120000_add_org_policy_revision_date/down.sql b/migrations/sqlite/2026-08-10-120000_add_org_policy_revision_date/down.sql new file mode 100644 index 00000000..e69de29b diff --git a/migrations/sqlite/2026-08-10-120000_add_org_policy_revision_date/up.sql b/migrations/sqlite/2026-08-10-120000_add_org_policy_revision_date/up.sql new file mode 100644 index 00000000..4ab5d2e5 --- /dev/null +++ b/migrations/sqlite/2026-08-10-120000_add_org_policy_revision_date/up.sql @@ -0,0 +1,2 @@ +ALTER TABLE org_policies +ADD COLUMN revision_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP; diff --git a/src/db/models/org_policy.rs b/src/db/models/org_policy.rs index 88b7872c..095ca7b4 100644 --- a/src/db/models/org_policy.rs +++ b/src/db/models/org_policy.rs @@ -1,3 +1,4 @@ +use chrono::{NaiveDateTime, Utc}; use derive_more::{AsRef, From}; use diesel::prelude::*; use serde::Deserialize; @@ -11,6 +12,7 @@ use crate::{ schema::{org_policies, users_organizations}, }, error::MapResult, + util::format_date, }; use super::{Membership, MembershipId, MembershipStatus, MembershipType, OrganizationId, TwoFactor, UserId}; @@ -24,6 +26,7 @@ pub struct OrgPolicy { pub atype: i32, pub enabled: bool, pub data: String, + pub revision_date: NaiveDateTime, } // https://github.com/bitwarden/server/blob/9ebe16587175b1c0e9208f84397bb75d0d595510/src/Core/AdminConsole/Enums/PolicyType.cs @@ -76,6 +79,7 @@ impl OrgPolicy { atype: atype as i32, enabled, data, + revision_date: Utc::now().naive_utc(), } } @@ -91,6 +95,7 @@ impl OrgPolicy { "type": self.atype, "data": data_json, "enabled": self.enabled, + "revisionDate": format_date(&self.revision_date), "object": "policy", }); @@ -108,11 +113,13 @@ impl OrgPolicy { /// Database methods impl OrgPolicy { - pub async fn save(&self, conn: &DbConn) -> EmptyResult { + pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { + self.revision_date = Utc::now().naive_utc(); + db_run! { conn: sqlite, mysql { match diesel::replace_into(org_policies::table) - .values(self) + .values(&*self) .execute(conn) { Ok(_) => Ok(()), @@ -120,7 +127,7 @@ impl OrgPolicy { Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => { diesel::update(org_policies::table) .filter(org_policies::uuid.eq(&self.uuid)) - .set(self) + .set(&*self) .execute(conn) .map_res("Error saving org_policy") } @@ -140,10 +147,10 @@ impl OrgPolicy { .map_res("Error deleting org_policy for insert")?; diesel::insert_into(org_policies::table) - .values(self) + .values(&*self) .on_conflict(org_policies::uuid) .do_update() - .set(self) + .set(&*self) .execute(conn) .map_res("Error saving org_policy") } diff --git a/src/db/schema.rs b/src/db/schema.rs index af342186..acf12554 100644 --- a/src/db/schema.rs +++ b/src/db/schema.rs @@ -116,6 +116,7 @@ table! { atype -> Integer, enabled -> Bool, data -> Text, + revision_date -> Timestamp, } }