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 Unknown,
31}
32
33impl From<ApiAccessLeaseStatus> for AccessLeaseStatus {
34 fn from(status: ApiAccessLeaseStatus) -> Self {
35 match status {
36 ApiAccessLeaseStatus::Active => Self::Active,
37 ApiAccessLeaseStatus::Expired => Self::Expired,
38 ApiAccessLeaseStatus::Revoked => Self::Revoked,
39 ApiAccessLeaseStatus::__Unknown(_) => Self::Unknown,
40 }
41 }
42}
43
44#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
52#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
53#[serde(rename_all = "camelCase")]
54pub struct AccessLeaseView {
55 pub id: AccessLeaseId,
57 pub request_id: AccessRequestId,
59 pub cipher_id: CipherId,
61 pub collection_id: CollectionId,
63 pub organization_id: Option<OrganizationId>,
65 pub requester_id: UserId,
67 pub status: AccessLeaseStatus,
69 pub not_before: DateTime<Utc>,
71 pub not_after: DateTime<Utc>,
73 pub revoked_at: Option<DateTime<Utc>>,
75 pub revoked_by_user_id: Option<UserId>,
77}
78
79impl TryFrom<AccessLeaseResponseModel> for AccessLeaseView {
80 type Error = LeasingError;
81
82 fn try_from(response: AccessLeaseResponseModel) -> Result<Self, Self::Error> {
83 Ok(Self {
84 id: AccessLeaseId::new(require!(response.id)),
85 request_id: AccessRequestId::new(require!(response.request_id)),
86 cipher_id: CipherId::new(require!(response.cipher_id)),
87 collection_id: CollectionId::new(require!(response.collection_id)),
88 organization_id: response.organization_id.map(OrganizationId::new),
89 requester_id: UserId::new(require!(response.requester_id)),
90 status: AccessLeaseStatus::from(require!(response.status)),
91 not_before: require!(response.not_before).parse()?,
92 not_after: require!(response.not_after).parse()?,
93 revoked_at: response.revoked_at.map(|d| d.parse()).transpose()?,
94 revoked_by_user_id: response.revoked_by_user_id.map(UserId::new),
95 })
96 }
97}
98
99#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
101#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
102#[serde(rename_all = "camelCase")]
103pub struct AccessLeaseExtensionRequest {
104 pub duration_seconds: Option<NonZeroU32>,
107 pub reason: String,
109}
110
111impl From<AccessLeaseExtensionRequest> for AccessLeaseExtensionRequestModel {
112 fn from(request: AccessLeaseExtensionRequest) -> Self {
113 Self {
114 duration_seconds: request.duration_seconds.map(|d| d.get() as i32),
115 reason: request.reason,
116 }
117 }
118}
119
120#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
122#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
123#[serde(rename_all = "camelCase")]
124pub struct AccessLeaseRevokeRequest {
125 pub reason: Option<String>,
127}
128
129impl From<AccessLeaseRevokeRequest> for AccessLeaseRevokeRequestModel {
130 fn from(request: AccessLeaseRevokeRequest) -> Self {
131 Self {
132 reason: request.reason,
133 }
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use std::num::NonZeroU32;
140
141 use super::*;
142
143 #[test]
144 fn access_lease_extension_request_converts_to_model() {
145 let request = AccessLeaseExtensionRequest {
146 duration_seconds: NonZeroU32::new(3600),
147 reason: "Need more time".to_string(),
148 };
149
150 let model = AccessLeaseExtensionRequestModel::from(request);
151
152 assert_eq!(model.duration_seconds, Some(3600));
153 assert_eq!(model.reason, "Need more time".to_string());
154 }
155}