Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
9.5 kB · 403 lines
Rust
at develop
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404use std::{ sync::{Arc, RwLock}, time::Duration,};
use base64::{Engine, prelude::BASE64_STANDARD_NO_PAD};use reqwest::{Method, header::HeaderMap};use serde::{Deserialize, Deserializer, Serialize, Serializer};use serde_json::{Value, json};
use crate::{Error, JsonObject, PocketBase, PocketBaseInner, record::RecordService};
#[derive(Debug, Clone)]pub struct AuthStore { pub token: String, pub record: RecordModel,}
#[derive(Clone, Debug, Deserialize, Serialize)]pub struct AuthMethodList { pub mfa: AuthMethodMfa, pub oauth2: AuthMethodOAuth2, pub otp: AuthMethodOtp, pub password: AuthMethodPassword,}
#[derive(Clone, Debug, Deserialize, Serialize)]pub struct AuthMethodMfa { #[serde( deserialize_with = "deserialize_duration", serialize_with = "serialize_duration" )] pub duration: Duration, pub enabled: bool,}
#[derive(Clone, Debug, Deserialize, Serialize)]pub struct AuthMethodOAuth2 { pub enabled: bool, pub providers: Vec<AuthMethodProvider>,}
#[derive(Clone, Debug, Deserialize, Serialize)]pub struct AuthMethodOtp { #[serde( deserialize_with = "deserialize_duration", serialize_with = "serialize_duration" )] pub duration: Duration, pub enabled: bool,}
#[derive(Clone, Debug, Deserialize, Serialize)]#[serde(rename_all = "camelCase")]pub struct AuthMethodProvider { pub auth_url: String, pub code_challenge: String, pub code_challenge_method: String, pub code_verifier: String, pub display_name: String, pub name: String, pub pkce: Option<bool>, pub state: String,}
#[derive(Clone, Debug, Deserialize, Serialize)]#[serde(rename_all = "camelCase")]pub struct AuthMethodPassword { pub enabled: bool, pub identity_fields: Vec<String>,}
#[derive(Debug, Clone)]pub struct AuthRefreshBuilder<'a> { pub(crate) record_service: RecordService<'a>, pub(crate) expand_fields: Option<&'a str>, pub(crate) filter_fields: Option<&'a str>,}
#[derive(Debug, Clone)]pub struct AuthWithOtpBuilder<'a> { pub(crate) record_service: RecordService<'a>, pub(crate) otp_id: &'a str, pub(crate) password: &'a str, pub(crate) expand_fields: Option<&'a str>, pub(crate) filter_fields: Option<&'a str>,}
#[derive(Debug, Clone)]pub struct AuthWithPasswordBuilder<'a> { pub(crate) record_service: RecordService<'a>, pub(crate) identity: &'a str, pub(crate) password: &'a str, pub(crate) expand_fields: Option<&'a str>, pub(crate) filter_fields: Option<&'a str>,}
#[derive(Debug, Clone)]pub struct ImpersonateBuilder<'a> { pub(crate) record_service: RecordService<'a>, pub(crate) id: &'a str, pub(crate) duration: Option<Duration>, pub(crate) expand_fields: Option<&'a str>, pub(crate) filter_fields: Option<&'a str>,}
#[derive(Debug, Deserialize, Clone)]pub struct RecordAuth { token: String, record: RecordModel,}
#[derive(Debug, Deserialize, Clone)]#[serde(transparent)]pub struct RecordModel { pub(crate) data: JsonObject,}
fn deserialize_duration<'de, D>(deserializer: D) -> Result<Duration, D::Error>where D: Deserializer<'de>,{ u64::deserialize(deserializer).map(Duration::from_secs)}
#[cfg(target_arch = "wasm32")]fn time_since_unix_epoch() -> u64 { use web_time::{SystemTime, UNIX_EPOCH};
SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs()}
#[cfg(not(target_arch = "wasm32"))]fn time_since_unix_epoch() -> u64 { use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs()}
fn serialize_duration<S>(dur: &Duration, serializer: S) -> Result<S::Ok, S::Error>where S: Serializer,{ dur.as_secs().serialize(serializer)}
impl AuthStore { pub fn is_valid(&self) -> bool { let parts: Vec<&str> = self.token.split(".").collect(); if parts.len() != 3 { return false; }
let Ok(token_str) = String::from_utf8(BASE64_STANDARD_NO_PAD.decode(parts[1]).unwrap()) else { return false; }; let Ok(data) = serde_json::from_str::<JsonObject>(&token_str) else { return false; };
match data.get("exp") { Some(Value::Number(exp)) => { if let Some(exp) = exp.as_u64() { exp > time_since_unix_epoch() } else { false } } _ => false, } }}
impl<'a> AuthRefreshBuilder<'a> { pub fn expand_fields(mut self, field_expression: &'a str) -> Self { self.expand_fields = Some(field_expression); self }
pub fn filter_fields(mut self, field_expression: &'a str) -> Self { self.filter_fields = Some(field_expression); self }
pub async fn send(self) -> Result<RecordAuth, Error> { let mut params = Vec::new(); if let Some(expand) = self.expand_fields { params.push(("expand", expand)); } if let Some(fields) = self.filter_fields { params.push(("fields", fields)); }
let auth: RecordAuth = self .record_service .client .send( &format!( "/api/collections/{}/auth-refresh", self.record_service.collection ), Method::POST, HeaderMap::new(), ¶ms, None::<&()>, ) .await?; self.record_service.client.with_auth_store(AuthStore { token: auth.token.clone(), record: auth.record.clone(), }); debug_assert!({ let read = self.record_service.client.inner.read().unwrap(); read.auth_store.as_ref().unwrap().is_valid() }); Ok(auth) }}
impl<'a> AuthWithOtpBuilder<'a> { pub fn expand_fields(mut self, field_expression: &'a str) -> Self { self.expand_fields = Some(field_expression); self }
pub fn filter_fields(mut self, field_expression: &'a str) -> Self { self.filter_fields = Some(field_expression); self }
pub async fn send(self) -> Result<RecordAuth, Error> { let body = json!({"otpId": self.otp_id, "password": self.password}); let mut params = Vec::new(); if let Some(expand) = self.expand_fields { params.push(("expand", expand)); } if let Some(fields) = self.filter_fields { params.push(("fields", fields)); }
let auth: RecordAuth = self .record_service .client .send( &format!( "/api/collections/{}/auth-with-otp", self.record_service.collection ), Method::POST, HeaderMap::new(), ¶ms, Some(&body), ) .await?; self.record_service.client.with_auth_store(AuthStore { token: auth.token.clone(), record: auth.record.clone(), }); debug_assert!({ let read = self.record_service.client.inner.read().unwrap(); read.auth_store.as_ref().unwrap().is_valid() }); Ok(auth) }}
impl<'a> AuthWithPasswordBuilder<'a> { pub fn expand_fields(mut self, field_expression: &'a str) -> Self { self.expand_fields = Some(field_expression); self }
pub fn filter_fields(mut self, field_expression: &'a str) -> Self { self.filter_fields = Some(field_expression); self }
pub async fn send(self) -> Result<RecordAuth, Error> { let body = json!({"identity": self.identity, "password": self.password}); let mut params = Vec::new(); if let Some(expand) = self.expand_fields { params.push(("expand", expand)); } if let Some(fields) = self.filter_fields { params.push(("fields", fields)); }
let auth: RecordAuth = self .record_service .client .send( &format!( "/api/collections/{}/auth-with-password", self.record_service.collection ), Method::POST, HeaderMap::new(), ¶ms, Some(&body), ) .await?; self.record_service.client.with_auth_store(AuthStore { token: auth.token.clone(), record: auth.record.clone(), }); debug_assert!({ let read = self.record_service.client.inner.read().unwrap(); read.auth_store.as_ref().unwrap().is_valid() }); Ok(auth) }}
impl<'a> ImpersonateBuilder<'a> { /// Set the duration that the generated auth token will be valid. /// /// If duration is not set, then the generated auth token will fallback to the /// default collection auth token duration. pub fn duration(mut self, duration: Duration) -> Self { self.duration = Some(duration); self }
pub fn expand_fields(mut self, field_expression: &'a str) -> Self { self.expand_fields = Some(field_expression); self }
pub fn filter_fields(mut self, field_expression: &'a str) -> Self { self.filter_fields = Some(field_expression); self }
pub async fn send(self) -> Result<(PocketBase, RecordAuth), Error> { let mut params = Vec::new(); if let Some(expand) = self.expand_fields { params.push(("expand", expand)); } if let Some(fields) = self.filter_fields { params.push(("fields", fields)); }
let auth: RecordAuth = self .record_service .client .send( &format!( "/api/collections/{}/impersonate/{}", self.record_service.collection, self.id ), Method::POST, HeaderMap::new(), ¶ms, if let Some(dur) = self.duration { Some(json!({ "duration": dur.as_secs() })) } else { None } .as_ref(), ) .await?;
let client = { let inner = self.record_service.client.inner.read().unwrap(); PocketBase { inner: Arc::new(RwLock::new(PocketBaseInner { reqwest: inner.reqwest.clone(), auth_store: None, base_url: inner.base_url.clone(), lang: inner.lang.clone(), })), } };
client.with_auth_store(AuthStore { token: auth.token.clone(), record: auth.record.clone(), }); debug_assert!({ let read = client.inner.read().unwrap(); read.auth_store.as_ref().unwrap().is_valid() });
Ok((client, auth)) }}
impl RecordModel { pub fn as_object(&self) -> &'_ JsonObject { &self.data }
pub fn into_object(self) -> JsonObject { self.data }}