Can create accounts automatically for a given upstream provider
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-10-29 11:30:45 +01:00
parent 764ad3d5a1
commit 9a599fdde2
8 changed files with 130 additions and 15 deletions

View File

@@ -1,10 +1,11 @@
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};
use crate::utils::err::Res;
use crate::utils::string_utils::is_acceptable_login;
use actix::{Actor, Context, Handler, Message, MessageResult};
use light_openid::primitives::OpenIDUserInfo;
/// User storage interface
pub trait UsersSyncBackend {
@@ -38,6 +39,8 @@ pub enum LoginResult {
LocalAuthForbidden,
AuthFromProviderForbidden,
Success(Box<User>),
AccountAutoCreated(Box<User>),
CannotAutoCreateAccount(String),
}
#[derive(Message)]
@@ -51,6 +54,7 @@ pub struct LocalLoginRequest {
#[rtype(LoginResult)]
pub struct ProviderLoginRequest {
pub email: String,
pub user_info: OpenIDUserInfo,
pub provider: Provider,
}
@@ -187,7 +191,86 @@ impl Handler<ProviderLoginRequest> for UsersActor {
log::error!("Failed to find user! {e}");
MessageResult(LoginResult::Error)
}
Ok(None) => MessageResult(LoginResult::AccountNotFound),
Ok(None) => {
// Check if automatic account creation is enabled for this provider
if !msg.provider.allow_auto_account_creation {
return MessageResult(LoginResult::AccountNotFound);
}
// Extract username for account creation
let mut username = msg
.user_info
.preferred_username
.unwrap_or(msg.email.to_string());
// Determine username from email, if necessary
if !is_acceptable_login(&username)
|| matches!(
self.manager.find_by_username_or_email(&username),
Ok(Some(_))
)
{
username = msg.email.clone();
}
// Check if username is already taken
if matches!(
self.manager.find_by_username_or_email(&username),
Ok(Some(_))
) {
return MessageResult(LoginResult::CannotAutoCreateAccount(format!(
"username {username} is already taken!"
)));
}
if !is_acceptable_login(&username) {
return MessageResult(LoginResult::CannotAutoCreateAccount(
"could not determine acceptable login for user!".to_string(),
));
}
// Automatic account creation
let user_id = match self.manager.create_user_account(GeneralSettings {
uid: UserID::random(),
username,
first_name: msg.user_info.given_name.unwrap_or_default(),
last_name: msg.user_info.family_name.unwrap_or_default(),
email: msg.email.to_string(),
enabled: true,
two_factor_exemption_after_successful_login: false,
is_admin: false,
}) {
Ok(u) => u,
Err(e) => {
log::error!("Failed to create user account! {e}");
return MessageResult(LoginResult::CannotAutoCreateAccount(
"missing some user information".to_string(),
));
}
};
// Mark the provider as the only authorized source
if let Err(e) = self.manager.set_authorized_authentication_sources(
&user_id,
AuthorizedAuthenticationSources {
local: false,
upstream: vec![msg.provider.id],
},
) {
log::error!(
"Failed to set authorized authentication sources for newly created account! {e}"
);
}
// Extract user information to return them
let Ok(Some(user)) = self.manager.find_by_user_id(&user_id) else {
return MessageResult(LoginResult::CannotAutoCreateAccount(
"failed to get created user information".to_string(),
));
};
MessageResult(LoginResult::AccountAutoCreated(Box::new(user)))
}
Ok(Some(user)) => {
if !user.can_login_from_provider(&msg.provider) {
return MessageResult(LoginResult::AuthFromProviderForbidden);