mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-01-23 23:13:38 -06:00
Added support to connect PostgreSQL servers via Kerberos authentication. Fixes #6158
This commit is contained in:
parent
aa9a4c30d3
commit
72f3730c34
@ -9,6 +9,7 @@ This release contains a number of bug fixes and new features since the release o
|
|||||||
New features
|
New features
|
||||||
************
|
************
|
||||||
|
|
||||||
|
| `Issue #6158 <https://redmine.postgresql.org/issues/6158>`_ - Added support to connect PostgreSQL servers via Kerberos authentication.
|
||||||
|
|
||||||
Housekeeping
|
Housekeeping
|
||||||
************
|
************
|
||||||
|
@ -634,6 +634,9 @@ KRB_KTNAME = '<KRB5_KEYTAB_FILE>'
|
|||||||
|
|
||||||
KRB_AUTO_CREATE_USER = True
|
KRB_AUTO_CREATE_USER = True
|
||||||
|
|
||||||
|
KERBEROS_CCACHE_DIR = os.path.join(DATA_DIR, 'krbccache')
|
||||||
|
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# Local config settings
|
# Local config settings
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
28
web/migrations/versions/d0bc9f32b2b9_.py
Normal file
28
web/migrations/versions/d0bc9f32b2b9_.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: d0bc9f32b2b9
|
||||||
|
Revises: c6974f64df08
|
||||||
|
Create Date: 2021-04-27 12:40:08.899712
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from pgadmin.model import db
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'd0bc9f32b2b9'
|
||||||
|
down_revision = 'c6974f64df08'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
db.engine.execute(
|
||||||
|
'ALTER TABLE server ADD COLUMN kerberos_conn INTEGER DEFAULT 0'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# pgAdmin only upgrades, downgrade not implemented.
|
||||||
|
pass
|
@ -13,10 +13,13 @@ import flask
|
|||||||
import pickle
|
import pickle
|
||||||
from flask import current_app, flash, Response, request, url_for,\
|
from flask import current_app, flash, Response, request, url_for,\
|
||||||
render_template
|
render_template
|
||||||
from flask_security import current_user
|
from flask_babelex import gettext
|
||||||
|
from flask_security import current_user, login_required
|
||||||
from flask_security.views import _security, _ctx
|
from flask_security.views import _security, _ctx
|
||||||
from flask_security.utils import config_value, get_post_logout_redirect, \
|
from flask_security.utils import config_value, get_post_logout_redirect, \
|
||||||
get_post_login_redirect, logout_user
|
get_post_login_redirect, logout_user
|
||||||
|
from pgadmin.utils.ajax import make_json_response, internal_server_error
|
||||||
|
import os
|
||||||
|
|
||||||
from flask import session
|
from flask import session
|
||||||
|
|
||||||
@ -34,7 +37,9 @@ class AuthenticateModule(PgAdminModule):
|
|||||||
def get_exposed_url_endpoints(self):
|
def get_exposed_url_endpoints(self):
|
||||||
return ['authenticate.login',
|
return ['authenticate.login',
|
||||||
'authenticate.kerberos_login',
|
'authenticate.kerberos_login',
|
||||||
'authenticate.kerberos_logout']
|
'authenticate.kerberos_logout',
|
||||||
|
'authenticate.kerberos_update_ticket',
|
||||||
|
'authenticate.kerberos_validate_ticket']
|
||||||
|
|
||||||
|
|
||||||
blueprint = AuthenticateModule(MODULE_NAME, __name__, static_url_path='')
|
blueprint = AuthenticateModule(MODULE_NAME, __name__, static_url_path='')
|
||||||
@ -55,6 +60,12 @@ def kerberos_login():
|
|||||||
@pgCSRFProtect.exempt
|
@pgCSRFProtect.exempt
|
||||||
def kerberos_logout():
|
def kerberos_logout():
|
||||||
logout_user()
|
logout_user()
|
||||||
|
if 'KRB5CCNAME' in session:
|
||||||
|
# Remove the credential cache
|
||||||
|
cache_file_path = session['KRB5CCNAME'].split(":")[1]
|
||||||
|
if os.path.exists(cache_file_path):
|
||||||
|
os.remove(cache_file_path)
|
||||||
|
|
||||||
return Response(render_template("browser/kerberos_logout.html",
|
return Response(render_template("browser/kerberos_logout.html",
|
||||||
login_url=url_for('security.login'),
|
login_url=url_for('security.login'),
|
||||||
))
|
))
|
||||||
@ -165,6 +176,8 @@ class AuthSourceManager():
|
|||||||
|
|
||||||
if self.form.data['email'] and self.form.data['password'] and \
|
if self.form.data['email'] and self.form.data['password'] and \
|
||||||
source.get_source_name() == KERBEROS:
|
source.get_source_name() == KERBEROS:
|
||||||
|
msg = gettext('pgAdmin internal user authentication'
|
||||||
|
' is not enabled, please contact administrator.')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
status, msg = source.authenticate(self.form)
|
status, msg = source.authenticate(self.form)
|
||||||
@ -173,11 +186,13 @@ class AuthSourceManager():
|
|||||||
# OR When kerberos authentication failed while accessing pgadmin,
|
# OR When kerberos authentication failed while accessing pgadmin,
|
||||||
# we need to break the loop as no need to authenticate further
|
# we need to break the loop as no need to authenticate further
|
||||||
# even if the authentication sources set to multiple
|
# even if the authentication sources set to multiple
|
||||||
if not status and (hasattr(msg, 'status') and
|
if not status:
|
||||||
msg.status == '401 UNAUTHORIZED') or \
|
if (hasattr(msg, 'status') and
|
||||||
(source.get_source_name() == KERBEROS and
|
msg.status == '401 UNAUTHORIZED') or\
|
||||||
request.method == 'GET'):
|
(source.get_source_name() ==
|
||||||
break
|
KERBEROS and
|
||||||
|
request.method == 'GET'):
|
||||||
|
break
|
||||||
|
|
||||||
if status:
|
if status:
|
||||||
self.set_source(source)
|
self.set_source(source)
|
||||||
@ -224,3 +239,58 @@ def init_app(app):
|
|||||||
AuthSourceRegistry.load_auth_sources()
|
AuthSourceRegistry.load_auth_sources()
|
||||||
|
|
||||||
return auth_sources
|
return auth_sources
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route("/kerberos/update_ticket",
|
||||||
|
endpoint="kerberos_update_ticket", methods=["GET"])
|
||||||
|
@pgCSRFProtect.exempt
|
||||||
|
@login_required
|
||||||
|
def kerberos_update_ticket():
|
||||||
|
"""
|
||||||
|
Update the kerberos ticket.
|
||||||
|
"""
|
||||||
|
from werkzeug.datastructures import Headers
|
||||||
|
headers = Headers()
|
||||||
|
|
||||||
|
authorization = request.headers.get("Authorization", None)
|
||||||
|
|
||||||
|
if authorization is None:
|
||||||
|
# Send the Negotiate header to the client
|
||||||
|
# if Kerberos ticket is not found.
|
||||||
|
headers.add('WWW-Authenticate', 'Negotiate')
|
||||||
|
return Response("Unauthorised", 401, headers)
|
||||||
|
else:
|
||||||
|
source = get_auth_sources(KERBEROS)
|
||||||
|
auth_header = authorization.split()
|
||||||
|
in_token = auth_header[1]
|
||||||
|
|
||||||
|
# Validate the Kerberos ticket
|
||||||
|
status, context = source.negotiate_start(in_token)
|
||||||
|
if status:
|
||||||
|
return Response("Ticket updated successfully.")
|
||||||
|
|
||||||
|
return Response(context, 500)
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route("/kerberos/validate_ticket",
|
||||||
|
endpoint="kerberos_validate_ticket", methods=["GET"])
|
||||||
|
@pgCSRFProtect.exempt
|
||||||
|
@login_required
|
||||||
|
def kerberos_validate_ticket():
|
||||||
|
"""
|
||||||
|
Return the kerberos ticket lifetime left after getting the
|
||||||
|
ticket from the credential cache
|
||||||
|
"""
|
||||||
|
import gssapi
|
||||||
|
|
||||||
|
try:
|
||||||
|
del_creds = gssapi.Credentials(store={'ccache': session['KRB5CCNAME']})
|
||||||
|
creds = del_creds.acquire(store={'ccache': session['KRB5CCNAME']})
|
||||||
|
except Exception as e:
|
||||||
|
current_app.logger.exception(e)
|
||||||
|
return internal_server_error(errormsg=str(e))
|
||||||
|
|
||||||
|
return make_json_response(
|
||||||
|
data={'ticket_lifetime': creds.lifetime},
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
"""A blueprint module implementing the Spnego/Kerberos authentication."""
|
"""A blueprint module implementing the Spnego/Kerberos authentication."""
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
from os import environ
|
from os import environ, path
|
||||||
|
|
||||||
from werkzeug.datastructures import Headers
|
from werkzeug.datastructures import Headers
|
||||||
from flask_babelex import gettext
|
from flask_babelex import gettext
|
||||||
@ -128,19 +128,37 @@ class KerberosAuthentication(BaseAuthentication):
|
|||||||
if out_token and not context.complete:
|
if out_token and not context.complete:
|
||||||
return False, out_token
|
return False, out_token
|
||||||
if context.complete:
|
if context.complete:
|
||||||
|
deleg_creds = context.delegated_creds
|
||||||
|
if not hasattr(deleg_creds, 'name'):
|
||||||
|
error_msg = gettext('Delegated credentials not supplied.')
|
||||||
|
current_app.logger.error(error_msg)
|
||||||
|
return False, Exception(error_msg)
|
||||||
|
try:
|
||||||
|
cache_file_path = path.join(
|
||||||
|
config.KERBEROS_CCACHE_DIR, 'pgadmin_cache_{0}'.format(
|
||||||
|
deleg_creds.name)
|
||||||
|
)
|
||||||
|
CCACHE = 'FILE:{0}'.format(cache_file_path)
|
||||||
|
store = {'ccache': CCACHE}
|
||||||
|
deleg_creds.store(store, overwrite=True, set_default=True)
|
||||||
|
session['KRB5CCNAME'] = CCACHE
|
||||||
|
except Exception as e:
|
||||||
|
current_app.logger.exception(e)
|
||||||
|
return False, e
|
||||||
|
|
||||||
return True, context
|
return True, context
|
||||||
else:
|
else:
|
||||||
return False, None
|
return False, None
|
||||||
|
|
||||||
def negotiate_end(self, context):
|
def negotiate_end(self, context):
|
||||||
# Free gss_cred_id_t
|
# Free Delegated Credentials
|
||||||
del_creds = getattr(context, 'delegated_creds', None)
|
del_creds = getattr(context, 'delegated_creds', None)
|
||||||
if del_creds:
|
if del_creds:
|
||||||
deleg_creds = context.delegated_creds
|
deleg_creds = context.delegated_creds
|
||||||
del(deleg_creds)
|
del(deleg_creds)
|
||||||
|
|
||||||
def __auto_create_user(self, username):
|
def __auto_create_user(self, username):
|
||||||
"""Add the ldap user to the internal SQLite database."""
|
"""Add the kerberos user to the internal SQLite database."""
|
||||||
username = str(username)
|
username = str(username)
|
||||||
if config.KRB_AUTO_CREATE_USER:
|
if config.KRB_AUTO_CREATE_USER:
|
||||||
user = User.query.filter_by(
|
user = User.query.filter_by(
|
||||||
|
59
web/pgadmin/authenticate/static/js/kerberos.js
Normal file
59
web/pgadmin/authenticate/static/js/kerberos.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import url_for from 'sources/url_for';
|
||||||
|
import userInfo from 'pgadmin.user_management.current_user';
|
||||||
|
import pgConst from 'pgadmin.browser.constants';
|
||||||
|
|
||||||
|
function fetch_ticket() {
|
||||||
|
// Fetch the Kerberos Updated ticket through SPNEGO
|
||||||
|
return fetch(url_for('authenticate.kerberos_update_ticket')
|
||||||
|
)
|
||||||
|
.then(function(response){
|
||||||
|
if (response.status >= 200 && response.status < 300) {
|
||||||
|
return Promise.resolve(response);
|
||||||
|
} else {
|
||||||
|
return Promise.reject(new Error(response.statusText));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetch_ticket_lifetime () {
|
||||||
|
// Fetch the Kerberos ticket lifetime left
|
||||||
|
|
||||||
|
return fetch(url_for('authenticate.kerberos_validate_ticket')
|
||||||
|
)
|
||||||
|
.then(
|
||||||
|
function(response){
|
||||||
|
if (response.status >= 200 && response.status < 300) {
|
||||||
|
return response.json();
|
||||||
|
} else {
|
||||||
|
return Promise.reject(new Error(response.statusText));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(function(response){
|
||||||
|
let ticket_lifetime = response.data.ticket_lifetime;
|
||||||
|
if (ticket_lifetime > 0) {
|
||||||
|
return Promise.resolve(ticket_lifetime);
|
||||||
|
} else {
|
||||||
|
return Promise.reject();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function validate_kerberos_ticket() {
|
||||||
|
// Ping pgAdmin server every 10 seconds
|
||||||
|
// to fetch the Kerberos ticket lifetime left
|
||||||
|
if (userInfo['current_auth_source'] != pgConst['KERBEROS']) return;
|
||||||
|
|
||||||
|
return setInterval(function() {
|
||||||
|
let newPromise = fetch_ticket_lifetime();
|
||||||
|
newPromise.then(
|
||||||
|
function() {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
fetch_ticket
|
||||||
|
);
|
||||||
|
}, 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
export {fetch_ticket, validate_kerberos_ticket, fetch_ticket_lifetime};
|
@ -50,7 +50,7 @@ from pgadmin.utils.master_password import validate_master_password, \
|
|||||||
set_crypt_key, process_masterpass_disabled
|
set_crypt_key, process_masterpass_disabled
|
||||||
from pgadmin.model import User
|
from pgadmin.model import User
|
||||||
from pgadmin.utils.constants import MIMETYPE_APP_JS, PGADMIN_NODE,\
|
from pgadmin.utils.constants import MIMETYPE_APP_JS, PGADMIN_NODE,\
|
||||||
INTERNAL, KERBEROS
|
INTERNAL, KERBEROS, LDAP
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from flask_security.views import default_render_json
|
from flask_security.views import default_render_json
|
||||||
@ -197,7 +197,8 @@ class BrowserModule(PgAdminModule):
|
|||||||
for name, script in [
|
for name, script in [
|
||||||
[PGADMIN_BROWSER, 'js/browser'],
|
[PGADMIN_BROWSER, 'js/browser'],
|
||||||
['pgadmin.browser.endpoints', 'js/endpoints'],
|
['pgadmin.browser.endpoints', 'js/endpoints'],
|
||||||
['pgadmin.browser.error', 'js/error']
|
['pgadmin.browser.error', 'js/error'],
|
||||||
|
['pgadmin.browser.constants', 'js/constants']
|
||||||
]:
|
]:
|
||||||
scripts.append({
|
scripts.append({
|
||||||
'name': name,
|
'name': name,
|
||||||
@ -864,6 +865,18 @@ def exposed_urls():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route("/js/constants.js")
|
||||||
|
@pgCSRFProtect.exempt
|
||||||
|
def app_constants():
|
||||||
|
return make_response(
|
||||||
|
render_template('browser/js/constants.js',
|
||||||
|
INTERNAL=INTERNAL,
|
||||||
|
LDAP=LDAP,
|
||||||
|
KERBEROS=KERBEROS),
|
||||||
|
200, {'Content-Type': MIMETYPE_APP_JS}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route("/js/error.js")
|
@blueprint.route("/js/error.js")
|
||||||
@pgCSRFProtect.exempt
|
@pgCSRFProtect.exempt
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -253,7 +253,8 @@ class ServerModule(sg.ServerGroupPluginModule):
|
|||||||
errmsg=errmsg,
|
errmsg=errmsg,
|
||||||
user_id=server.user_id,
|
user_id=server.user_id,
|
||||||
user_name=server.username,
|
user_name=server.username,
|
||||||
shared=server.shared
|
shared=server.shared,
|
||||||
|
is_kerberos_conn=bool(server.kerberos_conn),
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -547,7 +548,8 @@ class ServerNode(PGChildNodeView):
|
|||||||
if server.tunnel_password is not None else False,
|
if server.tunnel_password is not None else False,
|
||||||
errmsg=errmsg,
|
errmsg=errmsg,
|
||||||
user_name=server.username,
|
user_name=server.username,
|
||||||
shared=server.shared
|
shared=server.shared,
|
||||||
|
is_kerberos_conn=bool(server.kerberos_conn)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -614,7 +616,8 @@ class ServerNode(PGChildNodeView):
|
|||||||
if server.tunnel_password is not None else False,
|
if server.tunnel_password is not None else False,
|
||||||
errmsg=errmsg,
|
errmsg=errmsg,
|
||||||
shared=server.shared,
|
shared=server.shared,
|
||||||
user_name=server.username
|
user_name=server.username,
|
||||||
|
is_kerberos_conn=bool(server.kerberos_conn)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -721,7 +724,8 @@ class ServerNode(PGChildNodeView):
|
|||||||
'tunnel_username': 'tunnel_username',
|
'tunnel_username': 'tunnel_username',
|
||||||
'tunnel_authentication': 'tunnel_authentication',
|
'tunnel_authentication': 'tunnel_authentication',
|
||||||
'tunnel_identity_file': 'tunnel_identity_file',
|
'tunnel_identity_file': 'tunnel_identity_file',
|
||||||
'shared': 'shared'
|
'shared': 'shared',
|
||||||
|
'kerberos_conn': 'kerberos_conn',
|
||||||
}
|
}
|
||||||
|
|
||||||
disp_lbl = {
|
disp_lbl = {
|
||||||
@ -985,7 +989,8 @@ class ServerNode(PGChildNodeView):
|
|||||||
'tunnel_username': tunnel_username,
|
'tunnel_username': tunnel_username,
|
||||||
'tunnel_identity_file': server.tunnel_identity_file
|
'tunnel_identity_file': server.tunnel_identity_file
|
||||||
if server.tunnel_identity_file else None,
|
if server.tunnel_identity_file else None,
|
||||||
'tunnel_authentication': tunnel_authentication
|
'tunnel_authentication': tunnel_authentication,
|
||||||
|
'kerberos_conn': bool(server.kerberos_conn),
|
||||||
}
|
}
|
||||||
|
|
||||||
return ajax_response(response)
|
return ajax_response(response)
|
||||||
@ -1072,7 +1077,8 @@ class ServerNode(PGChildNodeView):
|
|||||||
tunnel_authentication=data.get('tunnel_authentication', 0),
|
tunnel_authentication=data.get('tunnel_authentication', 0),
|
||||||
tunnel_identity_file=data.get('tunnel_identity_file', None),
|
tunnel_identity_file=data.get('tunnel_identity_file', None),
|
||||||
shared=data.get('shared', None),
|
shared=data.get('shared', None),
|
||||||
passfile=data.get('passfile', None)
|
passfile=data.get('passfile', None),
|
||||||
|
kerberos_conn=1 if data.get('kerberos_conn', False) else 0,
|
||||||
)
|
)
|
||||||
db.session.add(server)
|
db.session.add(server)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
@ -1154,7 +1160,8 @@ class ServerNode(PGChildNodeView):
|
|||||||
else 'pg',
|
else 'pg',
|
||||||
version=manager.version
|
version=manager.version
|
||||||
if manager and manager.version
|
if manager and manager.version
|
||||||
else None
|
else None,
|
||||||
|
is_kerberos_conn=bool(server.kerberos_conn),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1348,7 +1355,7 @@ class ServerNode(PGChildNodeView):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.exception(e)
|
current_app.logger.exception(e)
|
||||||
return internal_server_error(errormsg=str(e))
|
return internal_server_error(errormsg=str(e))
|
||||||
if 'password' not in data:
|
if 'password' not in data and server.kerberos_conn is False:
|
||||||
conn_passwd = getattr(conn, 'password', None)
|
conn_passwd = getattr(conn, 'password', None)
|
||||||
if conn_passwd is None and not server.save_password and \
|
if conn_passwd is None and not server.save_password and \
|
||||||
server.passfile is None and server.service is None:
|
server.passfile is None and server.service is None:
|
||||||
@ -1400,6 +1407,9 @@ class ServerNode(PGChildNodeView):
|
|||||||
"Could not connect to server(#{0}) - '{1}'.\nError: {2}"
|
"Could not connect to server(#{0}) - '{1}'.\nError: {2}"
|
||||||
.format(server.id, server.name, errmsg)
|
.format(server.id, server.name, errmsg)
|
||||||
)
|
)
|
||||||
|
if errmsg.find('Ticket expired') != -1:
|
||||||
|
return internal_server_error(errmsg)
|
||||||
|
|
||||||
return self.get_response_for_password(server, 401, True,
|
return self.get_response_for_password(server, 401, True,
|
||||||
True, errmsg)
|
True, errmsg)
|
||||||
else:
|
else:
|
||||||
@ -1467,6 +1477,7 @@ class ServerNode(PGChildNodeView):
|
|||||||
'is_password_saved': bool(server.save_password),
|
'is_password_saved': bool(server.save_password),
|
||||||
'is_tunnel_password_saved': True
|
'is_tunnel_password_saved': True
|
||||||
if server.tunnel_password is not None else False,
|
if server.tunnel_password is not None else False,
|
||||||
|
'is_kerberos_conn': bool(server.kerberos_conn),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -490,6 +490,7 @@ class DatabaseView(PGChildNodeView):
|
|||||||
did, errmsg
|
did, errmsg
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return internal_server_error(errmsg)
|
return internal_server_error(errmsg)
|
||||||
else:
|
else:
|
||||||
current_app.logger.info(
|
current_app.logger.info(
|
||||||
|
@ -10,9 +10,10 @@
|
|||||||
define('pgadmin.node.database', [
|
define('pgadmin.node.database', [
|
||||||
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
|
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
|
||||||
'sources/utils', 'sources/pgadmin', 'pgadmin.browser.utils',
|
'sources/utils', 'sources/pgadmin', 'pgadmin.browser.utils',
|
||||||
'pgadmin.alertifyjs', 'pgadmin.backform', 'pgadmin.browser.collection',
|
'pgadmin.alertifyjs', 'pgadmin.backform',
|
||||||
|
'pgadmin.authenticate.kerberos', 'pgadmin.browser.collection',
|
||||||
'pgadmin.browser.server.privilege', 'pgadmin.browser.server.variable',
|
'pgadmin.browser.server.privilege', 'pgadmin.browser.server.variable',
|
||||||
], function(gettext, url_for, $, _, pgadminUtils, pgAdmin, pgBrowser, Alertify, Backform) {
|
], function(gettext, url_for, $, _, pgadminUtils, pgAdmin, pgBrowser, Alertify, Backform, Kerberos) {
|
||||||
|
|
||||||
if (!pgBrowser.Nodes['coll-database']) {
|
if (!pgBrowser.Nodes['coll-database']) {
|
||||||
pgBrowser.Nodes['coll-database'] =
|
pgBrowser.Nodes['coll-database'] =
|
||||||
@ -556,24 +557,39 @@ define('pgadmin.node.database', [
|
|||||||
onFailure = function(
|
onFailure = function(
|
||||||
xhr, status, error, _model, _data, _tree, _item, _status
|
xhr, status, error, _model, _data, _tree, _item, _status
|
||||||
) {
|
) {
|
||||||
if (!_status) {
|
if (xhr.status != 200 && xhr.responseText.search('Ticket expired') !== -1) {
|
||||||
tree.setInode(_item);
|
tree.addIcon(_item, {icon: 'icon-server-connecting'});
|
||||||
tree.addIcon(_item, {icon: 'icon-database-not-connected'});
|
let fetchTicket = Kerberos.fetch_ticket();
|
||||||
}
|
fetchTicket.then(
|
||||||
|
function() {
|
||||||
Alertify.pgNotifier('error', xhr, error, function(msg) {
|
|
||||||
setTimeout(function() {
|
|
||||||
if (msg == 'CRYPTKEY_SET') {
|
|
||||||
connect_to_database(_model, _data, _tree, _item, _wasConnected);
|
connect_to_database(_model, _data, _tree, _item, _wasConnected);
|
||||||
} else {
|
},
|
||||||
Alertify.dlgServerPass(
|
function(error) {
|
||||||
gettext('Connect to database'),
|
tree.setInode(_item);
|
||||||
msg, _model, _data, _tree, _item, _status,
|
tree.addIcon(_item, {icon: 'icon-database-not-connected'});
|
||||||
onSuccess, onFailure, onCancel
|
Alertify.pgNotifier(error, xhr, gettext('Connect to database.'));
|
||||||
).resizeTo();
|
|
||||||
}
|
}
|
||||||
}, 100);
|
);
|
||||||
});
|
} else {
|
||||||
|
if (!_status) {
|
||||||
|
tree.setInode(_item);
|
||||||
|
tree.addIcon(_item, {icon: 'icon-database-not-connected'});
|
||||||
|
}
|
||||||
|
|
||||||
|
Alertify.pgNotifier('error', xhr, error, function(msg) {
|
||||||
|
setTimeout(function() {
|
||||||
|
if (msg == 'CRYPTKEY_SET') {
|
||||||
|
connect_to_database(_model, _data, _tree, _item, _wasConnected);
|
||||||
|
} else {
|
||||||
|
Alertify.dlgServerPass(
|
||||||
|
gettext('Connect to database'),
|
||||||
|
msg, _model, _data, _tree, _item, _status,
|
||||||
|
onSuccess, onFailure, onCancel
|
||||||
|
).resizeTo();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onSuccess = function(
|
onSuccess = function(
|
||||||
res, model, _data, _tree, _item, _connected
|
res, model, _data, _tree, _item, _connected
|
||||||
@ -640,6 +656,7 @@ define('pgadmin.node.database', [
|
|||||||
if (xhr.status === 410) {
|
if (xhr.status === 410) {
|
||||||
error = gettext('Error: Object not found - %s.', error);
|
error = gettext('Error: Object not found - %s.', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return onFailure(
|
return onFailure(
|
||||||
xhr, status, error, obj, data, tree, item, wasConnected
|
xhr, status, error, obj, data, tree, item, wasConnected
|
||||||
);
|
);
|
||||||
|
@ -13,11 +13,13 @@ define('pgadmin.node.server', [
|
|||||||
'pgadmin.server.supported_servers', 'pgadmin.user_management.current_user',
|
'pgadmin.server.supported_servers', 'pgadmin.user_management.current_user',
|
||||||
'pgadmin.alertifyjs', 'pgadmin.backform',
|
'pgadmin.alertifyjs', 'pgadmin.backform',
|
||||||
'sources/browser/server_groups/servers/model_validation',
|
'sources/browser/server_groups/servers/model_validation',
|
||||||
|
'pgadmin.authenticate.kerberos',
|
||||||
|
'pgadmin.browser.constants',
|
||||||
'pgadmin.browser.server.privilege',
|
'pgadmin.browser.server.privilege',
|
||||||
], function(
|
], function(
|
||||||
gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser,
|
gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser,
|
||||||
supported_servers, current_user, Alertify, Backform,
|
supported_servers, current_user, Alertify, Backform,
|
||||||
modelValidation
|
modelValidation, Kerberos, pgConst,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
if (!pgBrowser.Nodes['server']) {
|
if (!pgBrowser.Nodes['server']) {
|
||||||
@ -904,20 +906,36 @@ define('pgadmin.node.server', [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
},{
|
||||||
|
id: 'kerberos_conn', label: gettext('Kerberos authentication?'), type: 'switch',
|
||||||
|
group: gettext('Connection'), 'options': {
|
||||||
|
'onText': gettext('True'), 'offText': gettext('False'), 'size': 'mini',
|
||||||
|
}, disabled: function() {
|
||||||
|
if (current_user['current_auth_source'] != pgConst['KERBEROS'])
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
},
|
||||||
},{
|
},{
|
||||||
id: 'password', label: gettext('Password'), type: 'password', maxlength: null,
|
id: 'password', label: gettext('Password'), type: 'password', maxlength: null,
|
||||||
group: gettext('Connection'), control: 'input', mode: ['create'], deps: ['connect_now'],
|
group: gettext('Connection'), control: 'input', mode: ['create'],
|
||||||
|
deps: ['connect_now', 'kerberos_conn'],
|
||||||
visible: function(model) {
|
visible: function(model) {
|
||||||
return model.get('connect_now') && model.isNew();
|
return model.get('connect_now') && model.isNew();
|
||||||
},
|
},
|
||||||
|
disabled: function(model) {
|
||||||
|
if (model.get('kerberos_conn'))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
},{
|
},{
|
||||||
id: 'save_password', controlLabel: gettext('Save password?'),
|
id: 'save_password', controlLabel: gettext('Save password?'),
|
||||||
type: 'checkbox', group: gettext('Connection'), mode: ['create'],
|
type: 'checkbox', group: gettext('Connection'), mode: ['create'],
|
||||||
deps: ['connect_now'], visible: function(model) {
|
deps: ['connect_now', 'kerberos_conn'], visible: function(model) {
|
||||||
return model.get('connect_now') && model.isNew();
|
return model.get('connect_now') && model.isNew();
|
||||||
},
|
},
|
||||||
disabled: function() {
|
disabled: function(model) {
|
||||||
if (!current_user.allow_save_password)
|
if (!current_user.allow_save_password || model.get('kerberos_conn'))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -1279,19 +1297,32 @@ define('pgadmin.node.server', [
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
if (xhr.status != 200 && xhr.responseText.search('Ticket expired') !== -1) {
|
||||||
Alertify.pgNotifier('error', xhr, error, function(msg) {
|
tree.addIcon(_item, {icon: 'icon-server-connecting'});
|
||||||
setTimeout(function() {
|
let fetchTicket = Kerberos.fetch_ticket();
|
||||||
if (msg == 'CRYPTKEY_SET') {
|
fetchTicket.then(
|
||||||
|
function() {
|
||||||
connect_to_server(_node, _data, _tree, _item, _wasConnected);
|
connect_to_server(_node, _data, _tree, _item, _wasConnected);
|
||||||
} else {
|
},
|
||||||
Alertify.dlgServerPass(
|
function() {
|
||||||
gettext('Connect to Server'),
|
tree.addIcon(_item, {icon: 'icon-server-not-connected'});
|
||||||
msg, _node, _data, _tree, _item, _wasConnected
|
Alertify.pgNotifier('Connection error', xhr, gettext('Connect to server.'));
|
||||||
).resizeTo();
|
|
||||||
}
|
}
|
||||||
}, 100);
|
);
|
||||||
});
|
} else {
|
||||||
|
Alertify.pgNotifier('error', xhr, error, function(msg) {
|
||||||
|
setTimeout(function() {
|
||||||
|
if (msg == 'CRYPTKEY_SET') {
|
||||||
|
connect_to_server(_node, _data, _tree, _item, _wasConnected);
|
||||||
|
} else {
|
||||||
|
Alertify.dlgServerPass(
|
||||||
|
gettext('Connect to Server'),
|
||||||
|
msg, _node, _data, _tree, _item, _wasConnected
|
||||||
|
).resizeTo();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onSuccess = function(res, node, _data, _tree, _item, _wasConnected) {
|
onSuccess = function(res, node, _data, _tree, _item, _wasConnected) {
|
||||||
if (res && res.data) {
|
if (res && res.data) {
|
||||||
|
@ -12,19 +12,22 @@ define('pgadmin.browser', [
|
|||||||
'sources/gettext', 'sources/url_for', 'require', 'jquery', 'underscore',
|
'sources/gettext', 'sources/url_for', 'require', 'jquery', 'underscore',
|
||||||
'bootstrap', 'sources/pgadmin', 'pgadmin.alertifyjs', 'bundled_codemirror',
|
'bootstrap', 'sources/pgadmin', 'pgadmin.alertifyjs', 'bundled_codemirror',
|
||||||
'sources/check_node_visibility', './toolbar', 'pgadmin.help',
|
'sources/check_node_visibility', './toolbar', 'pgadmin.help',
|
||||||
'sources/csrf', 'sources/utils', 'sources/window', 'pgadmin.browser.utils',
|
'sources/csrf', 'sources/utils', 'sources/window', 'pgadmin.authenticate.kerberos',
|
||||||
'wcdocker', 'jquery.contextmenu', 'jquery.aciplugin', 'jquery.acitree',
|
'pgadmin.browser.utils', 'wcdocker', 'jquery.contextmenu', 'jquery.aciplugin',
|
||||||
|
'jquery.acitree',
|
||||||
'pgadmin.browser.preferences', 'pgadmin.browser.messages',
|
'pgadmin.browser.preferences', 'pgadmin.browser.messages',
|
||||||
'pgadmin.browser.menu', 'pgadmin.browser.panel', 'pgadmin.browser.layout',
|
'pgadmin.browser.menu', 'pgadmin.browser.panel', 'pgadmin.browser.layout',
|
||||||
'pgadmin.browser.runtime', 'pgadmin.browser.error', 'pgadmin.browser.frame',
|
'pgadmin.browser.runtime', 'pgadmin.browser.error', 'pgadmin.browser.frame',
|
||||||
'pgadmin.browser.node', 'pgadmin.browser.collection', 'pgadmin.browser.activity',
|
'pgadmin.browser.node', 'pgadmin.browser.collection', 'pgadmin.browser.activity',
|
||||||
'sources/codemirror/addon/fold/pgadmin-sqlfoldcode',
|
'sources/codemirror/addon/fold/pgadmin-sqlfoldcode',
|
||||||
'pgadmin.browser.keyboard', 'sources/tree/pgadmin_tree_save_state','jquery.acisortable', 'jquery.acifragment',
|
'pgadmin.browser.keyboard', 'sources/tree/pgadmin_tree_save_state','jquery.acisortable',
|
||||||
|
'jquery.acifragment',
|
||||||
], function(
|
], function(
|
||||||
tree,
|
tree,
|
||||||
gettext, url_for, require, $, _,
|
gettext, url_for, require, $, _,
|
||||||
Bootstrap, pgAdmin, Alertify, codemirror,
|
Bootstrap, pgAdmin, Alertify, codemirror,
|
||||||
checkNodeVisibility, toolBar, help, csrfToken, pgadminUtils, pgWindow
|
checkNodeVisibility, toolBar, help, csrfToken, pgadminUtils, pgWindow,
|
||||||
|
Kerberos
|
||||||
) {
|
) {
|
||||||
window.jQuery = window.$ = $;
|
window.jQuery = window.$ = $;
|
||||||
// Some scripts do export their object in the window only.
|
// Some scripts do export their object in the window only.
|
||||||
@ -38,6 +41,8 @@ define('pgadmin.browser', [
|
|||||||
|
|
||||||
csrfToken.setPGCSRFToken(pgAdmin.csrf_token_header, pgAdmin.csrf_token);
|
csrfToken.setPGCSRFToken(pgAdmin.csrf_token_header, pgAdmin.csrf_token);
|
||||||
|
|
||||||
|
Kerberos.validate_kerberos_ticket();
|
||||||
|
|
||||||
var panelEvents = {};
|
var panelEvents = {};
|
||||||
panelEvents[wcDocker.EVENT.VISIBILITY_CHANGED] = function() {
|
panelEvents[wcDocker.EVENT.VISIBILITY_CHANGED] = function() {
|
||||||
if (this.isVisible()) {
|
if (this.isVisible()) {
|
||||||
|
17
web/pgadmin/browser/templates/browser/js/constants.js
Normal file
17
web/pgadmin/browser/templates/browser/js/constants.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// pgAdmin 4 - PostgreSQL Tools
|
||||||
|
//
|
||||||
|
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||||
|
// This software is released under the PostgreSQL Licence
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
define('pgadmin.browser.constants', [], function() {
|
||||||
|
return {
|
||||||
|
'INTERNAL': '{{ INTERNAL }}',
|
||||||
|
'LDAP': '{{ LDAP }}',
|
||||||
|
'KERBEROS': '{{ KERBEROS }}'
|
||||||
|
}
|
||||||
|
});
|
@ -12,6 +12,7 @@ from pgadmin.utils.route import BaseTestGenerator
|
|||||||
from regression.python_test_utils import test_utils as utils
|
from regression.python_test_utils import test_utils as utils
|
||||||
from pgadmin.authenticate.registry import AuthSourceRegistry
|
from pgadmin.authenticate.registry import AuthSourceRegistry
|
||||||
from unittest.mock import patch, MagicMock
|
from unittest.mock import patch, MagicMock
|
||||||
|
from werkzeug.datastructures import Headers
|
||||||
|
|
||||||
|
|
||||||
class KerberosLoginMockTestCase(BaseTestGenerator):
|
class KerberosLoginMockTestCase(BaseTestGenerator):
|
||||||
@ -30,6 +31,11 @@ class KerberosLoginMockTestCase(BaseTestGenerator):
|
|||||||
auth_source=['kerberos'],
|
auth_source=['kerberos'],
|
||||||
auto_create_user=True,
|
auto_create_user=True,
|
||||||
flag=2
|
flag=2
|
||||||
|
)),
|
||||||
|
('Spnego/Kerberos Update Ticket', dict(
|
||||||
|
auth_source=['kerberos'],
|
||||||
|
auto_create_user=True,
|
||||||
|
flag=3
|
||||||
))
|
))
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -54,8 +60,13 @@ class KerberosLoginMockTestCase(BaseTestGenerator):
|
|||||||
self.skipTest(
|
self.skipTest(
|
||||||
"Can not run Kerberos Authentication in the Desktop mode."
|
"Can not run Kerberos Authentication in the Desktop mode."
|
||||||
)
|
)
|
||||||
|
|
||||||
self.test_authorized()
|
self.test_authorized()
|
||||||
|
elif self.flag == 3:
|
||||||
|
if app_config.SERVER_MODE is False:
|
||||||
|
self.skipTest(
|
||||||
|
"Can not run Kerberos Authentication in the Desktop mode."
|
||||||
|
)
|
||||||
|
self.test_update_ticket()
|
||||||
|
|
||||||
def test_unauthorized(self):
|
def test_unauthorized(self):
|
||||||
"""
|
"""
|
||||||
@ -73,13 +84,7 @@ class KerberosLoginMockTestCase(BaseTestGenerator):
|
|||||||
passed on to the routed method.
|
passed on to the routed method.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class delCrads:
|
del_crads = self.mock_negotiate_start()
|
||||||
def __init__(self):
|
|
||||||
self.initiator_name = 'user@PGADMIN.ORG'
|
|
||||||
del_crads = delCrads()
|
|
||||||
|
|
||||||
AuthSourceRegistry.registry['kerberos'].negotiate_start = MagicMock(
|
|
||||||
return_value=[True, del_crads])
|
|
||||||
res = self.tester.login(None,
|
res = self.tester.login(None,
|
||||||
None,
|
None,
|
||||||
True,
|
True,
|
||||||
@ -89,6 +94,33 @@ class KerberosLoginMockTestCase(BaseTestGenerator):
|
|||||||
respdata = 'Gravatar image for %s' % del_crads.initiator_name
|
respdata = 'Gravatar image for %s' % del_crads.initiator_name
|
||||||
self.assertTrue(respdata in res.data.decode('utf8'))
|
self.assertTrue(respdata in res.data.decode('utf8'))
|
||||||
|
|
||||||
|
def mock_negotiate_start(self):
|
||||||
|
class delCrads:
|
||||||
|
def __init__(self):
|
||||||
|
self.initiator_name = 'user@PGADMIN.ORG'
|
||||||
|
|
||||||
|
del_crads = delCrads()
|
||||||
|
|
||||||
|
AuthSourceRegistry.registry['kerberos'].negotiate_start = MagicMock(
|
||||||
|
return_value=[True, del_crads])
|
||||||
|
return del_crads
|
||||||
|
|
||||||
|
def test_update_ticket(self):
|
||||||
|
# Response header should include the Negotiate header in the first call
|
||||||
|
response = self.tester.get('/authenticate/kerberos/update_ticket')
|
||||||
|
self.assertEqual(response.status_code, 401)
|
||||||
|
self.assertEqual(response.headers.get('www-authenticate'), 'Negotiate')
|
||||||
|
|
||||||
|
# When we send the Kerberos Ticket, it should return success
|
||||||
|
del_crads = self.mock_negotiate_start()
|
||||||
|
|
||||||
|
krb_token = Headers({})
|
||||||
|
krb_token['Authorization'] = 'Negotiate CTOKEN'
|
||||||
|
|
||||||
|
response = self.tester.get('/authenticate/kerberos/update_ticket',
|
||||||
|
headers=krb_token)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.app.PGADMIN_EXTERNAL_AUTH_SOURCE = 'ldap'
|
self.app.PGADMIN_EXTERNAL_AUTH_SOURCE = 'ldap'
|
||||||
|
|
||||||
|
@ -24,10 +24,11 @@ import logging
|
|||||||
from pgadmin.utils import u_encode, file_quote, fs_encoding, \
|
from pgadmin.utils import u_encode, file_quote, fs_encoding, \
|
||||||
get_complete_file_path, get_storage_directory, IS_WIN
|
get_complete_file_path, get_storage_directory, IS_WIN
|
||||||
from pgadmin.browser.server_groups.servers.utils import does_server_exists
|
from pgadmin.browser.server_groups.servers.utils import does_server_exists
|
||||||
|
from pgadmin.utils.constants import KERBEROS
|
||||||
|
|
||||||
import pytz
|
import pytz
|
||||||
from dateutil import parser
|
from dateutil import parser
|
||||||
from flask import current_app
|
from flask import current_app, session
|
||||||
from flask_babelex import gettext as _
|
from flask_babelex import gettext as _
|
||||||
from flask_security import current_user
|
from flask_security import current_user
|
||||||
|
|
||||||
@ -278,13 +279,16 @@ class BatchProcess(object):
|
|||||||
env['PROCID'] = self.id
|
env['PROCID'] = self.id
|
||||||
env['OUTDIR'] = self.log_dir
|
env['OUTDIR'] = self.log_dir
|
||||||
env['PGA_BGP_FOREGROUND'] = "1"
|
env['PGA_BGP_FOREGROUND'] = "1"
|
||||||
|
if config.SERVER_MODE and session and \
|
||||||
|
session['_auth_source_manager_obj']['current_source'] == \
|
||||||
|
KERBEROS:
|
||||||
|
env['KRB5CCNAME'] = session['KRB5CCNAME']
|
||||||
|
|
||||||
if self.env:
|
if self.env:
|
||||||
env.update(self.env)
|
env.update(self.env)
|
||||||
|
|
||||||
if cb is not None:
|
if cb is not None:
|
||||||
cb(env)
|
cb(env)
|
||||||
|
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
DETACHED_PROCESS = 0x00000008
|
DETACHED_PROCESS = 0x00000008
|
||||||
from subprocess import CREATE_NEW_PROCESS_GROUP
|
from subprocess import CREATE_NEW_PROCESS_GROUP
|
||||||
|
@ -29,7 +29,7 @@ from flask_sqlalchemy import SQLAlchemy
|
|||||||
#
|
#
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
SCHEMA_VERSION = 28
|
SCHEMA_VERSION = 29
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
#
|
#
|
||||||
@ -184,6 +184,7 @@ class Server(db.Model):
|
|||||||
tunnel_identity_file = db.Column(db.String(64), nullable=True)
|
tunnel_identity_file = db.Column(db.String(64), nullable=True)
|
||||||
tunnel_password = db.Column(db.String(64), nullable=True)
|
tunnel_password = db.Column(db.String(64), nullable=True)
|
||||||
shared = db.Column(db.Boolean(), nullable=False)
|
shared = db.Column(db.Boolean(), nullable=False)
|
||||||
|
kerberos_conn = db.Column(db.Boolean(), nullable=False)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import getpass
|
import getpass
|
||||||
|
from flask import current_app
|
||||||
|
from pgadmin.utils.constants import KERBEROS
|
||||||
|
|
||||||
FAILED_CREATE_DIR = \
|
FAILED_CREATE_DIR = \
|
||||||
"ERROR : Failed to create the directory {}:\n {}"
|
"ERROR : Failed to create the directory {}:\n {}"
|
||||||
@ -104,3 +106,20 @@ def create_app_data_directory(config):
|
|||||||
getpass.getuser(),
|
getpass.getuser(),
|
||||||
config.APP_VERSION))
|
config.APP_VERSION))
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
# Create Kerberos Credential Cache directory (if not present).
|
||||||
|
if config.SERVER_MODE and KERBEROS in config.AUTHENTICATION_SOURCES:
|
||||||
|
try:
|
||||||
|
_create_directory_if_not_exists(config.KERBEROS_CCACHE_DIR)
|
||||||
|
except PermissionError as e:
|
||||||
|
print(FAILED_CREATE_DIR.format(config.KERBEROS_CCACHE_DIR, e))
|
||||||
|
print(
|
||||||
|
"HINT : Create the directory {}, ensure it is writable by\n"
|
||||||
|
"'{}', and try again, or, create a config_local.py file\n"
|
||||||
|
" and override the KERBEROS_CCACHE_DIR setting per\n"
|
||||||
|
" https://www.pgadmin.org/docs/pgadmin4/{}/config_py.html".
|
||||||
|
format(
|
||||||
|
config.KERBEROS_CCACHE_DIR,
|
||||||
|
getpass.getuser(),
|
||||||
|
config.APP_VERSION))
|
||||||
|
exit(1)
|
||||||
|
@ -48,6 +48,7 @@
|
|||||||
'pgadmin.browser.utils': "{{ url_for('browser.index') }}" + "js/utils",
|
'pgadmin.browser.utils': "{{ url_for('browser.index') }}" + "js/utils",
|
||||||
'pgadmin.browser.endpoints': "{{ url_for('browser.index') }}" + "js/endpoints",
|
'pgadmin.browser.endpoints': "{{ url_for('browser.index') }}" + "js/endpoints",
|
||||||
'pgadmin.browser.messages': "{{ url_for('browser.index') }}" + "js/messages",
|
'pgadmin.browser.messages': "{{ url_for('browser.index') }}" + "js/messages",
|
||||||
|
'pgadmin.browser.constants': "{{ url_for('browser.index') }}" + "js/constants",
|
||||||
'pgadmin.server.supported_servers': "{{ url_for('browser.index') }}" + "server/supported_servers",
|
'pgadmin.server.supported_servers': "{{ url_for('browser.index') }}" + "server/supported_servers",
|
||||||
'pgadmin.user_management.current_user': "{{ url_for('user_management.index') }}" + "current_user",
|
'pgadmin.user_management.current_user': "{{ url_for('user_management.index') }}" + "current_user",
|
||||||
'translations': "{{ url_for('tools.index') }}" + "translations"
|
'translations': "{{ url_for('tools.index') }}" + "translations"
|
||||||
|
@ -13,6 +13,8 @@ import gettext from '../../../../static/js/gettext';
|
|||||||
import url_for from '../../../../static/js/url_for';
|
import url_for from '../../../../static/js/url_for';
|
||||||
import _ from 'underscore';
|
import _ from 'underscore';
|
||||||
import {DialogWrapper} from '../../../../static/js/alertify/dialog_wrapper';
|
import {DialogWrapper} from '../../../../static/js/alertify/dialog_wrapper';
|
||||||
|
import {fetch_ticket_lifetime} from '../../../../authenticate/static/js/kerberos';
|
||||||
|
import userInfo from 'pgadmin.user_management.current_user';
|
||||||
|
|
||||||
export class BackupDialogWrapper extends DialogWrapper {
|
export class BackupDialogWrapper extends DialogWrapper {
|
||||||
constructor(dialogContainerSelector, dialogTitle, typeOfDialog,
|
constructor(dialogContainerSelector, dialogTitle, typeOfDialog,
|
||||||
@ -165,10 +167,29 @@ export class BackupDialogWrapper extends DialogWrapper {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.setExtraParameters(selectedTreeNode, treeInfo);
|
this.setExtraParameters(selectedTreeNode, treeInfo);
|
||||||
|
let backupDate = this.view.model.toJSON();
|
||||||
|
|
||||||
|
if(userInfo['auth_sources'] == 'KERBEROS' && (backupDate.type == 'globals' || backupDate.type == 'server')) {
|
||||||
|
let newPromise = fetch_ticket_lifetime();
|
||||||
|
newPromise.then(
|
||||||
|
function(lifetime) {
|
||||||
|
if (lifetime < 1800 && lifetime > 0) {
|
||||||
|
dialog.alertify.warning(
|
||||||
|
'You have '+ (Math.round(parseInt(lifetime)/60)).toString() +' minutes left on your ticket - if the dump takes longer than that, it may fail."'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
dialog.alertify.warning(
|
||||||
|
gettext('Please renew your kerberos ticket, it has been expired.')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
axios.post(
|
axios.post(
|
||||||
baseUrl,
|
baseUrl,
|
||||||
this.view.model.toJSON()
|
backupDate
|
||||||
).then(function (res) {
|
).then(function (res) {
|
||||||
if (res.data.success) {
|
if (res.data.success) {
|
||||||
dialog.alertify.success(gettext('Backup job created.'), 5);
|
dialog.alertify.success(gettext('Backup job created.'), 5);
|
||||||
|
@ -13,11 +13,11 @@ define([
|
|||||||
'backbone', 'pgadmin.backgrid', 'codemirror', 'pgadmin.backform',
|
'backbone', 'pgadmin.backgrid', 'codemirror', 'pgadmin.backform',
|
||||||
'pgadmin.tools.debugger.ui', 'pgadmin.tools.debugger.utils',
|
'pgadmin.tools.debugger.ui', 'pgadmin.tools.debugger.utils',
|
||||||
'tools/datagrid/static/js/show_query_tool', 'sources/utils',
|
'tools/datagrid/static/js/show_query_tool', 'sources/utils',
|
||||||
'wcdocker', 'pgadmin.browser.frame',
|
'pgadmin.authenticate.kerberos', 'wcdocker', 'pgadmin.browser.frame',
|
||||||
], function(
|
], function(
|
||||||
gettext, url_for, $, _, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid,
|
gettext, url_for, $, _, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid,
|
||||||
CodeMirror, Backform, get_function_arguments, debuggerUtils, showQueryTool,
|
CodeMirror, Backform, get_function_arguments, debuggerUtils, showQueryTool,
|
||||||
pgadminUtils,
|
pgadminUtils, Kerberos
|
||||||
) {
|
) {
|
||||||
var pgTools = pgAdmin.Tools = pgAdmin.Tools || {},
|
var pgTools = pgAdmin.Tools = pgAdmin.Tools || {},
|
||||||
wcDocker = window.wcDocker;
|
wcDocker = window.wcDocker;
|
||||||
@ -472,8 +472,20 @@ define([
|
|||||||
.fail(function(xhr) {
|
.fail(function(xhr) {
|
||||||
try {
|
try {
|
||||||
var err = JSON.parse(xhr.responseText);
|
var err = JSON.parse(xhr.responseText);
|
||||||
if (err.success == 0) {
|
if (err.errormsg.search('Ticket expired') !== -1) {
|
||||||
Alertify.alert(gettext('Debugger Error'), err.errormsg);
|
let fetchTicket = Kerberos.fetch_ticket();
|
||||||
|
fetchTicket.then(
|
||||||
|
function() {
|
||||||
|
self.start_global_debugger();
|
||||||
|
},
|
||||||
|
function(error) {
|
||||||
|
Alertify.alert(gettext('Debugger Error'), error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (err.success == 0) {
|
||||||
|
Alertify.alert(gettext('Debugger Error'), err.errormsg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e.stack || e);
|
console.warn(e.stack || e);
|
||||||
|
@ -51,6 +51,7 @@ define('tools.querytool', [
|
|||||||
'sources/window',
|
'sources/window',
|
||||||
'sources/is_native',
|
'sources/is_native',
|
||||||
'sources/sqleditor/macro',
|
'sources/sqleditor/macro',
|
||||||
|
'pgadmin.authenticate.kerberos',
|
||||||
'sources/../bundle/slickgrid',
|
'sources/../bundle/slickgrid',
|
||||||
'pgadmin.file_manager',
|
'pgadmin.file_manager',
|
||||||
'slick.pgadmin.formatters',
|
'slick.pgadmin.formatters',
|
||||||
@ -65,7 +66,7 @@ define('tools.querytool', [
|
|||||||
GeometryViewer, historyColl, queryHist, querySources,
|
GeometryViewer, historyColl, queryHist, querySources,
|
||||||
keyboardShortcuts, queryToolActions, queryToolNotifications, Datagrid,
|
keyboardShortcuts, queryToolActions, queryToolNotifications, Datagrid,
|
||||||
modifyAnimation, calculateQueryRunTime, callRenderAfterPoll, queryToolPref, queryTxnStatus, csrfToken, panelTitleFunc,
|
modifyAnimation, calculateQueryRunTime, callRenderAfterPoll, queryToolPref, queryTxnStatus, csrfToken, panelTitleFunc,
|
||||||
pgWindow, isNative, MacroHandler) {
|
pgWindow, isNative, MacroHandler, Kerberos) {
|
||||||
/* Return back, this has been called more than once */
|
/* Return back, this has been called more than once */
|
||||||
if (pgAdmin.SqlEditor)
|
if (pgAdmin.SqlEditor)
|
||||||
return pgAdmin.SqlEditor;
|
return pgAdmin.SqlEditor;
|
||||||
@ -2454,9 +2455,23 @@ define('tools.querytool', [
|
|||||||
pgBrowser.report_error(gettext('Error fetching rows - %s.', xhr.statusText), xhr.responseJSON.errormsg, undefined, self.close.bind(self));
|
pgBrowser.report_error(gettext('Error fetching rows - %s.', xhr.statusText), xhr.responseJSON.errormsg, undefined, self.close.bind(self));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pgBrowser.Events.trigger(
|
if (xhr.responseText.search('Ticket expired') !== -1) {
|
||||||
'pgadmin:query_tool:connected_fail:' + self.transId, xhr, error
|
let fetchTicket = Kerberos.fetch_ticket();
|
||||||
);
|
fetchTicket.then(
|
||||||
|
function() {
|
||||||
|
self.initTransaction();
|
||||||
|
},
|
||||||
|
function(error) {
|
||||||
|
pgBrowser.Events.trigger(
|
||||||
|
'pgadmin:query_tool:connected_fail:' + self.transId, xhr, error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
pgBrowser.Events.trigger(
|
||||||
|
'pgadmin:query_tool:connected_fail:' + self.transId, xhr, error
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -25,7 +25,7 @@ from pgadmin.utils.ajax import make_response as ajax_response, \
|
|||||||
make_json_response, bad_request, internal_server_error, forbidden
|
make_json_response, bad_request, internal_server_error, forbidden
|
||||||
from pgadmin.utils.csrf import pgCSRFProtect
|
from pgadmin.utils.csrf import pgCSRFProtect
|
||||||
from pgadmin.utils.constants import MIMETYPE_APP_JS, INTERNAL,\
|
from pgadmin.utils.constants import MIMETYPE_APP_JS, INTERNAL,\
|
||||||
SUPPORTED_AUTH_SOURCES, KERBEROS
|
SUPPORTED_AUTH_SOURCES, KERBEROS, LDAP
|
||||||
from pgadmin.utils.validation_utils import validate_email
|
from pgadmin.utils.validation_utils import validate_email
|
||||||
from pgadmin.model import db, Role, User, UserPreference, Server, \
|
from pgadmin.model import db, Role, User, UserPreference, Server, \
|
||||||
ServerGroup, Process, Setting, roles_users, SharedServer
|
ServerGroup, Process, Setting, roles_users, SharedServer
|
||||||
@ -157,7 +157,6 @@ def script():
|
|||||||
@pgCSRFProtect.exempt
|
@pgCSRFProtect.exempt
|
||||||
@login_required
|
@login_required
|
||||||
def current_user_info():
|
def current_user_info():
|
||||||
|
|
||||||
return Response(
|
return Response(
|
||||||
response=render_template(
|
response=render_template(
|
||||||
"user_management/js/current_user.js",
|
"user_management/js/current_user.js",
|
||||||
@ -176,7 +175,9 @@ def current_user_info():
|
|||||||
allow_save_tunnel_password='true' if
|
allow_save_tunnel_password='true' if
|
||||||
config.ALLOW_SAVE_TUNNEL_PASSWORD and session[
|
config.ALLOW_SAVE_TUNNEL_PASSWORD and session[
|
||||||
'allow_save_password'] else 'false',
|
'allow_save_password'] else 'false',
|
||||||
auth_sources=config.AUTHENTICATION_SOURCES
|
auth_sources=config.AUTHENTICATION_SOURCES,
|
||||||
|
current_auth_source=session['_auth_source_manager_obj'][
|
||||||
|
'current_source'] if config.SERVER_MODE is True else INTERNAL
|
||||||
),
|
),
|
||||||
status=200,
|
status=200,
|
||||||
mimetype=MIMETYPE_APP_JS
|
mimetype=MIMETYPE_APP_JS
|
||||||
|
@ -10,11 +10,11 @@
|
|||||||
define([
|
define([
|
||||||
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'pgadmin.alertifyjs',
|
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'pgadmin.alertifyjs',
|
||||||
'pgadmin.browser', 'backbone', 'backgrid', 'backform', 'pgadmin.browser.node', 'pgadmin.backform',
|
'pgadmin.browser', 'backbone', 'backgrid', 'backform', 'pgadmin.browser.node', 'pgadmin.backform',
|
||||||
'pgadmin.user_management.current_user', 'sources/utils',
|
'pgadmin.user_management.current_user', 'sources/utils', 'pgadmin.browser.constants',
|
||||||
'backgrid.select.all', 'backgrid.filter',
|
'backgrid.select.all', 'backgrid.filter',
|
||||||
], function(
|
], function(
|
||||||
gettext, url_for, $, _, alertify, pgBrowser, Backbone, Backgrid, Backform,
|
gettext, url_for, $, _, alertify, pgBrowser, Backbone, Backgrid, Backform,
|
||||||
pgNode, pgBackform, userInfo, commonUtils,
|
pgNode, pgBackform, userInfo, commonUtils, pgConst,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// if module is already initialized, refer to that.
|
// if module is already initialized, refer to that.
|
||||||
@ -25,7 +25,9 @@ define([
|
|||||||
var USERURL = url_for('user_management.users'),
|
var USERURL = url_for('user_management.users'),
|
||||||
ROLEURL = url_for('user_management.roles'),
|
ROLEURL = url_for('user_management.roles'),
|
||||||
SOURCEURL = url_for('user_management.auth_sources'),
|
SOURCEURL = url_for('user_management.auth_sources'),
|
||||||
DEFAULT_AUTH_SOURCE = 'internal',
|
DEFAULT_AUTH_SOURCE = pgConst['INTERNAL'],
|
||||||
|
LDAP = pgConst['LDAP'],
|
||||||
|
KERBEROS = pgConst['KERBEROS'],
|
||||||
AUTH_ONLY_INTERNAL = (userInfo['auth_sources'].length == 1 && userInfo['auth_sources'].includes(DEFAULT_AUTH_SOURCE)) ? true : false,
|
AUTH_ONLY_INTERNAL = (userInfo['auth_sources'].length == 1 && userInfo['auth_sources'].includes(DEFAULT_AUTH_SOURCE)) ? true : false,
|
||||||
userFilter = function(collection) {
|
userFilter = function(collection) {
|
||||||
return (new Backgrid.Extension.ClientSideFilter({
|
return (new Backgrid.Extension.ClientSideFilter({
|
||||||
@ -589,7 +591,17 @@ define([
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!!this.get('username') && this.collection.nonFilter.where({
|
if (!!this.get('username') && this.collection.nonFilter.where({
|
||||||
'username': this.get('username'), 'auth_source': 'ldap',
|
'username': this.get('username'), 'auth_source': LDAP,
|
||||||
|
}).length > 1) {
|
||||||
|
errmsg = gettext('The username %s already exists.',
|
||||||
|
this.get('username')
|
||||||
|
);
|
||||||
|
|
||||||
|
this.errorModel.set('username', errmsg);
|
||||||
|
return errmsg;
|
||||||
|
}
|
||||||
|
else if (!!this.get('username') && this.collection.nonFilter.where({
|
||||||
|
'username': this.get('username'), 'auth_source': KERBEROS,
|
||||||
}).length > 1) {
|
}).length > 1) {
|
||||||
errmsg = gettext('The username %s already exists.',
|
errmsg = gettext('The username %s already exists.',
|
||||||
this.get('username')
|
this.get('username')
|
||||||
@ -1041,7 +1053,7 @@ define([
|
|||||||
saveUser: function(m) {
|
saveUser: function(m) {
|
||||||
var d = m.toJSON(true);
|
var d = m.toJSON(true);
|
||||||
|
|
||||||
if((m.isNew() && m.get('auth_source') == 'ldap' && (!m.get('username') || !m.get('auth_source') || !m.get('role')))
|
if((m.isNew() && (m.get('auth_source') == LDAP || m.get('auth_source') == KERBEROS) && (!m.get('username') || !m.get('auth_source') || !m.get('role')))
|
||||||
|| (m.isNew() && m.get('auth_source') == DEFAULT_AUTH_SOURCE && (!m.get('email') || !m.get('role') ||
|
|| (m.isNew() && m.get('auth_source') == DEFAULT_AUTH_SOURCE && (!m.get('email') || !m.get('role') ||
|
||||||
!m.get('newPassword') || !m.get('confirmPassword') || m.get('newPassword') != m.get('confirmPassword')))
|
!m.get('newPassword') || !m.get('confirmPassword') || m.get('newPassword') != m.get('confirmPassword')))
|
||||||
|| (!m.isNew() && m.get('newPassword') != m.get('confirmPassword'))) {
|
|| (!m.isNew() && m.get('newPassword') != m.get('confirmPassword'))) {
|
||||||
|
@ -15,6 +15,7 @@ define('pgadmin.user_management.current_user', [], function() {
|
|||||||
'name': '{{ name }}',
|
'name': '{{ name }}',
|
||||||
'allow_save_password': {{ allow_save_password }},
|
'allow_save_password': {{ allow_save_password }},
|
||||||
'allow_save_tunnel_password': {{ allow_save_tunnel_password }},
|
'allow_save_tunnel_password': {{ allow_save_tunnel_password }},
|
||||||
'auth_sources': {{ auth_sources }}
|
'auth_sources': {{ auth_sources }},
|
||||||
|
'current_auth_source': '{{ current_auth_source }}'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -18,11 +18,13 @@ import select
|
|||||||
import datetime
|
import datetime
|
||||||
from collections import deque
|
from collections import deque
|
||||||
import psycopg2
|
import psycopg2
|
||||||
from flask import g, current_app
|
import threading
|
||||||
|
from flask import g, current_app, session
|
||||||
from flask_babelex import gettext
|
from flask_babelex import gettext
|
||||||
from flask_security import current_user
|
from flask_security import current_user
|
||||||
from pgadmin.utils.crypto import decrypt, encrypt
|
from pgadmin.utils.crypto import decrypt, encrypt
|
||||||
from psycopg2.extensions import encodings
|
from psycopg2.extensions import encodings
|
||||||
|
from os import environ
|
||||||
|
|
||||||
import config
|
import config
|
||||||
from pgadmin.model import User
|
from pgadmin.model import User
|
||||||
@ -38,6 +40,9 @@ from .encoding import get_encoding, configure_driver_encodings
|
|||||||
from pgadmin.utils import csv
|
from pgadmin.utils import csv
|
||||||
from pgadmin.utils.master_password import get_crypt_key
|
from pgadmin.utils.master_password import get_crypt_key
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
from pgadmin.utils.constants import KERBEROS
|
||||||
|
|
||||||
|
lock = threading.Lock()
|
||||||
|
|
||||||
_ = gettext
|
_ = gettext
|
||||||
|
|
||||||
@ -313,6 +318,13 @@ class Connection(BaseConnection):
|
|||||||
os.environ['PGAPPNAME'] = '{0} - {1}'.format(
|
os.environ['PGAPPNAME'] = '{0} - {1}'.format(
|
||||||
config.APP_NAME, conn_id)
|
config.APP_NAME, conn_id)
|
||||||
|
|
||||||
|
if config.SERVER_MODE and \
|
||||||
|
session['_auth_source_manager_obj']['current_source'] == \
|
||||||
|
KERBEROS and 'KRB5CCNAME' in session\
|
||||||
|
and manager.kerberos_conn:
|
||||||
|
lock.acquire()
|
||||||
|
environ['KRB5CCNAME'] = session['KRB5CCNAME']
|
||||||
|
|
||||||
pg_conn = psycopg2.connect(
|
pg_conn = psycopg2.connect(
|
||||||
host=manager.local_bind_host if manager.use_ssh_tunnel
|
host=manager.local_bind_host if manager.use_ssh_tunnel
|
||||||
else manager.host,
|
else manager.host,
|
||||||
@ -340,7 +352,13 @@ class Connection(BaseConnection):
|
|||||||
if self.async_ == 1:
|
if self.async_ == 1:
|
||||||
self._wait(pg_conn)
|
self._wait(pg_conn)
|
||||||
|
|
||||||
|
if config.SERVER_MODE and \
|
||||||
|
session['_auth_source_manager_obj']['current_source'] == \
|
||||||
|
KERBEROS:
|
||||||
|
environ['KRB5CCNAME'] = ''
|
||||||
|
|
||||||
except psycopg2.Error as e:
|
except psycopg2.Error as e:
|
||||||
|
environ['KRB5CCNAME'] = ''
|
||||||
manager.stop_ssh_tunnel()
|
manager.stop_ssh_tunnel()
|
||||||
if e.pgerror:
|
if e.pgerror:
|
||||||
msg = e.pgerror
|
msg = e.pgerror
|
||||||
@ -358,6 +376,11 @@ class Connection(BaseConnection):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
return False, msg
|
return False, msg
|
||||||
|
finally:
|
||||||
|
if config.SERVER_MODE and \
|
||||||
|
session['_auth_source_manager_obj']['current_source'] == \
|
||||||
|
KERBEROS and lock.locked():
|
||||||
|
lock.release()
|
||||||
|
|
||||||
# Overwrite connection notice attr to support
|
# Overwrite connection notice attr to support
|
||||||
# more than 50 notices at a time
|
# more than 50 notices at a time
|
||||||
@ -1438,7 +1461,6 @@ Failed to reset the connection to the server due to following error:
|
|||||||
Args:
|
Args:
|
||||||
conn: connection object
|
conn: connection object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
state = conn.poll()
|
state = conn.poll()
|
||||||
if state == psycopg2.extensions.POLL_OK:
|
if state == psycopg2.extensions.POLL_OK:
|
||||||
|
@ -105,6 +105,7 @@ class ServerManager(object):
|
|||||||
self.tunnel_identity_file = None
|
self.tunnel_identity_file = None
|
||||||
self.tunnel_password = None
|
self.tunnel_password = None
|
||||||
|
|
||||||
|
self.kerberos_conn = server.kerberos_conn
|
||||||
for con in self.connections:
|
for con in self.connections:
|
||||||
self.connections[con]._release()
|
self.connections[con]._release()
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ def get_crypt_key():
|
|||||||
and not config.SERVER_MODE and enc_key is None:
|
and not config.SERVER_MODE and enc_key is None:
|
||||||
return False, None
|
return False, None
|
||||||
elif config.SERVER_MODE and \
|
elif config.SERVER_MODE and \
|
||||||
session['_auth_source_manager_obj']['source_friendly_name']\
|
session['_auth_source_manager_obj']['current_source']\
|
||||||
== KERBEROS:
|
== KERBEROS:
|
||||||
return True, session['kerberos_key'] if 'kerberos_key' in session \
|
return True, session['kerberos_key'] if 'kerberos_key' in session \
|
||||||
else None
|
else None
|
||||||
|
@ -174,11 +174,13 @@ var webpackShimConfig = {
|
|||||||
'pgadmin.backgrid': path.join(__dirname, './pgadmin/static/js/backgrid.pgadmin'),
|
'pgadmin.backgrid': path.join(__dirname, './pgadmin/static/js/backgrid.pgadmin'),
|
||||||
|
|
||||||
'pgadmin.about': path.join(__dirname, './pgadmin/about/static/js/about'),
|
'pgadmin.about': path.join(__dirname, './pgadmin/about/static/js/about'),
|
||||||
|
'pgadmin.authenticate.kerberos': path.join(__dirname, './pgadmin/authenticate/static/js/kerberos'),
|
||||||
'pgadmin.browser': path.join(__dirname, './pgadmin/browser/static/js/browser'),
|
'pgadmin.browser': path.join(__dirname, './pgadmin/browser/static/js/browser'),
|
||||||
'pgadmin.browser.bgprocess': path.join(__dirname, './pgadmin/misc/bgprocess/static/js/bgprocess'),
|
'pgadmin.browser.bgprocess': path.join(__dirname, './pgadmin/misc/bgprocess/static/js/bgprocess'),
|
||||||
'pgadmin.browser.collection': path.join(__dirname, './pgadmin/browser/static/js/collection'),
|
'pgadmin.browser.collection': path.join(__dirname, './pgadmin/browser/static/js/collection'),
|
||||||
'pgadmin.browser.datamodel': path.join(__dirname, './pgadmin/browser/static/js/datamodel'),
|
'pgadmin.browser.datamodel': path.join(__dirname, './pgadmin/browser/static/js/datamodel'),
|
||||||
'pgadmin.browser.endpoints': '/browser/js/endpoints',
|
'pgadmin.browser.endpoints': '/browser/js/endpoints',
|
||||||
|
'pgadmin.browser.constants': '/browser/js/constants',
|
||||||
'pgadmin.browser.error': path.join(__dirname, './pgadmin/browser/static/js/error'),
|
'pgadmin.browser.error': path.join(__dirname, './pgadmin/browser/static/js/error'),
|
||||||
'pgadmin.browser.frame': path.join(__dirname, './pgadmin/browser/static/js/frame'),
|
'pgadmin.browser.frame': path.join(__dirname, './pgadmin/browser/static/js/frame'),
|
||||||
'pgadmin.browser.keyboard': path.join(__dirname, './pgadmin/browser/static/js/keyboard'),
|
'pgadmin.browser.keyboard': path.join(__dirname, './pgadmin/browser/static/js/keyboard'),
|
||||||
@ -300,6 +302,7 @@ var webpackShimConfig = {
|
|||||||
'pgadmin.browser.messages',
|
'pgadmin.browser.messages',
|
||||||
'pgadmin.browser.utils',
|
'pgadmin.browser.utils',
|
||||||
'pgadmin.server.supported_servers',
|
'pgadmin.server.supported_servers',
|
||||||
|
'pgadmin.browser.constants',
|
||||||
],
|
],
|
||||||
// Define list of pgAdmin common libraries to bundle them separately
|
// Define list of pgAdmin common libraries to bundle them separately
|
||||||
// into commons JS from app.bundle.js
|
// into commons JS from app.bundle.js
|
||||||
|
Loading…
Reference in New Issue
Block a user