1use super::{SqlxStore, entities::scim_user as user};
3use crate::{
4 model::{self, ActiveRow},
5 pool::Exec,
6};
7use alibi_core::{
8 AuthResult, AuthSchema,
9 store::{ReplaceScimUser, ScimProjectionPreparedUser, ScimUserMutationContext, ScimUserRecord},
10};
11impl<S: AuthSchema> SqlxStore<S> {
12 pub(super) async fn lock_scim_user_with_exec(
13 &self,
14 exec: Exec<'_>,
15 id: &str,
16 connection: &str,
17 domain: &str,
18 attributes: Option<&alibi_core::store::ScimUserAttributes>,
19 now: chrono::DateTime<chrono::Utc>,
20 ) -> AuthResult<ScimUserMutationContext> {
21 let mut source = self
22 .scim_user_owned_with_exec(exec, id, connection, domain)
23 .await?;
24 let subject = self
25 .acquire_scim_subject_with_exec(exec, &source.user_id, now, attributes.is_none())
26 .await?;
27 if let Some(attributes) = attributes {
28 self.assert_scim_user_keys_with_exec(
29 exec,
30 connection,
31 &attributes.user_name_key,
32 attributes.external_id_key.as_deref(),
33 None,
34 Some(id),
35 )
36 .await?;
37 } else {
38 let after = self
39 .scim_user_owned_with_exec(exec, id, connection, domain)
40 .await?;
41 if after.user_id != source.user_id {
42 return Err(alibi_core::AuthError::Api {
43 status: 409,
44 code: None,
45 message: "The SCIM User identity changed concurrently".into(),
46 });
47 }
48 source = after;
49 }
50 Ok(ScimUserMutationContext {
51 user_id: source.user_id.clone(),
52 source: source.into(),
53 manage_profile: subject.profile_source_id.as_deref() == Some(id),
54 })
55 }
56 pub(super) async fn replace_scim_user_with_exec(
57 &self,
58 exec: Exec<'_>,
59 id: &str,
60 data: ReplaceScimUser,
61 roles: bool,
62 ) -> AuthResult<(ScimUserRecord, Vec<ScimProjectionPreparedUser>)> {
63 let current = self
64 .scim_user_owned_with_exec(exec, id, &data.connection_id, &data.provisioning_domain_id)
65 .await?;
66 self.assert_scim_user_keys_with_exec(
67 exec,
68 &data.connection_id,
69 &data.attributes.user_name_key,
70 data.attributes.external_id_key.as_deref(),
71 None,
72 Some(id),
73 )
74 .await?;
75 let mut row = ActiveRow::new();
76 row.set("id", id);
77 row.set("userName", data.attributes.user_name.clone());
78 row.set("userNameKey", data.attributes.user_name_key.clone());
79 row.set("primaryEmail", data.attributes.primary_email.clone());
80 row.set(
81 "workEmailValueIndex",
82 data.attributes.work_email_value_index.clone(),
83 );
84 row.set("emailValueIndex", data.attributes.email_value_index.clone());
85 row.set("displayName", data.attributes.display_name.clone());
86 row.set("formattedName", data.attributes.formatted_name.clone());
87 row.set("givenName", data.attributes.given_name.clone());
88 row.set("familyName", data.attributes.family_name.clone());
89 row.set(
90 "serializedEmails",
91 data.attributes.serialized_emails.clone(),
92 );
93 row.set(
94 "serializedAttributes",
95 data.attributes.serialized_attributes.clone(),
96 );
97 row.set("externalId", data.attributes.external_id.clone());
98 row.set("externalIdKey", data.attributes.external_id_key.clone());
99 row.set("active", data.attributes.active);
100 row.set("updatedAt", data.updated_at);
101 let stored = model::update::<user::Model>(exec, &row)
102 .await?
103 .ok_or_else(|| alibi_core::AuthError::Api {
104 status: 404,
105 code: None,
106 message: "SCIM User not found".into(),
107 })?;
108 let states = self
109 .prepare_scim_projection_for_users_with_exec(
110 exec,
111 &data.provisioning_domain_id,
112 vec![current.user_id],
113 roles,
114 )
115 .await?;
116 Ok((
117 ScimUserRecord {
118 id: stored.id,
119 connection_id: stored.connection_id,
120 provisioning_domain_id: stored.provisioning_domain_id,
121 user_name: stored.user_name,
122 primary_email: stored.primary_email,
123 display_name: stored.display_name,
124 active: stored.active,
125 external_id: stored.external_id,
126 formatted_name: stored.formatted_name,
127 given_name: stored.given_name,
128 family_name: stored.family_name,
129 serialized_emails: stored.serialized_emails,
130 serialized_attributes: stored.serialized_attributes,
131 created_at: stored.created_at,
132 updated_at: stored.updated_at,
133 },
134 states,
135 ))
136 }
137}
138
139impl From<user::Model> for ScimUserRecord {
140 fn from(row: user::Model) -> Self {
141 Self {
142 id: row.id,
143 connection_id: row.connection_id,
144 provisioning_domain_id: row.provisioning_domain_id,
145 user_name: row.user_name,
146 primary_email: row.primary_email,
147 display_name: row.display_name,
148 active: row.active,
149 external_id: row.external_id,
150 formatted_name: row.formatted_name,
151 given_name: row.given_name,
152 family_name: row.family_name,
153 serialized_emails: row.serialized_emails,
154 serialized_attributes: row.serialized_attributes,
155 created_at: row.created_at,
156 updated_at: row.updated_at,
157 }
158 }
159}