LDAP - Option for not Anonymous / Not Simple
Is there an option to use bind_dn and bind_password for LDAP authentication?
As our LDAP server does not allow anonymous login.
Hi @charles-nulud script server doesn't use anonymous authentication (that's why a user has to provide username and password). It uses SIMPLE protocol, with bind_dn and password.
Unfortunately, other protocols are not supported. Which one is used by your server?
I have modified the auth_ldap.py and it works to assign username with bind_dn and password with bind_pw value. and it works.
But I think it is a good option, if you can include this as option for LDAP, as not all LDAP server just accept username and password, like for us, it requires to connect as admin user/password to query, thus I need to use bind_dn and bind_pw.
Could you tell me please, what did you modify exactly?
from:
def _connect(self, full_username, password):
connection = Connection(
self.url,
user=full_username,
password=password,
authentication=SIMPLE,
read_only=True,
version=self.version
)
connection.bind()
return connection
to:
def _connect(self, full_username, password): connection = Connection( self.url, user='uid=admin,ou=People,dc=xx,dc=xx,dc=xx', password='xxxxxxx', authentication=SIMPLE, read_only=True, version=self.version, ) connection.bind() return connection
and I suggest modify this so that in conf.json, you can provide bind_dn and bind_pw for optional admin user/pass and use this instead of the $username / password to authenticate.
But with such changes, a user can specify any credentials and still login (with admin rights)
oh yes, my mistake. it bypasses the username/password check enter in the ui.
need to modify that the search still need to function and only connect should use admin user/pass..
The implementation should be like this on my testldap.py:
LDAP connection and bind
server = ldap3.Server(LDAP_SERVER, port=LDAP_PORT) conn = ldap3.Connection(server, user=LDAP_BIND_DN, password=LDAP_BIND_PW, auto_bind=True)
LDAP search and authentication
conn.search(LDAP_BASE_DN, '(uid={})'.format(username)) if conn.entries: user_dn = conn.entries[0].entry_dn try: conn = ldap3.Connection(server, user=user_dn, password=password, auto_bind=True) print('Login successful!') except ldap3.core.exceptions.LDAPBindError: print('Login failed!') else: print('User not found!')
But this is effectively the same as what script server is doing. You are using SIMPLE protocol as well. The only difference is that you are using user_dn instead of username.
But for this purpose, there is username_pattern setting
Or am I missing something?
our LDAP server only accepts connection from admin user. How can I implement this?
login using admin, but search the user/pass using the $username/password of the gui
conn = ldap3.Connection(server, user=user_dn, password=password, auto_bind=True)
Isn't it what you are doing here? Logging in (connecting) as a normal user.
yes but it needs to connect bind first as admin:
LDAP connection and bind
server = ldap3.Server(LDAP_SERVER, port=LDAP_PORT) conn = ldap3.Connection(server, user=LDAP_BIND_DN, password=LDAP_BIND_PW, auto_bind=True)
Sorry, I'm not a LDAP expert, and don't fully understand how it works. From my understanding, by doing
conn = ldap3.Connection(server, user=user_dn, password=password, auto_bind=True)
you are creating a completely new connection.
What error do you get, if you use default script server implementation, with username_pattern specified? (or by entering user_dn in a login form)
I am getting "Invalid Credentials" using default. Tried different username_pattern already.
my sample testldap.py connects using bind_dn and bind_pw first for initial connection and then connect again using the username to verify the username / password is correct.
Just to double-check, will it also work, if you remove this part:
conn.search(LDAP_BASE_DN, '(uid={})'.format(username))
if conn.entries:
user_dn = conn.entries[0].entry_dn
And just use username set by script server?
Modified` into :
` username = input('Enter your LDAP username: ') password = input('Enter your LDAP password: ')
server = ldap3.Server(LDAP_SERVER, port=LDAP_PORT) conn = ldap3.Connection(server, user=username, password=password, auto_bind=True)
try: conn = ldap3.Connection(server, user=user_dn, password=password, auto_bind=True) print('Login successful!') except ldap3.core.exceptions.LDAPBindError: print('Login failed!')
and get error:
Traceback (most recent call last):
File "/opt/script-server/conf/runners/scripts/testldapuser.py", line 17, in
I think the goal, is first establish a connection and bind that uses bind_dn/bind_pw and then authenticate user using search should use the $username / password of the gui
def _connect(self, full_username, password):
server = ldap3.Server(LDAP_SERVER, port=LDAP_PORT)
conn = ldap3.Connection(server, user=LDAP_BIND_DN, password=LDAP_BIND_PW, auto_bind=True)
try:
conn = ldap3.Connection(server, user=full_username, password=password, auto_bind=True)
print('Login successful!')
except ldap3.core.exceptions.LDAPBindError:
print('Login failed!')
else:
print('User not found!')
Could you check, if this code works for you?
My point is:
conn.search(LDAP_BASE_DN, '(uid={})'.format(username))
this should be 100% redundant
On Fri, Apr 14, 2023 at 7:03 AM Iaroslav Shepilov @.***> wrote:
conn = ldap3.Connection(server, user=LDAP_BIND_DN, password=LDAP_BIND_PW, auto_bind=True) conn = ldap3.Connection(server, user=full_username, password=password, auto_bind=True)The first will work, the second (sometimes, depending on the LDAP server config) won't.
I'm not an LDAP expert either, but we just went through something like this with LDAP config here at my real job. LDAP supports the concept of the bind connection as a distinct user, separate from the search. You absolutely need the bind user/pass as their own settings, as LDAP administrators often don't grant bind permissions to users defined within the LDAP, so that they can limit who can actually poll the dirtree (and prevent slurping of the directory).
Once you establish the bind connection as the bind-user, you can attempt to auth with the GUI-user/pass.
Message ID: @.***>
Can you check https://ldap3.readthedocs.io/en/latest/bind.html particularly this part:
"Bind as a different user while the Connection is open"
`# import class and constants from ldap3 import Server, Connection, ALL, LDAPBindError
define the server
s = Server('servername', get_info=ALL) # define an unsecure LDAP server, requesting info on DSE and schema
define the connection
c = Connection(s, user='user_dn', password='user_password')
perform the Bind operation
if not c.bind(): print('error in bind', c.result)
Bind again with another user
if not c.rebind(user='different_user_dn', password='different_user_password') print('error in rebind', c.result)`
maybe we can use this.
This seems works:
def _connect(self, full_username, password):
LDAP_BIND_DN = 'uid=xxxxx,ou=People,dc=xx,dc=xx,dc=xx'
LDAP_BIND_PW = 'xxxxxxx'
LDAP_BASE_DN = 'ou=People,dc=xx,dc=xx,dc=xx'
connection = Connection(self.url, user=LDAP_BIND_DN, password=LDAP_BIND_PW, authentication=SIMPLE, read_only=True, version=self.version)
connection.bind()
connection.search(LDAP_BASE_DN, '(uid={})'.format(full_username))
if connection.entries:
user_dn = connection.entries[0].entry_dn
try:
connection = Connection(self.url, user=user_dn, password=password, auto_bind=True)
connection.bind()
return connection
except ldap3.core.exceptions.LDAPBindError:
LOGGER.exception('LDAP Error occurred while ldap authentication of user ' + username)
Hi @charles-nulud could you use username_template and remove "search" part from the code above? This step seems redundant to me. There is no need to search for user dn, if you can define it via username_pattern Anyways, if your current code works locally for you, it's great :)