nickw444 / flask-ldap3-login

LDAP3 Logins for Flask/Flask-Login
http://flask-ldap3-login.readthedocs.org/en/latest/
MIT License
73 stars 38 forks source link

User is found in AD OU Subtree but is not logged in - No AD/LDAP error #41

Closed geracastro closed 6 years ago

geracastro commented 6 years ago

I am trying to authenticate users that are within an OU several levels deep in AD. Other users are inside a security group. LDAP search returns 'success' (I take a traffic capture and I see that the user is found inside the OU and security group); yet, the user does not get logged in. Could this be a bug or is my implementation wrong? Here's part of my config:

app.config['LDAP_USER_RDN_ATTR'] = 'cn'
app.config['LDAP_USER_LOGIN_ATTR'] = 'sAMAccountName'
app.config['LDAP_BASE_DN'] = 'DC=mydomoain,DC=com'
app.config['LDAP_GROUP_DN'] = 'OU=theou'
app.config['LDAP_USER_DN'] = 'OU=theou'
app.config['LDAP_USER_SEARCH_SCOPE'] = 'SUBTREE'

@login_manager.user_loader
def load_user(id):
    if id in users:
        return users[id]
    return None

@ldap_manager.save_user
def save_user(dn, username, data, memberships):
    user = User(dn, username, data)
    users[dn] = user
    return user

@app.route("/login", methods=['GET', 'POST'])
def login():
    form = LDAPLoginForm()
    if form.validate_on_submit():
        flash('Welcome, ' + form.username.data + '!', 'success')
        login_user(form.user)  # Tell flask-login to log them in.
        return redirect(url_for('myotherpage'))
    return render_template('login.html', form=form)
nickw444 commented 6 years ago

Hi @thetechuser, if you haven't already, I suggest you take the quickstart for a spin by substituting in your configuration and see if yields a successful result.

Unfortunately it is difficult for me to debug exactly what is going wrong in your situation without seeing what is happening under the hood with regards to your LDAP structure and configuration.

nickw444 commented 6 years ago

The most interesting line in your example will probably be what happens when the form is submitted. My questions for you:

geracastro commented 6 years ago

Hi @thetechuser, if you haven't already, I suggest you take the quickstart for a spin by substituting in your configuration and see if yields a successful result.

Unfortunately it is difficult for me to debug exactly what is going wrong in your situation without seeing what is happening under the hood with regards to your LDAP structure and configuration.

@nickw444 Thank you for your input. I should have clarified what my intent is. Sorry about that. My goal is to allow users that belong to security groups which are all inside a specific OU, to login to the application. So, in my example code above, 'theou' is the parent OU where the security group lives. The security group is where the users are. So, I only want to allow users that are part of that group to login to the application. The problem is that all the users that I need to allow belong to different OUs. So, I made them all part of a specific group to try to allow them to login to the app. FYI, when I use the OU that a test user belongs to directly in the LDAP_GROUP_DN and LDAP_USER_DN, the user is logged in without issues. However, if I replace the OU with the one which contains the security group that the user is part of, the user is not logged in.

In my example, I added this (app.config['LDAP_USER_SEARCH_SCOPE'] = 'SUBTREE') parameter thinking that it would look make it work as it looks recursively inside the OU until it finds the user. However, it behaves as if it does find the user inside but does not log the user.

Summary:

1- User is logged in if the user belongs to the specified OU directly. In this scenario, I see that the user's username and password are sent to AD (I see that in the network traffic capture) 2- User is NOT logged in if the user does NOT belong to the specified OU directly. In this scenario, the user belongs to a security group that is inside the specified OU. In this case, I am using app.config['LDAP_USER_SEARCH_SCOPE'] = 'SUBTREE' to try to recursively look for the user. In this scenario, I don't see the user's username and password being sent to AD in the network traffic capture.

geracastro commented 6 years ago

The most interesting line in your example will probably be what happens when the form is submitted. My questions for you:

* What data is in the form?

* Is it the expected data?

* Is the validation succeeding or failing?

* If it's succeeding, then what is `form.user`? Is it what flask-login expects when you call `login_user`?

@nickw444 Validation succeeds but it does not login the user. I am debugging and I don't see any errors either. It just does not login the user.

So, I took the quickstart code and replaced my domain name and added a bind user and password (to be able to talk to AD). That's all I did, just to start. Here's the traceback:

Traceback (most recent call last): File "/var/www/mysite/env/lib/python3.6/site-packages/flask/app.py", line 2309, in __call__ return self.wsgi_app(environ, start_response) File "/var/www/mysite/env/lib/python3.6/site-packages/flask/app.py", line 2295, in wsgi_app response = self.handle_exception(e) File "/var/www/mysite/env/lib/python3.6/site-packages/flask/app.py", line 1741, in handle_exception reraise(exc_type, exc_value, tb) File "/var/www/mysite/env/lib/python3.6/site-packages/flask/_compat.py", line 35, in reraise raise value File "/var/www/mysite/env/lib/python3.6/site-packages/flask/app.py", line 2292, in wsgi_app response = self.full_dispatch_request() File "/var/www/mysite/env/lib/python3.6/site-packages/flask/app.py", line 1815, in full_dispatch_request rv = self.handle_user_exception(e) File "/var/www/mysite/env/lib/python3.6/site-packages/flask/app.py", line 1718, in handle_user_exception reraise(exc_type, exc_value, tb) File "/var/www/mysite/env/lib/python3.6/site-packages/flask/_compat.py", line 35, in reraise raise value File "/var/www/mysite/env/lib/python3.6/site-packages/flask/app.py", line 1813, in full_dispatch_request rv = self.dispatch_request() File "/var/www/mysite/env/lib/python3.6/site-packages/flask/app.py", line 1799, in dispatch_request return self.view_functions[rule.endpoint](**req.view_args) File "/var/www/mysite/__init__.py", line 125, in login if form.validate_on_submit(): File "/var/www/mysite/env/lib/python3.6/site-packages/flask_wtf/form.py", line 101, in validate_on_submit return self.is_submitted() and self.validate() File "/var/www/mysite/env/lib/python3.6/site-packages/flask_ldap3_login/forms.py", line 69, in validate return self.validate_ldap() File "/var/www/mysite/env/lib/python3.6/site-packages/flask_ldap3_login/forms.py", line 38, in validate_ldap result = ldap_mgr.authenticate(username, password) File "/var/www/mysite/env/lib/python3.6/site-packages/flask_ldap3_login/__init__.py", line 272, in authenticate result = self.authenticate_search_bind(username, password) File "/var/www/mysite/env/lib/python3.6/site-packages/flask_ldap3_login/__init__.py", line 466, in authenticate_search_bind attributes=self.config.get('LDAP_GET_USER_ATTRIBUTES') File "/var/www/mysite/env/lib/python3.6/site-packages/ldap3/core/connection.py", line 782, in search response = self.post_send_search(self.send('searchRequest', request, controls)) File "/var/www/mysite/env/lib/python3.6/site-packages/ldap3/strategy/sync.py", line 139, in post_send_search responses, result = self.get_response(message_id) File "/var/www/mysite/env/lib/python3.6/site-packages/ldap3/strategy/base.py", line 378, in get_response raise LDAPOperationResult(result=result['result'], description=result['description'], dn=result['dn'], message=result['message'], response_type=result['type']) ldap3.core.exceptions.LDAPNoSuchObjectResult: LDAPNoSuchObjectResult - 32 - noSuchObject - DC=exampledomain,DC=com - 0000208D: NameErr: DSID-03100238, problem 2001 (NO_OBJECT), data 0, best match of: 'DC=exampledomain,DC=com' - searchResDone - None

geracastro commented 6 years ago

There were two key lines I needed to make it work - There was never a bug with your code @nickw444 Thank you for the help.

app.config=['LDAP_REQUIRED_GROUP'] = 'cn=groupname,ou=ouname,dc=exampledomain,dc=com' app.config=['LDAP_USER_SEARCH_SCOPE'] = 'SUBTREE'

nickw444 commented 6 years ago

Glad you figured it out! Sorry for the delayed response 😄