1use crate::utils::json::JsValue;
3use indexmap::{IndexMap, IndexSet};
4use serde_json::Value;
5use std::{fmt, future::Future, pin::Pin, sync::Arc};
6
7#[derive(Clone, Default)]
9pub struct FieldValues {
10 values: IndexMap<String, JsValue>,
11 adapter_fields: Option<Arc<FieldConfigs>>,
12 undefined_input_keys: IndexSet<String>,
13 transform_omitted: bool,
14 binding_names: IndexMap<String, String>,
15}
16
17impl FieldValues {
18 #[must_use]
19 pub fn new() -> Self {
20 Self::default()
21 }
22 #[must_use]
25 pub fn has_input_fields(&self) -> bool {
26 !self.values.is_empty() || !self.undefined_input_keys.is_empty()
27 }
28 #[must_use]
30 pub fn binding_name<'a>(&'a self, name: &'a str) -> &'a str {
31 self.binding_names.get(name).map_or(name, String::as_str)
32 }
33 pub fn apply_adapter_transforms(&mut self) -> crate::AuthResult<()> {
40 if self.adapter_fields.as_ref().is_some_and(|fields| {
41 fields
42 .values()
43 .any(|field| field.adapter_input_transform.is_some())
44 }) {
45 return Err(crate::AuthError::config(
46 "Asynchronous adapter transforms require apply_adapter_transforms_async",
47 ));
48 }
49 if let Some(fields) = self.adapter_fields.take() {
50 for (name, field) in &*fields {
51 if self.prepare_adapter_value(name, field)
52 && let Some(transform) = &field.transform
53 {
54 let value = transform(self.values.get(name))?;
55 self.set_adapter_value(name, value);
56 }
57 }
58 }
59 self.finish_adapter_transforms();
60 Ok(())
61 }
62
63 pub async fn apply_adapter_transforms_async(&mut self) -> crate::AuthResult<()> {
69 if let Some(fields) = self.adapter_fields.take() {
70 for (name, field) in &*fields {
71 if !self.prepare_adapter_value(name, field) {
72 continue;
73 }
74 let value = if let Some(transform) = &field.adapter_input_transform {
75 transform(self.values.get(name).cloned()).await.map_err(
76 |error| match error {
77 crate::AuthError::Internal(_) => {
78 crate::AuthError::CallbackFailure(Box::new(error))
79 }
80 error => error,
81 },
82 )?
83 } else if let Some(transform) = &field.transform {
84 transform(self.values.get(name))?
85 } else {
86 continue;
87 };
88 self.set_adapter_value(name, value);
89 }
90 }
91 self.finish_adapter_transforms();
92 Ok(())
93 }
94
95 fn prepare_adapter_value(&mut self, name: &str, field: &FieldConfig) -> bool {
96 if !self.transform_omitted && !self.values.contains_key(name) && field.on_update.is_none() {
97 return false;
98 }
99 if self.transform_omitted
100 && (!self.values.contains_key(name)
101 || (field.required && self.values.get(name).is_some_and(JsValue::is_null)))
102 && let Some(default) = &field.default
103 {
104 drop(self.values.insert(name.to_owned(), default.value()));
105 }
106 if !self.transform_omitted
107 && !self.values.contains_key(name)
108 && let Some(update) = &field.on_update
109 {
110 drop(self.values.insert(name.to_owned(), update()));
111 }
112 true
113 }
114 fn set_adapter_value(&mut self, name: &str, value: Option<JsValue>) {
115 match value {
116 Some(value) => {
117 drop(self.values.insert(name.to_owned(), value));
118 }
119 None => {
120 drop(self.values.shift_remove(name));
121 }
122 }
123 }
124 fn finish_adapter_transforms(&mut self) {
125 self.undefined_input_keys.clear();
126 self.transform_omitted = false;
127 }
128 pub fn preserve_creation_value(&mut self, name: &str, value: JsValue) {
131 if self
132 .adapter_fields
133 .as_ref()
134 .is_some_and(|fields| fields.contains_key(name))
135 {
136 let _ignored_or_insert = self.values.entry(name.to_owned()).or_insert(value);
137 }
138 }
139}
140
141impl fmt::Debug for FieldValues {
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 self.values.fmt(f)
144 }
145}
146
147impl std::ops::Deref for FieldValues {
148 type Target = IndexMap<String, JsValue>;
149 fn deref(&self) -> &Self::Target {
150 &self.values
151 }
152}
153
154impl std::ops::DerefMut for FieldValues {
155 fn deref_mut(&mut self) -> &mut Self::Target {
156 &mut self.values
157 }
158}
159
160impl From<IndexMap<String, JsValue>> for FieldValues {
161 fn from(values: IndexMap<String, JsValue>) -> Self {
162 Self {
163 values,
164 ..Self::default()
165 }
166 }
167}
168
169impl FromIterator<(String, JsValue)> for FieldValues {
170 fn from_iter<T: IntoIterator<Item = (String, JsValue)>>(iter: T) -> Self {
171 IndexMap::from_iter(iter).into()
172 }
173}
174
175impl IntoIterator for FieldValues {
176 type Item = (String, JsValue);
177 type IntoIter = indexmap::map::IntoIter<String, JsValue>;
178 fn into_iter(self) -> Self::IntoIter {
179 self.values.into_iter()
180 }
181}
182
183impl<'a> IntoIterator for &'a FieldValues {
184 type Item = (&'a String, &'a JsValue);
185 type IntoIter = indexmap::map::Iter<'a, String, JsValue>;
186 fn into_iter(self) -> Self::IntoIter {
187 self.values.iter()
188 }
189}
190
191impl<'a> IntoIterator for &'a mut FieldValues {
192 type Item = (&'a String, &'a mut JsValue);
193 type IntoIter = indexmap::map::IterMut<'a, String, JsValue>;
194 fn into_iter(self) -> Self::IntoIter {
195 self.values.iter_mut()
196 }
197}
198
199impl serde::Serialize for FieldValues {
200 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
201 self.values.serialize(serializer)
202 }
203}
204
205impl<'de> serde::Deserialize<'de> for FieldValues {
206 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
207 IndexMap::deserialize(deserializer).map(Self::from)
208 }
209}
210
211pub type FieldOutput = serde_json::Map<String, Value>;
212
213pub type FieldConfigs = IndexMap<String, FieldConfig>;
214
215pub type FieldValidator = Arc<dyn Fn(&JsValue) -> Result<JsValue, String> + Send + Sync>;
216pub type AsyncFieldValidator = Arc<
217 dyn Fn(JsValue) -> Pin<Box<dyn Future<Output = Result<JsValue, String>> + Send>> + Send + Sync,
218>;
219
220pub type FieldTransform =
222 Arc<dyn Fn(Option<&JsValue>) -> crate::AuthResult<Option<JsValue>> + Send + Sync>;
223
224pub type FieldOutputTransform = Arc<
226 dyn Fn(
227 Option<JsValue>,
228 ) -> Pin<Box<dyn Future<Output = crate::AuthResult<Option<JsValue>>> + Send>>
229 + Send
230 + Sync,
231>;
232
233#[derive(Clone)]
234pub enum FieldDefault {
235 Value(JsValue),
236 Callback(Arc<dyn Fn() -> JsValue + Send + Sync>),
237}
238
239impl fmt::Debug for FieldDefault {
240 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241 match self {
242 Self::Value(value) => f.debug_tuple("Value").field(value).finish(),
243 Self::Callback(_) => f.write_str("Callback(..)"),
244 }
245 }
246}
247
248impl FieldDefault {
249 #[must_use]
250 pub fn value(&self) -> JsValue {
251 match self {
252 Self::Value(value) => value.clone(),
253 Self::Callback(callback) => callback(),
254 }
255 }
256}
257
258#[derive(Clone)]
260pub struct FieldConfig {
261 pub schema: Value,
262 pub required: bool,
263 pub input: bool,
264 pub returned: bool,
265 pub default: Option<FieldDefault>,
266 pub validator: Option<FieldValidator>,
267 pub async_validator: Option<AsyncFieldValidator>,
268 pub output_validator: Option<FieldValidator>,
270 pub adapter_input_transform: Option<FieldOutputTransform>,
271 pub transform: Option<FieldTransform>,
272 pub output_transform: Option<FieldOutputTransform>,
273 pub on_update: Option<Arc<dyn Fn() -> JsValue + Send + Sync>>,
274 pub field_name: Option<String>,
276}
277
278impl fmt::Debug for FieldConfig {
279 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
280 f.debug_struct("FieldConfig")
281 .field("schema", &self.schema)
282 .field("required", &self.required)
283 .field("input", &self.input)
284 .field("returned", &self.returned)
285 .field("default", &self.default)
286 .field("validator", &self.validator.as_ref().map(|_| "callback"))
287 .field(
288 "async_validator",
289 &self.async_validator.as_ref().map(|_| "callback"),
290 )
291 .field(
292 "output_validator",
293 &self.output_validator.as_ref().map(|_| "callback"),
294 )
295 .field(
296 "adapter_input_transform",
297 &self.adapter_input_transform.as_ref().map(|_| "callback"),
298 )
299 .field("transform", &self.transform.as_ref().map(|_| "callback"))
300 .field(
301 "output_transform",
302 &self.output_transform.as_ref().map(|_| "callback"),
303 )
304 .field("on_update", &self.on_update.as_ref().map(|_| "callback"))
305 .field("field_name", &self.field_name)
306 .finish()
307 }
308}
309
310impl FieldConfig {
311 #[must_use]
312 pub fn new(schema: Value) -> Self {
313 Self {
314 schema,
315 required: false,
316 input: true,
317 returned: true,
318 default: None,
319 validator: None,
320 async_validator: None,
321 output_validator: None,
322 adapter_input_transform: None,
323 transform: None,
324 output_transform: None,
325 on_update: None,
326 field_name: None,
327 }
328 }
329 #[must_use]
330 pub fn default_value(mut self, value: impl Into<JsValue>) -> Self {
331 self.default = Some(FieldDefault::Value(value.into()));
332 self
333 }
334 #[must_use]
335 pub fn default_callback(
336 mut self,
337 callback: impl Fn() -> JsValue + Send + Sync + 'static,
338 ) -> Self {
339 self.default = Some(FieldDefault::Callback(Arc::new(callback)));
340 self
341 }
342 #[must_use]
343 pub const fn read_only(mut self) -> Self {
344 self.input = false;
345 self
346 }
347 #[must_use]
348 pub const fn hidden(mut self) -> Self {
349 self.returned = false;
350 self
351 }
352 #[must_use]
353 pub fn validate(
354 mut self,
355 validator: impl Fn(&JsValue) -> Result<JsValue, String> + Send + Sync + 'static,
356 ) -> Self {
357 self.validator = Some(Arc::new(validator));
358 self.async_validator = None;
359 self
360 }
361 #[must_use]
362 pub fn transform(
363 mut self,
364 transform: impl Fn(Option<&JsValue>) -> crate::AuthResult<Option<JsValue>>
365 + Send
366 + Sync
367 + 'static,
368 ) -> Self {
369 self.transform = Some(Arc::new(transform));
370 self
371 }
372
373 #[must_use]
376 pub fn validate_async<F, Fut>(mut self, validate: F) -> Self
377 where
378 F: Fn(JsValue) -> Fut + Send + Sync + 'static,
379 Fut: Future<Output = Result<JsValue, String>> + Send + 'static,
380 {
381 self.async_validator = Some(Arc::new(move |value| Box::pin(validate(value))));
382 self.validator = None;
383 self
384 }
385
386 #[must_use]
387 pub fn validate_output(
388 mut self,
389 validate: impl Fn(&JsValue) -> Result<JsValue, String> + Send + Sync + 'static,
390 ) -> Self {
391 self.output_validator = Some(Arc::new(validate));
392 self
393 }
394
395 #[must_use]
399 pub fn transform_adapter_input<F, Fut>(mut self, transform: F) -> Self
400 where
401 F: Fn(Option<JsValue>) -> Fut + Send + Sync + 'static,
402 Fut: Future<Output = crate::AuthResult<Option<JsValue>>> + Send + 'static,
403 {
404 self.adapter_input_transform = Some(Arc::new(move |value| Box::pin(transform(value))));
405 self
406 }
407
408 #[must_use]
409 pub fn transform_output<F, Fut>(mut self, transform: F) -> Self
410 where
411 F: Fn(Option<JsValue>) -> Fut + Send + Sync + 'static,
412 Fut: Future<Output = crate::AuthResult<Option<JsValue>>> + Send + 'static,
413 {
414 self.output_transform = Some(Arc::new(move |value| Box::pin(transform(value))));
415 self
416 }
417
418 #[must_use]
419 pub fn on_update(mut self, callback: impl Fn() -> JsValue + Send + Sync + 'static) -> Self {
420 self.on_update = Some(Arc::new(callback));
421 self
422 }
423
424 #[must_use]
425 pub fn field_name(mut self, name: impl Into<String>) -> Self {
426 self.field_name = Some(name.into());
427 self
428 }
429}
430
431#[derive(Debug)]
432pub enum FieldInputError {
433 Validation { code: &'static str, message: String },
434 Transform(crate::AuthError),
435}
436
437#[derive(Debug, Clone, Default)]
439pub struct SessionFields(pub IndexMap<String, FieldConfig>);
440
441impl SessionFields {
442 pub fn defaults(&self, values: &mut FieldValues) {
443 for (name, field) in &self.0 {
444 if let Some(default) = &field.default {
445 let _ignored_value = values
446 .entry(name.clone())
447 .or_insert_with(|| default.value());
448 }
449 }
450 }
451 pub fn parse_update(
456 &self,
457 input: &IndexMap<String, JsValue>,
458 ) -> Result<FieldValues, FieldInputError> {
459 self.parse(input, false)
460 }
461
462 pub fn parse_create(
465 &self,
466 input: &IndexMap<String, JsValue>,
467 ) -> Result<FieldValues, FieldInputError> {
468 self.parse(input, true)
469 }
470
471 fn parse(
472 &self,
473 input: &IndexMap<String, JsValue>,
474 creation: bool,
475 ) -> Result<FieldValues, FieldInputError> {
476 let mut parsed = FieldValues::new();
477 for (name, field) in &self.0 {
478 let Some(value) = input.get(name) else {
479 if creation {
480 if let Some(default) = &field.default {
481 drop(parsed.insert(name.clone(), default.value()));
482 } else if field.required {
483 return Err(FieldInputError::Validation {
484 code: "MISSING_FIELD",
485 message: format!("{name} is required"),
486 });
487 }
488 }
489 continue;
490 };
491 if !field.input {
492 if creation && let Some(default) = &field.default {
493 drop(parsed.insert(name.clone(), default.value()));
494 continue;
495 }
496 if truthy(value) {
497 return Err(FieldInputError::Validation {
498 code: "FIELD_NOT_ALLOWED",
499 message: format!("{name} is not allowed to be set"),
500 });
501 }
502 continue;
503 }
504 if let Some(validator) = &field.async_validator {
505 drop(validator(value.clone()));
506 return Err(FieldInputError::Transform(crate::AuthError::Upstream {
507 status: 500,
508 code: "ASYNC_VALIDATION_NOT_SUPPORTED",
509 message: "Async validation is not supported",
510 }));
511 }
512 let value = if let Some(validator) = &field.validator {
513 Some(
514 validator(value).map_err(|message| FieldInputError::Validation {
515 code: "VALIDATION_ERROR",
516 message: if message.is_empty() {
517 "Validation Error".into()
518 } else {
519 message
520 },
521 })?,
522 )
523 } else if let Some(transform) = &field.transform {
524 transform(Some(value)).map_err(FieldInputError::Transform)?
525 } else {
526 Some(value.clone())
527 };
528 match value {
529 Some(value) => {
530 drop(parsed.insert(name.clone(), value));
531 }
532 None => {
533 let _ignored_clone = parsed.undefined_input_keys.insert(name.clone());
534 }
535 }
536 }
537 Ok(parsed)
538 }
539}
540
541#[derive(Debug, Clone, Default)]
543pub struct UserFields(pub SessionFields);
544
545#[derive(Debug, Clone, Default)]
547pub struct AccountFields(pub SessionFields);
548
549#[derive(Debug, Clone, Default)]
551pub struct AdapterFieldPolicies {
552 pub user: SessionAdapterFields,
553 pub account: SessionAdapterFields,
554}
555
556#[derive(Debug, Clone, Default)]
559pub struct SessionAdapterFields(pub Arc<FieldConfigs>);
560
561impl SessionAdapterFields {
562 pub(crate) fn attach(&self, values: &mut FieldValues, creation: bool) {
563 values.transform_omitted = creation;
564 values.adapter_fields = Some(Arc::clone(&self.0));
565 values.binding_names = self
566 .0
567 .iter()
568 .filter_map(|(name, field)| {
569 field
570 .field_name
571 .as_ref()
572 .map(|physical| (name.clone(), physical.clone()))
573 })
574 .collect();
575 }
576
577 pub(crate) async fn record_output(
581 &self,
582 canonical: Value,
583 mut additional: FieldOutput,
584 base: Value,
585 ) -> crate::AuthResult<crate::AdapterOutput> {
586 let (Value::Object(canonical), Value::Object(mut base)) = (canonical, base) else {
587 return Err(crate::AuthError::internal(
588 "Adapter output must be an object",
589 ));
590 };
591 additional.extend(canonical);
592 for name in self.0.keys() {
593 if !additional.contains_key(name)
597 && let Some(value) = base.get(name)
598 {
599 drop(additional.insert(name.clone(), value.clone()));
600 }
601 drop(base.remove(name));
602 }
603 let mut output = crate::AdapterOutput::from_values(base);
604 output.extend(self.output(additional).await?);
605 Ok(output)
606 }
607
608 pub(crate) async fn output(
610 &self,
611 values: FieldOutput,
612 ) -> crate::AuthResult<crate::AdapterOutput> {
613 let mut output = crate::AdapterOutput::default();
614 for (name, field) in &*self.0 {
615 let value = values
616 .get(name)
617 .or_else(|| {
618 field
619 .field_name
620 .as_ref()
621 .and_then(|physical| values.get(physical))
622 })
623 .cloned()
624 .map(JsValue::from);
625 let value = if let Some(transform) = &field.output_transform {
626 transform(value).await.map_err(|error| match error {
627 crate::AuthError::Api { .. } | crate::AuthError::Upstream { .. } => error,
628 error => crate::AuthError::CallbackFailure(Box::new(error)),
629 })?
630 } else {
631 value
632 };
633 output.insert(
634 name.clone(),
635 value.map(|value| value.to_json_value()).transpose()?,
636 );
637 }
638 Ok(output)
639 }
640}
641
642fn truthy(value: &JsValue) -> bool {
643 match value {
644 JsValue::Null => false,
645 JsValue::Bool(value) => *value,
646 JsValue::Number(value) => *value != 0.0 && !value.is_nan(),
647 JsValue::String(value) => !value.is_empty(),
648 JsValue::Array(_) | JsValue::Object(_) => true,
649 }
650}