bitwarden_organization_invite_link/
organization_invite_link.rs1use bitwarden_api_api::models::{
2 OrganizationInviteLinkResponseModel, OrganizationInviteLinkSsoResponseModel,
3 OrganizationInviteLinkStatusResponseModel,
4};
5use bitwarden_core::{
6 OrganizationId,
7 key_management::{KeySlotIds, SymmetricKeySlotId},
8 require,
9};
10use bitwarden_crypto::KeyStoreContext;
11use bitwarden_organization_crypto::invite::Invite;
12use chrono::{DateTime, Utc};
13use serde::{Deserialize, Serialize};
14#[cfg(feature = "wasm")]
15use tsify::Tsify;
16use uuid::Uuid;
17
18use crate::InviteLinkError;
19
20#[derive(Serialize, Deserialize, Debug, Clone)]
22#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
23#[serde(rename_all = "camelCase")]
24pub struct OrganizationInviteLink {
25 pub id: Uuid,
27 pub code: Uuid,
29 pub organization_id: OrganizationId,
31 pub allowed_domains: Vec<String>,
33 pub invite: Invite,
35 pub supports_confirmation: bool,
37 pub creation_date: DateTime<Utc>,
39}
40
41impl TryFrom<OrganizationInviteLinkResponseModel> for OrganizationInviteLink {
42 type Error = InviteLinkError;
43
44 fn try_from(response: OrganizationInviteLinkResponseModel) -> Result<Self, Self::Error> {
45 Ok(Self {
46 id: require!(response.id),
47 code: require!(response.code),
48 organization_id: OrganizationId::new(require!(response.organization_id)),
49 allowed_domains: response.allowed_domains.unwrap_or_default(),
50 invite: require!(response.invite).parse()?,
51 supports_confirmation: response.supports_confirmation.unwrap_or(false),
52 creation_date: require!(response.creation_date)
53 .parse()
54 .map_err(|_| InviteLinkError::ParseFailure("creation_date"))?,
55 })
56 }
57}
58
59impl OrganizationInviteLink {
60 pub fn to_view(
62 self,
63 ctx: &mut KeyStoreContext<KeySlotIds>,
64 ) -> Result<OrganizationInviteLinkView, InviteLinkError> {
65 let org_id = self.organization_id;
66
67 let org_key = SymmetricKeySlotId::Organization(org_id);
69 let invite_key = self
70 .invite
71 .unseal_invite_key_with_organization_key(org_key, ctx)?;
72 let invite_secret = self.invite.get_invite_secret(invite_key, ctx)?;
73
74 let invite_secret_str: String = (&invite_secret).into();
75 let code = self.code;
76 let url_fragment = format!("#/join/{org_id}/{code}?key={invite_secret_str}");
77
78 Ok(OrganizationInviteLinkView {
79 id: self.id,
80 organization_id: self.organization_id,
81 allowed_domains: self.allowed_domains,
82 supports_confirmation: self.supports_confirmation,
83 creation_date: self.creation_date,
84 url_fragment,
85 })
86 }
87}
88
89#[derive(Serialize, Deserialize, Debug, Clone)]
91#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
92#[serde(rename_all = "camelCase")]
93pub struct OrganizationInviteLinkView {
94 pub id: Uuid,
96 pub organization_id: OrganizationId,
98 pub allowed_domains: Vec<String>,
100 pub supports_confirmation: bool,
102 pub creation_date: DateTime<Utc>,
104 pub url_fragment: String,
107}
108
109#[derive(Serialize, Deserialize, Debug, Clone)]
112#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
113#[serde(rename_all = "camelCase")]
114pub struct OrganizationInviteLinkStatusView {
115 pub organization_name: String,
117 pub links_enabled: bool,
119 pub seats_available: bool,
121 pub supports_confirmation: bool,
123 pub sso: Option<OrganizationInviteLinkSsoView>,
125}
126
127#[derive(Serialize, Deserialize, Debug, Clone)]
129#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
130#[serde(rename_all = "camelCase")]
131pub struct OrganizationInviteLinkSsoView {
132 pub org_sso_id: Option<String>,
134 pub required: bool,
136}
137
138impl From<OrganizationInviteLinkSsoResponseModel> for OrganizationInviteLinkSsoView {
139 fn from(response: OrganizationInviteLinkSsoResponseModel) -> Self {
140 Self {
141 org_sso_id: response.org_sso_id,
142 required: response.required.unwrap_or(false),
143 }
144 }
145}
146
147impl TryFrom<OrganizationInviteLinkStatusResponseModel> for OrganizationInviteLinkStatusView {
148 type Error = InviteLinkError;
149
150 fn try_from(response: OrganizationInviteLinkStatusResponseModel) -> Result<Self, Self::Error> {
151 Ok(Self {
152 organization_name: require!(response.organization_name),
153 links_enabled: response.links_enabled.unwrap_or(false),
154 seats_available: response.seats_available.unwrap_or(false),
155 supports_confirmation: response.supports_confirmation.unwrap_or(false),
156 sso: response.sso.map(|sso| (*sso).into()),
157 })
158 }
159}