More pep8 related fixes, also some refactoring

This commit is contained in:
Adolfo Gómez 2014-02-19 15:17:55 +00:00
parent dab3e26223
commit 89addaf585
24 changed files with 975 additions and 905 deletions

View File

@ -32,6 +32,8 @@
@author: Adolfo Gómez, dkmaster at dkmon dot com @author: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
from __future__ import unicode_literals
from django.utils.translation import ugettext_noop as _ from django.utils.translation import ugettext_noop as _
from uds.core.ui.UserInterface import gui from uds.core.ui.UserInterface import gui
from uds.core import auths from uds.core import auths

View File

@ -36,8 +36,11 @@ from uds.core import auths
import logging import logging
__updated__ = '2014-02-19'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class SampleAuth(auths.Authenticator): class SampleAuth(auths.Authenticator):
''' '''
This class represents a sample authenticator. This class represents a sample authenticator.
@ -63,52 +66,51 @@ class SampleAuth(auths.Authenticator):
the string when it is sent to the administration client. the string when it is sent to the administration client.
''' '''
#: Name of type, used at administration interface to identify this # : Name of type, used at administration interface to identify this
#: authenticator (i.e. LDAP, SAML, ...) # : authenticator (i.e. LDAP, SAML, ...)
#: This string will be translated when provided to admin interface # : This string will be translated when provided to admin interface
#: using ugettext, so you can mark it as "_" at derived classes (using ugettext_noop) # : using ugettext, so you can mark it as "_" at derived classes (using ugettext_noop)
#: if you want so it can be translated. # : if you want so it can be translated.
typeName = _('Sample Authenticator') typeName = _('Sample Authenticator')
#: Name of type used by Managers to identify this type of service # : Name of type used by Managers to identify this type of service
#: We could have used here the Class name, but we decided that the # : We could have used here the Class name, but we decided that the
#: module implementator will be the one that will provide a name that # : module implementator will be the one that will provide a name that
#: will relation the class (type) and that name. # : will relation the class (type) and that name.
typeType = 'SampleAuthenticator' typeType = 'SampleAuthenticator'
#: Description shown at administration level for this authenticator. # : Description shown at administration level for this authenticator.
#: This string will be translated when provided to admin interface # : This string will be translated when provided to admin interface
#: using ugettext, so you can mark it as "_" at derived classes (using ugettext_noop) # : using ugettext, so you can mark it as "_" at derived classes (using ugettext_noop)
#: if you want so it can be translated. # : if you want so it can be translated.
typeDescription = _('Sample dummy authenticator') typeDescription = _('Sample dummy authenticator')
# : Icon file, used to represent this authenticator at administration interface
#: Icon file, used to represent this authenticator at administration interface # : This file should be at same folder as this class is, except if you provide
#: This file should be at same folder as this class is, except if you provide # : your own :py:meth:uds.core.BaseModule.BaseModule.icon method.
#: your own :py:meth:uds.core.BaseModule.BaseModule.icon method.
iconFile = 'auth.png' iconFile = 'auth.png'
#: Mark this authenticator as that the users comes from outside the UDS # : Mark this authenticator as that the users comes from outside the UDS
#: database, that are most authenticator (except Internal DB) # : database, that are most authenticator (except Internal DB)
#: True is the default value, so we do not need it in fact # : True is the default value, so we do not need it in fact
# isExternalSource = True # isExternalSource = True
#: If we need to enter the password for this user when creating a new # : If we need to enter the password for this user when creating a new
#: user at administration interface. Used basically by internal authenticator. # : user at administration interface. Used basically by internal authenticator.
#: False is the default value, so this is not needed in fact # : False is the default value, so this is not needed in fact
#: needsPassword = False # : needsPassword = False
#: Label for username field, shown at administration interface user form. # : Label for username field, shown at administration interface user form.
userNameLabel = _('Fake User') userNameLabel = _('Fake User')
# Label for group field, shown at administration interface user form. # Label for group field, shown at administration interface user form.
groupNameLabel = _('Fake Group') groupNameLabel = _('Fake Group')
#: Definition of this type of authenticator form # : Definition of this type of authenticator form
#: We will define a simple form where we will use a simple # : We will define a simple form where we will use a simple
#: list editor to allow entering a few group names # : list editor to allow entering a few group names
groups = gui.EditableList(label=_('Groups'), values = ['Gods', 'Daemons', 'Mortals']) groups = gui.EditableList(label=_('Groups'), values=['Gods', 'Daemons', 'Mortals'])
def initialize(self, values): def initialize(self, values):
''' '''
@ -192,7 +194,7 @@ class SampleAuth(auths.Authenticator):
:note: groupsManager is an in/out parameter :note: groupsManager is an in/out parameter
''' '''
if username != credentials: # All users with same username and password are allowed if username != credentials: # All users with same username and password are allowed
return False return False
# Now the tricky part. We will make this user belong to groups that contains at leat # Now the tricky part. We will make this user belong to groups that contains at leat
@ -232,22 +234,21 @@ class SampleAuth(auths.Authenticator):
# Here there is a sample, commented out # Here there is a sample, commented out
# In this sample, we will make a list of valid users, and when clicked, # In this sample, we will make a list of valid users, and when clicked,
# it will fill up original form with username and same password, and submit it. # it will fill up original form with username and same password, and submit it.
#res = '' # res = ''
#for u in self.dbAuthenticator().users.all(): # for u in self.dbAuthenticator().users.all():
# res += '<a class="myNames" id="{0}" href="">{0}</a><br/>'.format(u.name) # res += '<a class="myNames" id="{0}" href="">{0}</a><br/>'.format(u.name)
# #
#res += '<script type="text/javascript">$(".myNames").click(function() { ' # res += '<script type="text/javascript">$(".myNames").click(function() { '
#res += '$("#id_user").val(this.id); $("#id_password").val(this.id); $("#loginform").submit(); return false;});</script>' # res += '$("#id_user").val(this.id); $("#id_password").val(this.id); $("#loginform").submit(); return false;});</script>'
#return res # return res
# I know, this is a bit ugly, but this is just a sample :-) # I know, this is a bit ugly, but this is just a sample :-)
res = '<p>Login name: <input id="logname" type="text"/></p>' res = '<p>Login name: <input id="logname" type="text"/></p>'
res +='<p><a href="" onclick="window.location.replace(\'' + self.callbackUrl() + '?user=' res += '<p><a href="" onclick="window.location.replace(\'' + self.callbackUrl() + '?user='
res += '\' + $(\'#logname\').val()); return false;">Login</a></p>' res += '\' + $(\'#logname\').val()); return false;">Login</a></p>'
return res return res
def authCallback(self, parameters, gm): def authCallback(self, parameters, gm):
''' '''
We provide this as a sample of callback for an user. We provide this as a sample of callback for an user.

View File

@ -34,5 +34,8 @@ take care of registering it as provider
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com .. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
from __future__ import unicode_literals
from SampleAuth import SampleAuth from SampleAuth import SampleAuth
__updated__ = '2014-02-19'

View File

@ -32,33 +32,38 @@
@author: Adolfo Gómez, dkmaster at dkmon dot com @author: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
from __future__ import unicode_literals
from django.utils.translation import ugettext_noop as _ from django.utils.translation import ugettext_noop as _
from uds.core.ui.UserInterface import gui from uds.core.ui.UserInterface import gui
from uds.core.auths import Authenticator from uds.core.auths import Authenticator
import ldap
import logging
from uds.core.auths.Exceptions import AuthenticatorException from uds.core.auths.Exceptions import AuthenticatorException
import ldap
import logging
__updated__ = '2014-02-19'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
LDAP_RESULT_LIMIT = 50 LDAP_RESULT_LIMIT = 50
class SimpleLDAPAuthenticator(Authenticator): class SimpleLDAPAuthenticator(Authenticator):
host = gui.TextField(length=64, label = _('Host'), order = 1, tooltip = _('Ldap Server IP or Hostname'), required = True) host = gui.TextField(length=64, label=_('Host'), order=1, tooltip=_('Ldap 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) port = gui.NumericField(length=5, label=_('Port'), defvalue='389', order=2, tooltip=_('Ldap port (389 for non ssl, 636 for ssl normally'), required=True)
ssl = gui.CheckBoxField(label = _('Use SSL'), order = 3, tooltip = _('If checked, will use a ssl connection to ldap (if port is 389, will use in fact port 636)')) ssl = gui.CheckBoxField(label=_('Use SSL'), order=3, tooltip=_('If checked, will use a ssl connection to ldap (if port is 389, will use in fact port 636)'))
username = gui.TextField(length=64, label = _('Ldap User'), order = 4, tooltip = _('Username with read privileges on the base selected'), required = True) username = gui.TextField(length=64, label=_('Ldap User'), order=4, tooltip=_('Username with read privileges on the base selected'), required=True)
password = gui.PasswordField(lenth=32, label = _('Password'), order = 5, tooltip = _('Password of the ldap user'), required = True) password = gui.PasswordField(lenth=32, label=_('Password'), order=5, tooltip=_('Password of the ldap user'), required=True)
timeout = gui.NumericField(length=3, label = _('Timeout'), defvalue = '10', order = 6, tooltip = _('Timeout in seconds of connection to LDAP'), required = True) timeout = gui.NumericField(length=3, label=_('Timeout'), defvalue='10', order=6, tooltip=_('Timeout in seconds of connection to LDAP'), required=True)
ldapBase = gui.TextField(length=64, label = _('Base'), order = 7, tooltip = _('Common search base (used for "users" and "groups"'), required = True) 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) 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) 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) 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)
groupClass = gui.TextField(length=64, label = _('Group class'), defvalue = 'posixGroup', order = 11, tooltip = _('Class for LDAP groups (normally poxisGroup)'), required = True) groupClass = gui.TextField(length=64, label=_('Group class'), defvalue='posixGroup', order=11, tooltip=_('Class for LDAP groups (normally poxisGroup)'), required=True)
groupIdAttr = gui.TextField(length=64, label = _('Group Id Attr'), defvalue = 'cn', order = 12, tooltip = _('Attribute that contains the group id'), required = True) groupIdAttr = gui.TextField(length=64, label=_('Group Id Attr'), defvalue='cn', order=12, tooltip=_('Attribute that contains the group id'), required=True)
memberAttr = gui.TextField(length=64, label = _('Group membership attr'), defvalue = 'memberUid', order = 13, tooltip = _('Attribute of the group that contains the users belonging to it'), required = True) memberAttr = gui.TextField(length=64, label=_('Group membership attr'), defvalue='memberUid', order=13, tooltip=_('Attribute of the group that contains the users belonging to it'), required=True)
typeName = _('SimpleLDAP Authenticator') typeName = _('SimpleLDAP Authenticator')
typeType = 'SimpleLdapAuthenticator' typeType = 'SimpleLdapAuthenticator'
@ -76,7 +81,7 @@ class SimpleLDAPAuthenticator(Authenticator):
# Label for password field # Label for password field
passwordLabel = _("Password") passwordLabel = _("Password")
def __init__(self, dbAuth, environment, values = None): def __init__(self, dbAuth, environment, values=None):
super(SimpleLDAPAuthenticator, self).__init__(dbAuth, environment, values) super(SimpleLDAPAuthenticator, self).__init__(dbAuth, environment, values)
if values != None: if values != None:
self._host = values['host'] self._host = values['host']
@ -91,7 +96,7 @@ class SimpleLDAPAuthenticator(Authenticator):
self._userIdAttr = values['userIdAttr'] self._userIdAttr = values['userIdAttr']
self._groupIdAttr = values['groupIdAttr'] self._groupIdAttr = values['groupIdAttr']
self._memberAttr = values['memberAttr'] self._memberAttr = values['memberAttr']
self._userNameAttr = values['userNameAttr'].replace(' ', '') # Removes white spaces self._userNameAttr = values['userNameAttr'].replace(' ', '') # Removes white spaces
else: else:
self._host = None self._host = None
self._port = None self._port = None
@ -109,12 +114,13 @@ class SimpleLDAPAuthenticator(Authenticator):
self._connection = None self._connection = None
def valuesDict(self): def valuesDict(self):
return { 'host' : self._host, 'port' : self._port, 'ssl' : gui.boolToStr(self._ssl), return {
'username' : self._username, 'password' : self._password, 'timeout' : self._timeout, 'host': self._host, 'port': self._port, 'ssl': gui.boolToStr(self._ssl),
'ldapBase' : self._ldapBase, 'userClass' : self._userClass, 'groupClass' : self._groupClass, 'username': self._username, 'password': self._password, 'timeout': self._timeout,
'userIdAttr' : self._userIdAttr, 'groupIdAttr' : self._groupIdAttr, 'memberAttr' : self._memberAttr, 'ldapBase': self._ldapBase, 'userClass': self._userClass, 'groupClass': self._groupClass,
'userNameAttr' : self._userNameAttr 'userIdAttr': self._userIdAttr, 'groupIdAttr': self._groupIdAttr, 'memberAttr': self._memberAttr,
} 'userNameAttr': self._userNameAttr
}
def __str__(self): def __str__(self):
return "Ldap Auth: {0}:{1}@{2}:{3}, base = {4}, userClass = {5}, groupClass = {6}, userIdAttr = {7}, groupIdAttr = {8}, memberAttr = {9}, userName attr = {10}".format( return "Ldap Auth: {0}:{1}@{2}:{3}, base = {4}, userClass = {5}, groupClass = {6}, userIdAttr = {7}, groupIdAttr = {8}, memberAttr = {9}, userName attr = {10}".format(
@ -124,24 +130,24 @@ class SimpleLDAPAuthenticator(Authenticator):
def marshal(self): def marshal(self):
return str.join('\t', ['v1', return str.join('\t', ['v1',
self._host, self._port, gui.boolToStr(self._ssl), self._username, self._password, self._timeout, self._host, self._port, gui.boolToStr(self._ssl), self._username, self._password, self._timeout,
self._ldapBase, self._userClass, self._groupClass, self._userIdAttr, self._groupIdAttr, self._memberAttr, self._userNameAttr ]) self._ldapBase, self._userClass, self._groupClass, self._userIdAttr, self._groupIdAttr, self._memberAttr, self._userNameAttr])
def unmarshal(self, str): def unmarshal(self, str_):
data = str.split('\t') data = str_.split('\t')
if data[0] == 'v1': if data[0] == 'v1':
logger.debug("Data: {0}".format(data[1:])) logger.debug("Data: {0}".format(data[1:]))
self._host, self._port, self._ssl, self._username, self._password, self._timeout, self._ldapBase, self._userClass, self._groupClass, self._userIdAttr, self._groupIdAttr, self._memberAttr, self._userNameAttr = data[1:] self._host, self._port, self._ssl, self._username, self._password, self._timeout, self._ldapBase, self._userClass, self._groupClass, self._userIdAttr, self._groupIdAttr, self._memberAttr, self._userNameAttr = data[1:]
self._ssl = gui.strToBool(self._ssl) self._ssl = gui.strToBool(self._ssl)
def __connection(self, username = None, password = None): 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 if self._connection is None or username is not None: # We want this method also to check credentials
l = None l = None
cache = False cache = False
try: try:
#ldap.set_option(ldap.OPT_DEBUG_LEVEL, 9) # ldap.set_option(ldap.OPT_DEBUG_LEVEL, 9)
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
schema = self._ssl and 'ldaps' or 'ldap' schema = self._ssl and 'ldaps' or 'ldap'
port = self._port != '389' and ':' + self._port or '' port = self._port != '389' and ':' + self._port or ''
uri = "%s://%s%s" % (schema, self._host, port) uri = "%s://%s%s" % (schema, self._host, port)
logger.debug('Ldap uri: {0}'.format(uri)) logger.debug('Ldap uri: {0}'.format(uri))
l = ldap.initialize(uri=uri) l = ldap.initialize(uri=uri)
@ -153,63 +159,62 @@ class SimpleLDAPAuthenticator(Authenticator):
username = self._username username = self._username
password = self._password password = self._password
l.simple_bind_s(who = username, cred = password) l.simple_bind_s(who=username, cred=password)
except ldap.LDAPError, e: except ldap.LDAPError, e:
str = _('Ldap connection error: ') str_ = _('Ldap connection error: ')
if type(e.message) == dict: if type(e.message) == dict:
str += e.message.has_key('info') and e.message['info'] + ',' or '' str_ += 'info' in e.message and e.message['info'] + ',' or ''
str += e.message.has_key('desc') and e.message['desc'] or '' str_ += 'desc' in e.message and e.message['desc'] or ''
else : else:
str += str(e) str_ += str_(e)
raise Exception(str) raise Exception(str_)
if cache is True: if cache is True:
self._connection = l self._connection = l
else: else:
return l # Do not cache nor overwrite "global" connection return l # Do not cache nor overwrite "global" connection
return self._connection return self._connection
def __getUser(self, username): def __getUser(self, username):
try: try:
con = self.__connection() con = self.__connection()
filter = '(&(objectClass=%s)(%s=%s))' % (self._userClass, self._userIdAttr, username) filter_ = '(&(objectClass=%s)(%s=%s))' % (self._userClass, self._userIdAttr, username)
attrlist = self._userNameAttr.split(',') + [self._userIdAttr] attrlist = self._userNameAttr.split(',') + [self._userIdAttr]
logger.debug('Getuser filter: {0}, attr list: {1}'.format(filter, attrlist)) logger.debug('Getuser filter_: {0}, attr list: {1}'.format(filter_, attrlist))
res = con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE, res = con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE,
filterstr = filter, attrlist = attrlist, sizelimit = LDAP_RESULT_LIMIT)[0] filterstr=filter_, attrlist=attrlist, sizelimit=LDAP_RESULT_LIMIT)[0]
usr = dict(( k, '' ) for k in attrlist) usr = dict((k, '') for k in attrlist)
usr.update(res[1]) usr.update(res[1])
usr.update( {'dn' : res[0], '_id' : username }) usr.update({'dn': res[0], '_id': username})
logger.debug('Usr: {0}'.format(usr)) logger.debug('Usr: {0}'.format(usr))
return usr return usr
except Exception, e: except Exception:
logger.exception('Exception:') logger.exception('Exception:')
return None return None
def __getGroup(self, groupName): def __getGroup(self, groupName):
try: try:
con = self.__connection() con = self.__connection()
filter = '(&(objectClass=%s)(%s=%s))' % (self._groupClass, self._groupIdAttr, groupName) filter_ = '(&(objectClass=%s)(%s=%s))' % (self._groupClass, self._groupIdAttr, groupName)
attrlist = [self._memberAttr] attrlist = [self._memberAttr]
logger.debug('Getgroup filter: {0}, attr list {1}'.format(filter, attrlist)) logger.debug('Getgroup filter_: {0}, attr list {1}'.format(filter_, attrlist))
res = con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE, res = con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE,
filterstr = filter, attrlist = attrlist, sizelimit = LDAP_RESULT_LIMIT)[0] filterstr=filter_, attrlist=attrlist, sizelimit=LDAP_RESULT_LIMIT)[0]
grp = dict(( k, [''] ) for k in attrlist) grp = dict((k, ['']) for k in attrlist)
grp.update(res[1]) grp.update(res[1])
grp.update( {'dn' : res[0], '_id' : groupName }) grp.update({'dn': res[0], '_id': groupName})
logger.debug('Group: {0}'.format(grp)) logger.debug('Group: {0}'.format(grp))
return grp return grp
except Exception, e: except Exception:
logger.exception('Exception:') logger.exception('Exception:')
return None return None
def __getGroups(self, usr): def __getGroups(self, usr):
try: try:
con = self.__connection() con = self.__connection()
filter = '(&(objectClass=%s)(|(%s=%s)(%s=%s)))' % (self._groupClass, self._memberAttr, usr['_id'], self._memberAttr, usr['dn']) filter_ = '(&(objectClass=%s)(|(%s=%s)(%s=%s)))' % (self._groupClass, self._memberAttr, usr['_id'], self._memberAttr, usr['dn'])
logger.debug('Filter: {0}'.format(filter)) logger.debug('Filter: {0}'.format(filter_))
res = con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE, filterstr = filter, attrlist = [self._groupIdAttr], res = con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE, filterstr=filter_, attrlist=[self._groupIdAttr],
sizelimit = LDAP_RESULT_LIMIT) sizelimit=LDAP_RESULT_LIMIT)
groups = {} groups = {}
for g in res: for g in res:
v = g[1][self._groupIdAttr] v = g[1][self._groupIdAttr]
@ -223,13 +228,12 @@ class SimpleLDAPAuthenticator(Authenticator):
except Exception: except Exception:
return {} return {}
def __getUserRealName(self, usr): def __getUserRealName(self, usr):
''' '''
Tries to extract the real name for this user. Will return all atttributes (joint) Tries to extract the real name for this user. Will return all atttributes (joint)
specified in _userNameAttr (comma separated). specified in _userNameAttr (comma separated).
''' '''
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([(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): def authenticate(self, username, credentials, groupsManager):
''' '''
@ -249,7 +253,7 @@ class SimpleLDAPAuthenticator(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.__connection(usr['dn'], credentials) # Will raise an exception if it can't connect
groupsManager.validate(self.__getGroups(usr).keys()) groupsManager.validate(self.__getGroups(usr).keys())
@ -301,7 +305,6 @@ class SimpleLDAPAuthenticator(Authenticator):
if res is None: if res is None:
raise AuthenticatorException(_('Group not found')) raise AuthenticatorException(_('Group not found'))
def getGroups(self, username, groupsManager): def getGroups(self, username, groupsManager):
''' '''
Looks for the real groups to which the specified user belongs Looks for the real groups to which the specified user belongs
@ -317,13 +320,15 @@ class SimpleLDAPAuthenticator(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 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 = r[1].get(self._userIdAttr, '')
usrId = type(usrId) == list and usrId[0] or usrId usrId = type(usrId) == list and usrId[0] or usrId
res.append( { 'id' : usrId, res.append({
'name' : self.__getUserRealName(r[1]) } ) 'id': usrId,
'name': self.__getUserRealName(r[1])
})
return res return res
except Exception, e: except Exception:
logger.exception("Exception: ") logger.exception("Exception: ")
raise AuthenticatorException(_('Too many results, be more specific')) raise AuthenticatorException(_('Too many results, be more specific'))
@ -331,17 +336,18 @@ class SimpleLDAPAuthenticator(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._groupClass, self._groupIdAttr, pattern), sizelimit=LDAP_RESULT_LIMIT): for r in con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE, filterstr='(&(objectClass=%s)(%s=%s*))' % (self._groupClass, self._groupIdAttr, pattern), sizelimit=LDAP_RESULT_LIMIT):
grpId = r[1].get(self._groupIdAttr, '') grpId = r[1].get(self._groupIdAttr, '')
grpId = type(grpId) == list and grpId[0] or grpId grpId = type(grpId) == list and grpId[0] or grpId
res.append( { 'id' : grpId, res.append({
'name' : grpId } ) 'id': grpId,
'name': grpId
})
return res return res
except Exception, e: except Exception:
logger.exception("Exception: ") logger.exception("Exception: ")
raise AuthenticatorException(_('Too many results, be more specific')) raise AuthenticatorException(_('Too many results, be more specific'))
@staticmethod @staticmethod
def test(env, data): def test(env, data):
try: try:
@ -358,12 +364,12 @@ class SimpleLDAPAuthenticator(Authenticator):
return [False, str(e)] return [False, str(e)]
try: try:
con.search_s(base = self._ldapBase, scope = ldap.SCOPE_BASE) con.search_s(base=self._ldapBase, scope=ldap.SCOPE_BASE)
except Exception: except Exception:
return [False, _('Ldap search base is incorrect')] return [False, _('Ldap search base is incorrect')]
try: try:
if len(con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE, filterstr = '(objectClass=%s)' % self._userClass, sizelimit=1)) == 1: if len(con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE, filterstr='(objectClass=%s)' % self._userClass, sizelimit=1)) == 1:
raise Exception() raise Exception()
return [False, _('Ldap user class seems to be incorrect (no user found by that class)')] return [False, _('Ldap user class seems to be incorrect (no user found by that class)')]
except Exception, e: except Exception, e:
@ -371,7 +377,7 @@ class SimpleLDAPAuthenticator(Authenticator):
pass pass
try: try:
if len(con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE, filterstr = '(objectClass=%s)' % self._groupClass, sizelimit=1)) == 1: if len(con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE, filterstr='(objectClass=%s)' % self._groupClass, sizelimit=1)) == 1:
raise Exception() raise Exception()
return [False, _('Ldap group class seems to be incorrect (no group found by that class)')] return [False, _('Ldap group class seems to be incorrect (no group found by that class)')]
except Exception, e: except Exception, e:
@ -379,7 +385,7 @@ class SimpleLDAPAuthenticator(Authenticator):
pass pass
try: try:
if len(con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE, filterstr = '(%s=*)' % self._userIdAttr, sizelimit=1)) == 1: if len(con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE, filterstr='(%s=*)' % self._userIdAttr, sizelimit=1)) == 1:
raise Exception() raise Exception()
return [False, _('Ldap user id attribute seems to be incorrect (no user found by that attribute)')] return [False, _('Ldap user id attribute seems to be incorrect (no user found by that attribute)')]
except Exception, e: except Exception, e:
@ -387,7 +393,7 @@ class SimpleLDAPAuthenticator(Authenticator):
pass pass
try: try:
if len(con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE, filterstr = '(%s=*)' % self._groupIdAttr, sizelimit=1)) == 1: if len(con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE, filterstr='(%s=*)' % self._groupIdAttr, sizelimit=1)) == 1:
raise Exception() raise Exception()
return [False, _('Ldap group id attribute seems to be incorrect (no group found by that attribute)')] return [False, _('Ldap group id attribute seems to be incorrect (no group found by that attribute)')]
except Exception, e: except Exception, e:
@ -396,7 +402,7 @@ class SimpleLDAPAuthenticator(Authenticator):
# Now test objectclass and attribute of users # Now test objectclass and attribute of users
try: 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: 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() 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 class or user id attr is probably wrong (can\'t find any user with both conditions)')]
except Exception, e: except Exception, e:
@ -405,12 +411,12 @@ class SimpleLDAPAuthenticator(Authenticator):
# And group part, with membership # And group part, with membership
try: try:
res = con.search_ext_s(base = self._ldapBase, scope = ldap.SCOPE_SUBTREE, filterstr = '(&(objectClass=%s)(%s=*))' % (self._groupClass, self._groupIdAttr), attrlist = [self._memberAttr]) res = con.search_ext_s(base=self._ldapBase, scope=ldap.SCOPE_SUBTREE, filterstr='(&(objectClass=%s)(%s=*))' % (self._groupClass, self._groupIdAttr), attrlist=[self._memberAttr])
if len(res) == 0: if len(res) == 0:
raise Exception(_('Ldap group class or group id attr is probably wrong (can\'t find any group with both conditions)')) raise Exception(_('Ldap group class or group id attr is probably wrong (can\'t find any group with both conditions)'))
ok = False ok = False
for r in res: for r in res:
if r[1].has_key(self._memberAttr) is True: if self._memberAttr in r[1]:
ok = True ok = True
break break
if ok is False: if ok is False:
@ -418,7 +424,4 @@ class SimpleLDAPAuthenticator(Authenticator):
except Exception, e: except Exception, e:
return [False, str(e)] return [False, str(e)]
return [True, _("Connection params seem correct, test was succesfully executed")] return [True, _("Connection params seem correct, test was succesfully executed")]

View File

@ -32,5 +32,8 @@
@author: Adolfo Gómez, dkmaster at dkmon dot com @author: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
from __future__ import unicode_literals
from Authenticator import SimpleLDAPAuthenticator from Authenticator import SimpleLDAPAuthenticator
__updated__ = '2014-02-19'

View File

@ -70,7 +70,7 @@ class Serializable(object):
In that case, initialize the object with default values In that case, initialize the object with default values
Args: Args:
str _ : String readed from persistent storage to deseralilize str_ _ : String readed from persistent storage to deseralilize
:note: This method must be overridden :note: This method must be overridden
''' '''

View File

@ -30,6 +30,10 @@
''' '''
@author: Adolfo Gómez, dkmaster at dkmon dot com @author: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
from __future__ import unicode_literals
__updated__ = '2014-02-19'
class AuthsFactory(object): class AuthsFactory(object):
''' '''
@ -48,7 +52,7 @@ class AuthsFactory(object):
''' '''
Returns the factory that keeps the register of authentication providers. Returns the factory that keeps the register of authentication providers.
''' '''
if AuthsFactory._factory == None: if AuthsFactory._factory == None:
AuthsFactory._factory = AuthsFactory() AuthsFactory._factory = AuthsFactory()
return AuthsFactory._factory return AuthsFactory._factory

View File

@ -31,14 +31,19 @@ Base module for all authenticators
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com .. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
from __future__ import unicode_literals
from uds.core import Module from uds.core import Module
from django.utils.translation import ugettext_noop as _ from django.utils.translation import ugettext_noop as _
from GroupsManager import GroupsManager from GroupsManager import GroupsManager
from Exceptions import InvalidUserException from Exceptions import InvalidUserException
import logging import logging
__updated__ = '2014-02-19'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Authenticator(Module): class Authenticator(Module):
''' '''
This class represents the base interface to implement authenticators. This class represents the base interface to implement authenticators.
@ -90,65 +95,64 @@ class Authenticator(Module):
easier to understand. easier to understand.
''' '''
#: Name of type, used at administration interface to identify this # : Name of type, used at administration interface to identify this
#: authenticator (i.e. LDAP, SAML, ...) # : authenticator (i.e. LDAP, SAML, ...)
#: This string will be translated when provided to admin interface # : This string will be translated when provided to admin interface
#: using ugettext, so you can mark it as "_" at derived classes (using ugettext_noop) # : using ugettext, so you can mark it as "_" at derived classes (using ugettext_noop)
#: if you want so it can be translated. # : if you want so it can be translated.
typeName = _('Base Authenticator') typeName = _('Base Authenticator')
#: Name of type used by Managers to identify this type of service # : Name of type used by Managers to identify this type of service
#: We could have used here the Class name, but we decided that the # : We could have used here the Class name, but we decided that the
#: module implementator will be the one that will provide a name that # : module implementator will be the one that will provide a name that
#: will relation the class (type) and that name. # : will relation the class (type) and that name.
typeType = 'BaseAuthenticator' typeType = 'BaseAuthenticator'
#: Description shown at administration level for this authenticator. # : Description shown at administration level for this authenticator.
#: This string will be translated when provided to admin interface # : This string will be translated when provided to admin interface
#: using ugettext, so you can mark it as "_" at derived classes (using ugettext_noop) # : using ugettext, so you can mark it as "_" at derived classes (using ugettext_noop)
#: if you want so it can be translated. # : if you want so it can be translated.
typeDescription = _('Base Authenticator') typeDescription = _('Base Authenticator')
# : Icon file, used to represent this authenticator at administration interface
#: Icon file, used to represent this authenticator at administration interface # : This file should be at same folder as this class is, except if you provide
#: This file should be at same folder as this class is, except if you provide # : your own :py:meth:uds.core.BaseModule.BaseModule.icon method.
#: your own :py:meth:uds.core.BaseModule.BaseModule.icon method.
iconFile = 'auth.png' iconFile = 'auth.png'
#: Mark this authenticator as that the users comes from outside the UDS # : Mark this authenticator as that the users comes from outside the UDS
#: database, that are most authenticator (except Internal DB) # : database, that are most authenticator (except Internal DB)
#: So, isInternalSource means that "user is kept at database only" # : So, isInternalSource means that "user is kept at database only"
isExternalSource = True isExternalSource = True
#: If we need to enter the password for this user when creating a new # : If we need to enter the password for this user when creating a new
#: user at administration interface. Used basically by internal authenticator. # : user at administration interface. Used basically by internal authenticator.
needsPassword = False needsPassword = False
#: Label for username field, shown at administration interface user form. # : Label for username field, shown at administration interface user form.
userNameLabel = _('User name') userNameLabel = _('User name')
#: Label for group field, shown at administration interface user form. # : Label for group field, shown at administration interface user form.
groupNameLabel = _('Group name') groupNameLabel = _('Group name')
#: Label for password field, , shown at administration interface user form. # : Label for password field, , shown at administration interface user form.
#: Not needed for external authenticators (where credentials are stored with # : Not needed for external authenticators (where credentials are stored with
#: an already existing user. # : an already existing user.
passwordLabel = _('Password') passwordLabel = _('Password')
#: If this authenticators casues a temporal block of an user on repeated login failures # : If this authenticators casues a temporal block of an user on repeated login failures
blockUserOnLoginFailures = True blockUserOnLoginFailures = True
from User import User from User import User
from Group import Group from Group import Group
#: The type of user provided, normally standard user will be enough. # : The type of user provided, normally standard user will be enough.
#: This is here so if we need it in some case, we can write our own # : This is here so if we need it in some case, we can write our own
#: user class # : user class
userType = User userType = User
#: The type of group provided, normally standard group will be enough # : The type of group provided, normally standard group will be enough
#: This is here so if we need it in some case, we can write our own # : This is here so if we need it in some case, we can write our own
#: group class # : group class
groupType = Group groupType = Group
def __init__(self, dbAuth, environment, values): def __init__(self, dbAuth, environment, values):
@ -196,7 +200,7 @@ class Authenticator(Module):
if self.isExternalSource == True: if self.isExternalSource == True:
groupsManager = GroupsManager(self._dbAuth) groupsManager = GroupsManager(self._dbAuth)
self.getGroups(user.name, groupsManager) self.getGroups(user.name, groupsManager)
user.groups = [ g.dbGroup() for g in groupsManager.getValidGroups()] user.groups = [g.dbGroup() for g in groupsManager.getValidGroups()]
def callbackUrl(self): def callbackUrl(self):
''' '''
@ -320,7 +324,7 @@ class Authenticator(Module):
''' '''
return username return username
def internalAuthenticate(self,username, credentials, groupsManager): def internalAuthenticate(self, username, credentials, groupsManager):
''' '''
This method is provided so "plugins" (For example, a custom dispatcher), can test This method is provided so "plugins" (For example, a custom dispatcher), can test
the username/credentials in an alternative way. the username/credentials in an alternative way.
@ -535,7 +539,6 @@ class Authenticator(Module):
''' '''
raise InvalidUserException(_('Users can\'t be created inside this authenticator')) raise InvalidUserException(_('Users can\'t be created inside this authenticator'))
def modifyUser(self, usrData): def modifyUser(self, usrData):
''' '''
This method is used when modifying an user to allow the authenticator: This method is used when modifying an user to allow the authenticator:
@ -563,7 +566,6 @@ class Authenticator(Module):
''' '''
pass pass
def createGroup(self, groupData): def createGroup(self, groupData):
''' '''
This method is used when creating a new group to allow the authenticator: This method is used when creating a new group to allow the authenticator:
@ -617,7 +619,6 @@ class Authenticator(Module):
''' '''
pass pass
def removeUser(self, username): def removeUser(self, username):
''' '''
Remove user is used whenever from the administration interface, or from other Remove user is used whenever from the administration interface, or from other

View File

@ -30,6 +30,10 @@
''' '''
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com .. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
from __future__ import unicode_literals
__updated__ = '2014-02-19'
class AuthenticatorException(Exception): class AuthenticatorException(Exception):
''' '''
@ -37,26 +41,29 @@ class AuthenticatorException(Exception):
''' '''
pass pass
class InvalidUserException(AuthenticatorException): class InvalidUserException(AuthenticatorException):
''' '''
Invalid user specified. The user cant access the requested service Invalid user specified. The user cant access the requested service
''' '''
pass pass
class InvalidAuthenticatorException(AuthenticatorException): class InvalidAuthenticatorException(AuthenticatorException):
''' '''
Invalida authenticator has been specified Invalida authenticator has been specified
''' '''
pass pass
class Redirect(Exception): class Redirect(Exception):
''' '''
This exception indicates that a redirect is required. This exception indicates that a redirect is required.
Used in authUrlCallback to indicate that redirect is needed Used in authUrlCallback to indicate that redirect is needed
''' '''
class Logout(Exception): class Logout(Exception):
''' '''
This exceptions redirects logouts an user and redirects to an url This exceptions redirects logouts an user and redirects to an url
''' '''

View File

@ -31,11 +31,15 @@
''' '''
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com .. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
from __future__ import unicode_literals
import logging import logging
__updated__ = '2014-02-19'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Group(object): class Group(object):
''' '''
A group is simply a database group associated with its authenticator instance A group is simply a database group associated with its authenticator instance

View File

@ -30,6 +30,7 @@
''' '''
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com .. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
from __future__ import unicode_literals
from uds.core.util.State import State from uds.core.util.State import State
from uds.models import Group as dbGroup from uds.models import Group as dbGroup
@ -37,8 +38,11 @@ from Group import Group
import inspect import inspect
import logging import logging
__updated__ = '2014-02-19'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GroupsManager(object): class GroupsManager(object):
''' '''
Manages registered groups for an specific authenticator. Manages registered groups for an specific authenticator.
@ -67,14 +71,14 @@ class GroupsManager(object):
''' '''
self._dbAuthenticator = dbAuthenticator self._dbAuthenticator = dbAuthenticator
self._groups = {} # We just get active groups, inactive aren't visible to this class self._groups = {} # We just get active groups, inactive aren't visible to this class
for g in dbAuthenticator.groups.filter(state = State.ACTIVE, is_meta = False): for g in dbAuthenticator.groups.filter(state=State.ACTIVE, is_meta=False):
self._groups[g.name.lower()] = { 'group': Group(g), 'valid': False } self._groups[g.name.lower()] = {'group': Group(g), 'valid': False}
def contains(self, groupName): def contains(self, groupName):
''' '''
Returns true if this groups manager contains the specified group name (string) Returns true if this groups manager contains the specified group name (string)
''' '''
return self._groups.has_key(groupName.lower()) return groupName.lower() in self._groups
def getGroupsNames(self): def getGroupsNames(self):
''' '''
@ -96,11 +100,10 @@ class GroupsManager(object):
# Now, get metagroups and also return them # Now, get metagroups and also return them
for g in dbGroup.objects.filter(manager__id=self._dbAuthenticator.id, is_meta=True): for g in dbGroup.objects.filter(manager__id=self._dbAuthenticator.id, is_meta=True):
gn = g.groups.filter(id__in=lst, state=State.ACTIVE).count() gn = g.groups.filter(id__in=lst, state=State.ACTIVE).count()
if gn == g.groups.count(): # If a meta group is empty, all users belongs to it. we can use gn != 0 to check that if it is empty, is not valid if gn == g.groups.count(): # If a meta group is empty, all users belongs to it. we can use gn != 0 to check that if it is empty, is not valid
# This group matches # This group matches
yield Group(g) yield Group(g)
def hasValidGroups(self): def hasValidGroups(self):
''' '''
Checks if this groups manager has at least one group that has been Checks if this groups manager has at least one group that has been
@ -116,7 +119,7 @@ class GroupsManager(object):
If this groups manager contains that group manager, it returns the If this groups manager contains that group manager, it returns the
:py:class:uds.core.auths.Group.Group representing that group name. :py:class:uds.core.auths.Group.Group representing that group name.
''' '''
if self._groups.has_key(groupName.lower()): if groupName.lower() in self._groups:
return self._groups[groupName.lower()]['group'] return self._groups[groupName.lower()]['group']
else: else:
return None return None
@ -137,7 +140,7 @@ class GroupsManager(object):
for n in groupName: for n in groupName:
self.validate(n) self.validate(n)
else: else:
if self._groups.has_key(groupName.lower()): if groupName.lower() in self._groups:
self._groups[groupName.lower()]['valid'] = True self._groups[groupName.lower()]['valid'] = True
def isValid(self, groupName): def isValid(self, groupName):
@ -145,11 +148,9 @@ class GroupsManager(object):
Checks if this group name is marked as valid inside this groups manager. Checks if this group name is marked as valid inside this groups manager.
Returns True if group name is marked as valid, False if it isn't. Returns True if group name is marked as valid, False if it isn't.
''' '''
if self._groups.has_key(groupName.lower()): if groupName.lower() in self._groups:
return self._groups[groupName.lower()]['valid'] return self._groups[groupName.lower()]['valid']
return False return False
def __str__(self): def __str__(self):
return "Groupsmanager: {0}".format(self._groups) return "Groupsmanager: {0}".format(self._groups)

View File

@ -30,11 +30,15 @@
''' '''
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com .. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
from __future__ import unicode_literals
import logging import logging
__updated__ = '2014-02-19'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class User(object): class User(object):
''' '''
An user represents a database user, associated with its authenticator (instance) An user represents a database user, associated with its authenticator (instance)
@ -47,7 +51,6 @@ class User(object):
self._dbUser = dbUser self._dbUser = dbUser
self._groups = None self._groups = None
def _groupsManager(self): def _groupsManager(self):
''' '''
If the groups manager for this user already exists, it returns this. If the groups manager for this user already exists, it returns this.
@ -89,7 +92,6 @@ class User(object):
self._groups = [Group(g) for g in usr.getGroups()] self._groups = [Group(g) for g in usr.getGroups()]
return self._groups return self._groups
def manager(self): def manager(self):
''' '''
Returns the authenticator instance Returns the authenticator instance
@ -101,4 +103,3 @@ class User(object):
Returns the database user Returns the database user
''' '''
return self._dbUser return self._dbUser

View File

@ -32,17 +32,20 @@ UDS authentication related interfaces and classes
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com .. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
from __future__ import unicode_literals
from BaseAuthenticator import Authenticator from BaseAuthenticator import Authenticator
from User import User from User import User
from Group import Group from Group import Group
from GroupsManager import GroupsManager from GroupsManager import GroupsManager
import Exceptions import Exceptions
__updated__ = '2014-02-19'
def factory(): def factory():
''' '''
Returns factory for register/access to authenticators Returns factory for register/access to authenticators
''' '''
from AuthsFactory import AuthsFactory from AuthsFactory import AuthsFactory
return AuthsFactory.factory() return AuthsFactory.factory()

View File

@ -49,23 +49,27 @@ from uds.models import User
import logging import logging
__updated__ = '2014-02-19'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
authLogger = logging.getLogger('authLog') authLogger = logging.getLogger('authLog')
USER_KEY = 'uk' USER_KEY = 'uk'
PASS_KEY = 'pk' PASS_KEY = 'pk'
ROOT_ID = -20091204 # Any negative number will do the trick ROOT_ID = -20091204 # Any negative number will do the trick
def getRootUser(): def getRootUser():
from uds.models import Authenticator from uds.models import Authenticator
u = User(id=ROOT_ID, name=GlobalConfig.SUPER_USER_LOGIN.get(True), real_name=_('System Administrator'), state= State.ACTIVE, staff_member = True, is_admin = True ) u = User(id=ROOT_ID, name=GlobalConfig.SUPER_USER_LOGIN.get(True), real_name=_('System Administrator'), state=State.ACTIVE, staff_member=True, is_admin=True)
u.manager = Authenticator() u.manager = Authenticator()
u.getGroups = lambda: [] u.getGroups = lambda: []
u.updateLastAccess = lambda: None u.updateLastAccess = lambda: None
u.logout = lambda: None u.logout = lambda: None
return u return u
def getIp(request, translateProxy = True):
def getIp(request, translateProxy=True):
''' '''
Obtains the IP of a Django Request, even behind a proxy Obtains the IP of a Django Request, even behind a proxy
@ -73,12 +77,13 @@ def getIp(request, translateProxy = True):
''' '''
try: try:
if translateProxy is False: if translateProxy is False:
raise KeyError() # Do not allow HTTP_X_FORWARDED_FOR raise KeyError() # Do not allow HTTP_X_FORWARDED_FOR
request.ip = request.META['HTTP_X_FORWARDED_FOR'].split(",")[0] request.ip = request.META['HTTP_X_FORWARDED_FOR'].split(",")[0]
except KeyError: except KeyError:
request.ip = request.META['REMOTE_ADDR'] request.ip = request.META['REMOTE_ADDR']
return request.ip return request.ip
# Decorator to make easier protect pages that needs to be logged in # Decorator to make easier protect pages that needs to be logged in
def webLoginRequired(view_func): def webLoginRequired(view_func):
''' '''
@ -106,12 +111,13 @@ def webLoginRequired(view_func):
logger.debug('No user found, redirecting to {0}'.format(url)) logger.debug('No user found, redirecting to {0}'.format(url))
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
# Refresh session duration # Refresh session duration
#request.session.set_expiry(GlobalConfig.USER_SESSION_LENGTH.getInt()) # request.session.set_expiry(GlobalConfig.USER_SESSION_LENGTH.getInt())
request.user = user request.user = user
getIp(request) getIp(request)
return view_func(request, *args, **kwargs) return view_func(request, *args, **kwargs)
return _wrapped_view return _wrapped_view
# Decorator to protect pages that needs to be accessed from "trusted sites" # Decorator to protect pages that needs to be accessed from "trusted sites"
def trustedSourceRequired(view_func): def trustedSourceRequired(view_func):
''' '''
@ -149,7 +155,7 @@ def __registerUser(authenticator, authInstance, username):
return None return None
def authenticate(username, password, authenticator, useInternalAuthenticate = False): def authenticate(username, password, authenticator, useInternalAuthenticate=False):
''' '''
Given an username, password and authenticator, try to authenticate user Given an username, password and authenticator, try to authenticate user
@param username: username to authenticate @param username: username to authenticate
@ -217,6 +223,7 @@ def authenticateViaCallback(authenticator, params):
return __registerUser(authenticator, authInstance, username) return __registerUser(authenticator, authInstance, username)
def authCallbackUrl(authenticator): def authCallbackUrl(authenticator):
''' '''
Helper method, so we can get the auth call back url for an authenticator Helper method, so we can get the auth call back url for an authenticator
@ -224,18 +231,20 @@ def authCallbackUrl(authenticator):
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
return reverse('uds.web.views.authCallback', kwargs={'authName': authenticator.name}) return reverse('uds.web.views.authCallback', kwargs={'authName': authenticator.name})
def authInfoUrl(authenticator): def authInfoUrl(authenticator):
''' '''
Helper method, so we can get the info url for an authenticator Helper method, so we can get the info url for an authenticator
''' '''
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
if isinstance(authenticator,unicode) or isinstance(authenticator, str): if isinstance(authenticator, unicode) or isinstance(authenticator, str):
name = authenticator name = authenticator
else: else:
name = authenticator.name name = authenticator.name
return reverse('uds.web.views.authInfo', kwargs={'authName': name}) return reverse('uds.web.views.authInfo', kwargs={'authName': name})
def webLogin(request, response, user, password): def webLogin(request, response, user, password):
''' '''
Helper function to, once the user is authenticated, store the information at the user session. Helper function to, once the user is authenticated, store the information at the user session.
@ -243,7 +252,7 @@ def webLogin(request, response, user, password):
''' '''
from uds import REST from uds import REST
if user.id != ROOT_ID: # If not ROOT user (this user is not inside any authenticator) if user.id != ROOT_ID: # If not ROOT user (this user is not inside any authenticator)
manager_id = user.manager.id manager_id = user.manager.id
else: else:
manager_id = -1 manager_id = -1
@ -266,7 +275,8 @@ def webPassword(request):
''' '''
return CryptoManager.manager().xor(request.session.get(PASS_KEY), request.COOKIES['uds']).decode('utf-8') return CryptoManager.manager().xor(request.session.get(PASS_KEY), request.COOKIES['uds']).decode('utf-8')
def webLogout(request, exit_url = None):
def webLogout(request, exit_url=None):
''' '''
Helper function to clear user related data from session. If this method is not used, the session we be cleaned anyway Helper function to clear user related data from session. If this method is not used, the session we be cleaned anyway
by django in regular basis. by django in regular basis.
@ -278,7 +288,8 @@ def webLogout(request, exit_url = None):
# Try to delete session # Try to delete session
return HttpResponseRedirect(request.build_absolute_uri(exit_url)) return HttpResponseRedirect(request.build_absolute_uri(exit_url))
def authLogLogin(request, authenticator, userName, java, os, logStr = ''):
def authLogLogin(request, authenticator, userName, java, os, logStr=''):
''' '''
Logs authentication Logs authentication
''' '''
@ -303,4 +314,3 @@ def authLogLogin(request, authenticator, userName, java, os, logStr = ''):
def authLogLogout(request): def authLogLogout(request):
log.doLog(request.user.manager, log.INFO, 'user {0} has logged out from {1}'.format(request.user.name, request.ip), log.WEB) log.doLog(request.user.manager, log.INFO, 'user {0} has logged out from {1}'.format(request.user.name, request.ip), log.WEB)
log.doLog(request.user, log.INFO, 'has logged out from {0}'.format(request.ip), log.WEB) log.doLog(request.user, log.INFO, 'has logged out from {0}'.format(request.ip), log.WEB)

View File

@ -7,8 +7,11 @@ Author:
from django.db import models, connection from django.db import models, connection
import logging import logging
__updated__ = '2014-02-19'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Table locking in mysql at least is based on thread requesting it, so we should not have problems with a bit of care # Table locking in mysql at least is based on thread requesting it, so we should not have problems with a bit of care
class LockingManager(models.Manager): class LockingManager(models.Manager):
""" Add lock/unlock functionality to manager. """ Add lock/unlock functionality to manager.
@ -57,17 +60,17 @@ class LockingManager(models.Manager):
con = connection con = connection
cursor = con.cursor() cursor = con.cursor()
table = self.model._meta.db_table table = self.model._meta.db_table
#logger.debug("Locking table %s" % table) # logger.debug("Locking table %s" % table)
cursor.execute("LOCK TABLES %s WRITE" % table) cursor.execute("LOCK TABLES %s WRITE" % table)
row = cursor.fetchone() row = cursor.fetchone()
return row return row
def unlock(self): def unlock(self):
""" Unlock the table. """ """ Unlock the table. """
#logger.debug("Unlocked tables") # logger.debug("Unlocked tables")
con = connection con = connection
cursor = con.cursor() cursor = con.cursor()
#table = self.model._meta.db_table # table = self.model._meta.db_table
cursor.execute("UNLOCK TABLES") cursor.execute("UNLOCK TABLES")
row = cursor.fetchone() row = cursor.fetchone()
return row return row

View File

@ -30,3 +30,4 @@
@author: Adolfo Gómez, dkmaster at dkmon dot com @author: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
__updated__ = '2014-02-19'

View File

@ -30,13 +30,16 @@
''' '''
@author: Adolfo Gómez, dkmaster at dkmon dot com @author: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
from __future__ import unicode_literals
from uds.core.Environment import Environmentable from uds.core.Environment import Environmentable
import logging import logging
__updated__ = '2014-02-19'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class DelayedTask(Environmentable): class DelayedTask(Environmentable):
def __init__(self): def __init__(self):
''' '''

View File

@ -35,19 +35,22 @@ from __future__ import unicode_literals
from django.db import transaction from django.db import transaction
from django.db.models import Q from django.db.models import Q
from uds.models import DelayedTask as dbDelayedTask, getSqlDatetime from uds.models import DelayedTask as dbDelayedTask, getSqlDatetime
from uds.core.util.Decorators import retryOnException
from ..Environment import Environment from ..Environment import Environment
from socket import gethostname from socket import gethostname
from pickle import loads, dumps from pickle import loads, dumps
from datetime import datetime, timedelta from datetime import timedelta
import threading, time import threading
import time
import logging import logging
__updated__ = '2014-02-19'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class DelayedTaskThread(threading.Thread): class DelayedTaskThread(threading.Thread):
def __init__(self, taskInstance): def __init__(self, taskInstance):
super(DelayedTaskThread,self).__init__() super(DelayedTaskThread, self).__init__()
self._taskInstance = taskInstance self._taskInstance = taskInstance
def run(self): def run(self):
@ -56,6 +59,7 @@ class DelayedTaskThread(threading.Thread):
except Exception, e: except Exception, e:
logger.debug("Exception in thread {0}: {1}".format(e.__class__, e)) logger.debug("Exception in thread {0}: {1}".format(e.__class__, e))
class DelayedTaskRunner(object): class DelayedTaskRunner(object):
CODEC = 'base64' # Can be zip, hez, bzip, base64, uuencoded CODEC = 'base64' # Can be zip, hez, bzip, base64, uuencoded
# How often tasks r checked # How often tasks r checked
@ -74,7 +78,7 @@ class DelayedTaskRunner(object):
@staticmethod @staticmethod
def runner(): def runner():
if DelayedTaskRunner._runner == None: if DelayedTaskRunner._runner == None:
DelayedTaskRunner._runner = DelayedTaskRunner() DelayedTaskRunner._runner = DelayedTaskRunner()
return DelayedTaskRunner._runner return DelayedTaskRunner._runner
@ -84,7 +88,7 @@ class DelayedTaskRunner(object):
# If next execution is before now or last execution is in the future (clock changed on this server, we take that task as executable) # If next execution is before now or last execution is in the future (clock changed on this server, we take that task as executable)
taskInstance = None taskInstance = None
try: try:
with transaction.atomic(): # Encloses with transaction.atomic(): # Encloses
task = dbDelayedTask.objects.select_for_update().filter(filt).order_by('execution_time')[0] task = dbDelayedTask.objects.select_for_update().filter(filt).order_by('execution_time')[0]
task.delete() task.delete()
taskInstance = loads(task.instance.decode(self.CODEC)) taskInstance = loads(task.instance.decode(self.CODEC))
@ -100,17 +104,17 @@ class DelayedTaskRunner(object):
def __insert(self, instance, delay, tag): def __insert(self, instance, delay, tag):
now = getSqlDatetime() now = getSqlDatetime()
exec_time = now + timedelta(seconds = delay) exec_time = now + timedelta(seconds=delay)
cls = instance.__class__ cls = instance.__class__
instanceDump = dumps(instance).encode(self.CODEC) instanceDump = dumps(instance).encode(self.CODEC)
typeName = str(cls.__module__ + '.' + cls.__name__) typeName = str(cls.__module__ + '.' + cls.__name__)
logger.debug('Inserting delayed task {0} with {1} bytes ({2})'.format(typeName, len(instanceDump), exec_time)) logger.debug('Inserting delayed task {0} with {1} bytes ({2})'.format(typeName, len(instanceDump), exec_time))
dbDelayedTask.objects.create(type = typeName, instance = instanceDump, dbDelayedTask.objects.create(type=typeName, instance=instanceDump,
insert_date = now, execution_delay = delay, execution_time = exec_time, tag = tag) insert_date=now, execution_delay=delay, execution_time=exec_time, tag=tag)
def insert(self, instance, delay, tag = ''): def insert(self, instance, delay, tag=''):
retries = 3 retries = 3
while retries > 0: while retries > 0:
retries -= 1 retries -= 1
@ -119,7 +123,7 @@ class DelayedTaskRunner(object):
break break
except Exception, e: except Exception, e:
logger.info('Exception inserting a delayed task {0}: {1}'.format(str(e.__class__), e)) logger.info('Exception inserting a delayed task {0}: {1}'.format(str(e.__class__), e))
time.sleep(1) # Wait a bit before next try... time.sleep(1) # Wait a bit before next try...
# If retries == 0, this is a big error # If retries == 0, this is a big error
if retries == 0: if retries == 0:
logger.error("Could not insert delayed task!!!! {0} {1} {2}".format(instance, delay, tag)) logger.error("Could not insert delayed task!!!! {0} {1} {2}".format(instance, delay, tag))
@ -141,7 +145,7 @@ class DelayedTaskRunner(object):
number = 0 number = 0
try: try:
number = dbDelayedTask.objects.filter(tag=tag).count() number = dbDelayedTask.objects.filter(tag=tag).count()
except Exception as e: except Exception:
logger.error('Exception looking for a delayed task tag {0}'.format(tag)) logger.error('Exception looking for a delayed task tag {0}'.format(tag))
return number > 0 return number > 0

View File

@ -30,16 +30,20 @@
''' '''
@author: Adolfo Gómez, dkmaster at dkmon dot com @author: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
from __future__ import unicode_literals
from uds.core import Environmentable from uds.core import Environmentable
import logging import logging
__updated__ = '2014-02-19'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Job(Environmentable): class Job(Environmentable):
# Default frecuency, once a day. Remenber that precision will be based on "granurality" of Scheduler # Default frecuency, once a day. Remenber that precision will be based on "granurality" of Scheduler
# If a job is used for delayed execution, this attribute is in fact ignored # If a job is used for delayed execution, this attribute is in fact ignored
frecuency = 24*3600+3 frecuency = 24 * 3600 + 3
friendly_name = 'Unknown' friendly_name = 'Unknown'
def __init__(self, environment): def __init__(self, environment):
@ -51,7 +55,7 @@ class Job(Environmentable):
def execute(self): def execute(self):
try: try:
self.run() self.run()
except Exception, e: except Exception:
logger.exception('Job {0} raised an exception:'.format(self.__class__)) logger.exception('Job {0} raised an exception:'.format(self.__class__))
def run(self): def run(self):

View File

@ -30,11 +30,16 @@
''' '''
@author: Adolfo Gómez, dkmaster at dkmon dot com @author: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
from __future__ import unicode_literals
import datetime import datetime
import logging import logging
__updated__ = '2014-02-19'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class JobsFactory(object): class JobsFactory(object):
_factory = None _factory = None
@ -43,7 +48,7 @@ class JobsFactory(object):
@staticmethod @staticmethod
def factory(): def factory():
if JobsFactory._factory == None: if JobsFactory._factory == None:
JobsFactory._factory = JobsFactory() JobsFactory._factory = JobsFactory()
return JobsFactory._factory return JobsFactory._factory
@ -68,18 +73,17 @@ class JobsFactory(object):
# We use database server datetime # We use database server datetime
now = getSqlDatetime() now = getSqlDatetime()
next_ = now next_ = now
job = Scheduler.objects.create(name = name, frecuency = type_.frecuency, last_execution = now, next_execution = next_, state = State.FOR_EXECUTE) job = Scheduler.objects.create(name=name, frecuency=type_.frecuency, last_execution=now, next_execution=next_, state=State.FOR_EXECUTE)
except Exception: # already exists except Exception: # already exists
logger.debug('Already added {0}'.format(name)) logger.debug('Already added {0}'.format(name))
job = Scheduler.objects.get(name=name) job = Scheduler.objects.get(name=name)
job.frecuency = type_.frecuency job.frecuency = type_.frecuency
if job.next_execution > job.last_execution + datetime.timedelta(seconds = type_.frecuency): if job.next_execution > job.last_execution + datetime.timedelta(seconds=type_.frecuency):
job.next_execution = job.last_execution + datetime.timedelta(seconds = type_.frecuency); job.next_execution = job.last_execution + datetime.timedelta(seconds=type_.frecuency)
job.save() job.save()
except Exception, e: except Exception, e:
logger.debug('Exception at ensureJobsInDatabase in JobsFactory: {0}, {1}'.format(e.__class__, e)) logger.debug('Exception at ensureJobsInDatabase in JobsFactory: {0}, {1}'.format(e.__class__, e))
def lookup(self, typeName): def lookup(self, typeName):
try: try:
return self._jobs[typeName] return self._jobs[typeName]

View File

@ -30,6 +30,7 @@
''' '''
@author: Adolfo Gómez, dkmaster at dkmon dot com @author: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
from __future__ import unicode_literals
from django.db.models import Q from django.db.models import Q
from django.db import transaction, DatabaseError from django.db import transaction, DatabaseError
@ -37,14 +38,18 @@ from uds.models import Scheduler as dbScheduler, getSqlDatetime, State
from uds.core.jobs.JobsFactory import JobsFactory from uds.core.jobs.JobsFactory import JobsFactory
from datetime import timedelta from datetime import timedelta
from socket import gethostname from socket import gethostname
import threading, time import threading
import time
import logging import logging
__updated__ = '2014-02-19'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class JobThread(threading.Thread): class JobThread(threading.Thread):
def __init__(self, jobInstance, dbJob): def __init__(self, jobInstance, dbJob):
super(JobThread,self).__init__() super(JobThread, self).__init__()
self._jobInstance = jobInstance self._jobInstance = jobInstance
self._dbJobId = dbJob.id self._dbJobId = dbJob.id
@ -66,18 +71,18 @@ class JobThread(threading.Thread):
logger.info('Database access locked... Retrying') logger.info('Database access locked... Retrying')
time.sleep(1) time.sleep(1)
@transaction.atomic @transaction.atomic
def __updateDb(self): def __updateDb(self):
job = dbScheduler.objects.select_for_update().get(id=self._dbJobId) job = dbScheduler.objects.select_for_update().get(id=self._dbJobId)
job.state = State.FOR_EXECUTE job.state = State.FOR_EXECUTE
job.owner_server = '' job.owner_server = ''
job.next_execution = getSqlDatetime() + timedelta(seconds = job.frecuency) job.next_execution = getSqlDatetime() + timedelta(seconds=job.frecuency)
# Update state and last execution time at database # Update state and last execution time at database
job.save() job.save()
class Scheduler(object): class Scheduler(object):
granularity = 2 # We check for cron jobs every THIS seconds granularity = 2 # We check for cron jobs every THIS seconds
# to keep singleton Scheduler # to keep singleton Scheduler
_scheduler = None _scheduler = None
@ -88,7 +93,7 @@ class Scheduler(object):
@staticmethod @staticmethod
def scheduler(): def scheduler():
if Scheduler._scheduler == None: if Scheduler._scheduler == None:
Scheduler._scheduler = Scheduler() Scheduler._scheduler = Scheduler()
return Scheduler._scheduler return Scheduler._scheduler
@ -101,12 +106,12 @@ class Scheduler(object):
''' '''
jobInstance = None jobInstance = None
try: try:
now = getSqlDatetime() # Datetimes are based on database server times now = getSqlDatetime() # Datetimes are based on database server times
filter = Q(state = State.FOR_EXECUTE) & (Q(owner_server = self._hostname) | Q(owner_server = '')) & (Q(last_execution__gt = now) | Q(next_execution__lt = now)) fltr = Q(state=State.FOR_EXECUTE) & (Q(owner_server=self._hostname) | Q(owner_server='')) & (Q(last_execution__gt=now) | Q(next_execution__lt=now))
with transaction.atomic(): with transaction.atomic():
# If next execution is before now or last execution is in the future (clock changed on this server, we take that task as executable) # If next execution is before now or last execution is in the future (clock changed on this server, we take that task as executable)
# This params are all set inside filter (look at __init__) # This params are all set inside fltr (look at __init__)
job = dbScheduler.objects.select_for_update().filter(filter).order_by('next_execution')[0] job = dbScheduler.objects.select_for_update().filter(fltr).order_by('next_execution')[0]
job.state = State.RUNNING job.state = State.RUNNING
job.owner_server = self._hostname job.owner_server = self._hostname
job.last_execution = now job.last_execution = now
@ -119,7 +124,7 @@ class Scheduler(object):
job.delete() job.delete()
return return
logger.debug('Executing job:>{0}<'.format(job.name)) logger.debug('Executing job:>{0}<'.format(job.name))
JobThread(jobInstance, job).start() # Do not instatiate thread, just run it JobThread(jobInstance, job).start() # Do not instatiate thread, just run it
except IndexError: except IndexError:
# Do nothing, there is no jobs for execution # Do nothing, there is no jobs for execution
return return
@ -135,8 +140,7 @@ class Scheduler(object):
''' '''
Releases all scheduleds being executed by this scheduler Releases all scheduleds being executed by this scheduler
''' '''
dbScheduler.objects.select_for_update().filter(owner_server = self._hostname).update(owner_server = '', state = State.FOR_EXECUTE) dbScheduler.objects.select_for_update().filter(owner_server=self._hostname).update(owner_server='', state=State.FOR_EXECUTE)
def run(self): def run(self):
# We ensure that the jobs are also in database so we can # We ensure that the jobs are also in database so we can
@ -150,4 +154,3 @@ class Scheduler(object):
self.executeOneJob() self.executeOneJob()
except Exception, e: except Exception, e:
logger.exception('Unexpected exception at run loop {0}: {1}'.format(e.__class__, e)) logger.exception('Unexpected exception at run loop {0}: {1}'.format(e.__class__, e))

View File

@ -32,9 +32,14 @@ UDS jobs related modules
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com .. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
from __future__ import unicode_literals
from Job import Job from Job import Job
from DelayedTask import DelayedTask from DelayedTask import DelayedTask
__updated__ = '2014-02-19'
def factory(): def factory():
from JobsFactory import JobsFactory from JobsFactory import JobsFactory
return JobsFactory.factory() return JobsFactory.factory()

View File

@ -93,8 +93,8 @@ class RDPTransport(Transport):
gui.boolToStr(self._allowDrives), gui.boolToStr(self._allowSerials), gui.boolToStr(self._allowDrives), gui.boolToStr(self._allowSerials),
self._fixedName, self._fixedPassword, self._fixedDomain ] ) self._fixedName, self._fixedPassword, self._fixedDomain ] )
def unmarshal(self, str): def unmarshal(self, str_):
data = str.split('\t') data = str_.split('\t')
if data[0] == 'v1': if data[0] == 'v1':
self._useEmptyCreds = gui.strToBool(data[1]) self._useEmptyCreds = gui.strToBool(data[1])
self._allowSmartcards = gui.strToBool(data[2]) self._allowSmartcards = gui.strToBool(data[2])

View File

@ -104,8 +104,8 @@ class TSRDPTransport(Transport):
gui.boolToStr(self._allowDrives), gui.boolToStr(self._allowSerials), gui.boolToStr(self._allowDrives), gui.boolToStr(self._allowSerials),
self._fixedName, self._fixedPassword, self._fixedDomain, self._tunnelServer, self._tunnelCheckServer ] ) self._fixedName, self._fixedPassword, self._fixedDomain, self._tunnelServer, self._tunnelCheckServer ] )
def unmarshal(self, str): def unmarshal(self, str_):
data = str.split('\t') data = str_.split('\t')
if data[0] == 'v1': if data[0] == 'v1':
self._useEmptyCreds = gui.strToBool(data[1]) self._useEmptyCreds = gui.strToBool(data[1])
self._allowSmartcards = gui.strToBool(data[2]) self._allowSmartcards = gui.strToBool(data[2])