From 78ab1c17eda29f2cd13f56c88e9247c90be57c3e Mon Sep 17 00:00:00 2001 From: BlackDex Date: Thu, 21 Nov 2024 21:32:16 +0100 Subject: [PATCH] Add partial role support for manager only - Add the custom role which replaces the manager role - Added mini-details endpoint used by v2024.11.1 These changes try to add the custom role in such a way that it stays compatible with the older manager role. It will convert a manager role into a custom role, and if a manager has `access-all` rights, it will enable the correct custom roles. Upon saving it will convert these back to the old format. What this does is making sure you are able to revert back to an older version of Vaultwarden without issues. This way we can support newer web-vault's and still be compatible with a previous Vaultwarden version if needed. In the future this needs to be changed to full role support though. Signed-off-by: BlackDex --- Cargo.lock | 132 ++++++++++++++++------------------ Cargo.toml | 8 +-- src/api/core/organizations.rs | 65 ++++++++++++++--- src/db/models/group.rs | 6 +- src/db/models/organization.rs | 95 +++++++++++++++++------- 5 files changed, 195 insertions(+), 111 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9edd20bf36..1c986f71ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,9 +55,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611cc2ae7d2e242c457e4be7f97036b8ad9ca152b499f53faf99b1ed8fc2553f" +checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" [[package]] name = "android-tzdata" @@ -441,9 +441,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" [[package]] name = "byteorder" @@ -495,9 +495,9 @@ checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" [[package]] name = "cc" -version = "1.1.37" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" +checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" dependencies = [ "shlex", ] @@ -614,9 +614,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" dependencies = [ "libc", ] @@ -762,9 +762,9 @@ dependencies = [ [[package]] name = "diesel" -version = "2.2.4" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "158fe8e2e68695bd615d7e4f3227c0727b151330d3e253b525086c348d055d5e" +checksum = "cbf9649c05e0a9dbd6d0b0b8301db5182b972d0fd02f0a7c6736cf632d7c0fd5" dependencies = [ "bigdecimal", "bitflags 2.6.0", @@ -799,9 +799,9 @@ dependencies = [ [[package]] name = "diesel_logger" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23010b507517129dc9b11fb35f36d76fd2d3dd4c85232733697622e345375f2f" +checksum = "8074833fffb675cf22a6ee669124f65f02971e48dd520bb80c7473ff70aeaf95" dependencies = [ "diesel", "log", @@ -1010,9 +1010,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", @@ -1271,9 +1271,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" dependencies = [ "atomic-waker", "bytes", @@ -1532,14 +1532,14 @@ dependencies = [ [[package]] name = "hyper" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.6", + "h2 0.4.7", "http 1.1.0", "http-body 1.0.1", "httparse", @@ -1558,9 +1558,9 @@ checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http 1.1.0", - "hyper 1.5.0", + "hyper 1.5.1", "hyper-util", - "rustls 0.23.16", + "rustls 0.23.17", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -1588,7 +1588,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.5.0", + "hyper 1.5.1", "hyper-util", "native-tls", "tokio", @@ -1607,7 +1607,7 @@ dependencies = [ "futures-util", "http 1.1.0", "http-body 1.0.1", - "hyper 1.5.0", + "hyper 1.5.1", "pin-project-lite", "socket2", "tokio", @@ -1762,16 +1762,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.4.0" @@ -1851,9 +1841,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2" [[package]] name = "jetscii" @@ -1953,9 +1943,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.162" +version = "0.2.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" [[package]] name = "libm" @@ -2341,9 +2331,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "300.4.0+3.4.0" +version = "300.4.1+3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a709e02f2b4aca747929cca5ed248880847c650233cf8b8cdc48f40aaf4898a6" +checksum = "faa4eac4138c62414b5622d1b31c5c304f34b406b013c079c2bbc652fdd6678c" dependencies = [ "cc", ] @@ -2640,9 +2630,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -2668,20 +2658,20 @@ checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" [[package]] name = "psm" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" dependencies = [ "cc", ] [[package]] name = "publicsuffix" -version = "2.2.3" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" dependencies = [ - "idna 0.3.0", + "idna 1.0.3", "psl-types", ] @@ -2808,7 +2798,7 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -2823,9 +2813,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -2909,11 +2899,11 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2 0.4.6", + "h2 0.4.7", "http 1.1.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.0", + "hyper 1.5.1", "hyper-rustls", "hyper-tls 0.6.0", "hyper-util", @@ -2929,7 +2919,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "system-configuration 0.6.1", "tokio", "tokio-native-tls", @@ -3114,9 +3104,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" -version = "0.38.40" +version = "0.38.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" dependencies = [ "bitflags 2.6.0", "errno", @@ -3139,9 +3129,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.16" +version = "0.23.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" +checksum = "7f1a745511c54ba6d4465e8d5dfbd81b45791756de28d4981af70d6dca128f1e" dependencies = [ "once_cell", "rustls-pki-types", @@ -3218,9 +3208,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] @@ -3287,9 +3277,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] @@ -3306,9 +3296,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", @@ -3317,9 +3307,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -3513,9 +3503,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.87" +version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ "proc-macro2", "quote", @@ -3530,9 +3520,9 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "sync_wrapper" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] @@ -3768,7 +3758,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.16", + "rustls 0.23.17", "rustls-pki-types", "tokio", ] @@ -3999,9 +3989,9 @@ checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-normalization" diff --git a/Cargo.toml b/Cargo.toml index 150b3b9ddd..7d0127fcd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,13 +70,13 @@ futures = "0.3.31" tokio = { version = "1.41.1", features = ["rt-multi-thread", "fs", "io-util", "parking_lot", "time", "signal", "net"] } # A generic serialization/deserialization framework -serde = { version = "1.0.214", features = ["derive"] } -serde_json = "1.0.132" +serde = { version = "1.0.215", features = ["derive"] } +serde_json = "1.0.133" # A safe, extensible ORM and Query builder -diesel = { version = "2.2.4", features = ["chrono", "r2d2", "numeric"] } +diesel = { version = "2.2.5", features = ["chrono", "r2d2", "numeric"] } diesel_migrations = "2.2.0" -diesel_logger = { version = "0.3.0", optional = true } +diesel_logger = { version = "0.4.0", optional = true } # Bundled/Static SQLite libsqlite3-sys = { version = "0.30.1", features = ["bundled"], optional = true } diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index 2b51a14412..ccc933e8a2 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -48,6 +48,7 @@ pub fn routes() -> Vec { confirm_invite, bulk_confirm_invite, accept_invite, + get_org_user_mini_details, get_user, edit_user, put_organization_user, @@ -857,13 +858,16 @@ struct InviteData { collections: Option>, #[serde(default)] access_all: bool, + #[serde(default)] + permissions: HashMap, } #[post("/organizations//users/invite", data = "")] async fn send_invite(org_id: &str, data: Json, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { - let data: InviteData = data.into_inner(); + let mut data: InviteData = data.into_inner(); - let new_type = match UserOrgType::from_str(&data.r#type.into_string()) { + let raw_type = &data.r#type.into_string(); + let new_type = match UserOrgType::from_str(raw_type) { Some(new_type) => new_type as i32, None => err!("Invalid type"), }; @@ -872,6 +876,14 @@ async fn send_invite(org_id: &str, data: Json, headers: AdminHeaders err!("Only Owners can invite Managers, Admins or Owners") } + if raw_type.eq("4") + && data.permissions.get("editAnyCollection") == Some(&json!(true)) + && data.permissions.get("deleteAnyCollection") == Some(&json!(true)) + && data.permissions.get("createNewCollections") == Some(&json!(true)) + { + data.access_all = true; + } + for email in data.emails.iter() { let mut user_org_status = UserOrgStatus::Invited as i32; let user = match User::find_by_mail(email, &mut conn).await { @@ -1279,7 +1291,31 @@ async fn _confirm_invite( save_result } -#[get("/organizations//users/?")] + +#[get("/organizations//users/mini-details", rank = 1)] +async fn get_org_user_mini_details( + org_id: &str, + _headers: AdminHeaders, + mut conn: DbConn, +) -> Json { + let mut users_json = Vec::new(); + for u in UserOrganization::find_by_org(org_id, &mut conn).await { + users_json.push( + u.to_json_mini_details( + &mut conn, + ) + .await, + ); + } + + Json(json!({ + "data": users_json, + "object": "list", + "continuationToken": null, + })) +} + +#[get("/organizations//users/?", rank = 2)] async fn get_user( org_id: &str, org_user_id: &str, @@ -1308,6 +1344,8 @@ struct EditUserData { groups: Option>, #[serde(default)] access_all: bool, + #[serde(default)] + permissions: HashMap, } #[put("/organizations//users/", data = "", rank = 1)] @@ -1329,13 +1367,22 @@ async fn edit_user( headers: AdminHeaders, mut conn: DbConn, ) -> EmptyResult { - let data: EditUserData = data.into_inner(); + let mut data: EditUserData = data.into_inner(); - let new_type = match UserOrgType::from_str(&data.r#type.into_string()) { + let raw_type = &data.r#type.into_string(); + let new_type = match UserOrgType::from_str(raw_type) { Some(new_type) => new_type, None => err!("Invalid type"), }; + if raw_type.eq("4") + && data.permissions.get("editAnyCollection") == Some(&json!(true)) + && data.permissions.get("deleteAnyCollection") == Some(&json!(true)) + && data.permissions.get("createNewCollections") == Some(&json!(true)) + { + data.access_all = true; + } + let mut user_to_edit = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await { Some(user) => user, None => err!("The specified user isn't member of the organization"), @@ -2308,14 +2355,14 @@ async fn _restore_organization_user( } #[get("/organizations//groups")] -async fn get_groups(org_id: &str, _headers: ManagerHeadersLoose, mut conn: DbConn) -> JsonResult { +async fn get_groups(org_id: &str, headers: ManagerHeadersLoose, mut conn: DbConn) -> JsonResult { let groups: Vec = if CONFIG.org_groups_enabled() { // Group::find_by_organization(&org_id, &mut conn).await.iter().map(Group::to_json).collect::() let groups = Group::find_by_organization(org_id, &mut conn).await; let mut groups_json = Vec::with_capacity(groups.len()); for g in groups { - groups_json.push(g.to_json_details(&mut conn).await) + groups_json.push(g.to_json_details(&headers.org_user.atype, &mut conn).await) } groups_json } else { @@ -2503,7 +2550,7 @@ async fn add_update_group( } #[get("/organizations/<_org_id>/groups//details")] -async fn get_group_details(_org_id: &str, group_id: &str, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { +async fn get_group_details(_org_id: &str, group_id: &str, headers: AdminHeaders, mut conn: DbConn) -> JsonResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); } @@ -2513,7 +2560,7 @@ async fn get_group_details(_org_id: &str, group_id: &str, _headers: AdminHeaders _ => err!("Group could not be found!"), }; - Ok(Json(group.to_json_details(&mut conn).await)) + Ok(Json(group.to_json_details(&(headers.org_user_type as i32), &mut conn).await)) } #[post("/organizations//groups//delete")] diff --git a/src/db/models/group.rs b/src/db/models/group.rs index e226512d15..66ad338a51 100644 --- a/src/db/models/group.rs +++ b/src/db/models/group.rs @@ -1,4 +1,4 @@ -use super::{User, UserOrganization}; +use super::{User, UserOrgType, UserOrganization}; use crate::api::EmptyResult; use crate::db::DbConn; use crate::error::MapResult; @@ -73,7 +73,7 @@ impl Group { }) } - pub async fn to_json_details(&self, conn: &mut DbConn) -> Value { + pub async fn to_json_details(&self, user_org_type: &i32, conn: &mut DbConn) -> Value { let collections_groups: Vec = CollectionGroup::find_by_group(&self.uuid, conn) .await .iter() @@ -82,7 +82,7 @@ impl Group { "id": entry.collections_uuid, "readOnly": entry.read_only, "hidePasswords": entry.hide_passwords, - "manage": false + "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 15f009918f..484b4e0525 100644 --- a/src/db/models/organization.rs +++ b/src/db/models/organization.rs @@ -73,6 +73,7 @@ impl UserOrgType { "1" | "Admin" => Some(UserOrgType::Admin), "2" | "User" => Some(UserOrgType::User), "3" | "Manager" => Some(UserOrgType::Manager), + "4" | "Custom" => Some(UserOrgType::Manager), // Link the Custom type to Manager _ => None, } } @@ -85,7 +86,7 @@ impl Ord for UserOrgType { 3, // Owner 2, // Admin 0, // User - 1, // Manager + 1, // Manager && Custom ]; ACCESS_LEVEL[*self as usize].cmp(&ACCESS_LEVEL[*other as usize]) } @@ -158,33 +159,46 @@ impl Organization { pub fn to_json(&self) -> Value { json!({ "id": self.uuid, - "identifier": null, // not supported by us "name": self.name, "seats": null, "maxCollections": null, "maxStorageGb": i16::MAX, // The value doesn't matter, we don't check server-side "use2fa": true, - "useCustomPermissions": false, + "useCustomPermissions": true, "useDirectory": false, // Is supported, but this value isn't checked anywhere (yet) "useEvents": CONFIG.org_events_enabled(), "useGroups": CONFIG.org_groups_enabled(), "useTotp": true, "usePolicies": true, - // "useScim": false, // Not supported (Not AGPLv3 Licensed) + "useScim": false, // Not supported (Not AGPLv3 Licensed) "useSso": false, // Not supported - // "useKeyConnector": false, // Not supported + "useKeyConnector": false, // Not supported + "usePasswordManager": true, + "useSecretsManager": false, // Not supported (Not AGPLv3 Licensed) "selfHost": true, "useApi": true, "hasPublicAndPrivateKeys": self.private_key.is_some() && self.public_key.is_some(), "useResetPassword": CONFIG.mail_enabled(), + "allowAdminAccessToAllCollectionItems": true, + "limitCollectionCreation": true, + "limitCollectionCreationDeletion": true, + "limitCollectionDeletion": true, - "businessName": null, + "businessName": self.name, "businessAddress1": null, "businessAddress2": null, "businessAddress3": null, "businessCountry": null, "businessTaxNumber": null, + "maxAutoscaleSeats": null, + "maxAutoscaleSmSeats": null, + "maxAutoscaleSmServiceAccounts": null, + + "secretsManagerPlan": null, + "smSeats": null, + "smServiceAccounts": null, + "billingEmail": self.billing_email, "planType": 6, // Custom plan "usersGetPremium": true, @@ -356,17 +370,20 @@ impl UserOrganization { pub async fn to_json(&self, conn: &mut DbConn) -> Value { let org = Organization::find_by_uuid(&self.org_uuid, conn).await.unwrap(); + let user_org_type = match self.atype { + 3 => 4, + _ => self.atype, + }; + 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, + "createNewCollections": user_org_type == 4 && self.access_all, + "editAnyCollection": user_org_type == 4 && self.access_all, + "deleteAnyCollection": user_org_type == 4 && self.access_all, "manageGroups": false, "managePolicies": false, "manageSso": false, // Not supported @@ -398,9 +415,9 @@ impl UserOrganization { "ssoBound": false, // Not supported "useSso": false, // Not supported "useKeyConnector": false, - "useSecretsManager": false, + "useSecretsManager": false, // Not supported (Not AGPLv3 Licensed) "usePasswordManager": true, - "useCustomPermissions": false, + "useCustomPermissions": true, "useActivateAutofillPolicy": false, "organizationUserId": self.uuid, @@ -417,9 +434,11 @@ impl UserOrganization { "familySponsorshipValidUntil": null, "familySponsorshipToDelete": null, "accessSecretsManager": false, - "limitCollectionCreationDeletion": false, // This should be set to true only when we can handle roles like createNewCollections + "limitCollectionCreation": true, + "limitCollectionCreationDeletion": true, + "limitCollectionDeletion": true, "allowAdminAccessToAllCollectionItems": true, - "flexibleCollections": false, + "userIsManagedByOrganization": false, // Means not managed via the Members UI, like SSO "permissions": permissions, @@ -429,7 +448,7 @@ impl UserOrganization { "userId": self.user_uuid, "key": self.akey, "status": self.status, - "type": self.atype, + "type": user_org_type, "enabled": true, "object": "profileOrganization", @@ -516,24 +535,29 @@ impl UserOrganization { Vec::with_capacity(0) }; - let permissions = json!({ + let user_org_type = match self.atype { + 3 => 4, + _ => self.atype, + }; + + let permissions = if user_org_type == 4 && self.access_all { 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, + "createNewCollections": true, + "editAnyCollection": true, + "deleteAnyCollection": true, "manageGroups": false, "managePolicies": false, "manageSso": false, // Not supported "manageUsers": false, "manageResetPassword": false, "manageScim": false // Not supported (Not AGPLv3 Licensed) - }); + })} else { + json!(null) + }; json!({ "id": self.uuid, @@ -546,7 +570,7 @@ impl UserOrganization { "collections": collections, "status": status, - "type": self.atype, + "type": user_org_type, "accessAll": self.access_all, "twoFactorEnabled": twofactor_enabled, "resetPasswordEnrolled": self.reset_password_key.is_some(), @@ -608,6 +632,29 @@ impl UserOrganization { "object": "organizationUserDetails", }) } + + pub async fn to_json_mini_details(&self, conn: &mut DbConn) -> Value { + let user = User::find_by_uuid(&self.user_uuid, conn).await.unwrap(); + + // Because BitWarden want the status to be -1 for revoked users we need to catch that here. + // We subtract/add a number so we can restore/activate the user to it's previous state again. + let status = if self.status < UserOrgStatus::Revoked as i32 { + UserOrgStatus::Revoked as i32 + } else { + self.status + }; + + json!({ + "id": self.uuid, + "userId": self.user_uuid, + "type": self.atype, + "status": status, + "name": user.name, + "email": user.email, + "object": "organizationUserUserMiniDetails", + }) + } + pub async fn save(&self, conn: &mut DbConn) -> EmptyResult { User::update_uuid_revision(&self.user_uuid, conn).await;