Django Auth is not finding User Account in LDAP - django

Morning,
I´m implementing Django Auth Ldap in my proyect but it is not working. I checked ldap connection (by Django shell) and returns a search, so I guess python-ldap is working. I used the next:
import ldap
con = ldap.initialize("ldap://hostname")
con.simple_bind_s( "CN=MyName MySurname, CN=Users, DC=CompanyName, DC=local", "MyPassword" )
con.search_s( 'DC=CompanyName, DC=local', ldap.SCOPE_SUBTREE, '(objectclass=person)', ['sn'] )
When I try to authenticate an user by web (using Django-Auth-Ldap), authentication always returns None.
Settings. (LDAP Configuration).
AUTH_LDAP_SERVER_URI = "ldap://hostname"
AUTH_LDAP_BIND_DN = "CN=MyName MySurname, CN=Users, DC=CompanyName, DC=local"
AUTH_LDAP_BIND_PASSWORD = "MyPassword"
AUTH_LDAP_USER_SEARCH = LDAPSearch("CN=Users, DC=CompanyName, DC=local", ldap.SCOPE_SUBTREE, "(uid=%(user)s)")
AUTH_LDAP_CONNECTION_OPTIONS = {
ldap.OPT_REFERRALS: False
}
from django_auth_ldap.backend import LDAPBackend
View.
def Login(request):
usr = "MyUserName"
pwd = "MyPassword"
if request.method == 'POST':
ldap_backend = LDAPBackend()
user = ldap_backend.authenticate(usr, pwd)
print user
print usr, pwd
In my view, I´m passing to the ldap authentication my user and password which I used for login in the domain. Is that correct?
I got the value "CN=MyName MySurname, CN=Users, DC=CompanyName, DC=local" from a command in Directory Active server, kind of: dsquery user
This is the AD Schema:
What Am I Doing wrong?
Thanks guys.
EDITED: The problem is when I define the search throug uid, if I define it as AUTH_LDAP_USER_SEARCH = LDAPSearch("CN=Users, DC=CompanyName, DC=local", ldap.SCOPE_SUBTREE, "(CN=%(user)s)") is working (and, in the view, I must to pass as usr = "MyNameMySurname" instead). How can I Define the search through the username which I used for login it.

Finally... I must to use samAccountName instead of CN. I hope it help you all. Thanks guys.

Related

Flask dance example for login with Azure AD

I am trying to implement SSO for one of my applications using flask-login and flask-dance. As a starting point I am using sample code given on Flask Dance website - https://flask-dance.readthedocs.io/en/v1.2.0/quickstarts/sqla-multiuser.html
Only change I did was - I replaced GitHub with my Azure AD credentials
Please find the code below:
import sys
from flask import Flask, redirect, url_for, flash, render_template
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm.exc import NoResultFound
from flask_dance.contrib.github import make_github_blueprint, github
from flask_dance.contrib.azure import make_azure_blueprint, azure
from flask_dance.consumer.storage.sqla import OAuthConsumerMixin, SQLAlchemyStorage
from flask_dance.consumer import oauth_authorized, oauth_error
from flask_login import (
LoginManager, UserMixin, current_user,
login_required, login_user, logout_user
)
# setup Flask application
app = Flask(__name__)
app.secret_key = "XXXXXXXXXXXXXX"
blueprint = make_azure_blueprint(
client_id="XXXXXXXXXXXXXXXXXXXXX",
client_secret="XXXXXXXXXXXXXXXXXXXXXXXX",
tenant="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
)
app.register_blueprint(blueprint, url_prefix="/login")
# setup database models
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///multi.db"
db = SQLAlchemy()
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
# Your User model can include whatever columns you want: Flask-Dance doesn't care.
# Here are a few columns you might find useful, but feel free to modify them
# as your application needs!
username = db.Column(db.String(1028), unique=True)
email = db.Column(db.String(1028), unique=True)
name = db.Column(db.String(1028))
class OAuth(OAuthConsumerMixin, db.Model):
provider_user_id = db.Column(db.String(1028), unique=True)
user_id = db.Column(db.Integer, db.ForeignKey(User.id))
user = db.relationship(User)
# setup login manager
login_manager = LoginManager()
login_manager.login_view = 'azure.login'
#login_manager.user_loader
def load_user(user_id):
#print(User.query.get(int(user_id)))
return User.query.get(int(user_id))
# setup SQLAlchemy backend
blueprint.storage = SQLAlchemyStorage(OAuth, db.session, user=current_user,user_required=False)
# create/login local user on successful OAuth login
#oauth_authorized.connect_via(blueprint)
def azure_logged_in(blueprint, token):
if not token:
#print(token)
flash("Failed to log in with azure.", category="error")
return False
resp = blueprint.session.get("/user")
if not resp.ok:
#print(resp)
msg = "Failed to fetch user info from Azure."
flash(msg, category="error")
return False
azure_info = resp.json()
azure_user_id = str(azure_info["id"])
#print(azure_user_id)
# Find this OAuth token in the database, or create it
query = OAuth.query.filter_by(
provider=blueprint.name,
provider_user_id=azure_user_id,
)
try:
oauth = query.one()
except NoResultFound:
oauth = OAuth(
provider=blueprint.name,
provider_user_id=azure_user_id,
token=token,
)
if oauth.user:
login_user(oauth.user)
flash("Successfully signed in with Azure.")
else:
# Create a new local user account for this user
user = User(
# Remember that `email` can be None, if the user declines
# to publish their email address on GitHub!
email=azure_info["email"],
name=azure_info["name"],
)
# Associate the new local user account with the OAuth token
oauth.user = user
# Save and commit our database models
db.session.add_all([user, oauth])
db.session.commit()
# Log in the new local user account
login_user(user)
flash("Successfully signed in with Azure.")
# Disable Flask-Dance's default behavior for saving the OAuth token
return False
# notify on OAuth provider error
#oauth_error.connect_via(blueprint)
def azure_error(blueprint, error, error_description=None, error_uri=None):
msg = (
"OAuth error from {name}! "
"error={error} description={description} uri={uri}"
).format(
name=blueprint.name,
error=error,
description=error_description,
uri=error_uri,
)
flash(msg, category="error")
#app.route("/logout")
#login_required
def logout():
logout_user()
flash("You have logged out")
return redirect(url_for("index"))
#app.route("/")
def index():
return render_template("home.html")
# hook up extensions to app
db.init_app(app)
login_manager.init_app(app)
if __name__ == "__main__":
if "--setup" in sys.argv:
with app.app_context():
db.create_all()
db.session.commit()
print("Database tables created")
else:
app.run(debug=True,port=5011)
I have also done appropriate changes in HTML file for 'azure.login'.
So after running it as python multi.py --setup database tables are getting created
and after I run python multi.py Oauth dance is actually starting but in the end I am getting error like below:
HTTP Response:
127.0.0.1 - - [28/Oct/2020 10:17:44] "?[32mGET /login/azure/authorized?code=0.<Token>HTTP/1.1?[0m" 302 -
127.0.0.1 - - [28/Oct/2020 10:17:44] "?[37mGET / HTTP/1.1?[0m" 200 -
Am I missing something? Is it a good idea to use Flask Dance and Flask Login to have SSO with Azure AD? Or I should go with MSAL only along with Flask Session?
Kindly give your valuable inputs..
Since you use Azure AD as the Flask dance provider, we need to use Microsoft Graph to get user's information. The URL should be https://graph.microsoft.com/v1.0/me. So please update the code resp = blueprint.session.get("/user") to resp = blueprint.session.get("/v1.0/me") in method azure_logged_in. Besides, please note that the azure ad user's information has different names. We also need to update the code about creating users.
For example
#oauth_authorized.connect_via(blueprint)
def azure_logged_in(blueprint, token):
if not token:
# print(token)
flash("Failed to log in with azure.", category="error")
return False
resp = blueprint.session.get("/v1.0/me")
# azure.get
if not resp.ok:
# print(resp)
msg = "Failed to fetch user info from Azure."
flash(msg, category="error")
return False
azure_info = resp.json()
azure_user_id = str(azure_info["id"])
# print(azure_user_id)
# Find this OAuth token in the database, or create it
query = OAuth.query.filter_by(
provider=blueprint.name,
provider_user_id=azure_user_id,
)
try:
oauth = query.one()
except NoResultFound:
oauth = OAuth(
provider=blueprint.name,
provider_user_id=azure_user_id,
token=token,
)
if oauth.user:
login_user(oauth.user)
flash("Successfully signed in with Azure.")
else:
# Create a new local user account for this user
user = User(
# create user with user information from Microsoft Graph
email=azure_info["mail"],
username=azure_info["displayName"],
name=azure_info["userPrincipalName"]
)
# Associate the new local user account with the OAuth token
oauth.user = user
# Save and commit our database models
db.session.add_all([user, oauth])
db.session.commit()
# Log in the new local user account
login_user(user)
flash("Successfully signed in with Azure.")
# Disable Flask-Dance's default behavior for saving the OAuth token
return False
For more details, please refer to here and here

Binding to Active Directory using django-auth-ldap

I'm trying to create user login authentication in my django app via Active Directory using django-auth-ldap. The problem is that I cannot bind to the AD using username (which is sAMAccountName LDAP equivalent). Part of my settings.py below:
import ldap
from django_auth_ldap.config import LDAPSearch
AUTHENTICATION_BACKENDS = [
'django_auth_ldap.backend.LDAPBackend',
]
AUTH_LDAP_START_TLS = False
AUTH_LDAP_ALWAYS_UPDATE_USER = False
AUTH_LDAP_SERVER_URI = 'ldap://ip_address:389'
AUTH_LDAP_BIND_DN = ''
AUTH_LDAP_BIND_PASSWORD = ''
AUTH_LDAP_USER_SEARCH = LDAPSearch('DC=example,DC=com', ldap.SCOPE_SUBTREE, '(sAMAccountName=%(user)s)')
AUTH_LDAP_CONNECTION_OPTIONS = {
ldap.OPT_REFERRALS: 0,
}
Console log:
ERROR search_s('DC=example,DC=com', 2, '(sAMAccountName=user)') raised OPERATIONS_ERROR({'desc': 'Operations error', 'info': '00000000: LdapErr: DSID-0C090627, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, vece'})
DEBUG search_s('DC=example,DC=com', 2, '(sAMAccountName=%(user)s)') returned 0 objects:
DEBUG Authentication failed for user: failed to map the username to a DN.
Any idea why this is not working?
Anonymous read access is not enabled by default. To perform the search operation, populate AUTH_LDAP_BIND_DN and AUTH_LDAP_BIND_PASSWORD with a valid account. I generally create dedicated "system" accounts (i.e. not a real person's account because your authentication starts failing every time the user changes their password).

How to login using LDAP in Django

I am trying to enable LDAP server for login and authenticate in my Django application. I read django-auth-ldap tutorial and done all the changes in settings.py.
But I not able to login from LDAP server users, Django always try to login only form local database.
What i have to do and change any thing while login user? any changes is required in view.py authenticate() function for login.
My code snippets are below :
settings.py
AUTH_LDAP_SERVER_URI = 'ldap://my_domain.com'
AUTH_LDAP_BIND_DN = 'cn=admin,dc=my_domain,dc=com'
AUTH_LDAP_BIND_PASSWORD = 'My_password'
AUTH_LDAP_USER_SEARCH = LDAPSearch(
'ou=users,dc=my_domain,dc=com',
ldap.SCOPE_SUBTREE,
'(uid=%(user)s)',
)
AUTH_LDAP_CONNECTION_OPTIONS = {
ldap.OPT_REFERRALS: 0
}
# Set up the basic group parameters.
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
'ou=django,dc=my_domain,dc=com',
ldap.SCOPE_SUBTREE,
'(objectClass=groupOfNames)',
)
AUTH_LDAP_GROUP_TYPE = GroupOfNamesType(name_attr='cn')
# Simple group restrictions
AUTH_LDAP_REQUIRE_GROUP = 'cn=enabled,ou=django,ou=groups,dc=my_domain,dc=com'
AUTH_LDAP_DENY_GROUP = 'cn=disabled,ou=django,ou=groups,dc=my_domain,dc=com'
# Populate the Django user from the LDAP directory.
AUTH_LDAP_USER_ATTR_MAP = {
"username": "uid",
"passsword": "userPassword"
}
AUTH_LDAP_USER_FLAGS_BY_GROUP = {
'is_active': 'cn=active,ou=django,ou=groups,dc=my_domain,dc=com',
'is_staff': 'cn=staff,ou=django,ou=groups,dc=my_domain,dc=com',
'is_superuser': 'cn=superuser,ou=django,ou=groups,dc=my_domain,dc=com',
}
# This is the default, but I like to be explicit.
AUTH_LDAP_ALWAYS_UPDATE_USER = True
# Use LDAP group membership to calculate group permissions.
AUTH_LDAP_FIND_GROUP_PERMS = True
# Cache distinguised names and group memberships for an hour to minimize
# LDAP traffic.
AUTH_LDAP_CACHE_TIMEOUT = 3600
# Keep ModelBackend around for per-user permissions and maybe a local
# superuser.
AUTHENTICATION_BACKENDS = (
'django_auth_ldap.backend.LDAPBackend',
'django.contrib.auth.backends.ModelBackend',
)
#view.py
from django.contrib.auth import authenticate, login
def user_login(request):
user = authenticate(username = username, password = password)
login(request, user)
return HttpResponseRedirect('/')
Any code changes required in user_login() function or djagno automatically checks and authenticate users from LDAP as well as local database.
I am not sure which Django function will used for login purpose.
Any one please help me.
I was struggling for this soo long. and finally its working
with django-auth-ldap on Django 2.2 + Python 3.6.8 .
This is my settings.py
and its working fine.
import ldap
from django_auth_ldap.config import LDAPSearch, LDAPGroupQuery,GroupOfNamesType
AUTH_LDAP_SERVER_URI = 'ldap://192.168.122.222'
AUTH_LDAP_BIND_DN = 'CN=Django Admin,CN=Users,DC=hqvfx,DC=com'
AUTH_LDAP_BIND_PASSWORD = 'MyPassword'
AUTH_LDAP_USER_SEARCH = LDAPSearch('OU=all,OU=LSA_Users,DC=hqvfx,DC=com',ldap.SCOPE_SUBTREE, '(sAMAccountName=%(user)s)')
AUTH_LDAP_GROUP_SEARCH = LDAPSearch('OU=HQ_Groups,DC=hqvfx,DC=com',ldap.SCOPE_SUBTREE, '(objectClass=top)')
AUTH_LDAP_GROUP_TYPE = GroupOfNamesType()
AUTH_LDAP_MIRROR_GROUPS = True
# Populate the Django user from the LDAP directory.
AUTH_LDAP_USER_ATTR_MAP = {
'username': 'sAMAccountName',
'first_name': 'displayName',
'last_name': 'sn',
'email': 'mail',
}
AUTH_LDAP_USER_FLAGS_BY_GROUP = {
'is_active': 'CN=all, OU=HQ_Groups, DC=hqvfx, DC=com',
'is_staff': 'CN=all, OU=HQ_Groups, DC=hqvfx, DC=com',
'is_superuser': 'CN=all, OU=HQ_Groups, DC=hqvfx, DC=com',
}
AUTH_LDAP_ALWAYS_UPDATE_USER = True
AUTH_LDAP_FIND_GROUP_PERMS = True
AUTH_LDAP_CACHE_TIMEOUT = 3600
AUTHENTICATION_BACKENDS = (
'django_auth_ldap.backend.LDAPBackend',
'django.contrib.auth.backends.ModelBackend',
)

django social auth limiting user data

I have configured django social auth's to take from google only e-mail, but google shows this screen alerting app user that gender, date of birth, picture, language will be collect:
My django-social-auth config is as follow:
WHITE_LISTED_DOMAINS = [ 'some_domain', ]
GOOGLE_WHITE_LISTED_DOMAINS = WHITE_LISTED_DOMAINS
SOCIAL_AUTH_EXTRA_DATA = False
#LOGIN_ERROR_URL = '/login-error/' Not set
#SOCIAL_AUTH_DEFAULT_USERNAME = 'new_social_auth_user' Not set
#GOOGLE_CONSUMER_KEY = '' Not set
#GOOGLE_CONSUMER_SECRET = '' Not set
#GOOGLE_OAUTH2_CLIENT_ID = '' Not set
#GOOGLE_OAUTH2_CLIENT_SECRET = '' Not set
SOCIAL_AUTH_USERNAME_IS_FULL_EMAIL = False
SOCIAL_AUTH_PROTECTED_USER_FIELDS = ['email',]
INSTALLED_APPS = (
'django.contrib.auth',
...
'social_auth',
)
How can I do to avoid this google message?
EDITED
I have move to GoogleOauth2 auth and inherit and change google backend:
from social_auth.backends.google import *
GOOGLE_OAUTH2_SCOPE = ['https://www.googleapis.com/auth/userinfo.email',]
class GoogleOAuth2(BaseOAuth2):
"""Google OAuth2 support"""
AUTH_BACKEND = GoogleOAuth2Backend
AUTHORIZATION_URL = 'https://accounts.google.com/o/oauth2/auth'
ACCESS_TOKEN_URL = 'https://accounts.google.com/o/oauth2/token'
REVOKE_TOKEN_URL = 'https://accounts.google.com/o/oauth2/revoke'
REVOKE_TOKEN_METHOD = 'GET'
SETTINGS_SECRET_NAME = 'GOOGLE_OAUTH2_CLIENT_SECRET'
SCOPE_VAR_NAME = 'GOOGLE_OAUTH_EXTRA_SCOPE'
DEFAULT_SCOPE = GOOGLE_OAUTH2_SCOPE
REDIRECT_STATE = False
print DEFAULT_SCOPE #<------ to be sure
def user_data(self, access_token, *args, **kwargs):
"""Return user data from Google API"""
return googleapis_profile(GOOGLEAPIS_PROFILE, access_token)
#classmethod
def revoke_token_params(cls, token, uid):
return {'token': token}
#classmethod
def revoke_token_headers(cls, token, uid):
return {'Content-type': 'application/json'}
But google still ask for profile data, profile is still in scope:
https://accounts.google.com/o/oauth2/auth?response_type=code&scope=https://www.googleapis.com/auth/userinfo.email+https://www.googleapis.com/auth/userinfo.profile&redirect_uri=...
Runs fine if I modify by hand social-auth code instead inherit:
def get_scope(self):
return ['https://www.googleapis.com/auth/userinfo.email',]
What is wrong with my code?
That's because the default scope used on google backend is set to that (email and profile information), it's defined here. In order to avoid that you can create your own google backend which just sets the desired scope, then use that backend instead of the built in one. Example:
from social_auth.backends.google import GoogleOAuth2
class SimplerGoogleOAuth2(GoogleOAuth2):
DEFAULT_SCOPE = ['https://www.googleapis.com/auth/userinfo.email']
Those who don't know how to add in AUTHENTICATION_BACKENDS, if using the way Omab suggested you need to add newly defined backend in your setting.py file:
AUTHENTICATION_BACKENDS = (
'app_name.file_name.class_name', #ex: google_auth.views.SimplerGoogleOAuth2
# 'social_core.backends.google.GoogleOAuth2', # comment this as no longer used
'django.contrib.auth.backends.ModelBackend',
)
To know how to create the class SimplerGoogleOAuth2 check Omab's answer.

How to get all files of all users using google drive API

I am a 'domain admin' for a google account and would like to:
Get all of the users in my domain and "for each" user, give another user read-access everyone's files . I can get each user, but right now I do not understand how to get each users documents.
#!/usr/bin/python
import gdata.docs
import gdata.docs.service
from gdata.apps import client
userNameAtGmailCom = 'domainAdmin#someplace.com'
password = 'mypassword'
personToShareWith = "someGuy#gmail.com"
domain = 'someplace.com'
def login(userNameAtGmailCom, password, personToShareWith, domain):
client = gdata.apps.client.AppsClient(domain=domain)
client.ssl = True
client.ClientLogin(email=userNameAtGmailCom, password=password, source='apps')
all_users = client.RetrieveAllUsers()
for user in all_users.entry:
user_name = user.login.user_name
print user_name
password = user.login.password
print password
clientDocs = gdata.docs.service.DocsService()
#password always returns 'none'
#therefore I've commented out the 'bad authentication'
#that would happen if the lines below ran
#clientDocs.ClientLogin(user_name, password)
#documents_feed = clientDocs.GetDocumentListFeed()
#for document_entry in documents_feed.entry:
#print document_entry.title.text
#scope = gdata.docs.Scope(value=personToShareWith, type='user')
#role = gdata.docs.Role(value='reader')
#acl_entry = gdata.docs.DocumentListAclEntry(scope=scope, role=role)
#created_acl_entry = client.Post(acl_entry, document_entry.GetAclLink().href, converter=gdata.docs.DocumentListAclEntryFromString)
login(userNameAtGmailCom, password, personToShareWith, domain)
You should use the Google Drive API and use service accounts to perform Google Apps domain-wide delegation of authority:
https://developers.google.com/drive/delegation