diff --git a/docs/en_US/release_notes_4_21.rst b/docs/en_US/release_notes_4_21.rst index 720a45b96..be681176b 100644 --- a/docs/en_US/release_notes_4_21.rst +++ b/docs/en_US/release_notes_4_21.rst @@ -80,4 +80,7 @@ Bug fixes | `Issue #5402 `_ - Fixed an issue where the checkbox is not visible on Configuration dialog in runtime for the dark theme. | `Issue #5409 `_ - Fixed validation issue in Synonyms node. | `Issue #5410 `_ - Fixed an issue while removing the package body showing wrong modified SQL. -| `Issue #5415 `_ - Ensure that the query tool context menu should work on the collection nodes. \ No newline at end of file +| `Issue #5415 `_ - Ensure that the query tool context menu should work on the collection nodes. +| `Issue #5419 `_ - Ensure that the user should not be able to change the authentication source. +| `Issue #5420 `_ - Ensure error should be handled properly when LDAP user is created with the same name. +| `Issue #5432 `_ - Fixed an issue where an internal user is not created if the authentication source is set to internal and ldap. \ No newline at end of file diff --git a/web/pgadmin/authenticate/ldap.py b/web/pgadmin/authenticate/ldap.py index f90ab90a3..5aadda642 100644 --- a/web/pgadmin/authenticate/ldap.py +++ b/web/pgadmin/authenticate/ldap.py @@ -14,7 +14,7 @@ import config from ldap3 import Connection, Server, Tls, ALL, ALL_ATTRIBUTES from ldap3.core.exceptions import LDAPSocketOpenError, LDAPBindError,\ LDAPInvalidScopeError, LDAPAttributeError, LDAPInvalidFilterError,\ - LDAPStartTLSError + LDAPStartTLSError, LDAPSSLConfigurationError from flask_babelex import gettext from .internal import BaseAuthentication @@ -81,12 +81,17 @@ class LDAPAuthentication(BaseAuthentication): if ca_cert_file and cert_file and key_file: cert_validate = ssl.CERT_REQUIRED - tls = Tls( - local_private_key_file=key_file, - local_certificate_file=cert_file, - validate=cert_validate, - version=ssl.PROTOCOL_TLSv1_2, - ca_certs_file=ca_cert_file) + try: + tls = Tls( + local_private_key_file=key_file, + local_certificate_file=cert_file, + validate=cert_validate, + version=ssl.PROTOCOL_TLSv1_2, + ca_certs_file=ca_cert_file) + except LDAPSSLConfigurationError as e: + current_app.logger.exception( + "LDAP configuration error: %s\n" % e) + return False, "LDAP configuration error: %s\n" % e.args[0] try: # Create the server object diff --git a/web/pgadmin/tools/user_management/__init__.py b/web/pgadmin/tools/user_management/__init__.py index 20ef16bb8..6b8d84e2a 100644 --- a/web/pgadmin/tools/user_management/__init__.py +++ b/web/pgadmin/tools/user_management/__init__.py @@ -241,7 +241,8 @@ def create(): def create_user(data): - if 'auth_source' in data and data['auth_source'] != 'internal': + if 'auth_source' in data and data['auth_source'] != \ + current_app.PGADMIN_DEFAULT_AUTH_SOURCE: req_params = ('username', 'role', 'active', 'auth_source') else: req_params = ('email', 'role', 'active', 'newPassword', @@ -264,12 +265,13 @@ def create_user(data): try: - username = new_data['username'] if 'username' in new_data \ - else new_data['email'] - email = new_data['email'] if 'email' in new_data else None - password = new_data['password'] if 'password' in new_data else None auth_source = new_data['auth_source'] if 'auth_source' in new_data \ else current_app.PGADMIN_DEFAULT_AUTH_SOURCE + username = new_data['username'] if \ + 'username' in new_data and auth_source !=\ + current_app.PGADMIN_DEFAULT_AUTH_SOURCE else new_data['email'] + email = new_data['email'] if 'email' in new_data else None + password = new_data['password'] if 'password' in new_data else None usr = User(username=username, email=email, diff --git a/web/pgadmin/tools/user_management/static/js/user_management.js b/web/pgadmin/tools/user_management/static/js/user_management.js index a2edeb7da..751df0014 100644 --- a/web/pgadmin/tools/user_management/static/js/user_management.js +++ b/web/pgadmin/tools/user_management/static/js/user_management.js @@ -25,7 +25,8 @@ define([ var USERURL = url_for('user_management.users'), ROLEURL = url_for('user_management.roles'), SOURCEURL = url_for('user_management.auth_sources'), - AUTH_ONLY_INTERNAL = (userInfo['auth_sources'].length == 1 && userInfo['auth_sources'].includes('internal')) ? true : false, + DEFAULT_AUTH_SOURCE = 'internal', + AUTH_ONLY_INTERNAL = (userInfo['auth_sources'].length == 1 && userInfo['auth_sources'].includes(DEFAULT_AUTH_SOURCE)) ? true : false, userFilter = function(collection) { return (new Backgrid.Extension.ClientSideFilter({ collection: collection, @@ -286,12 +287,12 @@ define([ role: '2', newPassword: undefined, confirmPassword: undefined, - auth_source: 'internal', + auth_source: DEFAULT_AUTH_SOURCE, authOnlyInternal: AUTH_ONLY_INTERNAL, }, schema: [{ id: 'auth_source', - label: gettext('Authentication Source'), + label: gettext('Authentication source'), type: 'text', control: 'Select2', url: url_for('user_management.auth_sources'), @@ -328,7 +329,14 @@ define([ cellHeaderClasses: 'width_percent_30', deps: ['auth_source'], editable: function(m) { - if (m.get('authOnlyInternal') || m.get('auth_source') == 'internal') return false; + if (m.get('authOnlyInternal') || m.get('auth_source') == DEFAULT_AUTH_SOURCE) { + if (m.isNew() && m.get('username') != undefined && m.get('username') != '') { + setTimeout( function() { + m.set('username', undefined); + }, 10); + } + return false; + } return true; }, disabled: false, @@ -421,7 +429,7 @@ define([ deps: ['auth_source'], sortable: false, editable: function(m) { - if (m.get('auth_source') == 'internal') { + if (m.get('auth_source') == DEFAULT_AUTH_SOURCE) { return true; } else { return false; @@ -438,7 +446,7 @@ define([ deps: ['auth_source'], sortable: false, editable: function(m) { - if (m.get('auth_source') == 'internal') { + if (m.get('auth_source') == DEFAULT_AUTH_SOURCE) { return true; } else { return false; @@ -450,7 +458,7 @@ define([ changedAttrs = this.changed || {}, email_filter = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; - if (this.get('auth_source') == 'internal' && ('email' in changedAttrs || !this.isNew()) && (_.isUndefined(this.get('email')) || + if (this.get('auth_source') == DEFAULT_AUTH_SOURCE && ('email' in changedAttrs || !this.isNew()) && (_.isUndefined(this.get('email')) || _.isNull(this.get('email')) || String(this.get('email')).replace(/^\s+|\s+$/g, '') == '')) { errmsg = gettext('Email address cannot be empty.'); @@ -464,7 +472,7 @@ define([ this.errorModel.set('email', errmsg); return errmsg; } else if (!!this.get('email') && this.collection.nonFilter.where({ - 'email': this.get('email'), 'auth_source': 'internal', + 'email': this.get('email'), 'auth_source': DEFAULT_AUTH_SOURCE, }).length > 1) { errmsg = gettext('The email address %s already exists.', this.get('email') @@ -490,7 +498,7 @@ define([ this.errorModel.unset('role'); } - if (this.get('auth_source') == 'internal') { + if (this.get('auth_source') == DEFAULT_AUTH_SOURCE) { if (this.isNew()) { // Password is compulsory for new user. if ('newPassword' in changedAttrs && (_.isUndefined(this.get('newPassword')) || @@ -598,6 +606,17 @@ define([ this.errorModel.unset('confirmPassword'); } } + } else { + if (!!this.get('username') && this.collection.nonFilter.where({ + '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; + } } return null; }, @@ -824,10 +843,10 @@ define([ saveUser: function(m) { var d = m.toJSON(true); - if(m.isNew() && m.get('authOnlyInternal') === false && + if(m.isNew() && m.get('auth_source') == 'ldap' && (!m.get('username') || !m.get('auth_source') || !m.get('role')) ) { return false; - } else if (m.isNew() && m.get('authOnlyInternal') === true && (!m.get('email') || !m.get('role') || + } else if (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'))) { // New user model is valid but partially filled so return without saving. @@ -845,9 +864,16 @@ define([ wait: true, success: function() { // User created/updated on server now start new session for this user. + let temp_auth_sources = m.get('auth_source'); m.set({ 'newPassword': undefined, 'confirmPassword': undefined, + 'auth_source': undefined, + }); + + // It's a heck to re-render the Auth Source control. + m.set({ + 'auth_source': temp_auth_sources, }); m.startNewSession();