public inbox for [email protected]help / color / mirror / Atom feed
[pgAdmin4][Patch] - RM 6657 - Support for REMOTE_USER as Authentication. 3+ messages / 3 participants [nested] [flat]
* [pgAdmin4][Patch] - RM 6657 - Support for REMOTE_USER as Authentication. @ 2021-10-12 05:13 Khushboo Vashi <[email protected]> 0 siblings, 2 replies; 3+ messages in thread From: Khushboo Vashi @ 2021-10-12 05:13 UTC (permalink / raw) To: pgadmin-hackers Hi, Please find the attached patch for #6657 - Support for REMOTE_USER as Authentication. The initial patch was provided by *Tom Schreiber,* I have made the necessary changes to work with pgAdmin and also added test cases and documentation. Please commit the patch on *Tom Schreiber's *name. This patch also includes the fix for the RM #6719 - OAuth2 integration redirect issue Thanks, Khushboo Attachments: [application/octet-stream] RM_6657.patch (17.0K, 3-RM_6657.patch) download | inline diff: diff --git a/docs/en_US/getting_started.rst b/docs/en_US/getting_started.rst index 700e8d48b..92967847e 100644 --- a/docs/en_US/getting_started.rst +++ b/docs/en_US/getting_started.rst @@ -39,6 +39,7 @@ Mode is pre-configured for security. ldap kerberos oauth2 + webserver .. note:: Pre-compiled and configured installation packages are available for diff --git a/docs/en_US/webserver.rst b/docs/en_US/webserver.rst new file mode 100644 index 000000000..abd0cf6c4 --- /dev/null +++ b/docs/en_US/webserver.rst @@ -0,0 +1,44 @@ +.. _webserver: + +******************************************** +`Enabling Webserver Authentication`:index: +******************************************** + +To configure Webserver authentication, you must setup your webserver +with any authentication plug-in (such as Shibboleth, HTTP BASIC auth) +as long as it sets the REMOTE_USER environment variable. +To enable Webserver authentication for pgAdmin, you must configure the Webserver +settings in the *config_local.py* or *config_system.py* file (see the +:ref:`config.py <config_py>` documentation) on the system where pgAdmin is +installed in Server mode. You can copy these settings from *config.py* file +and modify the values for the following parameters: + + +.. csv-table:: + :header: "**Parameter**", "**Description**" + :class: longtable + :widths: 35, 55 + + "AUTHENTICATION_SOURCES", "The default value for this parameter is *internal*. + To enable OAUTH2 authentication, you must include *webserver* in the list of values + for this parameter. you can modify the value as follows: + + * [‘webserver’]: pgAdmin will use only Webserver authentication. + + * [‘webserver’, ‘internal’]: pgAdmin will first try to authenticate the user + through webserver. If that authentication fails, then it will return back + to the login dialog where you need to provide internal pgAdmin user + credentials for authentication." + "WEBSERVER_AUTO_CREATE_USER", "Set the value to *True* if you want to automatically + create a pgAdmin user corresponding to a successfully authenticated Webserver user. + Please note that password is not stored in the pgAdmin database." + + +Master Password +=============== + +In the multi user mode, pgAdmin uses user's login password to encrypt/decrypt the PostgreSQL server password. +In the Webserver authentication, the pgAdmin does not store the user's password, so we need an encryption key to store +the PostgreSQL server password. +To accomplish this, set the configuration parameter MASTER_PASSWORD to *True*, so upon setting the master password, +it will be used as an encryption key while storing the password. If it is False, the server password can not be stored. diff --git a/web/config.py b/web/config.py index 7a1f4ab1f..db313273e 100644 --- a/web/config.py +++ b/web/config.py @@ -570,7 +570,8 @@ ENHANCED_COOKIE_PROTECTION = True # Default setting is internal # External Supported Sources: ldap, kerberos, oauth2 # Multiple authentication can be achieved by setting this parameter to -# ['ldap', 'internal'] or ['oauth2', 'internal'] etc. +# ['ldap', 'internal'] or ['oauth2', 'internal'] or +# ['webserver', 'internal'] etc. # pgAdmin will authenticate the user with ldap/oauth2 whatever first in the # list, in case of failure the second authentication option will be considered. @@ -729,6 +730,12 @@ OAUTH2_CONFIG = [ OAUTH2_AUTO_CREATE_USER = True +########################################################################## +# Webserver Configuration +########################################################################## + +WEBSERVER_AUTO_CREATE_USER = True + ########################################################################## # PSQL tool settings ########################################################################## diff --git a/web/pgAdmin4.wsgi b/web/pgAdmin4.wsgi index 973d2701d..95aab8de2 100644 --- a/web/pgAdmin4.wsgi +++ b/web/pgAdmin4.wsgi @@ -13,6 +13,8 @@ import sys if sys.version_info < (3, 4): raise Exception('This application must be run under Python 3.4 or later.') +os.environ['SCRIPT_NAME'] = '/pgadmin4' + import builtins root = os.path.dirname(os.path.realpath(__file__)) diff --git a/web/pgadmin/__init__.py b/web/pgadmin/__init__.py index 37eb26ccc..8047b3417 100644 --- a/web/pgadmin/__init__.py +++ b/web/pgadmin/__init__.py @@ -46,7 +46,7 @@ from pgadmin.utils.ajax import internal_server_error, make_json_response from pgadmin.utils.csrf import pgCSRFProtect from pgadmin import authenticate from pgadmin.utils.security_headers import SecurityHeaders -from pgadmin.utils.constants import KERBEROS, OAUTH2, INTERNAL, LDAP +from pgadmin.utils.constants import KERBEROS, OAUTH2, INTERNAL, LDAP, WEBSERVER # Explicitly set the mime-types so that a corrupted windows registry will not # affect pgAdmin 4 to be load properly. This will avoid the issues that may @@ -470,11 +470,17 @@ def create_app(app_name=None): 'SECURITY_EMAIL_VALIDATOR_ARGS': config.SECURITY_EMAIL_VALIDATOR_ARGS })) + if 'SCRIPT_NAME' in os.environ and os.environ["SCRIPT_NAME"]: + app.config.update(dict({ + 'APPLICATION_ROOT': os.environ["SCRIPT_NAME"] + })) + app.config.update(dict({ 'INTERNAL': INTERNAL, 'LDAP': LDAP, 'KERBEROS': KERBEROS, - 'OAUTH2': OAUTH2 + 'OAUTH2': OAUTH2, + 'WEBSERVER': WEBSERVER })) security.init_app(app, user_datastore) @@ -771,15 +777,14 @@ def create_app(app_name=None): elif config.SERVER_MODE and \ not current_user.is_authenticated and \ request.endpoint in ('redirects.index', 'security.login'): - if app.PGADMIN_EXTERNAL_AUTH_SOURCE == KERBEROS: + if app.PGADMIN_EXTERNAL_AUTH_SOURCE in [KERBEROS, WEBSERVER]: return authenticate.login() # if the server is restarted the in memory key will be lost # but the user session may still be active. Logout the user # to get the key again when login if config.SERVER_MODE and current_user.is_authenticated and \ - app.PGADMIN_EXTERNAL_AUTH_SOURCE != \ - KERBEROS and app.PGADMIN_EXTERNAL_AUTH_SOURCE != \ - OAUTH2 and\ + app.PGADMIN_EXTERNAL_AUTH_SOURCE not in [ + KERBEROS, OAUTH2, WEBSERVER] and \ current_app.keyManager.get() is None and \ request.endpoint not in ('security.login', 'security.logout'): logout_user() diff --git a/web/pgadmin/authenticate/webserver.py b/web/pgadmin/authenticate/webserver.py new file mode 100644 index 000000000..47af8becd --- /dev/null +++ b/web/pgadmin/authenticate/webserver.py @@ -0,0 +1,119 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2021, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""A blueprint module implementing the Webserver authentication.""" + +import random +import string +import config +from flask import request, current_app, session, Response, render_template, \ + url_for +from flask_babelex import gettext +from flask_security import login_user +from .internal import BaseAuthentication +from pgadmin.model import User +from pgadmin.tools.user_management import create_user +from pgadmin.utils.constants import WEBSERVER +from pgadmin.utils import PgAdminModule +from pgadmin.utils.csrf import pgCSRFProtect +from flask_security.utils import logout_user +from os import environ, path, remove + + +class WebserverModule(PgAdminModule): + def register(self, app, options, first_registration=False): + # Do not look for the sub_modules, + # instead call blueprint.register(...) directly + super(PgAdminModule, self).register(app, options, first_registration) + + def get_exposed_url_endpoints(self): + return ['webserver.login', + 'webserver.logout'] + + +def init_app(app): + MODULE_NAME = 'webserver' + + blueprint = WebserverModule(MODULE_NAME, __name__, static_url_path='') + + @blueprint.route("/login", + endpoint="login", methods=["GET"]) + @pgCSRFProtect.exempt + def webserver_login(): + logout_user() + return Response(render_template("browser/kerberos_login.html", + login_url=url_for('security.login'), + )) + + @blueprint.route("/logout", + endpoint="logout", methods=["GET"]) + @pgCSRFProtect.exempt + def webserver_logout(): + logout_user() + return Response(render_template("browser/kerberos_logout.html", + login_url=url_for('security.login'), + )) + + app.register_blueprint(blueprint) + + +class WebserverAuthentication(BaseAuthentication): + LOGIN_VIEW = 'webserver.login' + LOGOUT_VIEW = 'webserver.logout' + + def get_source_name(self): + return WEBSERVER + + def get_friendly_name(self): + return gettext("webserver") + + def validate(self, form): + return True + + def get_user(self): + return request.environ.get('REMOTE_USER') + + def authenticate(self, form): + username = self.get_user() + + if not username: + return False, gettext( + "Webserver authenticate failed.") + + session['pass_enc_key'] = ''.join( + (random.choice(string.ascii_lowercase) for x in range(10))) + useremail = request.environ.get('mail') + if not useremail: + useremail = '' + return self.__auto_create_user(username, '') + + def login(self, form): + username = self.get_user() + if username: + user = User.query.filter_by(username=username).first() + status = login_user(user) + if not status: + current_app.logger.exception(self.messages('LOGIN_FAILED')) + return False, self.messages('LOGIN_FAILED') + return True, None + return False, self.messages('LOGIN_FAILED') + + def __auto_create_user(self, username, useremail): + """Add the webserver user to the internal SQLite database.""" + if config.WEBSERVER_AUTO_CREATE_USER: + user = User.query.filter_by(username=username).first() + if not user: + return create_user({ + 'username': username, + 'email': useremail, + 'role': 2, + 'active': True, + 'auth_source': WEBSERVER + }) + return True, None diff --git a/web/pgadmin/browser/tests/test_kerberos_with_mocking.py b/web/pgadmin/browser/tests/test_kerberos_with_mocking.py index 0f49c444d..982e7bcde 100644 --- a/web/pgadmin/browser/tests/test_kerberos_with_mocking.py +++ b/web/pgadmin/browser/tests/test_kerberos_with_mocking.py @@ -133,4 +133,5 @@ class KerberosLoginMockTestCase(BaseTestGenerator): """ cls.tester.logout() app_config.AUTHENTICATION_SOURCES = [INTERNAL] + app_config.PGADMIN_EXTERNAL_AUTH_SOURCE = INTERNAL utils.login_tester_account(cls.tester) diff --git a/web/pgadmin/browser/tests/test_ldap_login.py b/web/pgadmin/browser/tests/test_ldap_login.py index 51b512afd..ec2c7d025 100644 --- a/web/pgadmin/browser/tests/test_ldap_login.py +++ b/web/pgadmin/browser/tests/test_ldap_login.py @@ -95,4 +95,5 @@ class LDAPLoginTestCase(BaseTestGenerator): """ cls.tester.logout() app_config.AUTHENTICATION_SOURCES = [INTERNAL] + app_config.PGADMIN_EXTERNAL_AUTH_SOURCE = INTERNAL utils.login_tester_account(cls.tester) diff --git a/web/pgadmin/browser/tests/test_ldap_with_mocking.py b/web/pgadmin/browser/tests/test_ldap_with_mocking.py index 38c6b4724..19b548cd4 100644 --- a/web/pgadmin/browser/tests/test_ldap_with_mocking.py +++ b/web/pgadmin/browser/tests/test_ldap_with_mocking.py @@ -81,4 +81,5 @@ class LDAPLoginMockTestCase(BaseTestGenerator): """ cls.tester.logout() app_config.AUTHENTICATION_SOURCES = [INTERNAL] + app_config.PGADMIN_EXTERNAL_AUTH_SOURCE = INTERNAL utils.login_tester_account(cls.tester) diff --git a/web/pgadmin/browser/tests/test_login.py b/web/pgadmin/browser/tests/test_login.py index 451c05b64..f5ce10927 100644 --- a/web/pgadmin/browser/tests/test_login.py +++ b/web/pgadmin/browser/tests/test_login.py @@ -100,6 +100,7 @@ class LoginTestCase(BaseTestGenerator): # No need to call base class setup function def setUp(self): app_config.AUTHENTICATION_SOURCES = [INTERNAL] + app_config.PGADMIN_EXTERNAL_AUTH_SOURCE = INTERNAL def runTest(self): """This function checks login functionality.""" diff --git a/web/pgadmin/browser/tests/test_oauth2_with_mocking.py b/web/pgadmin/browser/tests/test_oauth2_with_mocking.py index 71706ebe6..0ab6f7cdd 100644 --- a/web/pgadmin/browser/tests/test_oauth2_with_mocking.py +++ b/web/pgadmin/browser/tests/test_oauth2_with_mocking.py @@ -145,4 +145,5 @@ class Oauth2LoginMockTestCase(BaseTestGenerator): """ cls.tester.logout() app_config.AUTHENTICATION_SOURCES = [INTERNAL] + app_config.PGADMIN_EXTERNAL_AUTH_SOURCE = INTERNAL utils.login_tester_account(cls.tester) diff --git a/web/pgadmin/browser/tests/test_webserver_with_mocking.py b/web/pgadmin/browser/tests/test_webserver_with_mocking.py new file mode 100644 index 000000000..74343d135 --- /dev/null +++ b/web/pgadmin/browser/tests/test_webserver_with_mocking.py @@ -0,0 +1,86 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2021, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +import config as app_config +from pgadmin.utils.route import BaseTestGenerator +from regression.python_test_utils import test_utils as utils +from pgadmin.authenticate.registry import AuthSourceRegistry +from unittest.mock import patch, MagicMock +from pgadmin.authenticate import AuthSourceManager +from pgadmin.utils.constants import OAUTH2, LDAP, INTERNAL, WEBSERVER +from flask import request + + +class WebserverLoginMockTestCase(BaseTestGenerator): + """ + This class checks oauth2 login functionality by mocking + Webserver Authentication. + """ + + scenarios = [ + ('Webserver Authentication', dict( + auth_source=[WEBSERVER], + username='test_mock_webserver_user' + )), + ] + + @classmethod + def setUpClass(cls): + """ + We need to logout the test client as we are testing + spnego/kerberos login scenarios. + """ + cls.tester.logout() + + def setUp(self): + app_config.AUTHENTICATION_SOURCES = self.auth_source + self.app.PGADMIN_EXTERNAL_AUTH_SOURCE = WEBSERVER + + def runTest(self): + """This function checks webserver login functionality.""" + if app_config.SERVER_MODE is False: + self.skipTest( + "Can not run Webserver Authentication in the Desktop mode." + ) + + self.test_webserver_authentication() + + def test_webserver_authentication(self): + """ + Ensure that when the client sends an correct authorization token, + they receive a 200 OK response and the user principal is extracted and + passed on to the routed method. + """ + + # Mock Oauth2 Authenticate + AuthSourceRegistry._registry[WEBSERVER].get_user = MagicMock( + return_value=self.username) + + res = self.tester.login(None, + None, + True, + None + ) + self.assertEqual(res.status_code, 200) + respdata = 'Gravatar image for %s' % self.username + self.assertTrue(respdata in res.data.decode('utf8')) + + def tearDown(self): + pass + + @classmethod + def tearDownClass(cls): + """ + We need to again login the test client as soon as test scenarios + finishes. + """ + cls.tester.logout() + app_config.AUTHENTICATION_SOURCES = [INTERNAL] + app_config.PGADMIN_EXTERNAL_AUTH_SOURCE = INTERNAL + utils.login_tester_account(cls.tester) diff --git a/web/pgadmin/utils/constants.py b/web/pgadmin/utils/constants.py index 91931c422..96d46367b 100644 --- a/web/pgadmin/utils/constants.py +++ b/web/pgadmin/utils/constants.py @@ -55,12 +55,14 @@ ERROR_FETCHING_DATA = gettext('Unable to fetch data.') INTERNAL = 'internal' LDAP = 'ldap' KERBEROS = 'kerberos' -OAUTH2 = "oauth2" +OAUTH2 = 'oauth2' +WEBSERVER = 'webserver' SUPPORTED_AUTH_SOURCES = [INTERNAL, LDAP, KERBEROS, - OAUTH2] + OAUTH2, + WEBSERVER] BINARY_PATHS = { "as_bin_paths": [ ^ permalink raw reply [nested|flat] 3+ messages in thread
* Re: [pgAdmin4][Patch] - RM 6657 - Support for REMOTE_USER as Authentication. @ 2021-10-12 08:10 Dave Page <[email protected]> parent: Khushboo Vashi <[email protected]> 1 sibling, 0 replies; 3+ messages in thread From: Dave Page @ 2021-10-12 08:10 UTC (permalink / raw) To: Khushboo Vashi <[email protected]>; +Cc: pgadmin-hackers Very nice (the existence of the patch that is - I haven't reviewed it)! I think this nicely rounds off and completes the auth mechanisms we're likely to want. On Tue, Oct 12, 2021 at 6:13 AM Khushboo Vashi < [email protected]> wrote: > Hi, > > Please find the attached patch for #6657 - Support for REMOTE_USER as > Authentication. > > The initial patch was provided by *Tom Schreiber,* I have made the > necessary changes to work with pgAdmin and also added test cases and > documentation. > Please commit the patch on *Tom Schreiber's *name. > > This patch also includes the fix for the RM #6719 - OAuth2 integration > redirect issue > > > Thanks, > Khushboo > -- Dave Page Blog: https://pgsnake.blogspot.com Twitter: @pgsnake EDB: https://www.enterprisedb.com ^ permalink raw reply [nested|flat] 3+ messages in thread
* Re: [pgAdmin4][Patch] - RM 6657 - Support for REMOTE_USER as Authentication. @ 2021-10-12 09:35 Akshay Joshi <[email protected]> parent: Khushboo Vashi <[email protected]> 1 sibling, 0 replies; 3+ messages in thread From: Akshay Joshi @ 2021-10-12 09:35 UTC (permalink / raw) To: Khushboo Vashi <[email protected]>; +Cc: pgadmin-hackers Thanks, the patch applied. On Tue, Oct 12, 2021 at 10:43 AM Khushboo Vashi < [email protected]> wrote: > Hi, > > Please find the attached patch for #6657 - Support for REMOTE_USER as > Authentication. > > The initial patch was provided by *Tom Schreiber,* I have made the > necessary changes to work with pgAdmin and also added test cases and > documentation. > Please commit the patch on *Tom Schreiber's *name. > > This patch also includes the fix for the RM #6719 - OAuth2 integration > redirect issue > > > Thanks, > Khushboo > -- *Thanks & Regards* *Akshay Joshi* *pgAdmin Hacker | Principal Software Architect* *EDB Postgres <http://edbpostgres.com>* *Mobile: +91 976-788-8246* ^ permalink raw reply [nested|flat] 3+ messages in thread
end of thread, other threads:[~2021-10-12 09:35 UTC | newest] Thread overview: 3+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2021-10-12 05:13 [pgAdmin4][Patch] - RM 6657 - Support for REMOTE_USER as Authentication. Khushboo Vashi <[email protected]> 2021-10-12 08:10 ` Dave Page <[email protected]> 2021-10-12 09:35 ` Akshay Joshi <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox