mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2026-08-17 16:34:44 -05:00
Gate external auth providers behind SERVER_MODE in desktop mode (#10240)
Desktop mode has no use for the kerberos, ldap, mfa, oauth2 or webserver authentication providers: AUTHENTICATION_SOURCES defaults to internal, MFA is inert because mfa_enabled() requires SERVER_MODE, and get_logout_url() already guards its kerberos and oauth2 branches on SERVER_MODE. So skip importing and initialising them unless SERVER_MODE is set, leaving desktop mode with internal authentication alone. The accompanying test needed wiring into our own runner before it would ever execute: web/pgadmin/authenticate/tests/ had no __init__.py, so pkgutil did not report it and find_modules() never reached the module, and the test class derived from unittest.TestCase rather than BaseTestGenerator, so the TestsGeneratorRegistry metaclass never registered it and get_suite() could not pick it up. Both are fixed here, and the two scenarios now run and pass under regression/runtests.py. The test no longer asserts that pgadmin.authenticate.mfa is absent from sys.modules in desktop mode, because it is not: both pgadmin/browser/__init__ and pgadmin/user_login_check import pgadmin.authenticate.mfa.utils unconditionally. The original assertion only held because the test itself popped the module first.
This commit is contained in:
committed by
Dave Page
parent
c2398d5b08
commit
2de30f25c0
@@ -10,6 +10,7 @@
|
|||||||
"""External Authentication Registry."""
|
"""External Authentication Registry."""
|
||||||
|
|
||||||
|
|
||||||
|
import config
|
||||||
from pgadmin.utils.dynamic_registry import create_registry_metaclass
|
from pgadmin.utils.dynamic_registry import create_registry_metaclass
|
||||||
|
|
||||||
|
|
||||||
@@ -19,20 +20,21 @@ def load_modules(cls, app=None):
|
|||||||
from . import internal as module
|
from . import internal as module
|
||||||
submodules.append(module)
|
submodules.append(module)
|
||||||
|
|
||||||
from . import kerberos as module
|
if config.SERVER_MODE:
|
||||||
submodules.append(module)
|
from . import kerberos as module
|
||||||
|
submodules.append(module)
|
||||||
|
|
||||||
from . import ldap as module
|
from . import ldap as module
|
||||||
submodules.append(module)
|
submodules.append(module)
|
||||||
|
|
||||||
from . import mfa as module
|
from . import mfa as module
|
||||||
submodules.append(module)
|
submodules.append(module)
|
||||||
|
|
||||||
from . import oauth2 as module
|
from . import oauth2 as module
|
||||||
submodules.append(module)
|
submodules.append(module)
|
||||||
|
|
||||||
from . import webserver as module
|
from . import webserver as module
|
||||||
submodules.append(module)
|
submodules.append(module)
|
||||||
|
|
||||||
for module in submodules:
|
for module in submodules:
|
||||||
if "init_app" in module.__dict__.keys():
|
if "init_app" in module.__dict__.keys():
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
##########################################################################
|
||||||
|
#
|
||||||
|
# pgAdmin 4 - PostgreSQL Tools
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 - 2026, The pgAdmin Development Team
|
||||||
|
# This software is released under the PostgreSQL Licence
|
||||||
|
#
|
||||||
|
##########################################################################
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
##########################################################################
|
||||||
|
#
|
||||||
|
# pgAdmin 4 - PostgreSQL Tools
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 - 2026, The pgAdmin Development Team
|
||||||
|
# This software is released under the PostgreSQL Licence
|
||||||
|
#
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
"""Verify that external authentication providers are only loaded in server
|
||||||
|
mode, and that desktop mode is left with internal authentication alone.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import config
|
||||||
|
from pgadmin.authenticate.registry import AuthSourceRegistry
|
||||||
|
from pgadmin.utils.route import BaseTestGenerator
|
||||||
|
|
||||||
|
# Providers registered as authentication sources. MFA is deliberately absent:
|
||||||
|
# it is loaded by the same registry but registers no auth source of its own.
|
||||||
|
EXTERNAL_SOURCES = ['kerberos', 'ldap', 'oauth2', 'webserver']
|
||||||
|
|
||||||
|
# Modules whose import can be asserted on. mfa is excluded because
|
||||||
|
# pgadmin/browser/__init__.py and pgadmin/user_login_check.py both import
|
||||||
|
# pgadmin.authenticate.mfa.utils unconditionally, so the mfa package is
|
||||||
|
# present in sys.modules in desktop mode whatever this registry does.
|
||||||
|
EXTERNAL_MODULES = [
|
||||||
|
'pgadmin.authenticate.{0}'.format(name) for name in EXTERNAL_SOURCES
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class AuthSourceGatingTestCase(BaseTestGenerator):
|
||||||
|
"""Checks AuthSourceRegistry.load_modules() honours config.SERVER_MODE."""
|
||||||
|
|
||||||
|
scenarios = [
|
||||||
|
('desktop mode registers internal authentication only',
|
||||||
|
dict(server_mode=False)),
|
||||||
|
('server mode registers every external provider',
|
||||||
|
dict(server_mode=True)),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Pure registry-contract test - no Postgres server interaction needed, so
|
||||||
|
# skip BaseTestGenerator.setUp's connect_server().
|
||||||
|
def setUp(self):
|
||||||
|
self._orig_server_mode = config.SERVER_MODE
|
||||||
|
self._orig_registry = dict(AuthSourceRegistry._registry or {})
|
||||||
|
self._orig_objects = dict(AuthSourceRegistry._objects or {})
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
config.SERVER_MODE = self._orig_server_mode
|
||||||
|
AuthSourceRegistry._registry = self._orig_registry
|
||||||
|
AuthSourceRegistry._objects = self._orig_objects
|
||||||
|
|
||||||
|
def _reset_registry(self):
|
||||||
|
"""Empty the registry and drop the external provider modules, so that
|
||||||
|
load_modules() is observed importing them (or not) from scratch.
|
||||||
|
"""
|
||||||
|
for module in EXTERNAL_MODULES:
|
||||||
|
sys.modules.pop(module, None)
|
||||||
|
|
||||||
|
AuthSourceRegistry._registry = dict()
|
||||||
|
AuthSourceRegistry._objects = dict()
|
||||||
|
|
||||||
|
internal = sys.modules.get('pgadmin.authenticate.internal')
|
||||||
|
if internal is not None and \
|
||||||
|
hasattr(internal, 'InternalAuthentication'):
|
||||||
|
AuthSourceRegistry._registry['internal'] = \
|
||||||
|
internal.InternalAuthentication
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
config.SERVER_MODE = self.server_mode
|
||||||
|
self._reset_registry()
|
||||||
|
|
||||||
|
AuthSourceRegistry.load_modules(_MockApp() if self.server_mode
|
||||||
|
else None)
|
||||||
|
|
||||||
|
registered = list(AuthSourceRegistry._registry.keys())
|
||||||
|
self.assertIn(
|
||||||
|
'internal', registered,
|
||||||
|
"Internal authentication must be registered in both modes")
|
||||||
|
|
||||||
|
for source, module in zip(EXTERNAL_SOURCES, EXTERNAL_MODULES):
|
||||||
|
if self.server_mode:
|
||||||
|
self.assertIn(
|
||||||
|
source, registered,
|
||||||
|
"{0} must be registered in server mode".format(source))
|
||||||
|
self.assertIn(
|
||||||
|
module, sys.modules,
|
||||||
|
"{0} must be imported in server mode".format(module))
|
||||||
|
else:
|
||||||
|
self.assertNotIn(
|
||||||
|
source, registered,
|
||||||
|
"{0} must not be registered in desktop mode".format(
|
||||||
|
source))
|
||||||
|
self.assertNotIn(
|
||||||
|
module, sys.modules,
|
||||||
|
"{0} must not be imported in desktop mode".format(module))
|
||||||
|
|
||||||
|
|
||||||
|
class _MockApp:
|
||||||
|
"""The minimum Flask-like surface the providers' init_app() touches."""
|
||||||
|
|
||||||
|
class _LoginManager:
|
||||||
|
logout_view = None
|
||||||
|
|
||||||
|
class _Logger:
|
||||||
|
def warning(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.login_manager = self._LoginManager()
|
||||||
|
self.logger = self._Logger()
|
||||||
|
|
||||||
|
def register_blueprint(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def register_logout_hook(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
Reference in New Issue
Block a user