1use alloc::collections::BTreeMap;
4use alloc::string::String;
5
6#[cfg(feature = "schemars")]
7use schemars::JsonSchema;
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11use crate::{TeamId, TenantContext, TenantCtx, TenantId, UserId};
12
13#[derive(Clone, Debug, PartialEq, Eq, Hash)]
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16#[cfg_attr(feature = "schemars", derive(JsonSchema))]
17pub struct Impersonation {
18 pub actor_id: UserId,
20 #[cfg_attr(
22 feature = "serde",
23 serde(default, skip_serializing_if = "Option::is_none")
24 )]
25 pub reason: Option<String>,
26}
27
28#[derive(Clone, Debug, PartialEq, Eq, Hash)]
30#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
31#[cfg_attr(feature = "schemars", derive(JsonSchema))]
32pub struct TenantIdentity {
33 pub tenant_id: TenantId,
35 #[cfg_attr(
37 feature = "serde",
38 serde(default, skip_serializing_if = "Option::is_none")
39 )]
40 pub team_id: Option<TeamId>,
41 #[cfg_attr(
43 feature = "serde",
44 serde(default, skip_serializing_if = "Option::is_none")
45 )]
46 pub user_id: Option<UserId>,
47 #[cfg_attr(
49 feature = "serde",
50 serde(default, skip_serializing_if = "Option::is_none")
51 )]
52 pub impersonation: Option<Impersonation>,
53 #[cfg_attr(
55 feature = "serde",
56 serde(default, skip_serializing_if = "BTreeMap::is_empty")
57 )]
58 pub attributes: BTreeMap<String, String>,
59}
60
61impl TenantIdentity {
62 pub fn new(tenant_id: TenantId) -> Self {
64 Self {
65 tenant_id,
66 team_id: None,
67 user_id: None,
68 impersonation: None,
69 attributes: BTreeMap::new(),
70 }
71 }
72}
73
74impl From<&TenantCtx> for TenantIdentity {
75 fn from(ctx: &TenantCtx) -> Self {
76 Self {
77 tenant_id: ctx.tenant_id.clone(),
78 team_id: ctx.team_id.clone().or_else(|| ctx.team.clone()),
79 user_id: ctx.user_id.clone().or_else(|| ctx.user.clone()),
80 impersonation: ctx.impersonation.clone(),
81 attributes: ctx.attributes.clone(),
82 }
83 }
84}
85
86impl TenantCtx {
87 pub fn identity(&self) -> TenantIdentity {
89 TenantIdentity::from(self)
90 }
91
92 pub fn tenant_context(&self) -> TenantContext {
94 TenantContext::from(self)
95 }
96
97 pub fn impersonated_by(&self) -> Option<&Impersonation> {
99 self.impersonation.as_ref()
100 }
101
102 pub fn with_identity(mut self, identity: TenantIdentity) -> Self {
104 self.tenant = identity.tenant_id.clone();
105 self.tenant_id = identity.tenant_id;
106 self.team = identity.team_id.clone();
107 self.team_id = identity.team_id;
108 self.user = identity.user_id.clone();
109 self.user_id = identity.user_id;
110 self.impersonation = identity.impersonation;
111 self.attributes = identity.attributes;
112 self
113 }
114}