mirror of
https://github.com/rommapp/romm.git
synced 2026-06-27 22:35:57 +00:00
Merge pull request #3577 from Spinnich/feature/oidc-allow-registration
feat(auth): add OIDC_ALLOW_REGISTRATION toggle to gate OIDC auto-registration
This commit is contained in:
@@ -142,6 +142,9 @@ INVITE_TOKEN_EXPIRY_SECONDS: Final[int] = safe_int(
|
||||
# OIDC
|
||||
OIDC_ENABLED: Final[bool] = safe_str_to_bool(_get_env("OIDC_ENABLED"))
|
||||
OIDC_AUTOLOGIN: Final[bool] = safe_str_to_bool(_get_env("OIDC_AUTOLOGIN"))
|
||||
OIDC_ALLOW_REGISTRATION: Final[bool] = safe_str_to_bool(
|
||||
_get_env("OIDC_ALLOW_REGISTRATION", "true")
|
||||
)
|
||||
OIDC_PROVIDER: Final[str] = _get_env("OIDC_PROVIDER", "")
|
||||
OIDC_CLIENT_ID: Final[str] = _get_env("OIDC_CLIENT_ID", "")
|
||||
OIDC_CLIENT_SECRET: Final[str] = _get_env("OIDC_CLIENT_SECRET", "")
|
||||
|
||||
@@ -13,6 +13,7 @@ from starlette.requests import HTTPConnection
|
||||
|
||||
from config import (
|
||||
INVITE_TOKEN_EXPIRY_SECONDS,
|
||||
OIDC_ALLOW_REGISTRATION,
|
||||
OIDC_CLAIM_ROLES,
|
||||
OIDC_ENABLED,
|
||||
OIDC_ROLE_ADMIN,
|
||||
@@ -436,6 +437,15 @@ class OpenIDHandler:
|
||||
|
||||
user = db_user_handler.get_user_by_email(email)
|
||||
if user is None:
|
||||
if not OIDC_ALLOW_REGISTRATION:
|
||||
log.error(
|
||||
"User with email '%s' not found and OIDC registration is disabled",
|
||||
hl(email, color=CYAN),
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="User registration is disabled. Please contact an administrator to create an account.",
|
||||
)
|
||||
log.info(
|
||||
"User with email '%s' not found, creating new user",
|
||||
hl(email, color=CYAN),
|
||||
|
||||
@@ -23,6 +23,16 @@ def mock_oidc_enabled(mocker):
|
||||
mocker.patch("handler.auth.base_handler.OIDC_ENABLED", True)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_oidc_allow_registration_enabled(mocker):
|
||||
mocker.patch("handler.auth.base_handler.OIDC_ALLOW_REGISTRATION", True)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_oidc_allow_registration_disabled(mocker):
|
||||
mocker.patch("handler.auth.base_handler.OIDC_ALLOW_REGISTRATION", False)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_token():
|
||||
return {
|
||||
@@ -171,6 +181,7 @@ async def test_oidc_valid_token_decoding(
|
||||
async def test_oidc_valid_add_user(
|
||||
mocker,
|
||||
mock_oidc_enabled,
|
||||
mock_oidc_allow_registration_enabled,
|
||||
mock_token,
|
||||
mock_openid_configuration,
|
||||
config_override,
|
||||
@@ -218,6 +229,85 @@ async def test_oidc_valid_add_user(
|
||||
assert mock_add_user.call_args.args[0].role == romm_role
|
||||
|
||||
|
||||
async def test_oidc_registration_disabled_rejects_unknown_user(
|
||||
mocker,
|
||||
mock_oidc_enabled,
|
||||
mock_oidc_allow_registration_disabled,
|
||||
mock_token,
|
||||
mock_openid_configuration,
|
||||
):
|
||||
"""Test that a login from an unknown email is rejected when registration is disabled."""
|
||||
mocker.patch(
|
||||
"handler.database.db_user_handler.get_user_by_email", return_value=None
|
||||
)
|
||||
mock_add_user = mocker.patch("handler.database.db_user_handler.add_user")
|
||||
mocker.patch.object(
|
||||
StarletteOAuth2App,
|
||||
"load_server_metadata",
|
||||
return_value=mock_openid_configuration,
|
||||
)
|
||||
|
||||
oidc_handler = OpenIDHandler()
|
||||
with pytest.raises(HTTPException, match="registration is disabled"):
|
||||
await oidc_handler.get_current_active_user_from_openid_token(mock_token)
|
||||
|
||||
mock_add_user.assert_not_called()
|
||||
|
||||
|
||||
async def test_oidc_registration_enabled_creates_unknown_user(
|
||||
mocker,
|
||||
mock_oidc_enabled,
|
||||
mock_oidc_allow_registration_enabled,
|
||||
mock_token,
|
||||
mock_openid_configuration,
|
||||
):
|
||||
"""Test that a login from an unknown email creates a new user when registration is enabled."""
|
||||
mocker.patch(
|
||||
"handler.database.db_user_handler.get_user_by_email", return_value=None
|
||||
)
|
||||
mock_user = MagicMock(enabled=True, role=Role.VIEWER)
|
||||
mock_add_user = mocker.patch(
|
||||
"handler.database.db_user_handler.add_user", return_value=mock_user
|
||||
)
|
||||
mocker.patch.object(
|
||||
StarletteOAuth2App,
|
||||
"load_server_metadata",
|
||||
return_value=mock_openid_configuration,
|
||||
)
|
||||
|
||||
oidc_handler = OpenIDHandler()
|
||||
user, _ = await oidc_handler.get_current_active_user_from_openid_token(mock_token)
|
||||
|
||||
mock_add_user.assert_called_once()
|
||||
assert user == mock_user
|
||||
|
||||
|
||||
async def test_oidc_registration_disabled_allows_existing_user(
|
||||
mocker,
|
||||
mock_oidc_enabled,
|
||||
mock_oidc_allow_registration_disabled,
|
||||
mock_token,
|
||||
mock_openid_configuration,
|
||||
):
|
||||
"""Test that an existing user can still log in when registration is disabled."""
|
||||
mock_user = MagicMock(enabled=True, role=Role.VIEWER)
|
||||
mocker.patch(
|
||||
"handler.database.db_user_handler.get_user_by_email", return_value=mock_user
|
||||
)
|
||||
mock_add_user = mocker.patch("handler.database.db_user_handler.add_user")
|
||||
mocker.patch.object(
|
||||
StarletteOAuth2App,
|
||||
"load_server_metadata",
|
||||
return_value=mock_openid_configuration,
|
||||
)
|
||||
|
||||
oidc_handler = OpenIDHandler()
|
||||
user, _ = await oidc_handler.get_current_active_user_from_openid_token(mock_token)
|
||||
|
||||
assert user == mock_user
|
||||
mock_add_user.assert_not_called()
|
||||
|
||||
|
||||
async def test_oidc_valid_edit_user_role(
|
||||
mocker,
|
||||
mock_oidc_enabled,
|
||||
|
||||
@@ -1512,6 +1512,7 @@ Falls back to `FakeRedis` in test mode.
|
||||
| Variable | Default | Description |
|
||||
| ------------------------------- | -------------------- | ------------------------------- |
|
||||
| `OIDC_ENABLED` | `false` | Enable OpenID Connect |
|
||||
| `OIDC_ALLOW_REGISTRATION` | `true` | Auto-create accounts on login |
|
||||
| `OIDC_PROVIDER` | | Provider URL |
|
||||
| `OIDC_CLIENT_ID` | | Client ID |
|
||||
| `OIDC_CLIENT_SECRET` | | Client secret |
|
||||
|
||||
@@ -38,6 +38,7 @@ DISABLE_LOGS_VIEWER=false # Disable the backend logs viewer
|
||||
# OpenID Connect
|
||||
OIDC_ENABLED=false # Enable OpenID Connect authentication
|
||||
OIDC_AUTOLOGIN=false # Skip the OIDC button on the login page and auto-redirect
|
||||
OIDC_ALLOW_REGISTRATION=true # Allow new accounts to be created automatically on first OIDC login
|
||||
OIDC_PROVIDER= # Name of the OIDC provider in use
|
||||
OIDC_CLIENT_ID= # Client ID for OIDC authentication
|
||||
OIDC_CLIENT_SECRET= # Client secret for OIDC authentication
|
||||
|
||||
Reference in New Issue
Block a user