use std::collections::HashMap;
use crate::credentials::Credential;
use crate::errors::ResultAuth;
use crate::forms::{EmailPassForm, UserPassForm};
use crate::properties::Properties;
pub const REALM_DEFAULT: &str = "GLOBAL";
pub trait Identity {
fn identity_id(&self) -> &str;
fn display_name(&self) -> Option<String> {
None
}
fn realm(&self) -> &str {
REALM_DEFAULT
}
fn permissions(&self) -> &[String] {
&[]
}
fn roles(&self) -> &[String] {
&[]
}
fn credentials(&self) -> Vec<Credential> {
vec![]
}
fn properties(&self) -> Option<HashMap<String, Properties>> {
None
}
}
pub trait IdentityProvider<Credential, Token> {
type Identity: Identity;
fn find(&self, id: &str) -> ResultAuth<Option<Self::Identity>>;
fn find_by_token(&self, token: &Token) -> ResultAuth<Option<Self::Identity>>;
fn logout(&self, token: &Token) -> ResultAuth<bool>;
}
pub trait IdentityProviderUserPwd<Token>: IdentityProvider<UserPassForm, Token> {
fn login(&self, identity: &UserPassForm) -> ResultAuth<Token> {
self.verify_password(identity)
}
fn verify_password(&self, credentials: &UserPassForm) -> ResultAuth<Token>;
}
pub trait IdentityProviderEmailPwd<Token>: IdentityProvider<EmailPassForm, Token> {
fn login(&self, identity: &EmailPassForm) -> ResultAuth<Token> {
self.verify_password(identity)
}
fn verify_password(&self, credentials: &EmailPassForm) -> ResultAuth<Token>;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::errors::ResultPwd;
use crate::password::{Password, PasswordIsSafe};
use crate::prelude::AuthError;
use crate::users::UserPass;
const TEST_PWD: &str = "1";
const USER_1: &str = "user1";
const USER_2: &str = "user2";
struct ByPass {}
impl PasswordIsSafe for ByPass {
fn is_safe(&self, _raw: &str) -> ResultPwd<()> {
Ok(())
}
}
struct TestProvider {
users: [UserPass; 2],
}
impl TestProvider {
pub fn new() -> Self {
let p = Password::hash(TEST_PWD, ByPass {}).unwrap();
let u1 = UserPass::new(USER_1, p.clone());
let u2 = UserPass::new(USER_2, p);
TestProvider { users: [u1, u2] }
}
}
impl IdentityProvider<UserPassForm, String> for TestProvider {
type Identity = UserPass;
fn find(&self, id: &str) -> ResultAuth<Option<Self::Identity>> {
Ok(self.users.iter().find(|x| x.identity_id() == id).cloned())
}
fn find_by_token(&self, _token: &String) -> ResultAuth<Option<Self::Identity>> {
todo!()
}
fn logout(&self, _token: &String) -> ResultAuth<bool> {
Ok(true)
}
}
impl IdentityProviderUserPwd<String> for TestProvider {
fn verify_password(&self, credentials: &UserPassForm) -> ResultAuth<String> {
if let Some(user) = self.find(&credentials.username)? {
user.pwd.validate_password(&credentials.pwd)?;
Ok(credentials.username.clone())
} else {
Err(AuthError::UserNotFound {
named: credentials.username.clone(),
})
}
}
}
#[test]
fn user_provider() {
let idp = TestProvider::new();
assert!(idp.find(USER_1).map(|x| x.is_some()).unwrap_or(false));
let mut form = UserPassForm::new(USER_1, "wrong");
assert!(idp.login(&form).is_err());
form.pwd = TEST_PWD.into();
assert!(idp.login(&form).is_ok());
}
}