use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value as Json};
#[derive(Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct User {
pub id: String,
pub user_name: String,
pub normalized_user_name: String,
pub email: Option<String>,
pub normalized_email: Option<String>,
pub email_confirmed: bool,
pub password_hash: Option<String>,
pub security_stamp: String,
pub concurrency_stamp: String,
pub phone_number: Option<String>,
pub phone_number_confirmed: bool,
pub two_factor_enabled: bool,
pub lockout_end: Option<i64>,
pub lockout_enabled: bool,
pub access_failed_count: i32,
#[serde(default)]
pub extra: Map<String, Json>,
}
impl std::fmt::Debug for User {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("User")
.field("id", &self.id)
.field("user_name", &self.user_name)
.field("email_confirmed", &self.email_confirmed)
.field(
"password_hash",
&self
.password_hash
.as_ref()
.map(|_| "[redacted]"),
)
.field("security_stamp", &"[redacted]")
.field("two_factor_enabled", &self.two_factor_enabled)
.field("lockout_end", &self.lockout_end)
.field("access_failed_count", &self.access_failed_count)
.finish_non_exhaustive()
}
}
impl User {
pub fn new(user_name: &str) -> User {
User {
user_name: user_name.to_string(),
..User::default()
}
}
pub fn with_email(mut self, email: &str) -> User {
self.email = Some(email.to_string());
self
}
pub fn data<X: DeserializeOwned>(&self) -> crate::Result<X> {
serde_json::from_value(Json::Object(self.extra.clone()))
.map_err(|e| crate::Error::Internal(format!("user data: {e}")))
}
pub fn set_data<X: Serialize>(&mut self, data: &X) -> crate::Result<()> {
match serde_json::to_value(data) {
Ok(Json::Object(map)) => {
self.extra = map;
Ok(())
}
_ => Err(crate::Error::Internal(
"user data must serialize to a JSON object".into(),
)),
}
}
pub fn is_locked_out(&self, now: i64) -> bool {
self.lockout_enabled
&& self
.lockout_end
.is_some_and(|end| end > now)
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct Role {
pub id: String,
pub name: String,
pub normalized_name: String,
pub concurrency_stamp: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Claim {
pub kind: String,
pub value: String,
}
impl Claim {
pub fn new(kind: &str, value: &str) -> Claim {
Claim {
kind: kind.to_string(),
value: value.to_string(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct UserLogin {
pub provider: String,
pub provider_key: String,
pub display_name: Option<String>,
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Passkey {
pub credential_id: Vec<u8>,
pub public_key: Vec<u8>,
pub name: Option<String>,
pub created_at: i64,
pub sign_count: u32,
pub transports: Vec<String>,
pub user_verified: bool,
pub backup_eligible: bool,
pub backed_up: bool,
}
impl std::fmt::Debug for Passkey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Passkey")
.field("name", &self.name)
.field("created_at", &self.created_at)
.field("sign_count", &self.sign_count)
.finish_non_exhaustive()
}
}
pub const INTERNAL_PROVIDER: &str = "[zeyn]";
pub const AUTHENTICATOR_KEY: &str = "authenticator_key";
pub const RECOVERY_CODES: &str = "recovery_codes";
pub const AUTHENTICATOR_STEP: &str = "authenticator_step";