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:
Ursprung
40a788084b
Commit
64ae0aa386
9 geänderte Dateien mit 204 neuen und 128 gelöschten Zeilen
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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!"),
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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::*;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Laden …
In neuem Issue referenzieren