1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186 | # ----------------------------------------
# ActiveDirectoryBackend
# ----------------------------------------
#
# Created new snippet:
# http://www.djangosnippets.org/snippets/edit/1397/
# based on:
# http://www.djangosnippets.org/snippets/501/
# similar snippet (ldaps:// - secured) is on:
# http://www.djangosnippets.org/snippets/901/
#
# ---------- settings.py -------------------
#
# # Active directory auth backend setup
# # on win32 check env.vars:
# # USERDNSDOMAIN=EXAMPLE.COM
# # USERDOMAIN=EXAMPLE
# AD_DNS_NAME = 'domaindnsname.local'
# AD_NT4_DOMAIN = 'DOMAINNAME' # This is the NT4/Samba domain name
# AD_SEARCH_DN = 'dc=domaindnsname,dc=local'
# AD_LDAP_PORT = 389
#
# AUTHENTICATION_BACKENDS = (
# 'company.django.auth.ActiveDirectoryBackend',
# # if you want to have default django backend too
# 'django.contrib.auth.backends.ModelBackend',
# )
#
import ldap
import logging
from django.contrib.auth.models import User
from django.contrib.auth.backends import ModelBackend
from django.conf import settings
logger = logging.getLogger()
# -------------------------------------------
# general function - belongs to utils
def get_exc_str(bClear=False):
import sys, traceback
x=sys.exc_info()
if not x[0]:
return "No py exception"
out="%s/%s/%s" % (str(x[0]), str(traceback.extract_tb(x[2])), str(x[1]))
if bClear:
sys.exc_clear()
return out
# --------------------------------------------
class ADUser(object):
# class level, makes "operational error" problem by search occurs less
ldap_connection = None
AD_SEARCH_FIELDS = ['mail','givenName','sn','sAMAccountName']
@classmethod
def get_ldap_url(cls):
return 'ldap://%s:%s' % (settings.AD_DNS_NAME, settings.AD_LDAP_PORT)
def __init__(self, username):
self.username = username
self.user_bind_name = "%s@%s" % (self.username, settings.AD_NT4_DOMAIN)
self.is_bound = False
self.has_data = False
self.first_name = None
self.last_name = None
self.email = None
def connect(self, password):
had_connection = ADUser.ldap_connection is not None
ret = self._connect(password)
# WORKAROUND: for invalid connection
if not ret and had_connection and ADUser.ldap_connection is None:
logger.warning("AD reset connection - invalid connection, try again with new connection")
ret = self._connect(password)
return ret
def _connect(self, password):
if not password:
return False # Disallowing null or blank string as password
try:
if ADUser.ldap_connection is None:
logger.info("AD auth backend ldap connecting")
ADUser.ldap_connection = ldap.initialize(self.get_ldap_url())
assert self.ldap_connection==ADUser.ldap_connection # python won't do that ;)
self.ldap_connection.simple_bind_s(self.user_bind_name,password)
self.is_bound = True
except Exception, e:
if str(e.message).find("connection invalid")>=0:
logger.warning("AD reset connection - it looks like invalid: %s (%s)" % (str(e), get_exc_str()))
ADUser.ldap_connection = None
else:
logger.error("AD auth backend ldap - probably bad credentials: %s (%s)" % (str(e), get_exc_str()))
return False
return True
def disconnect(self):
if self.is_bound:
logger.info("AD auth backend ldap unbind")
self.ldap_connection.unbind_s()
self.is_bound=False
def get_data(self):
try:
assert self.ldap_connection
# NOTE: Something goes wrong in my case - ignoring this until solved :(
# {'info': '00000000: LdapErr: DSID-0C090627, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, vece', 'desc': 'Operations error'}
res = self.ldap_connection.search_ext_s(settings.AD_SEARCH_DN,
ldap.SCOPE_SUBTREE,
"sAMAccountName=%s" % self.username,
self.AD_SEARCH_FIELDS)
self.disconnect()
if not res:
logger.error("AD auth ldap backend error by searching %s. No result." % settings.AD_SEARCH_DN)
return False
assert len(res)>=1, "Result should contain at least one element: %s" % res
result = res[0][1]
except Exception, e:
self.disconnect()
logger.error("AD auth backend error by fetching ldap data: %s (%s)" % (str(e), get_exc_str()))
return False
try:
self.first_name = None
if result.has_key('givenName'):
self.first_name = result['givenName'][0]
self.last_name = None
if result.has_key('sn'):
self.last_name = result['sn'][0]
self.email = None
if result.has_key('mail'):
self.email = result['mail'][0]
self.has_data = True
except Exception, e:
logger.error("AD auth backend error by reading fetched data: %s (%s)" % (str(e), get_exc_str()))
return False
return True
def __del__(self):
try:
self.disconnect()
except Exception, e:
logger.error("AD auth backend error when disconnecting: %s (%s)" % (str(e), get_exc_str()))
return False
def __str__(self):
return "AdUser(<%s>, connected=%s, is_bound=%s, has_data=%s)" % (self.username, self.ldap_connection is not None, self.is_bound, self.has_data)
# -------------------------------------------
class ActiveDirectoryBackend(ModelBackend):
def authenticate(self,username=None,password=None):
logger.info("AD auth backend for %s" % username)
aduser = ADUser(username)
if not aduser.connect(password):
return None
user = None
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
user = User(username=username, is_staff = False, is_superuser = False)
if not aduser.get_data():
logger.warning("AD auth backend failed when reading data for %s. User detail data won't be updated in User model." % username)
else:
# NOTE: update user data exchange to User model
assert user.username==aduser.username
user.first_name=aduser.first_name
user.last_name=aduser.last_name
user.email=aduser.email
#user.set_password(password)
logger.warning("AD auth backend overwriting auth.User data with data from ldap for %s." % username)
user.save()
logger.info("AD auth backend check passed for %s" % username)
return user
# NOTE: no need to implement get_user - ModelBackend.get_user is all I need
|
Comments
I believe ActiveDirectoryBackend should inherit from django.contrib.auth.backends.ModelBackend, or else permissions will not work.
#
Thanks, now is based on ModelBackend. Other things I did is:
#
where does it store the log file?
#
try it with
ldap.set_option(ldap.OPT_REFERRALS, 0)
right after importing the ldap module.
This will let you override the backend auth.User data with data from ldap.
#
To fix the comment on line 109, I changed my AD port from 389 to 3268
See here: http://www.experts-exchange.com/Software/Server_Software/File_Servers/Active_Directory/Q_26659333.html
#
When you're in the corner and have got no cash to move out from that, you would require to receive the home loans. Because it would help you emphatically. I get term loan every time I need and feel great because of this.
#