Add authentication from upstream providers (#107)
All checks were successful
continuous-integration/drone/push Build is passing

Let BasicOIDC delegate authentication to upstream providers (Google, GitHub, GitLab, Keycloak...)

Reviewed-on: #107
This commit is contained in:
2023-04-27 10:10:28 +00:00
parent 4f7c56a4b8
commit 9b18b787a9
39 changed files with 1740 additions and 189 deletions

View File

@ -1,5 +1,6 @@
use std::net::IpAddr;
use crate::data::provider::{Provider, ProviderID};
use actix::{Actor, Context, Handler, Message, MessageResult};
use crate::data::user::{FactorID, GeneralSettings, GrantedClients, TwoFactor, User, UserID};
@ -8,6 +9,7 @@ use crate::utils::err::Res;
/// User storage interface
pub trait UsersSyncBackend {
fn find_by_username_or_email(&self, u: &str) -> Res<Option<User>>;
fn find_by_email(&self, u: &str) -> Res<Option<User>>;
fn find_by_user_id(&self, id: &UserID) -> Res<Option<User>>;
fn get_entire_users_list(&self) -> Res<Vec<User>>;
fn create_user_account(&mut self, settings: GeneralSettings) -> Res<UserID>;
@ -19,6 +21,11 @@ pub trait UsersSyncBackend {
fn save_new_successful_2fa_authentication(&mut self, id: &UserID, ip: IpAddr) -> Res;
fn clear_2fa_login_history(&mut self, id: &UserID) -> Res;
fn delete_account(&mut self, id: &UserID) -> Res;
fn set_authorized_authentication_sources(
&mut self,
id: &UserID,
sources: AuthorizedAuthenticationSources,
) -> Res;
fn set_granted_2fa_clients(&mut self, id: &UserID, clients: GrantedClients) -> Res;
}
@ -28,16 +35,25 @@ pub enum LoginResult {
AccountNotFound,
InvalidPassword,
AccountDisabled,
LocalAuthForbidden,
AuthFromProviderForbidden,
Success(Box<User>),
}
#[derive(Message)]
#[rtype(LoginResult)]
pub struct LoginRequest {
pub struct LocalLoginRequest {
pub login: String,
pub password: String,
}
#[derive(Message)]
#[rtype(LoginResult)]
pub struct ProviderLoginRequest {
pub email: String,
pub provider: Provider,
}
#[derive(Message)]
#[rtype(GetUserResult)]
pub struct GetUserRequest(pub UserID);
@ -88,6 +104,16 @@ pub struct AddSuccessful2FALogin(pub UserID, pub IpAddr);
#[rtype(result = "bool")]
pub struct Clear2FALoginHistory(pub UserID);
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct AuthorizedAuthenticationSources {
pub local: bool,
pub upstream: Vec<ProviderID>,
}
#[derive(Message)]
#[rtype(result = "bool")]
pub struct SetAuthorizedAuthenticationSources(pub UserID, pub AuthorizedAuthenticationSources);
#[derive(Message)]
#[rtype(result = "bool")]
pub struct SetGrantedClients(pub UserID, pub GrantedClients);
@ -119,10 +145,10 @@ impl Actor for UsersActor {
type Context = Context<Self>;
}
impl Handler<LoginRequest> for UsersActor {
type Result = MessageResult<LoginRequest>;
impl Handler<LocalLoginRequest> for UsersActor {
type Result = MessageResult<LocalLoginRequest>;
fn handle(&mut self, msg: LoginRequest, _ctx: &mut Self::Context) -> Self::Result {
fn handle(&mut self, msg: LocalLoginRequest, _ctx: &mut Self::Context) -> Self::Result {
match self.manager.find_by_username_or_email(&msg.login) {
Err(e) => {
log::error!("Failed to find user! {}", e);
@ -142,6 +168,35 @@ impl Handler<LoginRequest> for UsersActor {
return MessageResult(LoginResult::AccountDisabled);
}
if !user.allow_local_login {
return MessageResult(LoginResult::LocalAuthForbidden);
}
MessageResult(LoginResult::Success(Box::new(user)))
}
}
}
}
impl Handler<ProviderLoginRequest> for UsersActor {
type Result = MessageResult<ProviderLoginRequest>;
fn handle(&mut self, msg: ProviderLoginRequest, _ctx: &mut Self::Context) -> Self::Result {
match self.manager.find_by_email(&msg.email) {
Err(e) => {
log::error!("Failed to find user! {}", e);
MessageResult(LoginResult::Error)
}
Ok(None) => MessageResult(LoginResult::AccountNotFound),
Ok(Some(user)) => {
if !user.can_login_from_provider(&msg.provider) {
return MessageResult(LoginResult::AuthFromProviderForbidden);
}
if !user.enabled {
return MessageResult(LoginResult::AccountDisabled);
}
MessageResult(LoginResult::Success(Box::new(user)))
}
}
@ -241,6 +296,29 @@ impl Handler<Clear2FALoginHistory> for UsersActor {
}
}
impl Handler<SetAuthorizedAuthenticationSources> for UsersActor {
type Result = <SetAuthorizedAuthenticationSources as actix::Message>::Result;
fn handle(
&mut self,
msg: SetAuthorizedAuthenticationSources,
_ctx: &mut Self::Context,
) -> Self::Result {
match self
.manager
.set_authorized_authentication_sources(&msg.0, msg.1)
{
Ok(_) => true,
Err(e) => {
log::error!(
"Failed to set authorized authentication sources for user! {}",
e
);
false
}
}
}
}
impl Handler<SetGrantedClients> for UsersActor {
type Result = <SetGrantedClients as actix::Message>::Result;
fn handle(&mut self, msg: SetGrantedClients, _ctx: &mut Self::Context) -> Self::Result {