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:
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>,
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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!"),
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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 {
|
||||
|
|
Laden …
In neuem Issue referenzieren