mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Ensure that the user should not be able to change the authentication source. Fixes #5419
Ensure error should be handled properly when LDAP user is created with the same name. Fixes #5420 Fixed an issue where an internal user is not created if the authentication source is set to internal and ldap. Fixes #5432
This commit is contained in:
committed by
Akshay Joshi
parent
5e91ed2bb1
commit
b0464500ca
@@ -80,4 +80,7 @@ Bug fixes
|
|||||||
| `Issue #5402 <https://redmine.postgresql.org/issues/5402>`_ - Fixed an issue where the checkbox is not visible on Configuration dialog in runtime for the dark theme.
|
| `Issue #5402 <https://redmine.postgresql.org/issues/5402>`_ - Fixed an issue where the checkbox is not visible on Configuration dialog in runtime for the dark theme.
|
||||||
| `Issue #5409 <https://redmine.postgresql.org/issues/5409>`_ - Fixed validation issue in Synonyms node.
|
| `Issue #5409 <https://redmine.postgresql.org/issues/5409>`_ - Fixed validation issue in Synonyms node.
|
||||||
| `Issue #5410 <https://redmine.postgresql.org/issues/5410>`_ - Fixed an issue while removing the package body showing wrong modified SQL.
|
| `Issue #5410 <https://redmine.postgresql.org/issues/5410>`_ - Fixed an issue while removing the package body showing wrong modified SQL.
|
||||||
| `Issue #5415 <https://redmine.postgresql.org/issues/5415>`_ - Ensure that the query tool context menu should work on the collection nodes.
|
| `Issue #5415 <https://redmine.postgresql.org/issues/5415>`_ - Ensure that the query tool context menu should work on the collection nodes.
|
||||||
|
| `Issue #5419 <https://redmine.postgresql.org/issues/5419>`_ - Ensure that the user should not be able to change the authentication source.
|
||||||
|
| `Issue #5420 <https://redmine.postgresql.org/issues/5420>`_ - Ensure error should be handled properly when LDAP user is created with the same name.
|
||||||
|
| `Issue #5432 <https://redmine.postgresql.org/issues/5432>`_ - Fixed an issue where an internal user is not created if the authentication source is set to internal and ldap.
|
||||||
@@ -14,7 +14,7 @@ import config
|
|||||||
from ldap3 import Connection, Server, Tls, ALL, ALL_ATTRIBUTES
|
from ldap3 import Connection, Server, Tls, ALL, ALL_ATTRIBUTES
|
||||||
from ldap3.core.exceptions import LDAPSocketOpenError, LDAPBindError,\
|
from ldap3.core.exceptions import LDAPSocketOpenError, LDAPBindError,\
|
||||||
LDAPInvalidScopeError, LDAPAttributeError, LDAPInvalidFilterError,\
|
LDAPInvalidScopeError, LDAPAttributeError, LDAPInvalidFilterError,\
|
||||||
LDAPStartTLSError
|
LDAPStartTLSError, LDAPSSLConfigurationError
|
||||||
from flask_babelex import gettext
|
from flask_babelex import gettext
|
||||||
|
|
||||||
from .internal import BaseAuthentication
|
from .internal import BaseAuthentication
|
||||||
@@ -81,12 +81,17 @@ class LDAPAuthentication(BaseAuthentication):
|
|||||||
if ca_cert_file and cert_file and key_file:
|
if ca_cert_file and cert_file and key_file:
|
||||||
cert_validate = ssl.CERT_REQUIRED
|
cert_validate = ssl.CERT_REQUIRED
|
||||||
|
|
||||||
tls = Tls(
|
try:
|
||||||
local_private_key_file=key_file,
|
tls = Tls(
|
||||||
local_certificate_file=cert_file,
|
local_private_key_file=key_file,
|
||||||
validate=cert_validate,
|
local_certificate_file=cert_file,
|
||||||
version=ssl.PROTOCOL_TLSv1_2,
|
validate=cert_validate,
|
||||||
ca_certs_file=ca_cert_file)
|
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:
|
try:
|
||||||
# Create the server object
|
# Create the server object
|
||||||
|
|||||||
@@ -241,7 +241,8 @@ def create():
|
|||||||
|
|
||||||
|
|
||||||
def create_user(data):
|
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')
|
req_params = ('username', 'role', 'active', 'auth_source')
|
||||||
else:
|
else:
|
||||||
req_params = ('email', 'role', 'active', 'newPassword',
|
req_params = ('email', 'role', 'active', 'newPassword',
|
||||||
@@ -264,12 +265,13 @@ def create_user(data):
|
|||||||
|
|
||||||
try:
|
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 \
|
auth_source = new_data['auth_source'] if 'auth_source' in new_data \
|
||||||
else current_app.PGADMIN_DEFAULT_AUTH_SOURCE
|
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,
|
usr = User(username=username,
|
||||||
email=email,
|
email=email,
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ 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'),
|
||||||
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) {
|
userFilter = function(collection) {
|
||||||
return (new Backgrid.Extension.ClientSideFilter({
|
return (new Backgrid.Extension.ClientSideFilter({
|
||||||
collection: collection,
|
collection: collection,
|
||||||
@@ -286,12 +287,12 @@ define([
|
|||||||
role: '2',
|
role: '2',
|
||||||
newPassword: undefined,
|
newPassword: undefined,
|
||||||
confirmPassword: undefined,
|
confirmPassword: undefined,
|
||||||
auth_source: 'internal',
|
auth_source: DEFAULT_AUTH_SOURCE,
|
||||||
authOnlyInternal: AUTH_ONLY_INTERNAL,
|
authOnlyInternal: AUTH_ONLY_INTERNAL,
|
||||||
},
|
},
|
||||||
schema: [{
|
schema: [{
|
||||||
id: 'auth_source',
|
id: 'auth_source',
|
||||||
label: gettext('Authentication Source'),
|
label: gettext('Authentication source'),
|
||||||
type: 'text',
|
type: 'text',
|
||||||
control: 'Select2',
|
control: 'Select2',
|
||||||
url: url_for('user_management.auth_sources'),
|
url: url_for('user_management.auth_sources'),
|
||||||
@@ -328,7 +329,14 @@ define([
|
|||||||
cellHeaderClasses: 'width_percent_30',
|
cellHeaderClasses: 'width_percent_30',
|
||||||
deps: ['auth_source'],
|
deps: ['auth_source'],
|
||||||
editable: function(m) {
|
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;
|
return true;
|
||||||
},
|
},
|
||||||
disabled: false,
|
disabled: false,
|
||||||
@@ -421,7 +429,7 @@ define([
|
|||||||
deps: ['auth_source'],
|
deps: ['auth_source'],
|
||||||
sortable: false,
|
sortable: false,
|
||||||
editable: function(m) {
|
editable: function(m) {
|
||||||
if (m.get('auth_source') == 'internal') {
|
if (m.get('auth_source') == DEFAULT_AUTH_SOURCE) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -438,7 +446,7 @@ define([
|
|||||||
deps: ['auth_source'],
|
deps: ['auth_source'],
|
||||||
sortable: false,
|
sortable: false,
|
||||||
editable: function(m) {
|
editable: function(m) {
|
||||||
if (m.get('auth_source') == 'internal') {
|
if (m.get('auth_source') == DEFAULT_AUTH_SOURCE) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -450,7 +458,7 @@ define([
|
|||||||
changedAttrs = this.changed || {},
|
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])?)*$/;
|
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')) ||
|
_.isNull(this.get('email')) ||
|
||||||
String(this.get('email')).replace(/^\s+|\s+$/g, '') == '')) {
|
String(this.get('email')).replace(/^\s+|\s+$/g, '') == '')) {
|
||||||
errmsg = gettext('Email address cannot be empty.');
|
errmsg = gettext('Email address cannot be empty.');
|
||||||
@@ -464,7 +472,7 @@ define([
|
|||||||
this.errorModel.set('email', errmsg);
|
this.errorModel.set('email', errmsg);
|
||||||
return errmsg;
|
return errmsg;
|
||||||
} else if (!!this.get('email') && this.collection.nonFilter.where({
|
} 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) {
|
}).length > 1) {
|
||||||
errmsg = gettext('The email address %s already exists.',
|
errmsg = gettext('The email address %s already exists.',
|
||||||
this.get('email')
|
this.get('email')
|
||||||
@@ -490,7 +498,7 @@ define([
|
|||||||
this.errorModel.unset('role');
|
this.errorModel.unset('role');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.get('auth_source') == 'internal') {
|
if (this.get('auth_source') == DEFAULT_AUTH_SOURCE) {
|
||||||
if (this.isNew()) {
|
if (this.isNew()) {
|
||||||
// Password is compulsory for new user.
|
// Password is compulsory for new user.
|
||||||
if ('newPassword' in changedAttrs && (_.isUndefined(this.get('newPassword')) ||
|
if ('newPassword' in changedAttrs && (_.isUndefined(this.get('newPassword')) ||
|
||||||
@@ -598,6 +606,17 @@ define([
|
|||||||
this.errorModel.unset('confirmPassword');
|
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;
|
return null;
|
||||||
},
|
},
|
||||||
@@ -824,10 +843,10 @@ define([
|
|||||||
saveUser: function(m) {
|
saveUser: function(m) {
|
||||||
var d = m.toJSON(true);
|
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')) ) {
|
(!m.get('username') || !m.get('auth_source') || !m.get('role')) ) {
|
||||||
return false;
|
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') ||
|
||||||
m.get('newPassword') != m.get('confirmPassword'))) {
|
m.get('newPassword') != m.get('confirmPassword'))) {
|
||||||
// New user model is valid but partially filled so return without saving.
|
// New user model is valid but partially filled so return without saving.
|
||||||
@@ -845,9 +864,16 @@ define([
|
|||||||
wait: true,
|
wait: true,
|
||||||
success: function() {
|
success: function() {
|
||||||
// User created/updated on server now start new session for this user.
|
// User created/updated on server now start new session for this user.
|
||||||
|
let temp_auth_sources = m.get('auth_source');
|
||||||
m.set({
|
m.set({
|
||||||
'newPassword': undefined,
|
'newPassword': undefined,
|
||||||
'confirmPassword': 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();
|
m.startNewSession();
|
||||||
|
|||||||
Reference in New Issue
Block a user