bitwarden_pam/leases/
models.rs1use std::num::NonZeroU32;
2
3use bitwarden_api_api::models::{
4 AccessLeaseExtensionRequestModel, AccessLeaseResponseModel, AccessLeaseRevokeRequestModel,
5 AccessLeaseStatus as ApiAccessLeaseStatus,
6};
7use bitwarden_collections::collection::CollectionId;
8use bitwarden_core::{OrganizationId, UserId, require};
9use bitwarden_vault::CipherId;
10use chrono::{DateTime, Utc};
11use serde::{Deserialize, Serialize};
12#[cfg(feature = "wasm")]
13use tsify::Tsify;
14
15use crate::{AccessLeaseId, AccessRequestId, error::LeasingError};
16
17#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
19#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
20#[serde(rename_all = "snake_case")]
21pub enum AccessLeaseStatus {
22 Active,
24 Expired,
26 Revoked,
28 Canceled,
30 Unknown,
33}
34
35impl From<ApiAccessLeaseStatus> for AccessLeaseStatus {
36 fn from(status: ApiAccessLeaseStatus) -> Self {
37 match status {
38 ApiAccessLeaseStatus::Active => Self::Active,
39 ApiAccessLeaseStatus::Expired => Self::Expired,
40 ApiAccessLeaseStatus::Revoked => Self::Revoked,
41 ApiAccessLeaseStatus::Cancelled => Self::Canceled,
42 ApiAccessLeaseStatus::__Unknown(_) => Self::Unknown,
43 }
44 }
45}
46
47#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
55#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
56#[serde(rename_all = "camelCase")]
57pub struct AccessLeaseView {
58 pub id: AccessLeaseId,
60 pub request_id: AccessRequestId,
62 pub cipher_id: CipherId,
64 pub collection_id: CollectionId,
66 pub organization_id: Option<OrganizationId>,
68 pub requester_id: UserId,
70 pub status: AccessLeaseStatus,
72 pub not_before: DateTime<Utc>,
74 pub not_after: DateTime<Utc>,
76 pub revoked_at: Option<DateTime<Utc>>,
78 pub revoked_by_user_id: Option<UserId>,
80}
81
82impl TryFrom<AccessLeaseResponseModel> for AccessLeaseView {
83 type Error = LeasingError;
84
85 fn try_from(response: AccessLeaseResponseModel) -> Result<Self, Self::Error> {
86 Ok(Self {
87 id: AccessLeaseId::new(require!(response.id)),
88 request_id: AccessRequestId::new(require!(response.request_id)),
89 cipher_id: CipherId::new(require!(response.cipher_id)),
90 collection_id: CollectionId::new(require!(response.collection_id)),
91 organization_id: response.organization_id.map(OrganizationId::new),
92 requester_id: UserId::new(require!(response.requester_id)),
93 status: AccessLeaseStatus::from(require!(response.status)),
94 not_before: require!(response.not_before).parse()?,
95 not_after: require!(response.not_after).parse()?,
96 revoked_at: response.revoked_at.map(|d| d.parse()).transpose()?,
97 revoked_by_user_id: response.revoked_by_user_id.map(UserId::new),
98 })
99 }
100}
101
102#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
104#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
105#[serde(rename_all = "camelCase")]
106pub struct AccessLeaseExtensionRequest {
107 pub duration_seconds: Option<NonZeroU32>,
110 pub reason: String,
112}
113
114impl From<AccessLeaseExtensionRequest> for AccessLeaseExtensionRequestModel {
115 fn from(request: AccessLeaseExtensionRequest) -> Self {
116 Self {
117 duration_seconds: request.duration_seconds.map(|d| d.get() as i32),
118 reason: request.reason,
119 }
120 }
121}
122
123#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
125#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
126#[serde(rename_all = "camelCase")]
127pub struct AccessLeaseRevokeRequest {
128 pub reason: Option<String>,
130}
131
132impl From<AccessLeaseRevokeRequest> for AccessLeaseRevokeRequestModel {
133 fn from(request: AccessLeaseRevokeRequest) -> Self {
134 Self {
135 reason: request.reason,
136 }
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use std::num::NonZeroU32;
143
144 use super::*;
145
146 #[test]
147 fn access_lease_extension_request_converts_to_model() {
148 let request = AccessLeaseExtensionRequest {
149 duration_seconds: NonZeroU32::new(3600),
150 reason: "Need more time".to_string(),
151 };
152
153 let model = AccessLeaseExtensionRequestModel::from(request);
154
155 assert_eq!(model.duration_seconds, Some(3600));
156 assert_eq!(model.reason, "Need more time".to_string());
157 }
158}