public inbox for [email protected]
help / color / mirror / Atom feedFrom: Khushboo Vashi <[email protected]>
To: pgadmin-hackers <[email protected]>
Subject: [pgAdmin4][Patch] - RM 5457 - Kerberos Authentication - Phase 1
Date: Tue, 22 Dec 2020 15:44:29 +0530
Message-ID: <CAFOhELdXhWMR2zS4dnH+SudN0s7LiENH+vczC0YhuifPgm+G5g@mail.gmail.com> (raw)
Hi,
Please find the attached patch to support Kerberos Authentication in
pgAdmin RM 5457.
The patch introduces a new pluggable option for Kerberos authentication,
using SPNEGO to forward kerberos tickets through a browser which will
bypass the login page entirely if the Kerberos Authentication succeeds.
The complete setup of the Kerberos Server + pgAdmin Server + Client is
documented in a separate file and attached.
This patch also includes the small fix related to logging #5829
Thanks,
Khushboo
Attachments:
[application/octet-stream] RM_5457.patch (25.8K, 3-RM_5457.patch)
download | inline diff:
diff --git a/requirements.txt b/requirements.txt
index 0fe9c88bd..4675a0b94 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -42,3 +42,4 @@ bcrypt<=3.1.7;
cryptography<=3.0;
sshtunnel>=0.1.5
ldap3>=2.5.1
+gssapi>=1.6.11
diff --git a/web/config.py b/web/config.py
index 086083a52..b8b3714ab 100644
--- a/web/config.py
+++ b/web/config.py
@@ -535,7 +535,7 @@ ENHANCED_COOKIE_PROTECTION = True
##########################################################################
# Default setting is internal
-# External Supported Sources: ldap
+# External Supported Sources: ldap, kerberos
# Multiple authentication can be achieved by setting this parameter to
# ['ldap', 'internal']. pgAdmin will authenticate the user with ldap first,
# in case of failure internal authentication will be done.
@@ -618,6 +618,26 @@ LDAP_CA_CERT_FILE = ''
LDAP_CERT_FILE = ''
LDAP_KEY_FILE = ''
+
+##########################################################################
+# Kerberos Configuration
+##########################################################################
+
+KRB_APP_HOST_NAME = DEFAULT_SERVER
+
+# If the default_keytab_name is not set in krb5.conf or
+# the KRB_KTNAME environment variable is not set then, explicitly set
+# the Keytab file
+
+KRB_KTNAME = '<KRB5_KEYTAB_FILE>'
+
+# After kerberos authentication, user will be added into the SQLite database
+# automatically, if set to True.
+# Set it to False, if user should not be added automatically,
+# in this case Admin has to add the user manually in the SQLite database.
+
+KRB_AUTO_CREATE_USER = True
+
##########################################################################
# Local config settings
##########################################################################
diff --git a/web/pgAdmin4.py b/web/pgAdmin4.py
index 8e0eb99d3..5586875df 100644
--- a/web/pgAdmin4.py
+++ b/web/pgAdmin4.py
@@ -97,15 +97,19 @@ if config.SERVER_MODE:
# Authentication sources
app.PGADMIN_DEFAULT_AUTH_SOURCE = 'internal'
-app.PGADMIN_SUPPORTED_AUTH_SOURCE = ['internal', 'ldap']
+app.PGADMIN_LDAP_AUTH_SOURCE = 'ldap'
+app.PGADMIN_KERBEROS_AUTH_SOURCE = 'kerberos'
+
+app.PGADMIN_SUPPORTED_AUTH_SOURCE = [app.PGADMIN_DEFAULT_AUTH_SOURCE,
+ app.PGADMIN_LDAP_AUTH_SOURCE,
+ app.PGADMIN_KERBEROS_AUTH_SOURCE
+ ]
+
if len(config.AUTHENTICATION_SOURCES) > 0:
app.PGADMIN_EXTERNAL_AUTH_SOURCE = config.AUTHENTICATION_SOURCES[0]
else:
app.PGADMIN_EXTERNAL_AUTH_SOURCE = app.PGADMIN_DEFAULT_AUTH_SOURCE
-app.logger.debug(
- "Authentication Source: %s" % app.PGADMIN_DEFAULT_AUTH_SOURCE)
-
# Start the web server. The port number should have already been set by the
# runtime if we're running in desktop mode, otherwise we'll just use the
# Flask default.
diff --git a/web/pgAdmin4.wsgi b/web/pgAdmin4.wsgi
index 4ed2d7860..693f688ae 100644
--- a/web/pgAdmin4.wsgi
+++ b/web/pgAdmin4.wsgi
@@ -24,6 +24,10 @@ builtins.SERVER_MODE = True
import config
+
+config.AUTHENTICATION_SOURCES = ['kerberos']
+config.KERBEROS_AUTO_CREATE_USER = True
+
# When running it as a WSGI application, directory for the configuration file
# must present.
if not os.path.exists(os.path.dirname(config.SQLITE_PATH)):
diff --git a/web/pgadmin/__init__.py b/web/pgadmin/__init__.py
index 223c2053a..5dc5a1e60 100644
--- a/web/pgadmin/__init__.py
+++ b/web/pgadmin/__init__.py
@@ -674,6 +674,7 @@ def create_app(app_name=None):
# Check the auth key is valid, if it's set, and we're not in server
# mode, and it's not a help file request.
+
if not config.SERVER_MODE and app.PGADMIN_INT_KEY != '' and ((
'key' not in request.args or
request.args['key'] != app.PGADMIN_INT_KEY) and
@@ -695,11 +696,19 @@ def create_app(app_name=None):
)
abort(401)
login_user(user)
+ elif config.SERVER_MODE and\
+ app.PGADMIN_EXTERNAL_AUTH_SOURCE ==\
+ app.PGADMIN_KERBEROS_AUTH_SOURCE and \
+ not current_user.is_authenticated and \
+ request.endpoint in ('redirects.index', 'security.login'):
+ 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 != \
+ app.PGADMIN_KERBEROS_AUTH_SOURCE and \
current_app.keyManager.get() is None and \
request.endpoint not in ('security.login', 'security.logout'):
logout_user()
diff --git a/web/pgadmin/authenticate/__init__.py b/web/pgadmin/authenticate/__init__.py
index 5284e0c52..898daf0b1 100644
--- a/web/pgadmin/authenticate/__init__.py
+++ b/web/pgadmin/authenticate/__init__.py
@@ -11,7 +11,7 @@
import flask
import pickle
-from flask import current_app, flash
+from flask import current_app, flash, Response, request, url_for
from flask_babelex import gettext
from flask_security import current_user
from flask_security.views import _security, _ctx
@@ -56,15 +56,24 @@ def login():
if status:
# Login the user
status, msg = auth_obj.login()
+ current_auth_obj = auth_obj.as_dict()
if not status:
+ if current_auth_obj['current_source'] ==\
+ current_app.PGADMIN_KERBEROS_AUTH_SOURCE:
+ return flask.redirect('{0}?next={1}'.format(url_for(
+ 'browser.kerberos_login'), url_for('browser.index')))
+
flash(gettext(msg), 'danger')
return flask.redirect(get_post_logout_redirect())
- session['_auth_source_manager_obj'] = auth_obj.as_dict()
+ session['_auth_source_manager_obj'] = current_auth_obj
return flask.redirect(get_post_login_redirect())
+ elif isinstance(msg, Response):
+ return msg
flash(gettext(msg), 'danger')
- return flask.redirect(get_post_logout_redirect())
+ response = flask.redirect(get_post_logout_redirect())
+ return response
class AuthSourceManager():
@@ -75,6 +84,7 @@ class AuthSourceManager():
self.auth_sources = sources
self.source = None
self.source_friendly_name = None
+ self.current_source = None
def as_dict(self):
"""
@@ -84,9 +94,17 @@ class AuthSourceManager():
res = dict()
res['source_friendly_name'] = self.source_friendly_name
res['auth_sources'] = self.auth_sources
+ res['current_source'] = self.current_source
return res
+ def set_current_source(self, source):
+ self.current_source = source
+
+ @property
+ def get_current_source(self, source):
+ return self.current_source
+
def set_source(self, source):
self.source = source
@@ -115,9 +133,34 @@ class AuthSourceManager():
msg = None
for src in self.auth_sources:
source = get_auth_sources(src)
+ current_app.logger.debug(
+ "Authentication initiated via source: %s" %
+ source.get_source_name())
+
+ # if self.form.data['email'] and self.form.data['password'] and \
+ # source.get_source_name() ==\
+ # current_app.PGADMIN_KERBEROS_AUTH_SOURCE:
+ # continue
+
status, msg = source.authenticate(self.form)
+
+ # When server sends Unauthorized header to get the ticket over HTTP
+ # OR When kerberos authentication failed while accessing pgadmin,
+ # we need to break the loop as no need to authenticate further
+ # even if the authentication sources set to multiple
+ if not status:
+ if (hasattr(msg, 'status') and
+ msg.status == '401 UNAUTHORIZED') or\
+ (source.get_source_name() ==
+ current_app.PGADMIN_KERBEROS_AUTH_SOURCE and
+ request.method == 'GET'):
+ break
+
if status:
self.set_source(source)
+ self.set_current_source(source.get_source_name())
+ if msg is not None and 'username' in msg:
+ self.form._fields['email'].data = msg['username']
return status, msg
return status, msg
@@ -125,6 +168,9 @@ class AuthSourceManager():
status, msg = self.source.login(self.form)
if status:
self.set_source_friendly_name(self.source.get_friendly_name())
+ current_app.logger.debug(
+ "Authentication and Login successfully done via source : %s" %
+ self.source.get_source_name())
return status, msg
diff --git a/web/pgadmin/authenticate/internal.py b/web/pgadmin/authenticate/internal.py
index 105e78c4e..34361d6fd 100644
--- a/web/pgadmin/authenticate/internal.py
+++ b/web/pgadmin/authenticate/internal.py
@@ -31,7 +31,11 @@ class BaseAuthentication(object):
'INVALID_EMAIL': gettext('Email/Username is not valid')
}
- @abstractproperty
+ @abstractmethod
+ def get_source_name(self):
+ pass
+
+ @abstractmethod
def get_friendly_name(self):
pass
@@ -82,6 +86,9 @@ class BaseAuthentication(object):
class InternalAuthentication(BaseAuthentication):
+ def get_source_name(self):
+ return current_app.PGADMIN_DEFAULT_AUTH_SOURCE
+
def get_friendly_name(self):
return gettext("internal")
diff --git a/web/pgadmin/authenticate/kerberos.py b/web/pgadmin/authenticate/kerberos.py
new file mode 100644
index 000000000..9be5e0825
--- /dev/null
+++ b/web/pgadmin/authenticate/kerberos.py
@@ -0,0 +1,137 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""A blueprint module implementing the Spnego/Kerberos authentication."""
+
+import base64
+import gssapi
+from os import environ
+
+from werkzeug.datastructures import Headers
+from flask_babelex import gettext
+from flask import Flask, request, Response, session,\
+ current_app, render_template, flash
+
+import config
+from pgadmin.model import User, ServerGroup, db, Role
+from pgadmin.tools.user_management import create_user
+
+from flask_security.views import _security, _commit, _ctx
+from werkzeug.datastructures import MultiDict
+
+from .internal import BaseAuthentication
+
+
+# Set the Kerberos config file
+if config.KRB_KTNAME and config.KRB_KTNAME != '<KRB5_KEYTAB_FILE>':
+ environ['KRB5_KTNAME'] = config.KRB_KTNAME
+
+
+class KERBEROSAuthentication(BaseAuthentication):
+
+ def get_source_name(self):
+ return current_app.PGADMIN_KERBEROS_AUTH_SOURCE
+
+ def get_friendly_name(self):
+ return gettext("kerberos")
+
+ def validate(self, form):
+ return True
+
+ def authenticate(self, frm):
+ retval = [True, None]
+ negotiate = False
+ headers = Headers()
+ authorization = request.headers.get("Authorization", None)
+ form_class = _security.login_form
+
+ if request.json:
+ form = form_class(MultiDict(request.json))
+ else:
+ form = form_class()
+
+ try:
+ if authorization is not None:
+ auth_header = authorization.split()
+ if auth_header[0] == 'Negotiate':
+ status, negotiate = self.negotiate_start(auth_header[1])
+
+ if status:
+ # Saving the first 15 characters of the kerberos key
+ # to encrypt/decrypt database password
+ session['kerberos_key'] = auth_header[1][0:15]
+ # Create user
+ retval = self.__auto_create_user(
+ str(negotiate.initiator_name))
+ elif isinstance(negotiate, Exception):
+ flash(gettext(negotiate), 'danger')
+ retval = [status,
+ Response(render_template(
+ "security/login_user.html",
+ login_user_form=form))]
+ else:
+ headers.add('WWW-Authenticate', 'Negotiate ' +
+ str(base64.b64encode(negotiate), 'utf-8'))
+ return False, Response("Success", 200, headers)
+ else:
+ flash(gettext("Kerberos authentication failed."
+ " Couldn't find kerberos ticket."), 'danger')
+ headers.add('WWW-Authenticate', 'Negotiate')
+ retval = [False,
+ Response(render_template(
+ "security/login_user.html",
+ login_user_form=form), 401, headers)]
+ finally:
+ if negotiate is not False:
+ self.negotiate_end(negotiate)
+ return retval
+
+ def negotiate_start(self, in_token):
+ svc_princ = gssapi.Name('HTTP@%s' % config.KRB_APP_HOST_NAME,
+ name_type=gssapi.NameType.hostbased_service)
+ cname = svc_princ.canonicalize(gssapi.MechType.kerberos)
+
+ try:
+ server_creds = gssapi.Credentials(usage='accept', name=cname)
+ context = gssapi.SecurityContext(creds=server_creds)
+ out_token = context.step(base64.b64decode(in_token))
+ except Exception as e:
+ current_app.logger.exception(e)
+ return False, e
+
+ if out_token and not context.complete:
+ return False, out_token
+ if context.complete:
+ return True, context
+ else:
+ return False, None
+
+ def negotiate_end(self, context):
+ # Free gss_cred_id_t
+ del_creds = getattr(context, 'delegated_creds', None)
+ if del_creds:
+ deleg_creds = context.delegated_creds
+ del(deleg_creds)
+
+ def __auto_create_user(self, username):
+ """Add the ldap user to the internal SQLite database."""
+ username = str(username)
+ if config.KRB_AUTO_CREATE_USER:
+ user = User.query.filter_by(
+ username=username).first()
+ if user is None:
+ return create_user({
+ 'username': username,
+ 'email': username,
+ 'role': 2,
+ 'active': True,
+ 'auth_source': 'kerberos'
+ })
+
+ return True, {'username': username}
diff --git a/web/pgadmin/authenticate/ldap.py b/web/pgadmin/authenticate/ldap.py
index 2cdca8605..7c10143c0 100644
--- a/web/pgadmin/authenticate/ldap.py
+++ b/web/pgadmin/authenticate/ldap.py
@@ -31,6 +31,9 @@ ERROR_SEARCHING_LDAP_DIRECTORY = "Error searching the LDAP directory: {}"
class LDAPAuthentication(BaseAuthentication):
"""Ldap Authentication Class"""
+ def get_source_name(self):
+ return current_app.PGADMIN_LDAP_AUTH_SOURCE
+
def get_friendly_name(self):
return gettext("ldap")
diff --git a/web/pgadmin/browser/__init__.py b/web/pgadmin/browser/__init__.py
index 77120cd59..dd4807b51 100644
--- a/web/pgadmin/browser/__init__.py
+++ b/web/pgadmin/browser/__init__.py
@@ -29,7 +29,7 @@ from flask_security.recoverable import reset_password_token_status, \
generate_reset_password_token, update_password
from flask_security.signals import reset_password_instructions_sent
from flask_security.utils import config_value, do_flash, get_url, \
- get_message, slash_url_suffix, login_user, send_mail
+ get_message, slash_url_suffix, login_user, send_mail, logout_user
from flask_security.views import _security, _commit, _ctx
from werkzeug.datastructures import MultiDict
@@ -280,7 +280,10 @@ class BrowserModule(PgAdminModule):
'browser.check_master_password',
'browser.set_master_password',
'browser.reset_master_password',
- 'browser.lock_layout']
+ 'browser.lock_layout',
+ 'browser.kerberos_logout',
+ 'browser.kerberos_login',
+ ]
blueprint = BrowserModule(MODULE_NAME, __name__)
@@ -539,6 +542,11 @@ class BrowserPluginModule(PgAdminModule):
def _get_logout_url():
+ if session['_auth_source_manager_obj']['current_source'] == \
+ current_app.PGADMIN_KERBEROS_AUTH_SOURCE:
+ return '{0}?next={1}'.format(url_for(
+ 'browser.kerberos_logout'), url_for(BROWSER_INDEX))
+
return '{0}?next={1}'.format(
url_for('security.logout'), url_for(BROWSER_INDEX))
@@ -623,6 +631,28 @@ def check_browser_upgrade():
flash(msg, 'warning')
[email protected]("/kerberos_login",
+ endpoint="kerberos_login", methods=["GET"])
[email protected]
+def kerberos_login():
+ logout_user()
+ return Response(render_template(
+ MODULE_NAME + "/kerberos_login.html",
+ login_url=url_for('security.login'),
+ ))
+
+
[email protected]("/kerberos_logout",
+ endpoint="kerberos_logout", methods=["GET"])
[email protected]
+def kerberos_logout():
+ logout_user()
+ return Response(render_template(
+ MODULE_NAME + "/kerberos_logout.html",
+ login_url=url_for('security.login'),
+ ))
+
+
@blueprint.route("/")
@pgCSRFProtect.exempt
@login_required
diff --git a/web/pgadmin/browser/templates/browser/kerberos_login.html b/web/pgadmin/browser/templates/browser/kerberos_login.html
new file mode 100644
index 000000000..c112e3196
--- /dev/null
+++ b/web/pgadmin/browser/templates/browser/kerberos_login.html
@@ -0,0 +1,16 @@
+{% extends "base.html" %}
+{% block body %}
+<div class="container-fluid change_pass">
+ <div class="row align-items-center h-100">
+ <div class="col-md-5"></div>
+ <div class="col-md-5">
+ <div class="panel-header h4"><i class="app-icon pg-icon-blue" aria-hidden="true"></i> {{ _('%(appname)s', appname=config.APP_NAME) }}</div>
+ <div class="panel-body">
+ <div class="d-block text-color pb-3 h5">{{ _('Login Failed.') }}</div>
+ <div><a href="{{ login_url }}">Click here</a> to Login again.</div>
+ </div>
+ </div>
+ <div class="col-md-4"></div>
+ </div>
+</div>
+{% endblock %}
diff --git a/web/pgadmin/browser/templates/browser/kerberos_logout.html b/web/pgadmin/browser/templates/browser/kerberos_logout.html
new file mode 100644
index 000000000..430dc6f25
--- /dev/null
+++ b/web/pgadmin/browser/templates/browser/kerberos_logout.html
@@ -0,0 +1,16 @@
+{% extends "base.html" %}
+{% block body %}
+<div class="container-fluid change_pass">
+ <div class="row align-items-center h-100">
+ <div class="col-md-5"></div>
+ <div class="col-md-5">
+ <div class="panel-header h4"><i class="app-icon pg-icon-blue" aria-hidden="true"></i> {{ _('%(appname)s', appname=config.APP_NAME) }}</div>
+ <div class="panel-body">
+ <div class="d-block text-color pb-3 h5">{{ _('Logged out successfully.') }}</div>
+ <div><a href="{{ login_url }}">Click here</a> to Login again.</div>
+ </div>
+ </div>
+ <div class="col-md-4"></div>
+ </div>
+</div>
+{% endblock %}
diff --git a/web/pgadmin/browser/tests/test_kerberos_with_mocking.py b/web/pgadmin/browser/tests/test_kerberos_with_mocking.py
new file mode 100644
index 000000000..50caa71e9
--- /dev/null
+++ b/web/pgadmin/browser/tests/test_kerberos_with_mocking.py
@@ -0,0 +1,106 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, 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
+
+
+class KerberosLoginMockTestCase(BaseTestGenerator):
+ """
+ This class checks Spnego/Kerberos login functionality by mocking
+ HTTP negotiate authentication.
+ """
+
+ scenarios = [
+ ('Spnego/Kerberos Authentication: Test Unauthorized', dict(
+ auth_source=['kerberos'],
+ auto_create_user=True,
+ username='[email protected]',
+ password='PASSWORD',
+ flag=1
+ )),
+ ('Spnego/Kerberos Authentication: Test Authorized', dict(
+ auth_source=['kerberos'],
+ auto_create_user=True,
+ username='[email protected]',
+ password='PASSWORD',
+ flag=2
+ ))
+ ]
+
+ @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
+
+ def runTest(self):
+ """This function checks spnego/kerberos login functionality."""
+ if self.flag == 1:
+ self.test_unauthorized()
+ elif self.flag == 2:
+ if app_config.SERVER_MODE is False:
+ self.skipTest(
+ "Can not run Kerberos Authentication in the Desktop mode."
+ )
+
+ self.test_authorized()
+
+ def test_unauthorized(self):
+ """
+ Ensure that when client sends the first request,
+ the Negotiate request is sent.
+ """
+ res = self.tester.login(self.username, self.password, True)
+ self.assertEqual(res.status_code, 401)
+ self.assertEqual(res.headers.get('www-authenticate'), 'Negotiate')
+
+ def test_authorized(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.
+ """
+
+ class delCrads:
+ def __init__(self):
+ self.initiator_name = '[email protected]'
+ del_crads = delCrads()
+
+ AuthSourceRegistry.registry['kerberos'].negotiate_start = MagicMock(
+ return_value=[True, del_crads])
+ res = self.tester.login(self.username,
+ self.password,
+ True,
+ headers={'Authorization': 'Negotiate CTOKEN'}
+ )
+ self.assertEqual(res.status_code, 200)
+ respdata = 'Gravatar image for %s' % self.username
+ self.assertTrue(respdata in res.data.decode('utf8'))
+
+ def tearDown(self):
+ self.tester.logout()
+
+ @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']
+ utils.login_tester_account(cls.tester)
diff --git a/web/pgadmin/utils/master_password.py b/web/pgadmin/utils/master_password.py
index 759bf36e0..be655bef2 100644
--- a/web/pgadmin/utils/master_password.py
+++ b/web/pgadmin/utils/master_password.py
@@ -1,5 +1,5 @@
import config
-from flask import current_app
+from flask import current_app, session
from flask_login import current_user
from pgadmin.model import db, User, Server
from pgadmin.utils.crypto import encrypt, decrypt
@@ -32,6 +32,11 @@ def get_crypt_key():
elif config.MASTER_PASSWORD_REQUIRED \
and not config.SERVER_MODE and enc_key is None:
return False, None
+ elif config.SERVER_MODE and \
+ session['_auth_source_manager_obj']['source_friendly_name']\
+ == current_app.PGADMIN_KERBEROS_AUTH_SOURCE:
+ return True, session['kerberos_key'] if 'kerberos_key' in session \
+ else None
else:
return True, enc_key
diff --git a/web/regression/python_test_utils/csrf_test_client.py b/web/regression/python_test_utils/csrf_test_client.py
index ad3396790..d1adfdc54 100644
--- a/web/regression/python_test_utils/csrf_test_client.py
+++ b/web/regression/python_test_utils/csrf_test_client.py
@@ -101,7 +101,8 @@ class TestClient(testing.FlaskClient):
return csrf_token
- def login(self, email, password, _follow_redirects=False):
+ def login(self, email, password, _follow_redirects=False,
+ headers=None):
if config.SERVER_MODE is True:
res = self.get('/login', follow_redirects=True)
csrf_token = self.fetch_csrf(res)
@@ -113,7 +114,8 @@ class TestClient(testing.FlaskClient):
email=email, password=password,
csrf_token=csrf_token,
),
- follow_redirects=_follow_redirects
+ follow_redirects=_follow_redirects,
+ headers=headers
)
self.csrf_token = csrf_token
diff --git a/web/regression/runtests.py b/web/regression/runtests.py
index a62358fac..a6782f36e 100644
--- a/web/regression/runtests.py
+++ b/web/regression/runtests.py
@@ -119,6 +119,8 @@ app.config['WTF_CSRF_ENABLED'] = True
# Authentication sources
app.PGADMIN_DEFAULT_AUTH_SOURCE = 'internal'
app.PGADMIN_EXTERNAL_AUTH_SOURCE = 'ldap'
+app.PGADMIN_KERBEROS_AUTH_SOURCE = 'kerberos'
+app.PGADMIN_LDAP_AUTH_SOURCE = 'ldap'
app.test_client_class = TestClient
test_client = app.test_client()
diff --git a/yarn.lock b/yarn.lock
new file mode 100644
index 000000000..fb57ccd13
--- /dev/null
+++ b/yarn.lock
@@ -0,0 +1,4 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
[application/vnd.openxmlformats-officedocument.wordprocessingml.document] GSSAPI Authentication - pgAdmin.docx (10.1K, 4-GSSAPI%20Authentication%20-%20pgAdmin.docx)
download
view thread (32+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected]
Subject: Re: [pgAdmin4][Patch] - RM 5457 - Kerberos Authentication - Phase 1
In-Reply-To: <CAFOhELdXhWMR2zS4dnH+SudN0s7LiENH+vczC0YhuifPgm+G5g@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox