I was able to set up the authentication for my flask app using ldap3. We use active directory and hence I used ldap3 to connect to our active directory and verify the users against input credentials. The problem I am having is with the way it has been set up. Below is the code as you can see:
def global_ldap_authentication(user_id, user_pwd):
ldap_user_id = user_id.strip()
ldap_user_pwd = user_pwd.strip()
ldsp_server = f'our_internal_server'
root_dn = "OU=Users,OU=AUS - Austin,OU=Americas,OU=JCJJ,DC=jcjj,DC=com"
user = f'CN=Dave Simmon - {ldap_user_id},{root_dn}'
server = Server(ldsp_server, get_info=ALL)
connection = Connection(server, user=user, password=ldap_user_pwd)
connection.start_tls()
if not conn.bind():
print(f" *** cannot bind to ldap server: {conn.last_error}")
l_success_msg = f' *** Failed Authentication: {conn.last_error}'
else:
print(f" *** Successful bind to ldap server")
l_success_msg = 'Happy'
return l_success_msg
As you can see this connects to my ldap server and lets me log in. My login route is simple as it takes userID and password and calls this global_ldap_authentication function with these attributes and logs in the user if it authenticates against the active directory that we have. The problem is with the variable 'user' where I have specified a name Dave Simmon in order to connect to ldap which means only user Dave simmon can log in to my app using his user ID (A5564, this is the number for user id) and his password.
I tried other ways of connecting to our ldap directory but I was not successful and it only worked this way. I finally found a technique to make that name variable instead of Dave Simmon. We have a service account and my plan is to log in to ldap using that service account and its password everytime this function gets called and since I will be getting userID and password from the form in login page, I want to search the ldap active directory for the givenName and surname(sn) that belongs to the userid anyone inputs in login form and then replace that Dave simmon with that givenName and sn from active directory.
This will let anyone that is present in that directory log in to my app. The problem is I am having hard time figuring out how to do it. How do I search for givenName and Surname(sn) based on the userID I receive. The userID is unique and each userID is mapped to their users respective first name and last name. SO when A5564 is input in my login form then I should be able to query the Dave Simmon from my active directory so that I can make a successful connection and login.
I tried to make this as detailed as possible. Any help will be highly appreciated!!
My login route is super simple:
@app.route('/', methods=['GET', 'POST'])
def login():
form = LoginForm()
if request.method in ('POST'):
login_id = form.userid.data
login_password = form.password.data
#used to pass argument to main_page
print(login_id)
session['login_id'] = login_id
login_msg = global_ldap_authentication(login_id, login_password)
#validate the connection
print(login_msg)
if login_msg == "Success":
return redirect(url_for('main_page'))
#return render_template('index.html')
else:
return render_template('login.html', form=form)
return render_template('login.html', form=form)
CodePudding user response:
One way to achieve this is to use a "search and bind" approach.
Basically, it consists of using a manager/admin account in your app that will bind and search the directory for the user given his username/id, so that you can retrieve his DN instead of having to build it. Then, you can eventually bind with his dn/password for the authentication. The only condition is that the user must provide a unique identifier which is stored as an attribute in his directory entry, so that the search can be filtered effectively to match only this user.
In AD, there are two user attributes you can use to match account logon names :
userPrincipalName : The userPrincipalName attribute is the logon name for the user. The attribute consists of a user principal name (UPN), which is the most common logon name for Windows users. Users typically use their UPN to log on to a domain. A UPN consists of a UPN prefix (the user account name) and a UPN suffix (a DNS domain name). The prefix is joined with the suffix using the "@" symbol. For example, "someone@ example.com".
sAMAccountName : The sAMAccountName attribute is a logon name used to support clients and servers from previous version of Windows, such as Windows NT 4.0, Windows 95, Windows 98, and LAN Manager. The logon name must be 20 or fewer characters and be unique among all security principal objects within the domain.
The question is then, does a userid such as A5564 match one of these attributes ? Are the users providing their account logon name in the form ? If not, you will need either to change that, or to register the id they provide in their own user entry in AD, as a specific attribute you will need to use for filtering the search.
CodePudding user response:
I ultimately figured it out just in case someone needs it. I was able to use this for that.
conn.search('OU=AUS - Austin,OU=Americas,OU=JCJJ,DC=jcjj,DC=com', '(&(objectclass=person)(sAMAccountName={id}))'.format(id=ldap_user_name), attributes=['CN'])
entry = conn.entries[0]
name = entry.CN
So, as I stated before, I was able to connect to ldap active directory using a service account and password at first. And then, I ran the above search based on what 'id' user inputed in my login form. I then mentioned that I want 'CN' attribute which returns the name in this format: Dave Simmon - A5564. This is exactly what I needed to make a variable instead of hard coding it.
So, I stored this Dave Simmon - A5564 in a variable called name and used this variable to connect to my ldap again and let users see the main page of my app. Now, any users who input their ID would have their name stored in variable called 'name' and will be able to log in to the web page.
Thanks!
