Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion auth/server/src/api/login/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub async fn sign_up_local_user<I: AuthImpl + ?Sized>(

let no_users_exist = auth.no_users_exist().await?;

if auth.registration_disabled() && !no_users_exist {
if auth.local_registration_disabled() && !no_users_exist {
return Err(
anyhow!("User registration is disabled")
.status_code(StatusCode::UNAUTHORIZED),
Expand Down
166 changes: 165 additions & 1 deletion auth/server/src/api/login/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ pub fn get_login_options<I: AuthImpl + ?Sized>(
.google_config()
.map(|config| config.enabled())
.unwrap_or_default(),
registration_disabled: auth.registration_disabled(),
registration_disabled: auth.local_registration_disabled(),
}
}

Expand All @@ -128,6 +128,170 @@ impl Resolve<LoginArgs> for GetLoginOptions {
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::AuthImpl;
use mogh_auth_client::config::OidcConfig;

/// Minimal AuthImpl for testing
struct TestAuth {
local: bool,
oidc: Option<OidcConfig>,
registration_disabled: bool,
local_registration_disabled: Option<bool>,
oidc_registration_disabled: Option<bool>,
}

impl TestAuth {
fn default_test() -> Self {
Self {
local: true,
oidc: None,
registration_disabled: false,
local_registration_disabled: None,
oidc_registration_disabled: None,
}
}
}

impl AuthImpl for TestAuth {
fn new() -> Self {
Self::default_test()
}

fn local_auth_enabled(&self) -> bool {
self.local
}

fn oidc_config(&self) -> Option<&OidcConfig> {
self.oidc.as_ref()
}

fn registration_disabled(&self) -> bool {
self.registration_disabled
}

fn local_registration_disabled(&self) -> bool {
self
.local_registration_disabled
.unwrap_or_else(|| self.registration_disabled())
}

fn oidc_registration_disabled(&self) -> bool {
self
.oidc_registration_disabled
.unwrap_or_else(|| self.registration_disabled())
}

fn get_user(
&self,
_user_id: String,
) -> crate::DynFuture<mogh_error::Result<crate::user::BoxAuthUser>>
{
Box::pin(async {
Err(anyhow::anyhow!("not implemented").into())
})
}

fn handle_request_authentication(
&self,
_auth: crate::RequestAuthentication,
_require_user_enabled: bool,
_req: axum::extract::Request,
) -> crate::DynFuture<mogh_error::Result<axum::extract::Request>>
{
Box::pin(async {
Err(anyhow::anyhow!("not implemented").into())
})
}

fn jwt_provider(
&self,
) -> &crate::provider::jwt::JwtProvider {
panic!("not needed for these tests")
}
}

#[test]
fn test_default_granular_methods_delegate_to_registration_disabled()
{
// When granular overrides are None, they should
// fall back to the global registration_disabled flag.
let auth = TestAuth {
registration_disabled: true,
..TestAuth::default_test()
};
assert!(auth.local_registration_disabled());
assert!(auth.oidc_registration_disabled());
assert!(auth.github_registration_disabled());
assert!(auth.google_registration_disabled());
}

#[test]
fn test_global_disabled_local_override_enabled() {
// Global registration disabled, but local override allows it
let auth = TestAuth {
registration_disabled: true,
local_registration_disabled: Some(false),
..TestAuth::default_test()
};
assert!(!auth.local_registration_disabled());
assert!(auth.oidc_registration_disabled());
}

#[test]
fn test_global_enabled_local_override_disabled() {
// Global registration enabled, but local override blocks it
let auth = TestAuth {
registration_disabled: false,
local_registration_disabled: Some(true),
..TestAuth::default_test()
};
assert!(auth.local_registration_disabled());
assert!(!auth.oidc_registration_disabled());
}

#[test]
fn test_disable_local_allow_oidc() {
// The #1087 use case: disable local signup, allow OIDC
let auth = TestAuth {
registration_disabled: false,
local_registration_disabled: Some(true),
oidc_registration_disabled: Some(false),
..TestAuth::default_test()
};
assert!(auth.local_registration_disabled());
assert!(!auth.oidc_registration_disabled());
}

#[test]
fn test_registration_disabled_reflects_local_in_login_options() {
// registration_disabled in the response controls the Sign Up button,
// which is local-only. It should reflect local_registration_disabled.
let auth = TestAuth {
registration_disabled: false,
local_registration_disabled: Some(true),
oidc_registration_disabled: Some(false),
..TestAuth::default_test()
};
let opts = get_login_options(&auth);
assert!(opts.registration_disabled);
}

#[test]
fn test_registration_disabled_false_when_local_allowed() {
let auth = TestAuth {
registration_disabled: true,
local_registration_disabled: Some(false),
oidc_registration_disabled: Some(true),
..TestAuth::default_test()
};
let opts = get_login_options(&auth);
assert!(!opts.registration_disabled);
}
}

impl Resolve<LoginArgs> for ExchangeForJwt {
#[instrument("ExchangeForJwt", skip_all, fields(ip = ip.to_string()))]
async fn resolve(
Expand Down
2 changes: 1 addition & 1 deletion auth/server/src/api/named/github.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ pub async fn github_callback<I: AuthImpl>(
None => {
let no_users_exist = auth.no_users_exist().await?;

if auth.registration_disabled() && !no_users_exist {
if auth.github_registration_disabled() && !no_users_exist {
return Err(
anyhow!("User registration is disabled")
.status_code(StatusCode::UNAUTHORIZED),
Expand Down
2 changes: 1 addition & 1 deletion auth/server/src/api/named/google.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ pub async fn google_callback<I: AuthImpl>(
None => {
let no_users_exist = auth.no_users_exist().await?;

if auth.registration_disabled() && !no_users_exist {
if auth.google_registration_disabled() && !no_users_exist {
return Err(
anyhow!("User registration is disabled")
.status_code(StatusCode::UNAUTHORIZED),
Expand Down
2 changes: 1 addition & 1 deletion auth/server/src/api/oidc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ pub async fn oidc_callback<I: AuthImpl>(
None => {
let no_users_exist = auth.no_users_exist().await?;

if auth.registration_disabled() && !no_users_exist {
if auth.oidc_registration_disabled() && !no_users_exist {
return Err(
anyhow!("User registration is disabled")
.status_code(StatusCode::UNAUTHORIZED),
Expand Down
26 changes: 25 additions & 1 deletion auth/server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,35 @@ pub trait AuthImpl: Send + Sync + 'static {
"/auth"
}

/// Disable new user registration.
/// Disable new user registration (all providers).
fn registration_disabled(&self) -> bool {
false
}

/// Disable new user registration for local (username/password) signups only.
/// Defaults to [Self::registration_disabled].
fn local_registration_disabled(&self) -> bool {
self.registration_disabled()
}

/// Disable new user registration via OIDC only.
/// Defaults to [Self::registration_disabled].
fn oidc_registration_disabled(&self) -> bool {
self.registration_disabled()
}

/// Disable new user registration via GitHub only.
/// Defaults to [Self::registration_disabled].
fn github_registration_disabled(&self) -> bool {
self.registration_disabled()
}

/// Disable new user registration via Google only.
/// Defaults to [Self::registration_disabled].
fn google_registration_disabled(&self) -> bool {
self.registration_disabled()
}

/// Provide usernames to lock credential updates for,
/// such as demo users.
fn locked_usernames(&self) -> &'static [String] {
Expand Down