1
0
Fork 1
Spiegel von https://github.com/dani-garcia/vaultwarden.git synchronisiert 2025-01-08 11:55:42 +01:00

introduce group_id newtype pattern

Dieser Commit ist enthalten in:
Stefan Melmuk 2024-12-21 13:31:47 +01:00
Ursprung 7f2db91f23
Commit 189fd77806
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 817020C608FE9C09
4 geänderte Dateien mit 123 neuen und 64 gelöschten Zeilen

Datei anzeigen

@ -124,7 +124,7 @@ struct OrganizationUpdateData {
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct NewCollectionData { struct NewCollectionData {
name: String, name: String,
groups: Vec<NewCollectionObjectData>, groups: Vec<NewCollectionGroupData>,
users: Vec<NewCollectionMemberData>, users: Vec<NewCollectionMemberData>,
id: Option<CollectionId>, id: Option<CollectionId>,
external_id: Option<String>, external_id: Option<String>,
@ -132,9 +132,9 @@ struct NewCollectionData {
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct NewCollectionObjectData { struct NewCollectionGroupData {
hide_passwords: bool, hide_passwords: bool,
id: String, id: GroupId,
read_only: bool, read_only: bool,
} }
@ -155,8 +155,8 @@ struct OrgKeyData {
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct OrgBulkIds { struct BulkGroupIds {
ids: Vec<String>, ids: Vec<GroupId>,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@ -367,7 +367,7 @@ async fn get_org_collections_details(
.await .await
.iter() .iter()
.map(|collection_group| { .map(|collection_group| {
SelectionReadOnly::to_collection_group_details_read_only(collection_group).to_json() GroupSelection::to_collection_group_details_read_only(collection_group).to_json()
}) })
.collect() .collect()
} else { } else {
@ -651,7 +651,7 @@ async fn get_org_collection_detail(
.await .await
.iter() .iter()
.map(|collection_group| { .map(|collection_group| {
SelectionReadOnly::to_collection_group_details_read_only(collection_group).to_json() GroupSelection::to_collection_group_details_read_only(collection_group).to_json()
}) })
.collect() .collect()
} else { } else {
@ -856,7 +856,7 @@ struct MembershipData {
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct InviteData { struct InviteData {
emails: Vec<String>, emails: Vec<String>,
groups: Vec<String>, groups: Vec<GroupId>,
r#type: NumberOrString, r#type: NumberOrString,
collections: Option<Vec<CollectionData>>, collections: Option<Vec<CollectionData>>,
#[serde(default)] #[serde(default)]
@ -942,8 +942,8 @@ async fn send_invite(
new_member.save(&mut conn).await?; new_member.save(&mut conn).await?;
for group in data.groups.iter() { for group_id in data.groups.iter() {
let mut group_entry = GroupUser::new(String::from(group), new_member.uuid.clone()); let mut group_entry = GroupUser::new(group_id.clone(), new_member.uuid.clone());
group_entry.save(&mut conn).await?; group_entry.save(&mut conn).await?;
} }
@ -1330,7 +1330,7 @@ async fn get_user(
struct EditUserData { struct EditUserData {
r#type: NumberOrString, r#type: NumberOrString,
collections: Option<Vec<CollectionData>>, collections: Option<Vec<CollectionData>>,
groups: Option<Vec<String>>, groups: Option<Vec<GroupId>>,
#[serde(default)] #[serde(default)]
access_all: bool, access_all: bool,
} }
@ -1432,8 +1432,8 @@ async fn edit_user(
GroupUser::delete_all_by_member(&member_to_edit.uuid, &mut conn).await?; GroupUser::delete_all_by_member(&member_to_edit.uuid, &mut conn).await?;
for group in data.groups.iter().flatten() { for group_id in data.groups.iter().flatten() {
let mut group_entry = GroupUser::new(String::from(group), member_to_edit.uuid.clone()); let mut group_entry = GroupUser::new(group_id.clone(), member_to_edit.uuid.clone());
group_entry.save(&mut conn).await?; group_entry.save(&mut conn).await?;
} }
@ -2379,13 +2379,13 @@ impl GroupRequest {
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct SelectionReadOnly { struct GroupSelection {
id: String, id: GroupId,
read_only: bool, read_only: bool,
hide_passwords: bool, hide_passwords: bool,
} }
impl SelectionReadOnly { impl GroupSelection {
pub fn to_collection_group_details_read_only(collection_group: &CollectionGroup) -> Self { pub fn to_collection_group_details_read_only(collection_group: &CollectionGroup) -> Self {
Self { Self {
id: collection_group.groups_uuid.clone(), id: collection_group.groups_uuid.clone(),
@ -2408,7 +2408,7 @@ struct CollectionSelection {
} }
impl CollectionSelection { impl CollectionSelection {
pub fn to_collection_group(&self, groups_uuid: String) -> CollectionGroup { pub fn to_collection_group(&self, groups_uuid: GroupId) -> CollectionGroup {
CollectionGroup::new(self.id.clone(), groups_uuid, self.read_only, self.hide_passwords) CollectionGroup::new(self.id.clone(), groups_uuid, self.read_only, self.hide_passwords)
} }
} }
@ -2438,7 +2438,7 @@ impl UserSelection {
#[post("/organizations/<org_id>/groups/<group_id>", data = "<data>")] #[post("/organizations/<org_id>/groups/<group_id>", data = "<data>")]
async fn post_group( async fn post_group(
org_id: OrganizationId, org_id: OrganizationId,
group_id: &str, group_id: GroupId,
data: Json<GroupRequest>, data: Json<GroupRequest>,
headers: AdminHeaders, headers: AdminHeaders,
conn: DbConn, conn: DbConn,
@ -2477,7 +2477,7 @@ async fn post_groups(
#[put("/organizations/<org_id>/groups/<group_id>", data = "<data>")] #[put("/organizations/<org_id>/groups/<group_id>", data = "<data>")]
async fn put_group( async fn put_group(
org_id: OrganizationId, org_id: OrganizationId,
group_id: &str, group_id: GroupId,
data: Json<GroupRequest>, data: Json<GroupRequest>,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
@ -2486,15 +2486,15 @@ async fn put_group(
err!("Group support is disabled"); err!("Group support is disabled");
} }
let Some(group) = Group::find_by_uuid_and_org(group_id, &org_id, &mut conn).await else { let Some(group) = Group::find_by_uuid_and_org(&group_id, &org_id, &mut conn).await else {
err!("Group not found", "Group uuid is invalid or does not belong to the organization") err!("Group not found", "Group uuid is invalid or does not belong to the organization")
}; };
let group_request = data.into_inner(); let group_request = data.into_inner();
let updated_group = group_request.update_group(group); let updated_group = group_request.update_group(group);
CollectionGroup::delete_all_by_group(group_id, &mut conn).await?; CollectionGroup::delete_all_by_group(&group_id, &mut conn).await?;
GroupUser::delete_all_by_group(group_id, &mut conn).await?; GroupUser::delete_all_by_group(&group_id, &mut conn).await?;
log_event( log_event(
EventType::GroupUpdated as i32, EventType::GroupUpdated as i32,
@ -2553,7 +2553,7 @@ async fn add_update_group(
#[get("/organizations/<org_id>/groups/<group_id>/details")] #[get("/organizations/<org_id>/groups/<group_id>/details")]
async fn get_group_details( async fn get_group_details(
org_id: OrganizationId, org_id: OrganizationId,
group_id: &str, group_id: GroupId,
_headers: AdminHeaders, _headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> JsonResult { ) -> JsonResult {
@ -2561,7 +2561,7 @@ async fn get_group_details(
err!("Group support is disabled"); err!("Group support is disabled");
} }
let Some(group) = Group::find_by_uuid_and_org(group_id, &org_id, &mut conn).await else { let Some(group) = Group::find_by_uuid_and_org(&group_id, &org_id, &mut conn).await else {
err!("Group not found", "Group uuid is invalid or does not belong to the organization") err!("Group not found", "Group uuid is invalid or does not belong to the organization")
}; };
@ -2571,21 +2571,26 @@ async fn get_group_details(
#[post("/organizations/<org_id>/groups/<group_id>/delete")] #[post("/organizations/<org_id>/groups/<group_id>/delete")]
async fn post_delete_group( async fn post_delete_group(
org_id: OrganizationId, org_id: OrganizationId,
group_id: &str, group_id: GroupId,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> EmptyResult { ) -> EmptyResult {
_delete_group(&org_id, group_id, &headers, &mut conn).await _delete_group(&org_id, &group_id, &headers, &mut conn).await
} }
#[delete("/organizations/<org_id>/groups/<group_id>")] #[delete("/organizations/<org_id>/groups/<group_id>")]
async fn delete_group(org_id: OrganizationId, group_id: &str, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { async fn delete_group(
_delete_group(&org_id, group_id, &headers, &mut conn).await org_id: OrganizationId,
group_id: GroupId,
headers: AdminHeaders,
mut conn: DbConn,
) -> EmptyResult {
_delete_group(&org_id, &group_id, &headers, &mut conn).await
} }
async fn _delete_group( async fn _delete_group(
org_id: &OrganizationId, org_id: &OrganizationId,
group_id: &str, group_id: &GroupId,
headers: &AdminHeaders, headers: &AdminHeaders,
conn: &mut DbConn, conn: &mut DbConn,
) -> EmptyResult { ) -> EmptyResult {
@ -2614,7 +2619,7 @@ async fn _delete_group(
#[delete("/organizations/<org_id>/groups", data = "<data>")] #[delete("/organizations/<org_id>/groups", data = "<data>")]
async fn bulk_delete_groups( async fn bulk_delete_groups(
org_id: OrganizationId, org_id: OrganizationId,
data: Json<OrgBulkIds>, data: Json<BulkGroupIds>,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> EmptyResult { ) -> EmptyResult {
@ -2622,7 +2627,7 @@ async fn bulk_delete_groups(
err!("Group support is disabled"); err!("Group support is disabled");
} }
let data: OrgBulkIds = data.into_inner(); let data: BulkGroupIds = data.into_inner();
for group_id in data.ids { for group_id in data.ids {
_delete_group(&org_id, &group_id, &headers, &mut conn).await? _delete_group(&org_id, &group_id, &headers, &mut conn).await?
@ -2631,12 +2636,12 @@ async fn bulk_delete_groups(
} }
#[get("/organizations/<org_id>/groups/<group_id>")] #[get("/organizations/<org_id>/groups/<group_id>")]
async fn get_group(org_id: OrganizationId, group_id: &str, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { async fn get_group(org_id: OrganizationId, group_id: GroupId, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult {
if !CONFIG.org_groups_enabled() { if !CONFIG.org_groups_enabled() {
err!("Group support is disabled"); err!("Group support is disabled");
} }
let Some(group) = Group::find_by_uuid_and_org(group_id, &org_id, &mut conn).await else { let Some(group) = Group::find_by_uuid_and_org(&group_id, &org_id, &mut conn).await else {
err!("Group not found", "Group uuid is invalid or does not belong to the organization") err!("Group not found", "Group uuid is invalid or does not belong to the organization")
}; };
@ -2646,7 +2651,7 @@ async fn get_group(org_id: OrganizationId, group_id: &str, _headers: AdminHeader
#[get("/organizations/<org_id>/groups/<group_id>/users")] #[get("/organizations/<org_id>/groups/<group_id>/users")]
async fn get_group_users( async fn get_group_users(
org_id: OrganizationId, org_id: OrganizationId,
group_id: &str, group_id: GroupId,
_headers: AdminHeaders, _headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> JsonResult { ) -> JsonResult {
@ -2654,11 +2659,11 @@ async fn get_group_users(
err!("Group support is disabled"); err!("Group support is disabled");
} }
if Group::find_by_uuid_and_org(group_id, &org_id, &mut conn).await.is_none() { if Group::find_by_uuid_and_org(&&group_id, &org_id, &mut conn).await.is_none() {
err!("Group could not be found!", "Group uuid is invalid or does not belong to the organization") err!("Group could not be found!", "Group uuid is invalid or does not belong to the organization")
}; };
let group_users: Vec<MembershipId> = GroupUser::find_by_group(group_id, &mut conn) let group_users: Vec<MembershipId> = GroupUser::find_by_group(&group_id, &mut conn)
.await .await
.iter() .iter()
.map(|entry| entry.users_organizations_uuid.clone()) .map(|entry| entry.users_organizations_uuid.clone())
@ -2670,7 +2675,7 @@ async fn get_group_users(
#[put("/organizations/<org_id>/groups/<group_id>/users", data = "<data>")] #[put("/organizations/<org_id>/groups/<group_id>/users", data = "<data>")]
async fn put_group_users( async fn put_group_users(
org_id: OrganizationId, org_id: OrganizationId,
group_id: &str, group_id: GroupId,
headers: AdminHeaders, headers: AdminHeaders,
data: Json<Vec<MembershipId>>, data: Json<Vec<MembershipId>>,
mut conn: DbConn, mut conn: DbConn,
@ -2679,15 +2684,15 @@ async fn put_group_users(
err!("Group support is disabled"); err!("Group support is disabled");
} }
if Group::find_by_uuid_and_org(group_id, &org_id, &mut conn).await.is_none() { if Group::find_by_uuid_and_org(&group_id, &org_id, &mut conn).await.is_none() {
err!("Group could not be found!", "Group uuid is invalid or does not belong to the organization") err!("Group could not be found!", "Group uuid is invalid or does not belong to the organization")
}; };
GroupUser::delete_all_by_group(group_id, &mut conn).await?; GroupUser::delete_all_by_group(&group_id, &mut conn).await?;
let assigned_members = data.into_inner(); let assigned_members = data.into_inner();
for assigned_member in assigned_members { for assigned_member in assigned_members {
let mut user_entry = GroupUser::new(String::from(group_id), assigned_member.clone()); let mut user_entry = GroupUser::new(group_id.clone(), assigned_member.clone());
user_entry.save(&mut conn).await?; user_entry.save(&mut conn).await?;
log_event( log_event(
@ -2720,7 +2725,7 @@ async fn get_user_groups(
err!("User could not be found!") err!("User could not be found!")
}; };
let user_groups: Vec<String> = let user_groups: Vec<GroupId> =
GroupUser::find_by_member(&member_id, &mut conn).await.iter().map(|entry| entry.groups_uuid.clone()).collect(); GroupUser::find_by_member(&member_id, &mut conn).await.iter().map(|entry| entry.groups_uuid.clone()).collect();
Ok(Json(json!(user_groups))) Ok(Json(json!(user_groups)))
@ -2729,7 +2734,7 @@ async fn get_user_groups(
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct OrganizationUserUpdateGroupsRequest { struct OrganizationUserUpdateGroupsRequest {
group_ids: Vec<String>, group_ids: Vec<GroupId>,
} }
#[post("/organizations/<org_id>/users/<member_id>/groups", data = "<data>")] #[post("/organizations/<org_id>/users/<member_id>/groups", data = "<data>")]
@ -2784,7 +2789,7 @@ async fn put_user_groups(
#[post("/organizations/<org_id>/groups/<group_id>/delete-user/<member_id>")] #[post("/organizations/<org_id>/groups/<group_id>/delete-user/<member_id>")]
async fn post_delete_group_user( async fn post_delete_group_user(
org_id: OrganizationId, org_id: OrganizationId,
group_id: &str, group_id: GroupId,
member_id: MembershipId, member_id: MembershipId,
headers: AdminHeaders, headers: AdminHeaders,
conn: DbConn, conn: DbConn,
@ -2795,7 +2800,7 @@ async fn post_delete_group_user(
#[delete("/organizations/<org_id>/groups/<group_id>/users/<member_id>")] #[delete("/organizations/<org_id>/groups/<group_id>/users/<member_id>")]
async fn delete_group_user( async fn delete_group_user(
org_id: OrganizationId, org_id: OrganizationId,
group_id: &str, group_id: GroupId,
member_id: MembershipId, member_id: MembershipId,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
@ -2808,7 +2813,7 @@ async fn delete_group_user(
err!("User could not be found or does not belong to the organization."); err!("User could not be found or does not belong to the organization.");
} }
if Group::find_by_uuid_and_org(group_id, &org_id, &mut conn).await.is_none() { if Group::find_by_uuid_and_org(&group_id, &org_id, &mut conn).await.is_none() {
err!("Group could not be found or does not belong to the organization."); err!("Group could not be found or does not belong to the organization.");
} }
@ -2823,7 +2828,7 @@ async fn delete_group_user(
) )
.await; .await;
GroupUser::delete_by_group_and_member(group_id, &member_id, &mut conn).await GroupUser::delete_by_group_and_member(&group_id, &member_id, &mut conn).await
} }
#[derive(Deserialize)] #[derive(Deserialize)]

Datei anzeigen

@ -3,14 +3,20 @@ use crate::api::EmptyResult;
use crate::db::DbConn; use crate::db::DbConn;
use crate::error::MapResult; use crate::error::MapResult;
use chrono::{NaiveDateTime, Utc}; use chrono::{NaiveDateTime, Utc};
use rocket::request::FromParam;
use serde_json::Value; use serde_json::Value;
use std::{
borrow::Borrow,
fmt::{Display, Formatter},
ops::Deref,
};
db_object! { db_object! {
#[derive(Identifiable, Queryable, Insertable, AsChangeset)] #[derive(Identifiable, Queryable, Insertable, AsChangeset)]
#[diesel(table_name = groups)] #[diesel(table_name = groups)]
#[diesel(primary_key(uuid))] #[diesel(primary_key(uuid))]
pub struct Group { pub struct Group {
pub uuid: String, pub uuid: GroupId,
pub organizations_uuid: OrganizationId, pub organizations_uuid: OrganizationId,
pub name: String, pub name: String,
pub access_all: bool, pub access_all: bool,
@ -24,7 +30,7 @@ db_object! {
#[diesel(primary_key(collections_uuid, groups_uuid))] #[diesel(primary_key(collections_uuid, groups_uuid))]
pub struct CollectionGroup { pub struct CollectionGroup {
pub collections_uuid: CollectionId, pub collections_uuid: CollectionId,
pub groups_uuid: String, pub groups_uuid: GroupId,
pub read_only: bool, pub read_only: bool,
pub hide_passwords: bool, pub hide_passwords: bool,
} }
@ -33,7 +39,7 @@ db_object! {
#[diesel(table_name = groups_users)] #[diesel(table_name = groups_users)]
#[diesel(primary_key(groups_uuid, users_organizations_uuid))] #[diesel(primary_key(groups_uuid, users_organizations_uuid))]
pub struct GroupUser { pub struct GroupUser {
pub groups_uuid: String, pub groups_uuid: GroupId,
pub users_organizations_uuid: MembershipId pub users_organizations_uuid: MembershipId
} }
} }
@ -49,7 +55,7 @@ impl Group {
let now = Utc::now().naive_utc(); let now = Utc::now().naive_utc();
let mut new_model = Self { let mut new_model = Self {
uuid: crate::util::get_uuid(), uuid: GroupId(crate::util::get_uuid()),
organizations_uuid, organizations_uuid,
name, name,
access_all, access_all,
@ -113,7 +119,7 @@ impl Group {
} }
impl CollectionGroup { impl CollectionGroup {
pub fn new(collections_uuid: CollectionId, groups_uuid: String, read_only: bool, hide_passwords: bool) -> Self { pub fn new(collections_uuid: CollectionId, groups_uuid: GroupId, read_only: bool, hide_passwords: bool) -> Self {
Self { Self {
collections_uuid, collections_uuid,
groups_uuid, groups_uuid,
@ -124,7 +130,7 @@ impl CollectionGroup {
} }
impl GroupUser { impl GroupUser {
pub fn new(groups_uuid: String, users_organizations_uuid: MembershipId) -> Self { pub fn new(groups_uuid: GroupId, users_organizations_uuid: MembershipId) -> Self {
Self { Self {
groups_uuid, groups_uuid,
users_organizations_uuid, users_organizations_uuid,
@ -196,7 +202,7 @@ impl Group {
}} }}
} }
pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &OrganizationId, conn: &mut DbConn) -> Option<Self> { pub async fn find_by_uuid_and_org(uuid: &GroupId, org_uuid: &OrganizationId, conn: &mut DbConn) -> Option<Self> {
db_run! { conn: { db_run! { conn: {
groups::table groups::table
.filter(groups::uuid.eq(uuid)) .filter(groups::uuid.eq(uuid))
@ -269,13 +275,13 @@ impl Group {
}} }}
} }
pub async fn update_revision(uuid: &str, conn: &mut DbConn) { pub async fn update_revision(uuid: &GroupId, conn: &mut DbConn) {
if let Err(e) = Self::_update_revision(uuid, &Utc::now().naive_utc(), conn).await { if let Err(e) = Self::_update_revision(uuid, &Utc::now().naive_utc(), conn).await {
warn!("Failed to update revision for {}: {:#?}", uuid, e); warn!("Failed to update revision for {}: {:#?}", uuid, e);
} }
} }
async fn _update_revision(uuid: &str, date: &NaiveDateTime, conn: &mut DbConn) -> EmptyResult { async fn _update_revision(uuid: &GroupId, date: &NaiveDateTime, conn: &mut DbConn) -> EmptyResult {
db_run! {conn: { db_run! {conn: {
crate::util::retry(|| { crate::util::retry(|| {
diesel::update(groups::table.filter(groups::uuid.eq(uuid))) diesel::update(groups::table.filter(groups::uuid.eq(uuid)))
@ -343,7 +349,7 @@ impl CollectionGroup {
} }
} }
pub async fn find_by_group(group_uuid: &str, conn: &mut DbConn) -> Vec<Self> { pub async fn find_by_group(group_uuid: &GroupId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: { db_run! { conn: {
collections_groups::table collections_groups::table
.filter(collections_groups::groups_uuid.eq(group_uuid)) .filter(collections_groups::groups_uuid.eq(group_uuid))
@ -396,7 +402,7 @@ impl CollectionGroup {
}} }}
} }
pub async fn delete_all_by_group(group_uuid: &str, conn: &mut DbConn) -> EmptyResult { pub async fn delete_all_by_group(group_uuid: &GroupId, conn: &mut DbConn) -> EmptyResult {
let group_users = GroupUser::find_by_group(group_uuid, conn).await; let group_users = GroupUser::find_by_group(group_uuid, conn).await;
for group_user in group_users { for group_user in group_users {
group_user.update_user_revision(conn).await; group_user.update_user_revision(conn).await;
@ -475,7 +481,7 @@ impl GroupUser {
} }
} }
pub async fn find_by_group(group_uuid: &str, conn: &mut DbConn) -> Vec<Self> { pub async fn find_by_group(group_uuid: &GroupId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: { db_run! { conn: {
groups_users::table groups_users::table
.filter(groups_users::groups_uuid.eq(group_uuid)) .filter(groups_users::groups_uuid.eq(group_uuid))
@ -540,7 +546,7 @@ impl GroupUser {
} }
pub async fn delete_by_group_and_member( pub async fn delete_by_group_and_member(
group_uuid: &str, group_uuid: &GroupId,
member_uuid: &MembershipId, member_uuid: &MembershipId,
conn: &mut DbConn, conn: &mut DbConn,
) -> EmptyResult { ) -> EmptyResult {
@ -558,7 +564,7 @@ impl GroupUser {
}} }}
} }
pub async fn delete_all_by_group(group_uuid: &str, conn: &mut DbConn) -> EmptyResult { pub async fn delete_all_by_group(group_uuid: &GroupId, conn: &mut DbConn) -> EmptyResult {
let group_users = GroupUser::find_by_group(group_uuid, conn).await; let group_users = GroupUser::find_by_group(group_uuid, conn).await;
for group_user in group_users { for group_user in group_users {
group_user.update_user_revision(conn).await; group_user.update_user_revision(conn).await;
@ -586,3 +592,51 @@ impl GroupUser {
}} }}
} }
} }
#[derive(DieselNewType, FromForm, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct GroupId(String);
impl AsRef<str> for GroupId {
fn as_ref(&self) -> &str {
&self.0
}
}
impl Deref for GroupId {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Borrow<str> for GroupId {
fn borrow(&self) -> &str {
&self.0
}
}
impl Display for GroupId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<String> for GroupId {
fn from(raw: String) -> Self {
Self(raw)
}
}
impl<'r> FromParam<'r> for GroupId {
type Error = ();
#[inline(always)]
fn from_param(param: &'r str) -> Result<Self, Self::Error> {
if param.chars().all(|c| matches!(c, 'a'..='z' | 'A'..='Z' |'0'..='9' | '-')) {
Ok(Self(param.to_string()))
} else {
Err(())
}
}
}

Datei anzeigen

@ -25,7 +25,7 @@ pub use self::emergency_access::{EmergencyAccess, EmergencyAccessStatus, Emergen
pub use self::event::{Event, EventType}; pub use self::event::{Event, EventType};
pub use self::favorite::Favorite; pub use self::favorite::Favorite;
pub use self::folder::{Folder, FolderCipher}; pub use self::folder::{Folder, FolderCipher};
pub use self::group::{CollectionGroup, Group, GroupUser}; pub use self::group::{CollectionGroup, Group, GroupId, GroupUser};
pub use self::org_policy::{OrgPolicy, OrgPolicyErr, OrgPolicyType}; pub use self::org_policy::{OrgPolicy, OrgPolicyErr, OrgPolicyType};
pub use self::organization::{ pub use self::organization::{
Membership, MembershipId, MembershipStatus, MembershipType, Organization, OrganizationApiKey, OrganizationId, Membership, MembershipId, MembershipStatus, MembershipType, Organization, OrganizationApiKey, OrganizationId,

Datei anzeigen

@ -11,8 +11,8 @@ use std::{
}; };
use super::{ use super::{
Collection, CollectionGroup, CollectionId, CollectionUser, Group, GroupUser, OrgPolicy, OrgPolicyType, TwoFactor, Collection, CollectionGroup, CollectionId, CollectionUser, Group, GroupId, GroupUser, OrgPolicy, OrgPolicyType,
User, UserId, TwoFactor, User, UserId,
}; };
use crate::CONFIG; use crate::CONFIG;
@ -460,7 +460,7 @@ impl Membership {
let twofactor_enabled = !TwoFactor::find_by_user(&user.uuid, conn).await.is_empty(); let twofactor_enabled = !TwoFactor::find_by_user(&user.uuid, conn).await.is_empty();
let groups: Vec<String> = if include_groups && CONFIG.org_groups_enabled() { let groups: Vec<GroupId> = if include_groups && CONFIG.org_groups_enabled() {
GroupUser::find_by_member(&self.uuid, conn).await.iter().map(|gu| gu.groups_uuid.clone()).collect() GroupUser::find_by_member(&self.uuid, conn).await.iter().map(|gu| gu.groups_uuid.clone()).collect()
} else { } else {
// The Bitwarden clients seem to call this API regardless of whether groups are enabled, // The Bitwarden clients seem to call this API regardless of whether groups are enabled,