forked from shaba/openuds
Updated LDAP REGEX to be compatible with python2 & pytohn3
This commit is contained in:
parent
789084d3ed
commit
8a06132b1a
@ -38,6 +38,7 @@ from uds.core.ui.UserInterface import gui
|
|||||||
from uds.core import auths
|
from uds.core import auths
|
||||||
from uds.core.auths.Exceptions import AuthenticatorException
|
from uds.core.auths.Exceptions import AuthenticatorException
|
||||||
from uds.core.util import tools
|
from uds.core.util import tools
|
||||||
|
from uds.core.util import ldaputil
|
||||||
|
|
||||||
import six
|
import six
|
||||||
import ldap
|
import ldap
|
||||||
@ -45,7 +46,7 @@ import ldap.filter
|
|||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
__updated__ = '2018-01-15'
|
__updated__ = '2018-01-24'
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -145,7 +146,7 @@ class RegexLdap(auths.Authenticator):
|
|||||||
attr = line[:equalPos]
|
attr = line[:equalPos]
|
||||||
else:
|
else:
|
||||||
attr = line
|
attr = line
|
||||||
res.append(tools.b2(attr))
|
res.append(attr)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def __processField(self, field, attributes):
|
def __processField(self, field, attributes):
|
||||||
@ -167,10 +168,8 @@ class RegexLdap(auths.Authenticator):
|
|||||||
|
|
||||||
logger.debug('Pattern: {0}'.format(pattern))
|
logger.debug('Pattern: {0}'.format(pattern))
|
||||||
|
|
||||||
for vv in val:
|
for v in val:
|
||||||
try:
|
try:
|
||||||
v = tools.b2(vv)
|
|
||||||
logger.debug('v, vv: {}, {}'.format(v, vv))
|
|
||||||
srch = re.search(pattern, v, re.IGNORECASE)
|
srch = re.search(pattern, v, re.IGNORECASE)
|
||||||
logger.debug("Found against {0}: {1} ".format(v, srch.groups()))
|
logger.debug("Found against {0}: {1} ".format(v, srch.groups()))
|
||||||
if srch is None:
|
if srch is None:
|
||||||
@ -178,6 +177,7 @@ class RegexLdap(auths.Authenticator):
|
|||||||
res.append(''.join(srch.groups()))
|
res.append(''.join(srch.groups()))
|
||||||
except Exception:
|
except Exception:
|
||||||
pass # Ignore exceptions here
|
pass # Ignore exceptions here
|
||||||
|
logger.debug('Res: {}'.format(res))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def valuesDict(self):
|
def valuesDict(self):
|
||||||
@ -225,93 +225,36 @@ class RegexLdap(auths.Authenticator):
|
|||||||
self._groupNameAttr, self._userNameAttr, self._altClass = data[1:]
|
self._groupNameAttr, self._userNameAttr, self._altClass = data[1:]
|
||||||
self._ssl = gui.strToBool(self._ssl)
|
self._ssl = gui.strToBool(self._ssl)
|
||||||
|
|
||||||
def __connection(self, username=None, password=None):
|
def __connection(self):
|
||||||
if self._connection is None or username is not None: # We want this method also to check credentials
|
"""
|
||||||
l = None
|
Tries to connect to ldap. If username is None, it tries to connect using user provided credentials.
|
||||||
cache = False
|
@return: Connection established
|
||||||
try:
|
@raise exception: If connection could not be established
|
||||||
if password is not None:
|
"""
|
||||||
password = password.encode('utf-8')
|
if self._connection is None: # We want this method also to check credentials
|
||||||
|
self._connection = ldaputil.connection(self._username, self._password, self._host, ssl=self._ssl, timeout=self._timeout, debug=False)
|
||||||
|
|
||||||
# ldap.set_option(ldap.OPT_DEBUG_LEVEL, 9)
|
|
||||||
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
|
|
||||||
schema = self._ssl and 'ldaps' or 'ldap'
|
|
||||||
port = self._port != '389' and ':' + self._port or ''
|
|
||||||
uri = "%s://%s%s" % (schema, self._host, port)
|
|
||||||
logger.debug('Ldap uri: {0}'.format(uri))
|
|
||||||
l = ldap.initialize(uri=uri)
|
|
||||||
l.set_option(ldap.OPT_REFERRALS, 0)
|
|
||||||
l.network_timeout = l.timeout = int(self._timeout)
|
|
||||||
l.protocol_version = ldap.VERSION3
|
|
||||||
|
|
||||||
if username is None:
|
|
||||||
cache = True
|
|
||||||
username = self._username
|
|
||||||
password = self._password
|
|
||||||
|
|
||||||
l.simple_bind_s(who=username, cred=password)
|
|
||||||
except ldap.LDAPError as e:
|
|
||||||
str_ = _('Ldap connection error: ')
|
|
||||||
if hasattr(e, 'message') and isinstance(e.message, dict):
|
|
||||||
str_ += ', '.join((e.message.get('info', ''), e.message.get('desc')))
|
|
||||||
else:
|
|
||||||
str_ += six.text_type(e)
|
|
||||||
raise Exception(str_)
|
|
||||||
if cache is True:
|
|
||||||
self._connection = l
|
|
||||||
else:
|
|
||||||
return l # Do not cache nor overwrite "global" connection
|
|
||||||
return self._connection
|
return self._connection
|
||||||
|
|
||||||
|
def __connectAs(self, username, password):
|
||||||
|
return ldaputil.connection(username, password, self._host, ssl=self._ssl, timeout=self._timeout, debug=False)
|
||||||
|
|
||||||
def __getUser(self, username):
|
def __getUser(self, username):
|
||||||
username = ldap.filter.escape_filter_chars(tools.b2(username))
|
"""
|
||||||
try:
|
Searchs for the username and returns its LDAP entry
|
||||||
con = self.__connection()
|
@param username: username to search, using user provided parameters at configuration to map search entries.
|
||||||
filter_ = tools.b2('(&(objectClass={})({}={}))'.format(self._userClass, self._userIdAttr, username))
|
@return: None if username is not found, an dictionary of LDAP entry attributes if found.
|
||||||
attrlist = [tools.b2(self._userIdAttr)] + self.__getAttrsFromField(self._userNameAttr) + self.__getAttrsFromField(self._groupNameAttr)
|
@note: Active directory users contains the groups it belongs to in "memberOf" attribute
|
||||||
|
"""
|
||||||
logger.debug('Getuser filter_: {}, attr list: {}'.format(filter_, attrlist))
|
return ldaputil.getFirst(
|
||||||
res = con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE,
|
con=self.__connection(),
|
||||||
filterstr=filter_, attrlist=attrlist, sizelimit=LDAP_RESULT_LIMIT)[0]
|
base=self._ldapBase,
|
||||||
|
objectClass=self._userClass,
|
||||||
logger.debug('Res: {}'.format(res))
|
field=self._userIdAttr,
|
||||||
if res[0] is None:
|
value=username,
|
||||||
return None
|
attributes=[self._userIdAttr] + self.__getAttrsFromField(self._userNameAttr) + self.__getAttrsFromField(self._groupNameAttr),
|
||||||
usr = dict((tools.u2(k.lower()), ['']) for k in attrlist)
|
sizeLimit=LDAP_RESULT_LIMIT
|
||||||
for k, v in six.iteritems(res[1]):
|
)
|
||||||
usr[tools.u2(k.lower())] = list(i.decode('utf8') for i in v)
|
|
||||||
|
|
||||||
usr.update({'dn': res[0], '_id': username})
|
|
||||||
|
|
||||||
# If altClass
|
|
||||||
if self._altClass is not None and self._altClass != '':
|
|
||||||
logger.debug('Has alt class {}'.format(self._altClass))
|
|
||||||
filter_ = tools.b2('(&(objectClass={})({}={}))'.format(self._altClass, self._userIdAttr, username))
|
|
||||||
logger.debug('Get Alternate list filter: {}, attrlist: {}'.format(filter_, attrlist))
|
|
||||||
# Get alternate class objects
|
|
||||||
res = con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE,
|
|
||||||
filterstr=filter_, attrlist=attrlist, sizelimit=LDAP_RESULT_LIMIT)
|
|
||||||
|
|
||||||
for r in res:
|
|
||||||
if r[0] is None:
|
|
||||||
continue
|
|
||||||
logger.debug('*** Item: {}'.format(r))
|
|
||||||
|
|
||||||
for k, v in six.iteritems(r[1]):
|
|
||||||
kl = tools.b2(k.lower())
|
|
||||||
# If already exists the field
|
|
||||||
if kl in usr:
|
|
||||||
# Now append to existing values
|
|
||||||
for x in v:
|
|
||||||
usr[kl].append(x.decode('utf8'))
|
|
||||||
else:
|
|
||||||
usr[kl] = list(i.decode('utf8') for i in v)
|
|
||||||
|
|
||||||
logger.debug('Usr: {0}'.format(usr))
|
|
||||||
return usr
|
|
||||||
except Exception:
|
|
||||||
logger.exception('Exception:')
|
|
||||||
return None
|
|
||||||
|
|
||||||
def __getGroups(self, usr):
|
def __getGroups(self, usr):
|
||||||
return self.__processField(self._groupNameAttr, usr)
|
return self.__processField(self._groupNameAttr, usr)
|
||||||
@ -337,7 +280,7 @@ class RegexLdap(auths.Authenticator):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# Let's see first if it credentials are fine
|
# Let's see first if it credentials are fine
|
||||||
self.__connection(usr['dn'], credentials) # Will raise an exception if it can't connect
|
self.__connectAs(usr['dn'], credentials) # Will raise an exception if it can't connect
|
||||||
|
|
||||||
groupsManager.validate(self.__getGroups(usr))
|
groupsManager.validate(self.__getGroups(usr))
|
||||||
|
|
||||||
@ -406,16 +349,18 @@ class RegexLdap(auths.Authenticator):
|
|||||||
try:
|
try:
|
||||||
con = self.__connection()
|
con = self.__connection()
|
||||||
res = []
|
res = []
|
||||||
for r in con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE, filterstr='(&(objectClass=%s)(%s=%s*))' % (self._userClass, self._userIdAttr, pattern), sizelimit=LDAP_RESULT_LIMIT):
|
for r in ldaputil.getAsDict(
|
||||||
if r[0] is not None: # Must have a dn, we do not accept references to other
|
con=self.__connection(),
|
||||||
dct = {k.lower(): v for k, v in six.iteritems(r[1])}
|
base=self._ldapBase,
|
||||||
logger.debug('R: {0}'.format(dct))
|
ldapFilter='(&(&(objectClass={})({}={}*))(objectCategory=person))'.format(self._userClass, self._userIdAttr, ldaputil.escape(pattern)),
|
||||||
usrId = dct.get(self._userIdAttr.lower(), '')
|
attrList=None, # All attrs
|
||||||
usrId = type(usrId) == list and usrId[0] or usrId
|
sizeLimit=LDAP_RESULT_LIMIT
|
||||||
res.append({
|
):
|
||||||
'id': usrId,
|
logger.debug('R: {0}'.format(r))
|
||||||
'name': self.__getUserRealName(dct)
|
res.append({
|
||||||
})
|
'id': r.get(self._userIdAttr.lower(), '')[0],
|
||||||
|
'name': self.__getUserRealName(r)
|
||||||
|
})
|
||||||
logger.debug(res)
|
logger.debug(res)
|
||||||
return res
|
return res
|
||||||
except Exception:
|
except Exception:
|
||||||
|
@ -94,12 +94,15 @@ def getAsDict(con, base, ldapFilter, attrList, sizeLimit, scope=ldap.SCOPE_SUBTR
|
|||||||
"""
|
"""
|
||||||
logger.debug('Filter: {}, attr list: {}'.format(ldapFilter, attrList))
|
logger.debug('Filter: {}, attr list: {}'.format(ldapFilter, attrList))
|
||||||
|
|
||||||
|
if attrList is not None:
|
||||||
|
attrList = [tools.b2(i) for i in attrList]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# On python2, attrs and search string is str (not unicode), in 3, str (not bytes)
|
# On python2, attrs and search string is str (not unicode), in 3, str (not bytes)
|
||||||
res = con.search_ext_s(base,
|
res = con.search_ext_s(base,
|
||||||
scope=scope,
|
scope=scope,
|
||||||
filterstr=tools.b2(ldapFilter),
|
filterstr=tools.b2(ldapFilter),
|
||||||
attrlist=[tools.b2(i) for i in attrList],
|
attrlist=attrList,
|
||||||
sizelimit=sizeLimit
|
sizelimit=sizeLimit
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -111,11 +114,11 @@ def getAsDict(con, base, ldapFilter, attrList, sizeLimit, scope=ldap.SCOPE_SUBTR
|
|||||||
continue # Skip None entities
|
continue # Skip None entities
|
||||||
|
|
||||||
# Convert back attritutes to test_type ONLY on python2
|
# Convert back attritutes to test_type ONLY on python2
|
||||||
dct = dict((k, ['']) for k in attrList)
|
dct = tools.CaseInsensitiveDict((k, ['']) for k in attrList) if attrList is not None else tools.CaseInsensitiveDict()
|
||||||
|
|
||||||
# Convert back result fields to str
|
# Convert back result fields to str
|
||||||
for k, v in six.iteritems(r[1]):
|
for k, v in six.iteritems(r[1]):
|
||||||
dct[tools.u2(k)] = list(i.decode('utf8') for i in v)
|
dct[tools.u2(k)] = list(i.decode('utf8', errors='replace') for i in v)
|
||||||
|
|
||||||
dct.update({'dn': r[0]})
|
dct.update({'dn': r[0]})
|
||||||
|
|
||||||
@ -125,7 +128,7 @@ def getAsDict(con, base, ldapFilter, attrList, sizeLimit, scope=ldap.SCOPE_SUBTR
|
|||||||
logger.exception('Exception:')
|
logger.exception('Exception:')
|
||||||
|
|
||||||
|
|
||||||
def getFirst(con, base, objectClass, field, value, attributes, sizeLimit=50):
|
def getFirst(con, base, objectClass, field, value, attributes=None, sizeLimit=50):
|
||||||
"""
|
"""
|
||||||
Searchs for the username and returns its LDAP entry
|
Searchs for the username and returns its LDAP entry
|
||||||
@param username: username to search, using user provided parameters at configuration to map search entries.
|
@param username: username to search, using user provided parameters at configuration to map search entries.
|
||||||
|
@ -42,7 +42,7 @@ import sys
|
|||||||
import os
|
import os
|
||||||
import six
|
import six
|
||||||
|
|
||||||
__updated__ = '2018-01-15'
|
__updated__ = '2018-01-24'
|
||||||
|
|
||||||
|
|
||||||
class DictAsObj(object):
|
class DictAsObj(object):
|
||||||
@ -65,6 +65,50 @@ class DictAsObj(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CaseInsensitiveDict(dict):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _k(cls, key):
|
||||||
|
return key.lower() if isinstance(key, six.text_type) else key
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(CaseInsensitiveDict, self).__init__(*args, **kwargs)
|
||||||
|
self._convert_keys()
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
return super(CaseInsensitiveDict, self).__getitem__(self.__class__._k(key))
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
super(CaseInsensitiveDict, self).__setitem__(self.__class__._k(key), value)
|
||||||
|
|
||||||
|
def __delitem__(self, key):
|
||||||
|
return super(CaseInsensitiveDict, self).__delitem__(self.__class__._k(key))
|
||||||
|
|
||||||
|
def __contains__(self, key):
|
||||||
|
return super(CaseInsensitiveDict, self).__contains__(self.__class__._k(key))
|
||||||
|
|
||||||
|
def has_key(self, key):
|
||||||
|
return super(CaseInsensitiveDict, self).has_key(self.__class__._k(key))
|
||||||
|
|
||||||
|
def pop(self, key, *args, **kwargs):
|
||||||
|
return super(CaseInsensitiveDict, self).pop(self.__class__._k(key), *args, **kwargs)
|
||||||
|
|
||||||
|
def get(self, key, *args, **kwargs):
|
||||||
|
return super(CaseInsensitiveDict, self).get(self.__class__._k(key), *args, **kwargs)
|
||||||
|
|
||||||
|
def setdefault(self, key, *args, **kwargs):
|
||||||
|
return super(CaseInsensitiveDict, self).setdefault(self.__class__._k(key), *args, **kwargs)
|
||||||
|
|
||||||
|
def update(self, E={}, **F):
|
||||||
|
super(CaseInsensitiveDict, self).update(self.__class__(E))
|
||||||
|
super(CaseInsensitiveDict, self).update(self.__class__(**F))
|
||||||
|
|
||||||
|
def _convert_keys(self):
|
||||||
|
for k in list(self.keys()):
|
||||||
|
v = super(CaseInsensitiveDict, self).pop(k)
|
||||||
|
self.__setitem__(k, v)
|
||||||
|
|
||||||
|
|
||||||
def packageRelativeFile(moduleName, fileName):
|
def packageRelativeFile(moduleName, fileName):
|
||||||
"""
|
"""
|
||||||
Helper to get image path from relative to a module.
|
Helper to get image path from relative to a module.
|
||||||
|
File diff suppressed because one or more lines are too long
@ -362,8 +362,8 @@ module.exports = function (grunt) {
|
|||||||
dist: [
|
dist: [
|
||||||
//'babel',
|
//'babel',
|
||||||
'sass',
|
'sass',
|
||||||
'imagemin',
|
//'imagemin',
|
||||||
'svgmin'
|
//'svgmin'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^6.0.2",
|
"autoprefixer": "^6.7.7",
|
||||||
"grunt": "^0.4.5",
|
"grunt": "^0.4.5",
|
||||||
"grunt-babel": "^5.0.0",
|
"grunt-babel": "^5.0.0",
|
||||||
"grunt-browser-sync": "^2.1.2",
|
"grunt-browser-sync": "^2.2.0",
|
||||||
"grunt-concurrent": "^1.0.0",
|
"grunt-concurrent": "^1.0.0",
|
||||||
"grunt-contrib-clean": "^0.6.0",
|
"grunt-contrib-clean": "^0.6.0",
|
||||||
"grunt-contrib-concat": "^0.5.1",
|
"grunt-contrib-concat": "^0.5.1",
|
||||||
"grunt-contrib-copy": "^0.8.0",
|
"grunt-contrib-copy": "^0.8.2",
|
||||||
"grunt-contrib-cssmin": "^0.12.2",
|
"grunt-contrib-cssmin": "^0.12.2",
|
||||||
"grunt-contrib-htmlmin": "^0.4.0",
|
"grunt-contrib-htmlmin": "^0.4.0",
|
||||||
"grunt-contrib-imagemin": "^0.9.3",
|
"grunt-contrib-imagemin": "^0.9.3",
|
||||||
"grunt-contrib-uglify": "^0.8.0",
|
"grunt-contrib-uglify": "^0.8.0",
|
||||||
"grunt-contrib-watch": "^0.6.1",
|
"grunt-contrib-watch": "^0.6.1",
|
||||||
"grunt-eslint": "^17.0.0",
|
"grunt-eslint": "^17.3.2",
|
||||||
"grunt-filerev": "^2.2.0",
|
"grunt-filerev": "^2.2.0",
|
||||||
"grunt-mocha": "^0.4.12",
|
"grunt-mocha": "^0.4.15",
|
||||||
"grunt-modernizr": "^0.6.0",
|
"grunt-modernizr": "^0.6.1",
|
||||||
"grunt-newer": "^1.1.0",
|
"grunt-newer": "^1.3.0",
|
||||||
"grunt-postcss": "^0.6.0",
|
"grunt-postcss": "^0.6.0",
|
||||||
"grunt-sass": "^1.0.0",
|
"grunt-sass": "^1.2.1",
|
||||||
"grunt-svgmin": "^2.0.1",
|
"grunt-svgmin": "^2.0.1",
|
||||||
"grunt-usemin": "^3.0.0",
|
"grunt-usemin": "^3.0.0",
|
||||||
"grunt-wiredep": "^2.0.0",
|
"grunt-wiredep": "^2.0.0",
|
||||||
"jit-grunt": "^0.9.1",
|
"jit-grunt": "^0.9.1",
|
||||||
"time-grunt": "^1.1.0"
|
"time-grunt": "^1.4.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user