1
0
Fork 1
Spiegel von https://github.com/dani-garcia/vaultwarden.git synchronisiert 2025-01-07 11:45:40 +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>,
token: Option<String>,
#[allow(dead_code)]
organization_user_id: Option<String>,
organization_user_id: Option<MembershipId>,
}
#[derive(Debug, Deserialize)]
@ -106,7 +106,7 @@ fn enforce_password_hint_setting(password_hint: &Option<String>) -> EmptyResult
}
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() {
return false;
}

Datei anzeigen

@ -8,7 +8,7 @@ use crate::{
api::{EmptyResult, JsonResult},
auth::{AdminHeaders, Headers},
db::{
models::{Cipher, Event, Membership, OrganizationId},
models::{Cipher, Event, Membership, MembershipId, OrganizationId},
DbConn, DbPool,
},
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..>")]
async fn get_user_events(
org_id: &str,
member_id: &str,
member_id: MembershipId,
data: EventRange,
_headers: AdminHeaders,
mut conn: DbConn,
@ -110,7 +110,7 @@ async fn get_user_events(
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
.iter()
.map(|e| e.to_json())

Datei anzeigen

@ -125,7 +125,7 @@ struct OrganizationUpdateData {
struct NewCollectionData {
name: String,
groups: Vec<NewCollectionObjectData>,
users: Vec<NewCollectionObjectData>,
users: Vec<NewCollectionMemberData>,
id: Option<String>,
external_id: Option<String>,
}
@ -138,6 +138,14 @@ struct NewCollectionObjectData {
read_only: bool,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct NewCollectionMemberData {
hide_passwords: bool,
id: MembershipId,
read_only: bool,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct OrgKeyData {
@ -151,6 +159,12 @@ struct OrgBulkIds {
ids: Vec<String>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct BulkMembershipIds {
ids: Vec<MembershipId>,
}
#[post("/organizations", data = "<data>")]
async fn create_organization(headers: Headers, data: Json<OrgData>, mut conn: DbConn) -> JsonResult {
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(
org_id: OrganizationId,
col_id: &str,
member_id: &str,
member_id: MembershipId,
_headers: AdminHeaders,
mut conn: DbConn,
) -> 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")
};
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"),
Some(member) => {
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(
org_id: OrganizationId,
col_id: &str,
member_id: &str,
member_id: MembershipId,
headers: AdminHeaders,
conn: DbConn,
) -> EmptyResult {
@ -827,7 +841,7 @@ async fn post_org_keys(
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct CollectionData {
id: String,
id: MembershipId,
read_only: 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;
new_user.access_all = access_all;
new_user.atype = new_type;
new_user.status = member_status;
new_member.access_all = access_all;
new_member.atype = new_type;
new_member.status = member_status;
// If no accessAll, add the collections received
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() {
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?;
}
log_event(
EventType::OrganizationUserInvited as i32,
&new_user.uuid,
&new_member.uuid,
&org_id,
&headers.user.uuid,
headers.device.atype,
@ -947,7 +961,7 @@ async fn send_invite(
mail::send_invite(
&user,
Some(org_id.clone()),
Some(new_user.uuid),
Some(new_member.uuid),
&org_name,
Some(headers.user.email.clone()),
)
@ -961,11 +975,11 @@ async fn send_invite(
#[post("/organizations/<org_id>/users/reinvite", data = "<data>")]
async fn bulk_reinvite_user(
org_id: OrganizationId,
data: Json<OrgBulkIds>,
data: Json<BulkMembershipIds>,
headers: AdminHeaders,
mut conn: DbConn,
) -> Json<Value> {
let data: OrgBulkIds = data.into_inner();
let data: BulkMembershipIds = data.into_inner();
let mut bulk_response = Vec::new();
for member_id in data.ids {
@ -990,18 +1004,23 @@ async fn bulk_reinvite_user(
}))
}
#[post("/organizations/<org_id>/users/<member>/reinvite")]
async fn reinvite_user(org_id: OrganizationId, member: &str, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult {
_reinvite_user(&org_id, member, &headers.user.email, &mut conn).await
#[post("/organizations/<org_id>/users/<member_id>/reinvite")]
async fn reinvite_user(
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(
org_id: &OrganizationId,
member: &str,
member_id: &MembershipId,
invited_by_email: &str,
conn: &mut DbConn,
) -> 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.")
};
@ -1054,7 +1073,7 @@ struct AcceptData {
#[post("/organizations/<org_id>/users/<member_id>/accept", data = "<data>")]
async fn accept_invite(
org_id: OrganizationId,
member_id: &str,
member_id: MembershipId,
data: Json<AcceptData>,
mut conn: DbConn,
) -> 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.
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"),
}
@ -1139,7 +1158,7 @@ async fn accept_invite(
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct ConfirmData {
id: Option<String>,
id: Option<MembershipId>,
key: Option<String>,
}
@ -1163,7 +1182,7 @@ async fn bulk_confirm_invite(
match data.keys {
Some(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 err_msg = match _confirm_invite(&org_id, &member_id, &user_key, &headers, &mut conn, &nt).await {
Ok(_) => String::new(),
@ -1192,7 +1211,7 @@ async fn bulk_confirm_invite(
#[post("/organizations/<org_id>/users/<member_id>/confirm", data = "<data>")]
async fn confirm_invite(
org_id: OrganizationId,
member_id: &str,
member_id: MembershipId,
data: Json<ConfirmData>,
headers: AdminHeaders,
mut conn: DbConn,
@ -1200,12 +1219,12 @@ async fn confirm_invite(
) -> EmptyResult {
let data = data.into_inner();
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(
org_id: &OrganizationId,
member_id: &str,
member_id: &MembershipId,
key: &str,
headers: &AdminHeaders,
conn: &mut DbConn,
@ -1283,12 +1302,12 @@ async fn _confirm_invite(
#[get("/organizations/<org_id>/users/<member_id>?<data..>")]
async fn get_user(
org_id: OrganizationId,
member_id: &str,
member_id: MembershipId,
data: GetOrgUserData,
_headers: AdminHeaders,
mut conn: DbConn,
) -> 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")
};
@ -1313,7 +1332,7 @@ struct EditUserData {
#[put("/organizations/<org_id>/users/<member_id>", data = "<data>", rank = 1)]
async fn put_membership(
org_id: OrganizationId,
member_id: &str,
member_id: MembershipId,
data: Json<EditUserData>,
headers: AdminHeaders,
conn: DbConn,
@ -1324,7 +1343,7 @@ async fn put_membership(
#[post("/organizations/<org_id>/users/<member_id>", data = "<data>", rank = 1)]
async fn edit_user(
org_id: OrganizationId,
member_id: &str,
member_id: MembershipId,
data: Json<EditUserData>,
headers: AdminHeaders,
mut conn: DbConn,
@ -1335,7 +1354,7 @@ async fn edit_user(
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")
};
@ -1429,12 +1448,12 @@ async fn edit_user(
#[delete("/organizations/<org_id>/users", data = "<data>")]
async fn bulk_delete_user(
org_id: OrganizationId,
data: Json<OrgBulkIds>,
data: Json<BulkMembershipIds>,
headers: AdminHeaders,
mut conn: DbConn,
nt: Notify<'_>,
) -> Json<Value> {
let data: OrgBulkIds = data.into_inner();
let data: BulkMembershipIds = data.into_inner();
let mut bulk_response = Vec::new();
for member_id in data.ids {
@ -1462,28 +1481,28 @@ async fn bulk_delete_user(
#[delete("/organizations/<org_id>/users/<member_id>")]
async fn delete_user(
org_id: OrganizationId,
member_id: &str,
member_id: MembershipId,
headers: AdminHeaders,
mut conn: DbConn,
nt: Notify<'_>,
) -> 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")]
async fn post_delete_user(
org_id: OrganizationId,
member_id: &str,
member_id: MembershipId,
headers: AdminHeaders,
mut conn: DbConn,
nt: Notify<'_>,
) -> 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(
org_id: &OrganizationId,
member_id: &str,
member_id: &MembershipId,
headers: &AdminHeaders,
conn: &mut DbConn,
nt: &Notify<'_>,
@ -1525,11 +1544,11 @@ async fn _delete_user(
#[post("/organizations/<org_id>/users/public-keys", data = "<data>")]
async fn bulk_public_keys(
org_id: OrganizationId,
data: Json<OrgBulkIds>,
data: Json<BulkMembershipIds>,
_headers: AdminHeaders,
mut conn: DbConn,
) -> Json<Value> {
let data: OrgBulkIds = data.into_inner();
let data: BulkMembershipIds = data.into_inner();
let mut bulk_response = Vec::new();
// 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")]
async fn deactivate_membership(
org_id: OrganizationId,
member_id: &str,
member_id: MembershipId,
headers: AdminHeaders,
mut conn: DbConn,
) -> 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
#[put("/organizations/<org_id>/users/deactivate", data = "<data>")]
async fn bulk_deactivate_membership(
org_id: OrganizationId,
data: Json<OrgBulkRevokeData>,
data: Json<BulkRevokeMembershipIds>,
headers: AdminHeaders,
conn: DbConn,
) -> Json<Value> {
@ -2095,23 +2120,17 @@ async fn bulk_deactivate_membership(
#[put("/organizations/<org_id>/users/<member_id>/revoke")]
async fn revoke_membership(
org_id: OrganizationId,
member_id: &str,
member_id: MembershipId,
headers: AdminHeaders,
mut conn: DbConn,
) -> EmptyResult {
_revoke_membership(&org_id, member_id, &headers, &mut conn).await
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct OrgBulkRevokeData {
ids: Option<Vec<String>>,
_revoke_membership(&org_id, &member_id, &headers, &mut conn).await
}
#[put("/organizations/<org_id>/users/revoke", data = "<data>")]
async fn bulk_revoke_membership(
org_id: OrganizationId,
data: Json<OrgBulkRevokeData>,
data: Json<BulkRevokeMembershipIds>,
headers: AdminHeaders,
mut conn: DbConn,
) -> Json<Value> {
@ -2147,7 +2166,7 @@ async fn bulk_revoke_membership(
async fn _revoke_membership(
org_id: &OrganizationId,
member_id: &str,
member_id: &MembershipId,
headers: &AdminHeaders,
conn: &mut DbConn,
) -> EmptyResult {
@ -2189,18 +2208,18 @@ async fn _revoke_membership(
#[put("/organizations/<org_id>/users/<member_id>/activate")]
async fn activate_membership(
org_id: OrganizationId,
member_id: &str,
member_id: MembershipId,
headers: AdminHeaders,
mut conn: DbConn,
) -> 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
#[put("/organizations/<org_id>/users/activate", data = "<data>")]
async fn bulk_activate_membership(
org_id: OrganizationId,
data: Json<OrgBulkIds>,
data: Json<BulkMembershipIds>,
headers: AdminHeaders,
conn: DbConn,
) -> Json<Value> {
@ -2210,17 +2229,17 @@ async fn bulk_activate_membership(
#[put("/organizations/<org_id>/users/<member_id>/restore")]
async fn restore_membership(
org_id: OrganizationId,
member_id: &str,
member_id: MembershipId,
headers: AdminHeaders,
mut conn: DbConn,
) -> 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>")]
async fn bulk_restore_membership(
org_id: OrganizationId,
data: Json<OrgBulkIds>,
data: Json<BulkMembershipIds>,
headers: AdminHeaders,
mut conn: DbConn,
) -> Json<Value> {
@ -2251,7 +2270,7 @@ async fn bulk_restore_membership(
async fn _restore_membership(
org_id: &OrganizationId,
member_id: &str,
member_id: &MembershipId,
headers: &AdminHeaders,
conn: &mut DbConn,
) -> EmptyResult {
@ -2334,7 +2353,7 @@ struct GroupRequest {
access_all: bool,
external_id: Option<String>,
collections: Vec<SelectionReadOnly>,
users: Vec<String>,
users: Vec<MembershipId>,
}
impl GroupRequest {
@ -2464,7 +2483,7 @@ async fn put_group(
async fn add_update_group(
mut group: Group,
collections: Vec<SelectionReadOnly>,
users: Vec<String>,
members: Vec<MembershipId>,
org_id: OrganizationId,
headers: &AdminHeaders,
conn: &mut DbConn,
@ -2476,13 +2495,13 @@ async fn add_update_group(
collection_group.save(conn).await?;
}
for assigned_user_id in users {
let mut user_entry = GroupUser::new(group.uuid.clone(), assigned_user_id.clone());
for assigned_member in members {
let mut user_entry = GroupUser::new(group.uuid.clone(), assigned_member.clone());
user_entry.save(conn).await?;
log_event(
EventType::OrganizationUserUpdatedGroups as i32,
&assigned_user_id,
&assigned_member,
&org_id,
&headers.user.uuid,
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")
};
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
.iter()
.map(|entry| entry.users_organizations_uuid.clone())
@ -2623,7 +2642,7 @@ async fn put_group_users(
org_id: OrganizationId,
group_id: &str,
headers: AdminHeaders,
data: Json<Vec<String>>,
data: Json<Vec<MembershipId>>,
mut conn: DbConn,
) -> EmptyResult {
if !CONFIG.org_groups_enabled() {
@ -2636,14 +2655,14 @@ async fn put_group_users(
GroupUser::delete_all_by_group(group_id, &mut conn).await?;
let assigned_user_ids = data.into_inner();
for assigned_user_id in assigned_user_ids {
let mut user_entry = GroupUser::new(String::from(group_id), assigned_user_id.clone());
let assigned_members = data.into_inner();
for assigned_member in assigned_members {
let mut user_entry = GroupUser::new(String::from(group_id), assigned_member.clone());
user_entry.save(&mut conn).await?;
log_event(
EventType::OrganizationUserUpdatedGroups as i32,
&assigned_user_id,
&assigned_member,
&org_id,
&headers.user.uuid,
headers.device.atype,
@ -2656,10 +2675,10 @@ async fn put_group_users(
Ok(())
}
#[get("/organizations/<org_id>/users/<user_id>/groups")]
#[get("/organizations/<org_id>/users/<member_id>/groups")]
async fn get_user_groups(
org_id: OrganizationId,
user_id: &str,
member_id: MembershipId,
_headers: AdminHeaders,
mut conn: DbConn,
) -> JsonResult {
@ -2667,12 +2686,12 @@ async fn get_user_groups(
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!")
};
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)))
}
@ -2686,7 +2705,7 @@ struct OrganizationUserUpdateGroupsRequest {
#[post("/organizations/<org_id>/users/<member_id>/groups", data = "<data>")]
async fn post_user_groups(
org_id: OrganizationId,
member_id: &str,
member_id: MembershipId,
data: Json<OrganizationUserUpdateGroupsRequest>,
headers: AdminHeaders,
conn: DbConn,
@ -2697,7 +2716,7 @@ async fn post_user_groups(
#[put("/organizations/<org_id>/users/<member_id>/groups", data = "<data>")]
async fn put_user_groups(
org_id: OrganizationId,
member_id: &str,
member_id: MembershipId,
data: Json<OrganizationUserUpdateGroupsRequest>,
headers: AdminHeaders,
mut conn: DbConn,
@ -2706,7 +2725,7 @@ async fn put_user_groups(
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.");
}
@ -2714,13 +2733,13 @@ async fn put_user_groups(
let assigned_group_ids = data.into_inner();
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?;
}
log_event(
EventType::OrganizationUserUpdatedGroups as i32,
member_id,
&member_id,
&org_id,
&headers.user.uuid,
headers.device.atype,
@ -2736,7 +2755,7 @@ async fn put_user_groups(
async fn post_delete_group_user(
org_id: OrganizationId,
group_id: &str,
member_id: &str,
member_id: MembershipId,
headers: AdminHeaders,
conn: DbConn,
) -> EmptyResult {
@ -2747,7 +2766,7 @@ async fn post_delete_group_user(
async fn delete_group_user(
org_id: OrganizationId,
group_id: &str,
member_id: &str,
member_id: MembershipId,
headers: AdminHeaders,
mut conn: DbConn,
) -> EmptyResult {
@ -2755,7 +2774,7 @@ async fn delete_group_user(
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.");
}
@ -2765,7 +2784,7 @@ async fn delete_group_user(
log_event(
EventType::OrganizationUserUpdatedGroups as i32,
member_id,
&member_id,
&org_id,
&headers.user.uuid,
headers.device.atype,
@ -2774,7 +2793,7 @@ async fn delete_group_user(
)
.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)]
@ -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>")]
async fn put_reset_password(
org_id: OrganizationId,
member_id: &str,
member_id: MembershipId,
headers: AdminHeaders,
data: Json<OrganizationUserResetPasswordRequest>,
mut conn: DbConn,
@ -2827,7 +2846,7 @@ async fn put_reset_password(
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")
};
@ -2835,7 +2854,7 @@ async fn put_reset_password(
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() {
err!("Password reset not or not correctly enrolled");
@ -2860,7 +2879,7 @@ async fn put_reset_password(
log_event(
EventType::OrganizationUserAdminResetPassword as i32,
member_id,
&member_id,
&org_id,
&headers.user.uuid,
headers.device.atype,
@ -2875,7 +2894,7 @@ async fn put_reset_password(
#[get("/organizations/<org_id>/users/<member_id>/reset-password-details")]
async fn get_reset_password_details(
org_id: OrganizationId,
member_id: &str,
member_id: MembershipId,
headers: AdminHeaders,
mut conn: DbConn,
) -> JsonResult {
@ -2883,7 +2902,7 @@ async fn get_reset_password_details(
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")
};
@ -2891,7 +2910,7 @@ async fn get_reset_password_details(
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
Ok(Json(json!({
@ -2908,7 +2927,7 @@ async fn get_reset_password_details(
async fn check_reset_password_applicable_and_permissions(
org_id: &OrganizationId,
member_id: &str,
member_id: &MembershipId,
headers: &AdminHeaders,
conn: &mut DbConn,
) -> 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>")]
async fn put_reset_password_enrollment(
org_id: OrganizationId,
member_id: &str,
member_id: MembershipId,
headers: Headers,
data: Json<OrganizationUserResetPasswordEnrollmentRequest>,
mut conn: DbConn,
@ -2982,7 +3001,7 @@ async fn put_reset_password_enrollment(
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(())
}

Datei anzeigen

@ -14,7 +14,7 @@ use std::{
net::IpAddr,
};
use crate::db::models::OrganizationId;
use crate::db::models::{MembershipId, OrganizationId};
use crate::{error::Error, CONFIG};
const JWT_ALGORITHM: Algorithm = Algorithm::RS256;
@ -192,7 +192,7 @@ pub struct InviteJwtClaims {
pub email: String,
pub org_id: Option<OrganizationId>,
pub member_id: Option<String>,
pub member_id: Option<MembershipId>,
pub invited_by_email: Option<String>,
}
@ -200,7 +200,7 @@ pub fn generate_invite_claims(
uuid: String,
email: String,
org_id: Option<OrganizationId>,
member_id: Option<String>,
member_id: Option<MembershipId>,
invited_by_email: Option<String>,
) -> InviteJwtClaims {
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::db::DbConn;
use crate::error::MapResult;
@ -34,7 +34,7 @@ db_object! {
#[diesel(primary_key(groups_uuid, users_organizations_uuid))]
pub struct GroupUser {
pub groups_uuid: String,
pub users_organizations_uuid: String
pub users_organizations_uuid: MembershipId
}
}
@ -124,7 +124,7 @@ impl CollectionGroup {
}
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 {
groups_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: {
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)
.expect("Error loading groups for user")
.from_db()
@ -497,7 +497,7 @@ impl GroupUser {
pub async fn has_access_to_collection_by_member(
collection_uuid: &str,
member_uuid: &str,
member_uuid: &MembershipId,
conn: &mut DbConn,
) -> bool {
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: {
groups_users::table
.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,
users_organizations_uuid: &str,
member_uuid: &MembershipId,
conn: &mut DbConn,
) -> 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,
None => warn!("Member could not be found!"),
};
@ -548,7 +552,7 @@ impl GroupUser {
db_run! { conn: {
diesel::delete(groups_users::table)
.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)
.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 {
Some(member) => User::update_uuid_revision(&member.user_uuid, conn).await,
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::org_policy::{OrgPolicy, OrgPolicyErr, OrgPolicyType};
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::two_factor::{TwoFactor, TwoFactorType};

Datei anzeigen

@ -5,7 +5,7 @@ use crate::api::EmptyResult;
use crate::db::DbConn;
use crate::error::MapResult;
use super::{Membership, MembershipStatus, MembershipType, OrganizationId, TwoFactor};
use super::{Membership, MembershipId, MembershipStatus, MembershipType, OrganizationId, TwoFactor};
db_object! {
#[derive(Identifiable, Queryable, Insertable, AsChangeset)]
@ -336,7 +336,11 @@ impl OrgPolicy {
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(policy) = OrgPolicy::find_by_org_and_type(&member.org_uuid, policy_type, conn).await {
return policy.enabled;

Datei anzeigen

@ -30,7 +30,7 @@ db_object! {
#[diesel(table_name = users_organizations)]
#[diesel(primary_key(uuid))]
pub struct Membership {
pub uuid: String,
pub uuid: MembershipId,
pub user_uuid: String,
pub org_uuid: OrganizationId,
@ -206,7 +206,7 @@ static ACTIVATE_REVOKE_DIFF: i32 = 128;
impl Membership {
pub fn new(user_uuid: String, org_uuid: OrganizationId) -> Self {
Self {
uuid: crate::util::get_uuid(),
uuid: MembershipId(crate::util::get_uuid()),
user_uuid,
org_uuid,
@ -459,7 +459,7 @@ impl Membership {
let twofactor_enabled = !TwoFactor::find_by_user(&user.uuid, conn).await.is_empty();
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 {
// The Bitwarden clients seem to call this API regardless of whether groups are enabled,
// 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)
}
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: {
users_organizations::table
.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: {
users_organizations::table
.filter(users_organizations::uuid.eq(uuid))
@ -1078,16 +1082,61 @@ impl<'r> FromParam<'r> for OrganizationId {
#[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(OrganizationId(param.to_string()))
Ok(Self(param.to_string()))
} else {
Err(())
}
}
}
#[derive(DieselNewType, Clone, Debug, Hash, PartialEq, Eq, Serialize)]
#[derive(DieselNewType, FromForm, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
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)]
mod tests {
use super::*;

Datei anzeigen

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