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

introduce newtype membership_id

Dieser Commit ist enthalten in:
Stefan Melmuk 2024-12-21 09:09:09 +01:00
Ursprung 40a788084b
Commit 64ae0aa386
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 817020C608FE9C09
9 geänderte Dateien mit 204 neuen und 128 gelöschten Zeilen

Datei anzeigen

@ -79,7 +79,7 @@ pub struct RegisterData {
name: Option<String>, name: Option<String>,
token: Option<String>, token: Option<String>,
#[allow(dead_code)] #[allow(dead_code)]
organization_user_id: Option<String>, organization_user_id: Option<MembershipId>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -106,7 +106,7 @@ fn enforce_password_hint_setting(password_hint: &Option<String>) -> EmptyResult
} }
Ok(()) Ok(())
} }
async fn is_email_2fa_required(member_uuid: Option<String>, conn: &mut DbConn) -> bool { async fn is_email_2fa_required(member_uuid: Option<MembershipId>, conn: &mut DbConn) -> bool {
if !CONFIG._enable_email_2fa() { if !CONFIG._enable_email_2fa() {
return false; return false;
} }

Datei anzeigen

@ -8,7 +8,7 @@ use crate::{
api::{EmptyResult, JsonResult}, api::{EmptyResult, JsonResult},
auth::{AdminHeaders, Headers}, auth::{AdminHeaders, Headers},
db::{ db::{
models::{Cipher, Event, Membership, OrganizationId}, models::{Cipher, Event, Membership, MembershipId, OrganizationId},
DbConn, DbPool, DbConn, DbPool,
}, },
util::parse_date, util::parse_date,
@ -93,7 +93,7 @@ async fn get_cipher_events(cipher_id: &str, data: EventRange, headers: Headers,
#[get("/organizations/<org_id>/users/<member_id>/events?<data..>")] #[get("/organizations/<org_id>/users/<member_id>/events?<data..>")]
async fn get_user_events( async fn get_user_events(
org_id: &str, org_id: &str,
member_id: &str, member_id: MembershipId,
data: EventRange, data: EventRange,
_headers: AdminHeaders, _headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
@ -110,7 +110,7 @@ async fn get_user_events(
parse_date(&data.end) parse_date(&data.end)
}; };
Event::find_by_org_and_member(org_id, member_id, &start_date, &end_date, &mut conn) Event::find_by_org_and_member(org_id, &member_id, &start_date, &end_date, &mut conn)
.await .await
.iter() .iter()
.map(|e| e.to_json()) .map(|e| e.to_json())

Datei anzeigen

@ -125,7 +125,7 @@ struct OrganizationUpdateData {
struct NewCollectionData { struct NewCollectionData {
name: String, name: String,
groups: Vec<NewCollectionObjectData>, groups: Vec<NewCollectionObjectData>,
users: Vec<NewCollectionObjectData>, users: Vec<NewCollectionMemberData>,
id: Option<String>, id: Option<String>,
external_id: Option<String>, external_id: Option<String>,
} }
@ -138,6 +138,14 @@ struct NewCollectionObjectData {
read_only: bool, read_only: bool,
} }
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct NewCollectionMemberData {
hide_passwords: bool,
id: MembershipId,
read_only: bool,
}
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct OrgKeyData { struct OrgKeyData {
@ -151,6 +159,12 @@ struct OrgBulkIds {
ids: Vec<String>, ids: Vec<String>,
} }
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct BulkMembershipIds {
ids: Vec<MembershipId>,
}
#[post("/organizations", data = "<data>")] #[post("/organizations", data = "<data>")]
async fn create_organization(headers: Headers, data: Json<OrgData>, mut conn: DbConn) -> JsonResult { async fn create_organization(headers: Headers, data: Json<OrgData>, mut conn: DbConn) -> JsonResult {
if !CONFIG.is_org_creation_allowed(&headers.user.email) { if !CONFIG.is_org_creation_allowed(&headers.user.email) {
@ -510,7 +524,7 @@ async fn post_organization_collection_update(
async fn delete_organization_collection_user( async fn delete_organization_collection_user(
org_id: OrganizationId, org_id: OrganizationId,
col_id: &str, col_id: &str,
member_id: &str, member_id: MembershipId,
_headers: AdminHeaders, _headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> EmptyResult { ) -> EmptyResult {
@ -518,7 +532,7 @@ async fn delete_organization_collection_user(
err!("Collection not found", "Collection does not exist or does not belong to this organization") err!("Collection not found", "Collection does not exist or does not belong to this organization")
}; };
match Membership::find_by_uuid_and_org(member_id, &org_id, &mut conn).await { match Membership::find_by_uuid_and_org(&member_id, &org_id, &mut conn).await {
None => err!("User not found in organization"), None => err!("User not found in organization"),
Some(member) => { Some(member) => {
match CollectionUser::find_by_collection_and_user(&collection.uuid, &member.user_uuid, &mut conn).await { match CollectionUser::find_by_collection_and_user(&collection.uuid, &member.user_uuid, &mut conn).await {
@ -533,7 +547,7 @@ async fn delete_organization_collection_user(
async fn post_organization_collection_delete_user( async fn post_organization_collection_delete_user(
org_id: OrganizationId, org_id: OrganizationId,
col_id: &str, col_id: &str,
member_id: &str, member_id: MembershipId,
headers: AdminHeaders, headers: AdminHeaders,
conn: DbConn, conn: DbConn,
) -> EmptyResult { ) -> EmptyResult {
@ -827,7 +841,7 @@ async fn post_org_keys(
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct CollectionData { struct CollectionData {
id: String, id: MembershipId,
read_only: bool, read_only: bool,
hide_passwords: bool, hide_passwords: bool,
} }
@ -895,11 +909,11 @@ async fn send_invite(
} }
}; };
let mut new_user = Membership::new(user.uuid.clone(), org_id.clone()); let mut new_member = Membership::new(user.uuid.clone(), org_id.clone());
let access_all = data.access_all; let access_all = data.access_all;
new_user.access_all = access_all; new_member.access_all = access_all;
new_user.atype = new_type; new_member.atype = new_type;
new_user.status = member_status; new_member.status = member_status;
// If no accessAll, add the collections received // If no accessAll, add the collections received
if !access_all { if !access_all {
@ -920,16 +934,16 @@ async fn send_invite(
} }
} }
new_user.save(&mut conn).await?; new_member.save(&mut conn).await?;
for group in data.groups.iter() { for group in data.groups.iter() {
let mut group_entry = GroupUser::new(String::from(group), user.uuid.clone()); let mut group_entry = GroupUser::new(String::from(group), new_member.uuid.clone());
group_entry.save(&mut conn).await?; group_entry.save(&mut conn).await?;
} }
log_event( log_event(
EventType::OrganizationUserInvited as i32, EventType::OrganizationUserInvited as i32,
&new_user.uuid, &new_member.uuid,
&org_id, &org_id,
&headers.user.uuid, &headers.user.uuid,
headers.device.atype, headers.device.atype,
@ -947,7 +961,7 @@ async fn send_invite(
mail::send_invite( mail::send_invite(
&user, &user,
Some(org_id.clone()), Some(org_id.clone()),
Some(new_user.uuid), Some(new_member.uuid),
&org_name, &org_name,
Some(headers.user.email.clone()), Some(headers.user.email.clone()),
) )
@ -961,11 +975,11 @@ async fn send_invite(
#[post("/organizations/<org_id>/users/reinvite", data = "<data>")] #[post("/organizations/<org_id>/users/reinvite", data = "<data>")]
async fn bulk_reinvite_user( async fn bulk_reinvite_user(
org_id: OrganizationId, org_id: OrganizationId,
data: Json<OrgBulkIds>, data: Json<BulkMembershipIds>,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> Json<Value> { ) -> Json<Value> {
let data: OrgBulkIds = data.into_inner(); let data: BulkMembershipIds = data.into_inner();
let mut bulk_response = Vec::new(); let mut bulk_response = Vec::new();
for member_id in data.ids { for member_id in data.ids {
@ -990,18 +1004,23 @@ async fn bulk_reinvite_user(
})) }))
} }
#[post("/organizations/<org_id>/users/<member>/reinvite")] #[post("/organizations/<org_id>/users/<member_id>/reinvite")]
async fn reinvite_user(org_id: OrganizationId, member: &str, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { async fn reinvite_user(
_reinvite_user(&org_id, member, &headers.user.email, &mut conn).await org_id: OrganizationId,
member_id: MembershipId,
headers: AdminHeaders,
mut conn: DbConn,
) -> EmptyResult {
_reinvite_user(&org_id, &member_id, &headers.user.email, &mut conn).await
} }
async fn _reinvite_user( async fn _reinvite_user(
org_id: &OrganizationId, org_id: &OrganizationId,
member: &str, member_id: &MembershipId,
invited_by_email: &str, invited_by_email: &str,
conn: &mut DbConn, conn: &mut DbConn,
) -> EmptyResult { ) -> EmptyResult {
let Some(member) = Membership::find_by_uuid_and_org(member, org_id, conn).await else { let Some(member) = Membership::find_by_uuid_and_org(member_id, org_id, conn).await else {
err!("The user hasn't been invited to the organization.") err!("The user hasn't been invited to the organization.")
}; };
@ -1054,7 +1073,7 @@ struct AcceptData {
#[post("/organizations/<org_id>/users/<member_id>/accept", data = "<data>")] #[post("/organizations/<org_id>/users/<member_id>/accept", data = "<data>")]
async fn accept_invite( async fn accept_invite(
org_id: OrganizationId, org_id: OrganizationId,
member_id: &str, member_id: MembershipId,
data: Json<AcceptData>, data: Json<AcceptData>,
mut conn: DbConn, mut conn: DbConn,
) -> EmptyResult { ) -> EmptyResult {
@ -1064,7 +1083,7 @@ async fn accept_invite(
// If a claim does not have a member_id or it does not match the one in from the URI, something is wrong. // If a claim does not have a member_id or it does not match the one in from the URI, something is wrong.
match &claims.member_id { match &claims.member_id {
Some(ou_id) if ou_id.eq(member_id) => {} Some(ou_id) if ou_id.eq(&member_id) => {}
_ => err!("Error accepting the invitation", "Claim does not match the member_id"), _ => err!("Error accepting the invitation", "Claim does not match the member_id"),
} }
@ -1139,7 +1158,7 @@ async fn accept_invite(
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct ConfirmData { struct ConfirmData {
id: Option<String>, id: Option<MembershipId>,
key: Option<String>, key: Option<String>,
} }
@ -1163,7 +1182,7 @@ async fn bulk_confirm_invite(
match data.keys { match data.keys {
Some(keys) => { Some(keys) => {
for invite in keys { for invite in keys {
let member_id = invite.id.unwrap_or_default(); let member_id = invite.id.unwrap();
let user_key = invite.key.unwrap_or_default(); let user_key = invite.key.unwrap_or_default();
let err_msg = match _confirm_invite(&org_id, &member_id, &user_key, &headers, &mut conn, &nt).await { let err_msg = match _confirm_invite(&org_id, &member_id, &user_key, &headers, &mut conn, &nt).await {
Ok(_) => String::new(), Ok(_) => String::new(),
@ -1192,7 +1211,7 @@ async fn bulk_confirm_invite(
#[post("/organizations/<org_id>/users/<member_id>/confirm", data = "<data>")] #[post("/organizations/<org_id>/users/<member_id>/confirm", data = "<data>")]
async fn confirm_invite( async fn confirm_invite(
org_id: OrganizationId, org_id: OrganizationId,
member_id: &str, member_id: MembershipId,
data: Json<ConfirmData>, data: Json<ConfirmData>,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
@ -1200,12 +1219,12 @@ async fn confirm_invite(
) -> EmptyResult { ) -> EmptyResult {
let data = data.into_inner(); let data = data.into_inner();
let user_key = data.key.unwrap_or_default(); let user_key = data.key.unwrap_or_default();
_confirm_invite(&org_id, member_id, &user_key, &headers, &mut conn, &nt).await _confirm_invite(&org_id, &member_id, &user_key, &headers, &mut conn, &nt).await
} }
async fn _confirm_invite( async fn _confirm_invite(
org_id: &OrganizationId, org_id: &OrganizationId,
member_id: &str, member_id: &MembershipId,
key: &str, key: &str,
headers: &AdminHeaders, headers: &AdminHeaders,
conn: &mut DbConn, conn: &mut DbConn,
@ -1283,12 +1302,12 @@ async fn _confirm_invite(
#[get("/organizations/<org_id>/users/<member_id>?<data..>")] #[get("/organizations/<org_id>/users/<member_id>?<data..>")]
async fn get_user( async fn get_user(
org_id: OrganizationId, org_id: OrganizationId,
member_id: &str, member_id: MembershipId,
data: GetOrgUserData, data: GetOrgUserData,
_headers: AdminHeaders, _headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> JsonResult { ) -> JsonResult {
let Some(user) = Membership::find_by_uuid_and_org(member_id, &org_id, &mut conn).await else { let Some(user) = Membership::find_by_uuid_and_org(&member_id, &org_id, &mut conn).await else {
err!("The specified user isn't a member of the organization") err!("The specified user isn't a member of the organization")
}; };
@ -1313,7 +1332,7 @@ struct EditUserData {
#[put("/organizations/<org_id>/users/<member_id>", data = "<data>", rank = 1)] #[put("/organizations/<org_id>/users/<member_id>", data = "<data>", rank = 1)]
async fn put_membership( async fn put_membership(
org_id: OrganizationId, org_id: OrganizationId,
member_id: &str, member_id: MembershipId,
data: Json<EditUserData>, data: Json<EditUserData>,
headers: AdminHeaders, headers: AdminHeaders,
conn: DbConn, conn: DbConn,
@ -1324,7 +1343,7 @@ async fn put_membership(
#[post("/organizations/<org_id>/users/<member_id>", data = "<data>", rank = 1)] #[post("/organizations/<org_id>/users/<member_id>", data = "<data>", rank = 1)]
async fn edit_user( async fn edit_user(
org_id: OrganizationId, org_id: OrganizationId,
member_id: &str, member_id: MembershipId,
data: Json<EditUserData>, data: Json<EditUserData>,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
@ -1335,7 +1354,7 @@ async fn edit_user(
err!("Invalid type") err!("Invalid type")
}; };
let Some(mut member_to_edit) = Membership::find_by_uuid_and_org(member_id, &org_id, &mut conn).await else { let Some(mut member_to_edit) = Membership::find_by_uuid_and_org(&member_id, &org_id, &mut conn).await else {
err!("The specified user isn't member of the organization") err!("The specified user isn't member of the organization")
}; };
@ -1429,12 +1448,12 @@ async fn edit_user(
#[delete("/organizations/<org_id>/users", data = "<data>")] #[delete("/organizations/<org_id>/users", data = "<data>")]
async fn bulk_delete_user( async fn bulk_delete_user(
org_id: OrganizationId, org_id: OrganizationId,
data: Json<OrgBulkIds>, data: Json<BulkMembershipIds>,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> Json<Value> { ) -> Json<Value> {
let data: OrgBulkIds = data.into_inner(); let data: BulkMembershipIds = data.into_inner();
let mut bulk_response = Vec::new(); let mut bulk_response = Vec::new();
for member_id in data.ids { for member_id in data.ids {
@ -1462,28 +1481,28 @@ async fn bulk_delete_user(
#[delete("/organizations/<org_id>/users/<member_id>")] #[delete("/organizations/<org_id>/users/<member_id>")]
async fn delete_user( async fn delete_user(
org_id: OrganizationId, org_id: OrganizationId,
member_id: &str, member_id: MembershipId,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
_delete_user(&org_id, member_id, &headers, &mut conn, &nt).await _delete_user(&org_id, &member_id, &headers, &mut conn, &nt).await
} }
#[post("/organizations/<org_id>/users/<member_id>/delete")] #[post("/organizations/<org_id>/users/<member_id>/delete")]
async fn post_delete_user( async fn post_delete_user(
org_id: OrganizationId, org_id: OrganizationId,
member_id: &str, member_id: MembershipId,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> EmptyResult { ) -> EmptyResult {
_delete_user(&org_id, member_id, &headers, &mut conn, &nt).await _delete_user(&org_id, &member_id, &headers, &mut conn, &nt).await
} }
async fn _delete_user( async fn _delete_user(
org_id: &OrganizationId, org_id: &OrganizationId,
member_id: &str, member_id: &MembershipId,
headers: &AdminHeaders, headers: &AdminHeaders,
conn: &mut DbConn, conn: &mut DbConn,
nt: &Notify<'_>, nt: &Notify<'_>,
@ -1525,11 +1544,11 @@ async fn _delete_user(
#[post("/organizations/<org_id>/users/public-keys", data = "<data>")] #[post("/organizations/<org_id>/users/public-keys", data = "<data>")]
async fn bulk_public_keys( async fn bulk_public_keys(
org_id: OrganizationId, org_id: OrganizationId,
data: Json<OrgBulkIds>, data: Json<BulkMembershipIds>,
_headers: AdminHeaders, _headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> Json<Value> { ) -> Json<Value> {
let data: OrgBulkIds = data.into_inner(); let data: BulkMembershipIds = data.into_inner();
let mut bulk_response = Vec::new(); let mut bulk_response = Vec::new();
// Check all received Membership UUID's and find the matching User to retrieve the public-key. // Check all received Membership UUID's and find the matching User to retrieve the public-key.
@ -2074,18 +2093,24 @@ async fn import(org_id: OrganizationId, data: Json<OrgImportData>, headers: Head
#[put("/organizations/<org_id>/users/<member_id>/deactivate")] #[put("/organizations/<org_id>/users/<member_id>/deactivate")]
async fn deactivate_membership( async fn deactivate_membership(
org_id: OrganizationId, org_id: OrganizationId,
member_id: &str, member_id: MembershipId,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> EmptyResult { ) -> EmptyResult {
_revoke_membership(&org_id, member_id, &headers, &mut conn).await _revoke_membership(&org_id, &member_id, &headers, &mut conn).await
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct BulkRevokeMembershipIds {
ids: Option<Vec<MembershipId>>,
} }
// Pre web-vault v2022.9.x endpoint // Pre web-vault v2022.9.x endpoint
#[put("/organizations/<org_id>/users/deactivate", data = "<data>")] #[put("/organizations/<org_id>/users/deactivate", data = "<data>")]
async fn bulk_deactivate_membership( async fn bulk_deactivate_membership(
org_id: OrganizationId, org_id: OrganizationId,
data: Json<OrgBulkRevokeData>, data: Json<BulkRevokeMembershipIds>,
headers: AdminHeaders, headers: AdminHeaders,
conn: DbConn, conn: DbConn,
) -> Json<Value> { ) -> Json<Value> {
@ -2095,23 +2120,17 @@ async fn bulk_deactivate_membership(
#[put("/organizations/<org_id>/users/<member_id>/revoke")] #[put("/organizations/<org_id>/users/<member_id>/revoke")]
async fn revoke_membership( async fn revoke_membership(
org_id: OrganizationId, org_id: OrganizationId,
member_id: &str, member_id: MembershipId,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> EmptyResult { ) -> EmptyResult {
_revoke_membership(&org_id, member_id, &headers, &mut conn).await _revoke_membership(&org_id, &member_id, &headers, &mut conn).await
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct OrgBulkRevokeData {
ids: Option<Vec<String>>,
} }
#[put("/organizations/<org_id>/users/revoke", data = "<data>")] #[put("/organizations/<org_id>/users/revoke", data = "<data>")]
async fn bulk_revoke_membership( async fn bulk_revoke_membership(
org_id: OrganizationId, org_id: OrganizationId,
data: Json<OrgBulkRevokeData>, data: Json<BulkRevokeMembershipIds>,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> Json<Value> { ) -> Json<Value> {
@ -2147,7 +2166,7 @@ async fn bulk_revoke_membership(
async fn _revoke_membership( async fn _revoke_membership(
org_id: &OrganizationId, org_id: &OrganizationId,
member_id: &str, member_id: &MembershipId,
headers: &AdminHeaders, headers: &AdminHeaders,
conn: &mut DbConn, conn: &mut DbConn,
) -> EmptyResult { ) -> EmptyResult {
@ -2189,18 +2208,18 @@ async fn _revoke_membership(
#[put("/organizations/<org_id>/users/<member_id>/activate")] #[put("/organizations/<org_id>/users/<member_id>/activate")]
async fn activate_membership( async fn activate_membership(
org_id: OrganizationId, org_id: OrganizationId,
member_id: &str, member_id: MembershipId,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> EmptyResult { ) -> EmptyResult {
_restore_membership(&org_id, member_id, &headers, &mut conn).await _restore_membership(&org_id, &member_id, &headers, &mut conn).await
} }
// Pre web-vault v2022.9.x endpoint // Pre web-vault v2022.9.x endpoint
#[put("/organizations/<org_id>/users/activate", data = "<data>")] #[put("/organizations/<org_id>/users/activate", data = "<data>")]
async fn bulk_activate_membership( async fn bulk_activate_membership(
org_id: OrganizationId, org_id: OrganizationId,
data: Json<OrgBulkIds>, data: Json<BulkMembershipIds>,
headers: AdminHeaders, headers: AdminHeaders,
conn: DbConn, conn: DbConn,
) -> Json<Value> { ) -> Json<Value> {
@ -2210,17 +2229,17 @@ async fn bulk_activate_membership(
#[put("/organizations/<org_id>/users/<member_id>/restore")] #[put("/organizations/<org_id>/users/<member_id>/restore")]
async fn restore_membership( async fn restore_membership(
org_id: OrganizationId, org_id: OrganizationId,
member_id: &str, member_id: MembershipId,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> EmptyResult { ) -> EmptyResult {
_restore_membership(&org_id, member_id, &headers, &mut conn).await _restore_membership(&org_id, &member_id, &headers, &mut conn).await
} }
#[put("/organizations/<org_id>/users/restore", data = "<data>")] #[put("/organizations/<org_id>/users/restore", data = "<data>")]
async fn bulk_restore_membership( async fn bulk_restore_membership(
org_id: OrganizationId, org_id: OrganizationId,
data: Json<OrgBulkIds>, data: Json<BulkMembershipIds>,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> Json<Value> { ) -> Json<Value> {
@ -2251,7 +2270,7 @@ async fn bulk_restore_membership(
async fn _restore_membership( async fn _restore_membership(
org_id: &OrganizationId, org_id: &OrganizationId,
member_id: &str, member_id: &MembershipId,
headers: &AdminHeaders, headers: &AdminHeaders,
conn: &mut DbConn, conn: &mut DbConn,
) -> EmptyResult { ) -> EmptyResult {
@ -2334,7 +2353,7 @@ struct GroupRequest {
access_all: bool, access_all: bool,
external_id: Option<String>, external_id: Option<String>,
collections: Vec<SelectionReadOnly>, collections: Vec<SelectionReadOnly>,
users: Vec<String>, users: Vec<MembershipId>,
} }
impl GroupRequest { impl GroupRequest {
@ -2464,7 +2483,7 @@ async fn put_group(
async fn add_update_group( async fn add_update_group(
mut group: Group, mut group: Group,
collections: Vec<SelectionReadOnly>, collections: Vec<SelectionReadOnly>,
users: Vec<String>, members: Vec<MembershipId>,
org_id: OrganizationId, org_id: OrganizationId,
headers: &AdminHeaders, headers: &AdminHeaders,
conn: &mut DbConn, conn: &mut DbConn,
@ -2476,13 +2495,13 @@ async fn add_update_group(
collection_group.save(conn).await?; collection_group.save(conn).await?;
} }
for assigned_user_id in users { for assigned_member in members {
let mut user_entry = GroupUser::new(group.uuid.clone(), assigned_user_id.clone()); let mut user_entry = GroupUser::new(group.uuid.clone(), assigned_member.clone());
user_entry.save(conn).await?; user_entry.save(conn).await?;
log_event( log_event(
EventType::OrganizationUserUpdatedGroups as i32, EventType::OrganizationUserUpdatedGroups as i32,
&assigned_user_id, &assigned_member,
&org_id, &org_id,
&headers.user.uuid, &headers.user.uuid,
headers.device.atype, headers.device.atype,
@ -2609,7 +2628,7 @@ async fn get_group_users(
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<String> = 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())
@ -2623,7 +2642,7 @@ async fn put_group_users(
org_id: OrganizationId, org_id: OrganizationId,
group_id: &str, group_id: &str,
headers: AdminHeaders, headers: AdminHeaders,
data: Json<Vec<String>>, data: Json<Vec<MembershipId>>,
mut conn: DbConn, mut conn: DbConn,
) -> EmptyResult { ) -> EmptyResult {
if !CONFIG.org_groups_enabled() { if !CONFIG.org_groups_enabled() {
@ -2636,14 +2655,14 @@ async fn put_group_users(
GroupUser::delete_all_by_group(group_id, &mut conn).await?; GroupUser::delete_all_by_group(group_id, &mut conn).await?;
let assigned_user_ids = data.into_inner(); let assigned_members = data.into_inner();
for assigned_user_id in assigned_user_ids { for assigned_member in assigned_members {
let mut user_entry = GroupUser::new(String::from(group_id), assigned_user_id.clone()); let mut user_entry = GroupUser::new(String::from(group_id), assigned_member.clone());
user_entry.save(&mut conn).await?; user_entry.save(&mut conn).await?;
log_event( log_event(
EventType::OrganizationUserUpdatedGroups as i32, EventType::OrganizationUserUpdatedGroups as i32,
&assigned_user_id, &assigned_member,
&org_id, &org_id,
&headers.user.uuid, &headers.user.uuid,
headers.device.atype, headers.device.atype,
@ -2656,10 +2675,10 @@ async fn put_group_users(
Ok(()) Ok(())
} }
#[get("/organizations/<org_id>/users/<user_id>/groups")] #[get("/organizations/<org_id>/users/<member_id>/groups")]
async fn get_user_groups( async fn get_user_groups(
org_id: OrganizationId, org_id: OrganizationId,
user_id: &str, member_id: MembershipId,
_headers: AdminHeaders, _headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> JsonResult { ) -> JsonResult {
@ -2667,12 +2686,12 @@ async fn get_user_groups(
err!("Group support is disabled"); err!("Group support is disabled");
} }
if Membership::find_by_uuid_and_org(user_id, &org_id, &mut conn).await.is_none() { if Membership::find_by_uuid_and_org(&member_id, &org_id, &mut conn).await.is_none() {
err!("User could not be found!") err!("User could not be found!")
}; };
let user_groups: Vec<String> = let user_groups: Vec<String> =
GroupUser::find_by_user(user_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)))
} }
@ -2686,7 +2705,7 @@ struct OrganizationUserUpdateGroupsRequest {
#[post("/organizations/<org_id>/users/<member_id>/groups", data = "<data>")] #[post("/organizations/<org_id>/users/<member_id>/groups", data = "<data>")]
async fn post_user_groups( async fn post_user_groups(
org_id: OrganizationId, org_id: OrganizationId,
member_id: &str, member_id: MembershipId,
data: Json<OrganizationUserUpdateGroupsRequest>, data: Json<OrganizationUserUpdateGroupsRequest>,
headers: AdminHeaders, headers: AdminHeaders,
conn: DbConn, conn: DbConn,
@ -2697,7 +2716,7 @@ async fn post_user_groups(
#[put("/organizations/<org_id>/users/<member_id>/groups", data = "<data>")] #[put("/organizations/<org_id>/users/<member_id>/groups", data = "<data>")]
async fn put_user_groups( async fn put_user_groups(
org_id: OrganizationId, org_id: OrganizationId,
member_id: &str, member_id: MembershipId,
data: Json<OrganizationUserUpdateGroupsRequest>, data: Json<OrganizationUserUpdateGroupsRequest>,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
@ -2706,7 +2725,7 @@ async fn put_user_groups(
err!("Group support is disabled"); err!("Group support is disabled");
} }
if Membership::find_by_uuid_and_org(member_id, &org_id, &mut conn).await.is_none() { if Membership::find_by_uuid_and_org(&member_id, &org_id, &mut conn).await.is_none() {
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.");
} }
@ -2714,13 +2733,13 @@ async fn put_user_groups(
let assigned_group_ids = data.into_inner(); let assigned_group_ids = data.into_inner();
for assigned_group_id in assigned_group_ids.group_ids { for assigned_group_id in assigned_group_ids.group_ids {
let mut group_user = GroupUser::new(assigned_group_id.clone(), String::from(member_id)); let mut group_user = GroupUser::new(assigned_group_id.clone(), member_id.clone());
group_user.save(&mut conn).await?; group_user.save(&mut conn).await?;
} }
log_event( log_event(
EventType::OrganizationUserUpdatedGroups as i32, EventType::OrganizationUserUpdatedGroups as i32,
member_id, &member_id,
&org_id, &org_id,
&headers.user.uuid, &headers.user.uuid,
headers.device.atype, headers.device.atype,
@ -2736,7 +2755,7 @@ async fn put_user_groups(
async fn post_delete_group_user( async fn post_delete_group_user(
org_id: OrganizationId, org_id: OrganizationId,
group_id: &str, group_id: &str,
member_id: &str, member_id: MembershipId,
headers: AdminHeaders, headers: AdminHeaders,
conn: DbConn, conn: DbConn,
) -> EmptyResult { ) -> EmptyResult {
@ -2747,7 +2766,7 @@ async fn post_delete_group_user(
async fn delete_group_user( async fn delete_group_user(
org_id: OrganizationId, org_id: OrganizationId,
group_id: &str, group_id: &str,
member_id: &str, member_id: MembershipId,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> EmptyResult { ) -> EmptyResult {
@ -2755,7 +2774,7 @@ async fn delete_group_user(
err!("Group support is disabled"); err!("Group support is disabled");
} }
if Membership::find_by_uuid_and_org(member_id, &org_id, &mut conn).await.is_none() { if Membership::find_by_uuid_and_org(&member_id, &org_id, &mut conn).await.is_none() {
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.");
} }
@ -2765,7 +2784,7 @@ async fn delete_group_user(
log_event( log_event(
EventType::OrganizationUserUpdatedGroups as i32, EventType::OrganizationUserUpdatedGroups as i32,
member_id, &member_id,
&org_id, &org_id,
&headers.user.uuid, &headers.user.uuid,
headers.device.atype, headers.device.atype,
@ -2774,7 +2793,7 @@ async fn delete_group_user(
) )
.await; .await;
GroupUser::delete_by_group_id_and_user_id(group_id, member_id, &mut conn).await GroupUser::delete_by_group_and_member(group_id, &member_id, &mut conn).await
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -2817,7 +2836,7 @@ async fn get_organization_keys(org_id: OrganizationId, headers: Headers, conn: D
#[put("/organizations/<org_id>/users/<member_id>/reset-password", data = "<data>")] #[put("/organizations/<org_id>/users/<member_id>/reset-password", data = "<data>")]
async fn put_reset_password( async fn put_reset_password(
org_id: OrganizationId, org_id: OrganizationId,
member_id: &str, member_id: MembershipId,
headers: AdminHeaders, headers: AdminHeaders,
data: Json<OrganizationUserResetPasswordRequest>, data: Json<OrganizationUserResetPasswordRequest>,
mut conn: DbConn, mut conn: DbConn,
@ -2827,7 +2846,7 @@ async fn put_reset_password(
err!("Required organization not found") err!("Required organization not found")
}; };
let Some(member) = Membership::find_by_uuid_and_org(member_id, &org.uuid, &mut conn).await else { let Some(member) = Membership::find_by_uuid_and_org(&member_id, &org.uuid, &mut conn).await else {
err!("User to reset isn't member of required organization") err!("User to reset isn't member of required organization")
}; };
@ -2835,7 +2854,7 @@ async fn put_reset_password(
err!("User not found") err!("User not found")
}; };
check_reset_password_applicable_and_permissions(&org_id, member_id, &headers, &mut conn).await?; check_reset_password_applicable_and_permissions(&org_id, &member_id, &headers, &mut conn).await?;
if member.reset_password_key.is_none() { if member.reset_password_key.is_none() {
err!("Password reset not or not correctly enrolled"); err!("Password reset not or not correctly enrolled");
@ -2860,7 +2879,7 @@ async fn put_reset_password(
log_event( log_event(
EventType::OrganizationUserAdminResetPassword as i32, EventType::OrganizationUserAdminResetPassword as i32,
member_id, &member_id,
&org_id, &org_id,
&headers.user.uuid, &headers.user.uuid,
headers.device.atype, headers.device.atype,
@ -2875,7 +2894,7 @@ async fn put_reset_password(
#[get("/organizations/<org_id>/users/<member_id>/reset-password-details")] #[get("/organizations/<org_id>/users/<member_id>/reset-password-details")]
async fn get_reset_password_details( async fn get_reset_password_details(
org_id: OrganizationId, org_id: OrganizationId,
member_id: &str, member_id: MembershipId,
headers: AdminHeaders, headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
) -> JsonResult { ) -> JsonResult {
@ -2883,7 +2902,7 @@ async fn get_reset_password_details(
err!("Required organization not found") err!("Required organization not found")
}; };
let Some(member) = Membership::find_by_uuid_and_org(member_id, &org_id, &mut conn).await else { let Some(member) = Membership::find_by_uuid_and_org(&member_id, &org_id, &mut conn).await else {
err!("User to reset isn't member of required organization") err!("User to reset isn't member of required organization")
}; };
@ -2891,7 +2910,7 @@ async fn get_reset_password_details(
err!("User not found") err!("User not found")
}; };
check_reset_password_applicable_and_permissions(&org_id, member_id, &headers, &mut conn).await?; check_reset_password_applicable_and_permissions(&org_id, &member_id, &headers, &mut conn).await?;
// https://github.com/bitwarden/server/blob/3b50ccb9f804efaacdc46bed5b60e5b28eddefcf/src/Api/Models/Response/Organizations/OrganizationUserResponseModel.cs#L111 // https://github.com/bitwarden/server/blob/3b50ccb9f804efaacdc46bed5b60e5b28eddefcf/src/Api/Models/Response/Organizations/OrganizationUserResponseModel.cs#L111
Ok(Json(json!({ Ok(Json(json!({
@ -2908,7 +2927,7 @@ async fn get_reset_password_details(
async fn check_reset_password_applicable_and_permissions( async fn check_reset_password_applicable_and_permissions(
org_id: &OrganizationId, org_id: &OrganizationId,
member_id: &str, member_id: &MembershipId,
headers: &AdminHeaders, headers: &AdminHeaders,
conn: &mut DbConn, conn: &mut DbConn,
) -> EmptyResult { ) -> EmptyResult {
@ -2945,7 +2964,7 @@ async fn check_reset_password_applicable(org_id: &OrganizationId, conn: &mut DbC
#[put("/organizations/<org_id>/users/<member_id>/reset-password-enrollment", data = "<data>")] #[put("/organizations/<org_id>/users/<member_id>/reset-password-enrollment", data = "<data>")]
async fn put_reset_password_enrollment( async fn put_reset_password_enrollment(
org_id: OrganizationId, org_id: OrganizationId,
member_id: &str, member_id: MembershipId,
headers: Headers, headers: Headers,
data: Json<OrganizationUserResetPasswordEnrollmentRequest>, data: Json<OrganizationUserResetPasswordEnrollmentRequest>,
mut conn: DbConn, mut conn: DbConn,
@ -2982,7 +3001,7 @@ async fn put_reset_password_enrollment(
EventType::OrganizationUserResetPasswordWithdraw as i32 EventType::OrganizationUserResetPasswordWithdraw as i32
}; };
log_event(log_id, member_id, &org_id, &headers.user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await; log_event(log_id, &member_id, &org_id, &headers.user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await;
Ok(()) Ok(())
} }

Datei anzeigen

@ -14,7 +14,7 @@ use std::{
net::IpAddr, net::IpAddr,
}; };
use crate::db::models::OrganizationId; use crate::db::models::{MembershipId, OrganizationId};
use crate::{error::Error, CONFIG}; use crate::{error::Error, CONFIG};
const JWT_ALGORITHM: Algorithm = Algorithm::RS256; const JWT_ALGORITHM: Algorithm = Algorithm::RS256;
@ -192,7 +192,7 @@ pub struct InviteJwtClaims {
pub email: String, pub email: String,
pub org_id: Option<OrganizationId>, pub org_id: Option<OrganizationId>,
pub member_id: Option<String>, pub member_id: Option<MembershipId>,
pub invited_by_email: Option<String>, pub invited_by_email: Option<String>,
} }
@ -200,7 +200,7 @@ pub fn generate_invite_claims(
uuid: String, uuid: String,
email: String, email: String,
org_id: Option<OrganizationId>, org_id: Option<OrganizationId>,
member_id: Option<String>, member_id: Option<MembershipId>,
invited_by_email: Option<String>, invited_by_email: Option<String>,
) -> InviteJwtClaims { ) -> InviteJwtClaims {
let time_now = Utc::now(); let time_now = Utc::now();

Datei anzeigen

@ -1,4 +1,4 @@
use super::{Membership, OrganizationId, User}; use super::{Membership, MembershipId, OrganizationId, User};
use crate::api::EmptyResult; use crate::api::EmptyResult;
use crate::db::DbConn; use crate::db::DbConn;
use crate::error::MapResult; use crate::error::MapResult;
@ -34,7 +34,7 @@ db_object! {
#[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: String,
pub users_organizations_uuid: String pub users_organizations_uuid: MembershipId
} }
} }
@ -124,7 +124,7 @@ impl CollectionGroup {
} }
impl GroupUser { impl GroupUser {
pub fn new(groups_uuid: String, users_organizations_uuid: String) -> Self { pub fn new(groups_uuid: String, users_organizations_uuid: MembershipId) -> Self {
Self { Self {
groups_uuid, groups_uuid,
users_organizations_uuid, users_organizations_uuid,
@ -485,10 +485,10 @@ impl GroupUser {
}} }}
} }
pub async fn find_by_user(users_organizations_uuid: &str, conn: &mut DbConn) -> Vec<Self> { pub async fn find_by_member(member_uuid: &MembershipId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: { db_run! { conn: {
groups_users::table groups_users::table
.filter(groups_users::users_organizations_uuid.eq(users_organizations_uuid)) .filter(groups_users::users_organizations_uuid.eq(member_uuid))
.load::<GroupUserDb>(conn) .load::<GroupUserDb>(conn)
.expect("Error loading groups for user") .expect("Error loading groups for user")
.from_db() .from_db()
@ -497,7 +497,7 @@ impl GroupUser {
pub async fn has_access_to_collection_by_member( pub async fn has_access_to_collection_by_member(
collection_uuid: &str, collection_uuid: &str,
member_uuid: &str, member_uuid: &MembershipId,
conn: &mut DbConn, conn: &mut DbConn,
) -> bool { ) -> bool {
db_run! { conn: { db_run! { conn: {
@ -513,7 +513,11 @@ impl GroupUser {
}} }}
} }
pub async fn has_full_access_by_member(org_uuid: &OrganizationId, member_uuid: &str, conn: &mut DbConn) -> bool { pub async fn has_full_access_by_member(
org_uuid: &OrganizationId,
member_uuid: &MembershipId,
conn: &mut DbConn,
) -> bool {
db_run! { conn: { db_run! { conn: {
groups_users::table groups_users::table
.inner_join(groups::table.on( .inner_join(groups::table.on(
@ -535,12 +539,12 @@ impl GroupUser {
} }
} }
pub async fn delete_by_group_id_and_user_id( pub async fn delete_by_group_and_member(
group_uuid: &str, group_uuid: &str,
users_organizations_uuid: &str, member_uuid: &MembershipId,
conn: &mut DbConn, conn: &mut DbConn,
) -> EmptyResult { ) -> EmptyResult {
match Membership::find_by_uuid(users_organizations_uuid, conn).await { match Membership::find_by_uuid(member_uuid, conn).await {
Some(member) => User::update_uuid_revision(&member.user_uuid, conn).await, Some(member) => User::update_uuid_revision(&member.user_uuid, conn).await,
None => warn!("Member could not be found!"), None => warn!("Member could not be found!"),
}; };
@ -548,7 +552,7 @@ impl GroupUser {
db_run! { conn: { db_run! { conn: {
diesel::delete(groups_users::table) diesel::delete(groups_users::table)
.filter(groups_users::groups_uuid.eq(group_uuid)) .filter(groups_users::groups_uuid.eq(group_uuid))
.filter(groups_users::users_organizations_uuid.eq(users_organizations_uuid)) .filter(groups_users::users_organizations_uuid.eq(member_uuid))
.execute(conn) .execute(conn)
.map_res("Error deleting group users") .map_res("Error deleting group users")
}} }}
@ -568,7 +572,7 @@ impl GroupUser {
}} }}
} }
pub async fn delete_all_by_member(member_uuid: &str, conn: &mut DbConn) -> EmptyResult { pub async fn delete_all_by_member(member_uuid: &MembershipId, conn: &mut DbConn) -> EmptyResult {
match Membership::find_by_uuid(member_uuid, conn).await { match Membership::find_by_uuid(member_uuid, conn).await {
Some(member) => User::update_uuid_revision(&member.user_uuid, conn).await, Some(member) => User::update_uuid_revision(&member.user_uuid, conn).await,
None => warn!("Member could not be found!"), None => warn!("Member could not be found!"),

Datei anzeigen

@ -28,7 +28,7 @@ pub use self::folder::{Folder, FolderCipher};
pub use self::group::{CollectionGroup, Group, GroupUser}; pub use self::group::{CollectionGroup, Group, 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, MembershipStatus, MembershipType, Organization, OrganizationApiKey, OrganizationId, Membership, MembershipId, MembershipStatus, MembershipType, Organization, OrganizationApiKey, OrganizationId,
}; };
pub use self::send::{Send, SendType}; pub use self::send::{Send, SendType};
pub use self::two_factor::{TwoFactor, TwoFactorType}; pub use self::two_factor::{TwoFactor, TwoFactorType};

Datei anzeigen

@ -5,7 +5,7 @@ use crate::api::EmptyResult;
use crate::db::DbConn; use crate::db::DbConn;
use crate::error::MapResult; use crate::error::MapResult;
use super::{Membership, MembershipStatus, MembershipType, OrganizationId, TwoFactor}; use super::{Membership, MembershipId, MembershipStatus, MembershipType, OrganizationId, TwoFactor};
db_object! { db_object! {
#[derive(Identifiable, Queryable, Insertable, AsChangeset)] #[derive(Identifiable, Queryable, Insertable, AsChangeset)]
@ -336,7 +336,11 @@ impl OrgPolicy {
false false
} }
pub async fn is_enabled_for_member(member_uuid: &str, policy_type: OrgPolicyType, conn: &mut DbConn) -> bool { pub async fn is_enabled_for_member(
member_uuid: &MembershipId,
policy_type: OrgPolicyType,
conn: &mut DbConn,
) -> bool {
if let Some(member) = Membership::find_by_uuid(member_uuid, conn).await { if let Some(member) = Membership::find_by_uuid(member_uuid, conn).await {
if let Some(policy) = OrgPolicy::find_by_org_and_type(&member.org_uuid, policy_type, conn).await { if let Some(policy) = OrgPolicy::find_by_org_and_type(&member.org_uuid, policy_type, conn).await {
return policy.enabled; return policy.enabled;

Datei anzeigen

@ -30,7 +30,7 @@ db_object! {
#[diesel(table_name = users_organizations)] #[diesel(table_name = users_organizations)]
#[diesel(primary_key(uuid))] #[diesel(primary_key(uuid))]
pub struct Membership { pub struct Membership {
pub uuid: String, pub uuid: MembershipId,
pub user_uuid: String, pub user_uuid: String,
pub org_uuid: OrganizationId, pub org_uuid: OrganizationId,
@ -206,7 +206,7 @@ static ACTIVATE_REVOKE_DIFF: i32 = 128;
impl Membership { impl Membership {
pub fn new(user_uuid: String, org_uuid: OrganizationId) -> Self { pub fn new(user_uuid: String, org_uuid: OrganizationId) -> Self {
Self { Self {
uuid: crate::util::get_uuid(), uuid: MembershipId(crate::util::get_uuid()),
user_uuid, user_uuid,
org_uuid, org_uuid,
@ -459,7 +459,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<String> = if include_groups && CONFIG.org_groups_enabled() {
GroupUser::find_by_user(&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,
// so just act as if there are no groups. // so just act as if there are no groups.
@ -699,7 +699,7 @@ impl Membership {
(self.access_all || self.atype >= MembershipType::Admin) && self.has_status(MembershipStatus::Confirmed) (self.access_all || self.atype >= MembershipType::Admin) && self.has_status(MembershipStatus::Confirmed)
} }
pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option<Self> { pub async fn find_by_uuid(uuid: &MembershipId, conn: &mut DbConn) -> Option<Self> {
db_run! { conn: { db_run! { conn: {
users_organizations::table users_organizations::table
.filter(users_organizations::uuid.eq(uuid)) .filter(users_organizations::uuid.eq(uuid))
@ -708,7 +708,11 @@ impl Membership {
}} }}
} }
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: &MembershipId,
org_uuid: &OrganizationId,
conn: &mut DbConn,
) -> Option<Self> {
db_run! { conn: { db_run! { conn: {
users_organizations::table users_organizations::table
.filter(users_organizations::uuid.eq(uuid)) .filter(users_organizations::uuid.eq(uuid))
@ -1078,16 +1082,61 @@ impl<'r> FromParam<'r> for OrganizationId {
#[inline(always)] #[inline(always)]
fn from_param(param: &'r str) -> Result<Self, Self::Error> { fn from_param(param: &'r str) -> Result<Self, Self::Error> {
if param.chars().all(|c| matches!(c, 'a'..='z' | 'A'..='Z' |'0'..='9' | '-')) { if param.chars().all(|c| matches!(c, 'a'..='z' | 'A'..='Z' |'0'..='9' | '-')) {
Ok(OrganizationId(param.to_string())) Ok(Self(param.to_string()))
} else { } else {
Err(()) Err(())
} }
} }
} }
#[derive(DieselNewType, Clone, Debug, Hash, PartialEq, Eq, Serialize)] #[derive(DieselNewType, FromForm, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct MembershipId(String); pub struct MembershipId(String);
impl AsRef<str> for MembershipId {
fn as_ref(&self) -> &str {
&self.0
}
}
impl Deref for MembershipId {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Borrow<str> for MembershipId {
fn borrow(&self) -> &str {
&self.0
}
}
impl Display for MembershipId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<String> for MembershipId {
fn from(raw: String) -> Self {
Self(raw)
}
}
impl<'r> FromParam<'r> for MembershipId {
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(())
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

Datei anzeigen

@ -17,7 +17,7 @@ use crate::{
encode_jwt, generate_delete_claims, generate_emergency_access_invite_claims, generate_invite_claims, encode_jwt, generate_delete_claims, generate_emergency_access_invite_claims, generate_invite_claims,
generate_verify_email_claims, generate_verify_email_claims,
}, },
db::models::{Device, DeviceType, OrganizationId, User}, db::models::{Device, DeviceType, MembershipId, OrganizationId, User},
error::Error, error::Error,
CONFIG, CONFIG,
}; };
@ -260,7 +260,7 @@ pub async fn send_single_org_removed_from_org(address: &str, org_name: &str) ->
pub async fn send_invite( pub async fn send_invite(
user: &User, user: &User,
org_id: Option<OrganizationId>, org_id: Option<OrganizationId>,
member_id: Option<String>, member_id: Option<MembershipId>,
org_name: &str, org_name: &str,
invited_by_email: Option<String>, invited_by_email: Option<String>,
) -> EmptyResult { ) -> EmptyResult {