mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2026-08-17 16:34:44 -05:00
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.
120 lines
4.3 KiB
Python
120 lines
4.3 KiB
Python
##########################################################################
|
|
#
|
|
# 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
|