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};
14use uuid::Uuid;
15
16use crate::InviteLinkError;
17
18#[derive(Serialize, Deserialize, Debug, Clone)]
20#[bitwarden_ffi::wasm_record]
21#[serde(rename_all = "camelCase")]
22pub struct OrganizationInviteLink {
23 pub id: Uuid,
25 pub code: Uuid,
27 pub organization_id: OrganizationId,
29 pub allowed_domains: Vec<String>,
31 pub invite: Invite,
33 pub supports_confirmation: bool,
35 pub creation_date: DateTime<Utc>,
37}
38
39impl TryFrom<OrganizationInviteLinkResponseModel> for OrganizationInviteLink {
40 type Error = InviteLinkError;
41
42 fn try_from(response: OrganizationInviteLinkResponseModel) -> Result<Self, Self::Error> {
43 Ok(Self {
44 id: require!(response.id),
45 code: require!(response.code),
46 organization_id: OrganizationId::new(require!(response.organization_id)),
47 allowed_domains: response.allowed_domains.unwrap_or_default(),
48 invite: require!(response.invite).parse()?,
49 supports_confirmation: response.supports_confirmation.unwrap_or(false),
50 creation_date: require!(response.creation_date)
51 .parse()
52 .map_err(|_| InviteLinkError::ParseFailure("creation_date"))?,
53 })
54 }
55}
56
57impl OrganizationInviteLink {
58 pub fn to_view(
60 self,
61 ctx: &mut KeyStoreContext<KeySlotIds>,
62 ) -> Result<OrganizationInviteLinkView, InviteLinkError> {
63 let org_id = self.organization_id;
64
65 let org_key = SymmetricKeySlotId::Organization(org_id);
67 let invite_key = self
68 .invite
69 .unseal_invite_key_with_organization_key(org_key, ctx)?;
70 let invite_secret = self.invite.get_invite_secret(invite_key, ctx)?;
71
72 let invite_secret_str: String = (&invite_secret).into();
73 let code = self.code;
74 let url_fragment = format!("#/join/{org_id}/{code}?key={invite_secret_str}");
75
76 Ok(OrganizationInviteLinkView {
77 id: self.id,
78 organization_id: self.organization_id,
79 allowed_domains: self.allowed_domains,
80 supports_confirmation: self.supports_confirmation,
81 creation_date: self.creation_date,
82 url_fragment,
83 })
84 }
85}
86
87#[derive(Serialize, Deserialize, Debug, Clone)]
89#[bitwarden_ffi::wasm_record]
90#[serde(rename_all = "camelCase")]
91pub struct OrganizationInviteLinkView {
92 pub id: Uuid,
94 pub organization_id: OrganizationId,
96 pub allowed_domains: Vec<String>,
98 pub supports_confirmation: bool,
100 pub creation_date: DateTime<Utc>,
102 pub url_fragment: String,
105}
106
107#[derive(Serialize, Deserialize, Debug, Clone)]
110#[bitwarden_ffi::wasm_record]
111#[serde(rename_all = "camelCase")]
112pub struct OrganizationInviteLinkStatusView {
113 pub organization_name: String,
115 pub links_enabled: bool,
117 pub seats_available: bool,
119 pub supports_confirmation: bool,
121 pub sso: Option<OrganizationInviteLinkSsoView>,
123}
124
125#[derive(Serialize, Deserialize, Debug, Clone)]
127#[bitwarden_ffi::wasm_record]
128#[serde(rename_all = "camelCase")]
129pub struct OrganizationInviteLinkSsoView {
130 pub org_sso_id: Option<String>,
132 pub required: bool,
134}
135
136impl From<OrganizationInviteLinkSsoResponseModel> for OrganizationInviteLinkSsoView {
137 fn from(response: OrganizationInviteLinkSsoResponseModel) -> Self {
138 Self {
139 org_sso_id: response.org_sso_id,
140 required: response.required.unwrap_or(false),
141 }
142 }
143}
144
145impl TryFrom<OrganizationInviteLinkStatusResponseModel> for OrganizationInviteLinkStatusView {
146 type Error = InviteLinkError;
147
148 fn try_from(response: OrganizationInviteLinkStatusResponseModel) -> Result<Self, Self::Error> {
149 Ok(Self {
150 organization_name: require!(response.organization_name),
151 links_enabled: response.links_enabled.unwrap_or(false),
152 seats_available: response.seats_available.unwrap_or(false),
153 supports_confirmation: response.supports_confirmation.unwrap_or(false),
154 sso: response.sso.map(|sso| (*sso).into()),
155 })
156 }
157}