forked from shaba/openuds
* Added label access to Form Field (admin)
* Added Cache purge method for owners * Added improved LDAP Regex Auth * Added to only process USABLE machines at worker for never getted machines * Added check for port on server for tunnelers
This commit is contained in:
parent
3d6d478aed
commit
6be8a54af3
@ -34,7 +34,7 @@
|
||||
'''
|
||||
from django.utils.translation import ugettext_noop as _
|
||||
from uds.core.ui.UserInterface import gui
|
||||
from uds.core.auths import Authenticator
|
||||
from uds.core import auths
|
||||
import ldap, re
|
||||
|
||||
import logging
|
||||
@ -44,7 +44,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
LDAP_RESULT_LIMIT = 50
|
||||
|
||||
class RegexLdap(Authenticator):
|
||||
class RegexLdap(auths.Authenticator):
|
||||
|
||||
host = gui.TextField(length=64, label = _('Host'), order = 1, tooltip = _('VMWare VC Server IP or Hostname'), required = True)
|
||||
port = gui.NumericField(length=5, label = _('Port'), defvalue = '389', order = 2, tooltip = _('Ldap port (389 for non ssl, 636 for ssl normally'), required = True)
|
||||
@ -55,9 +55,9 @@ class RegexLdap(Authenticator):
|
||||
ldapBase = gui.TextField(length=64, label = _('Base'), order = 7, tooltip = _('Common search base (used for "users" and "groups"'), required = True)
|
||||
userClass = gui.TextField(length=64, label = _('User class'), defvalue = 'posixAccount', order = 8, tooltip = _('Class for LDAP users (normally posixAccount)'), required = True)
|
||||
userIdAttr = gui.TextField(length=64, label = _('User Id Attr'), defvalue = 'uid', order = 9, tooltip = _('Attribute that contains the user id'), required = True)
|
||||
userNameAttr = gui.TextField(length=64, label = _('User Name Attr'), defvalue = 'uid', order = 10, tooltip = _('Attributes that contains the user name (list of comma separated values)'), required = True)
|
||||
groupNameAttr = gui.TextField(length=64, label = _('Group Name Attr'), defvalue = 'cn', order = 11, tooltip = _('Attribute that contains the group name'), required = True)
|
||||
regex = gui.TextField(length=64, label = _('Regular Exp. for groups'), defvalue = '^(.*)', order = 12, tooltip = _('Regular Expression to extract the group name'), required = True)
|
||||
userNameAttr = gui.TextField(length=64, label = _('User Name Attr'), multiline=2, defvalue = 'uid', order = 10, tooltip = _('Attributes that contains the user name (list of comma separated values)'), required = True)
|
||||
groupNameAttr = gui.TextField(length=64, label = _('Group Name Attr'), multiline=2, defvalue = 'cn', order = 11, tooltip = _('Attribute that contains the group name'), required = True)
|
||||
#regex = gui.TextField(length=64, label = _('Regular Exp. for groups'), defvalue = '^(.*)', order = 12, tooltip = _('Regular Expression to extract the group name'), required = True)
|
||||
|
||||
typeName = _('Regex LDAP Authenticator')
|
||||
typeType = 'RegexLdapAuthenticator'
|
||||
@ -78,6 +78,10 @@ class RegexLdap(Authenticator):
|
||||
def __init__(self, dbAuth, environment, values = None):
|
||||
super(RegexLdap, self).__init__(dbAuth, environment, values)
|
||||
if values != None:
|
||||
self.__validateField(values['userNameAttr'], str(self.userNameAttr.label))
|
||||
self.__validateField(values['userIdAttr'], str(self.userIdAttr.label))
|
||||
self.__validateField(values['groupNameAttr'], str(self.groupNameAttr.label))
|
||||
|
||||
self._host = values['host']
|
||||
self._port = values['port']
|
||||
self._ssl = gui.strToBool(values['ssl'])
|
||||
@ -88,12 +92,9 @@ class RegexLdap(Authenticator):
|
||||
self._userClass = values['userClass']
|
||||
self._userIdAttr = values['userIdAttr']
|
||||
self._groupNameAttr = values['groupNameAttr']
|
||||
self._regex = values['regex']
|
||||
#self._regex = values['regex']
|
||||
self._userNameAttr = values['userNameAttr']
|
||||
try:
|
||||
re.search(self._regex, '')
|
||||
except:
|
||||
raise Authenticator.ValidationException(_('Invalid regular expression'))
|
||||
|
||||
else:
|
||||
self._host = None
|
||||
self._port = None
|
||||
@ -105,34 +106,85 @@ class RegexLdap(Authenticator):
|
||||
self._userClass = None
|
||||
self._userIdAttr = None
|
||||
self._groupNameAttr = None
|
||||
self._regex = None
|
||||
#self._regex = None
|
||||
self._userNameAttr = None
|
||||
self._connection = None
|
||||
|
||||
def __validateField(self, field, fieldLabel):
|
||||
'''
|
||||
Validates the multi line fields refering to attributes
|
||||
'''
|
||||
import re
|
||||
for line in field.splitlines():
|
||||
if line.find('=') != -1:
|
||||
attr, pattern = line.split('=')[0:2]
|
||||
if pattern.find('(') == -1:
|
||||
pattern = '(' + pattern + ')'
|
||||
try:
|
||||
re.search(pattern, '')
|
||||
except:
|
||||
raise auths.Authenticator.ValidationException('Invalid pattern in {0}: {1}'.format(fieldLabel, line))
|
||||
|
||||
|
||||
def __processField(self, field, attributes):
|
||||
import re
|
||||
res = []
|
||||
for line in field.splitlines():
|
||||
if line.find('=') != -1:
|
||||
attr, pattern = line.split('=')[0:2]
|
||||
attr = attr.lower()
|
||||
# if pattern do not have groups, define one with full re
|
||||
if pattern.find('(') == -1:
|
||||
pattern = '(' + pattern + ')'
|
||||
val = attributes.get(attr, [])
|
||||
|
||||
for v in val:
|
||||
try:
|
||||
logger.debug('Pattern: {0}'.format(pattern))
|
||||
srch = re.search(pattern, v)
|
||||
logger.debug(srch)
|
||||
if srch is None:
|
||||
continue
|
||||
res.append(''.join(srch.groups()))
|
||||
except Exception as e:
|
||||
logger.warn('Invalid regular expression')
|
||||
logger.debug(e)
|
||||
break
|
||||
else:
|
||||
res += attributes.get(line, [])
|
||||
return res
|
||||
|
||||
def valuesDict(self):
|
||||
return { 'host' : self._host, 'port' : self._port, 'ssl' : gui.boolToStr(self._ssl),
|
||||
'username' : self._username, 'password' : self._password, 'timeout' : self._timeout,
|
||||
'ldapBase' : self._ldapBase, 'userClass' : self._userClass,
|
||||
'userIdAttr' : self._userIdAttr, 'groupNameAttr' : self._groupNameAttr, 'regex' : self._regex,
|
||||
'userIdAttr' : self._userIdAttr, 'groupNameAttr' : self._groupNameAttr,
|
||||
'userNameAttr' : self._userNameAttr
|
||||
}
|
||||
|
||||
def __str__(self):
|
||||
return "Ldap Auth: {0}:{1}@{2}:{3}, base = {4}, userClass = {5}, userIdAttr = {6}, groupNameAttr = {7}, reg.ex. = {8}, userName attr = {9}".format(
|
||||
self._username, self._password, self._host, self._port, self._ldapBase, self._userClass, self._userIdAttr, self._groupNameAttr, self._regex,
|
||||
return "Ldap Auth: {0}:{1}@{2}:{3}, base = {4}, userClass = {5}, userIdAttr = {6}, groupNameAttr = {7}, userName attr = {8}".format(
|
||||
self._username, self._password, self._host, self._port, self._ldapBase, self._userClass, self._userIdAttr, self._groupNameAttr,
|
||||
self._userNameAttr)
|
||||
|
||||
def marshal(self):
|
||||
return str.join('\t', ['v1',
|
||||
return str.join('\t', ['v2',
|
||||
self._host, self._port, gui.boolToStr(self._ssl), self._username, self._password, self._timeout,
|
||||
self._ldapBase, self._userClass, self._userIdAttr, self._groupNameAttr, self._regex, self._userNameAttr ])
|
||||
self._ldapBase, self._userClass, self._userIdAttr, self._groupNameAttr, self._userNameAttr ])
|
||||
|
||||
def unmarshal(self, val):
|
||||
data = val.split('\t')
|
||||
if data[0] == 'v1':
|
||||
logger.debug("Data: {0}".format(data[1:]))
|
||||
self._host, self._port, self._ssl, self._username, self._password, self._timeout, self._ldapBase, self._userClass, self._userIdAttr, self._groupNameAttr, self._regex, self._userNameAttr = data[1:]
|
||||
self._ssl = gui.strToBool(self._ssl)
|
||||
self._host, self._port, self._ssl, self._username, self._password, self._timeout, self._ldapBase, self._userClass, self._userIdAttr, self._groupNameAttr, _regex, self._userNameAttr = data[1:]
|
||||
self._ssl = gui.strToBool(self._ssl)
|
||||
self._groupNameAttr = self._groupNameAttr + '=' + _regex
|
||||
self._userNameAttr = '\n'.join(self._userNameAttr.split(','))
|
||||
elif data[0] == 'v2':
|
||||
logger.debug("Data v2: {0}".format(data[1:]))
|
||||
self._host, self._port, self._ssl, self._username, self._password, self._timeout, self._ldapBase, self._userClass, self._userIdAttr, self._groupNameAttr, self._userNameAttr = data[1:]
|
||||
self._ssl = gui.strToBool(self._ssl)
|
||||
|
||||
|
||||
def __connection(self, username = None, password = None):
|
||||
if self._connection is None or username is not None: # We want this method also to check credentials
|
||||
@ -146,6 +198,7 @@ class RegexLdap(Authenticator):
|
||||
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
|
||||
|
||||
@ -173,12 +226,18 @@ class RegexLdap(Authenticator):
|
||||
try:
|
||||
con = self.__connection()
|
||||
filter = '(&(objectClass=%s)(%s=%s))' % (self._userClass, self._userIdAttr, username)
|
||||
attrlist = self._userNameAttr.split(',') + [self._userIdAttr, self._groupNameAttr]
|
||||
attrlist = [self._userIdAttr]
|
||||
for i in self._userNameAttr.split('\n'):
|
||||
attrlist.append( i.split('=')[0].lower() )
|
||||
for i in self._groupNameAttr.split('\n'):
|
||||
attrlist.append( i.split('=')[0].lower() )
|
||||
|
||||
logger.debug('Getuser filter: {0}, attr list: {1}'.format(filter, attrlist))
|
||||
res = con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE,
|
||||
filterstr = filter, attrlist = attrlist, sizelimit = LDAP_RESULT_LIMIT)[0]
|
||||
usr = dict(( k, '' ) for k in attrlist)
|
||||
usr.update(res[1])
|
||||
dct = { k.lower(): v for k, v in res[1].iteritems() }
|
||||
usr.update(dct)
|
||||
usr.update( {'dn' : res[0], '_id' : username })
|
||||
logger.debug('Usr: {0}'.format(usr))
|
||||
return usr
|
||||
@ -187,26 +246,11 @@ class RegexLdap(Authenticator):
|
||||
return None
|
||||
|
||||
def __getGroups(self, usr):
|
||||
grps = usr[self._groupNameAttr]
|
||||
if type(grps) is not list:
|
||||
grps = [grps]
|
||||
logger.debug("Groups: {0}".format(grps))
|
||||
logger.debug("Re: {0}".format(self._regex))
|
||||
regex = self._regex
|
||||
if regex.find('(') == -1:
|
||||
regex = '(' + regex + ')'
|
||||
rg = re.compile(self._regex)
|
||||
res = []
|
||||
for g in grps:
|
||||
ma = rg.search(g)
|
||||
if ma is not None:
|
||||
for m in ma.groups():
|
||||
res.append(m)
|
||||
logger.debug('Res: {0}'.format(res))
|
||||
return res
|
||||
return self.__processField(self._groupNameAttr, usr)
|
||||
|
||||
def __getUserRealName(self, usr):
|
||||
return ' '.join([ (type(usr.get(id_, '')) is list and ' '.join(( str(k) for k in usr.get(id_, ''))) or str(usr.get(id_, ''))) for id_ in self._userNameAttr.split(',') ]).strip()
|
||||
return ' '.join(self.__processField(self._userNameAttr, usr))
|
||||
#return ' '.join([ (type(usr.get(id_, '')) is list and ' '.join(( str(k) for k in usr.get(id_, ''))) or str(usr.get(id_, ''))) for id_ in self._userNameAttr.split(',') ]).strip()
|
||||
|
||||
def authenticate(self, username, credentials, groupsManager):
|
||||
'''
|
||||
@ -299,10 +343,14 @@ class RegexLdap(Authenticator):
|
||||
con = self.__connection()
|
||||
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):
|
||||
usrId = r[1].get(self._userIdAttr, '')
|
||||
usrId = type(usrId) == list and usrId[0] or usrId
|
||||
res.append( { 'id' : usrId,
|
||||
'name' : self.__getUserRealName(r[1]) } )
|
||||
if r[0] is not None: # Must have a dn, we do not accept references to other
|
||||
dct = { k.lower(): v for k, v in r[1].iteritems() }
|
||||
logger.debug('R: {0}'.format(dct))
|
||||
usrId = dct.get(self._userIdAttr.lower(), '')
|
||||
usrId = type(usrId) == list and usrId[0] or usrId
|
||||
res.append( { 'id' : usrId,
|
||||
'name' : self.__getUserRealName(dct) } )
|
||||
logger.debug(res)
|
||||
return res
|
||||
except Exception, e:
|
||||
logger.exception("Exception: ")
|
||||
@ -336,40 +384,26 @@ class RegexLdap(Authenticator):
|
||||
# If found 1 or more, all right
|
||||
pass
|
||||
|
||||
try:
|
||||
if len(con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE, filterstr = '(objectClass=%s)' % self._groupClass, sizelimit=1)) == 1:
|
||||
raise Exception()
|
||||
return [False, _('Ldap group class seems to be incorrect (no group found by that class)')]
|
||||
except Exception, e:
|
||||
# If found 1 or more, all right
|
||||
pass
|
||||
|
||||
try:
|
||||
if len(con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE, filterstr = '(%s=*)' % self._userIdAttr, sizelimit=1)) == 1:
|
||||
raise Exception()
|
||||
return [False, _('Ldap user id attribute seems to be incorrect (no user found by that attribute)')]
|
||||
except Exception, e:
|
||||
# If found 1 or more, all right
|
||||
pass
|
||||
|
||||
try:
|
||||
if self._groupNameAttr == 'dn':
|
||||
raise Exception() # Can't search entries by dn, so this is not possible and dn is always retrieved
|
||||
if len(con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE, filterstr = '(%s=*)' % self._groupNameAttr, sizelimit=1)) == 1:
|
||||
raise Exception()
|
||||
return [False, _('Ldap group id attribute seems to be incorrect (no group found by that attribute)')]
|
||||
except Exception, e:
|
||||
# If found 1 or more, all right
|
||||
pass
|
||||
|
||||
# Now test objectclass and attribute of users
|
||||
try:
|
||||
if len(con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE, filterstr = '(&(objectClass=%s)(%s=*))' % (self._userClass, self._userIdAttr), sizelimit=1)) == 1:
|
||||
raise Exception()
|
||||
return [False, _('Ldap user class or user id attr is probably wrong (can\'t find any user with both conditions)')]
|
||||
return [False, _('Ldap user id attr is probably wrong (can\'t find any user with both conditions)')]
|
||||
except Exception as e:
|
||||
# If found 1 or more, all right
|
||||
pass
|
||||
|
||||
for grpNameAttr in self._groupNameAttr.split('\n'):
|
||||
vals = grpNameAttr.split('=')[0]
|
||||
if vals == 'dn':
|
||||
continue
|
||||
try:
|
||||
if len(con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE, filterstr = '(%s=*)' % vals, sizelimit=1)) == 1:
|
||||
continue
|
||||
except:
|
||||
continue
|
||||
return [False, _('Ldap group id attribute seems to be incorrect (no group found by that attribute)')]
|
||||
|
||||
|
||||
# Now try to test regular expression to see if it matches anything (
|
||||
try:
|
||||
|
@ -278,6 +278,10 @@ class gui(object):
|
||||
'''
|
||||
self._data['defvalue'] = defValue
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
return self._data['label']
|
||||
|
||||
|
||||
class TextField(InputField):
|
||||
'''
|
||||
|
@ -76,6 +76,9 @@ class Cache(object):
|
||||
dbCache.objects.get(pk=key).delete()
|
||||
except dbCache.DoesNotExist:
|
||||
logger.debug('key not found')
|
||||
|
||||
def clean(self):
|
||||
Cache.delete(self._owner)
|
||||
|
||||
@transaction.autocommit
|
||||
def put(self, skey, value, validity = None):
|
||||
|
@ -198,7 +198,8 @@ class GlobalConfig(object):
|
||||
# Idle time before closing session on admin
|
||||
ADMIN_IDLE_TIME = Config.section(GLOBAL_SECTION).value('adminIdleTime', '14400') # Defaults to 4 hous
|
||||
# Time betwen checks of unused services by os managers
|
||||
# Unused services will be invoked for every machine assigned but not in use AND that has been assigned at least 1/2 of this time
|
||||
# Unused services will be invoked for every machine assigned but not in use AND that has been assigned at least this time
|
||||
# (only if os manager asks for this characteristic)
|
||||
CHECK_UNUSED_TIME = Config.section(GLOBAL_SECTION).value('checkUnusedTime', '631') # Defaults to 10 minutes
|
||||
# Default CSS Used
|
||||
CSS = Config.section(GLOBAL_SECTION).value('css', settings.STATIC_URL + 'css/uds.css')
|
||||
@ -219,13 +220,10 @@ class GlobalConfig(object):
|
||||
CUSTOM_HTML_LOGIN = Config.section(GLOBAL_SECTION).valueLong('customHtmlLogin', '')
|
||||
# Maximum logs per user service
|
||||
MAX_LOGS_PER_ELEMENT = Config.section(GLOBAL_SECTION).value('maxLogPerElement', '100')
|
||||
|
||||
# Time to restrain a deployed service in case it gives some error at some point
|
||||
RESTRAINT_TIME = Config.section(GLOBAL_SECTION).value('restrainTime', '600')
|
||||
|
||||
# Statistics duration, in days
|
||||
STATS_DURATION = Config.section(GLOBAL_SECTION).value('statsDuration', '365')
|
||||
|
||||
# If disallow login using /login url, and must go to an authenticator
|
||||
DISALLOW_GLOBAL_LOGIN = Config.section(GLOBAL_SECTION).value('disallowGlobalLogin', '0')
|
||||
|
||||
|
@ -48,11 +48,11 @@ class AssignedAndUnused(Job):
|
||||
super(AssignedAndUnused,self).__init__(environment)
|
||||
|
||||
def run(self):
|
||||
since_state = getSqlDatetime() - timedelta( seconds = GlobalConfig.CHECK_UNUSED_TIME.getInt() )
|
||||
for ds in DeployedService.objects.all():
|
||||
osm = ds.osmanager.getInstance()
|
||||
if osm.processUnusedMachines is True:
|
||||
logger.debug('Processing unused services for {0}'.format(osm))
|
||||
since_state = getSqlDatetime() - timedelta( seconds = GlobalConfig.CHECK_UNUSED_TIME.getInt() )
|
||||
for us in ds.assignedUserServices().select_for_update().filter(in_use=False,since_state__lt=since_state):
|
||||
for us in ds.assignedUserServices().select_for_update().filter(in_use=False,since_state__lt=since_state, state=State.USABLE):
|
||||
logger.debug('Found unused assigned service {0}'.format(us))
|
||||
osm.processUnused(us)
|
@ -49,6 +49,8 @@ class TSRDPTransport(Transport):
|
||||
def __init__(self, environment, values = None):
|
||||
super(TSRDPTransport, self).__init__(environment, values)
|
||||
if values != None:
|
||||
if values['tunnelServer'].find(':') == -1:
|
||||
raise Transport.ValidationException(_('Must use HOST:PORT in Tunnel Server Field'))
|
||||
self._tunnelServer = values['tunnelServer']
|
||||
self._tunnelCheckServer = values['tunnelCheckServer']
|
||||
self._useEmptyCreds = gui.strToBool(values['useEmptyCreds'])
|
||||
@ -59,6 +61,7 @@ class TSRDPTransport(Transport):
|
||||
self._allowPrinters = gui.strToBool(values['allowPrinters'])
|
||||
self._allowDrives = gui.strToBool(values['allowDrives'])
|
||||
self._allowSerials = gui.strToBool(values['allowSerials'])
|
||||
|
||||
else:
|
||||
self._tunnelServer = ''
|
||||
self._tunnelCheckServer = ''
|
||||
|
@ -99,6 +99,8 @@ class TSNXTransport(Transport):
|
||||
def __init__(self, environment, values = None):
|
||||
super(TSNXTransport, self).__init__(environment, values)
|
||||
if values != None:
|
||||
if values['tunnelServer'].find(':') == -1:
|
||||
raise Transport.ValidationException(_('Must use HOST:PORT in Tunnel Server Field'))
|
||||
self._tunnelServer = values['tunnelServer']
|
||||
self._tunnelCheckServer = values['tunnelCheckServer']
|
||||
self._useEmptyCreds = gui.strToBool(values['useEmptyCreds'])
|
||||
|
@ -190,6 +190,9 @@ def modifyAuthenticator(credentials, id, data):
|
||||
auth.save()
|
||||
except auths.Authenticator.ValidationException as e:
|
||||
raise ValidationException(str(e))
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
raise ValidationException(str(e))
|
||||
|
||||
return True
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user