From 1be7d9f0ab3f8f03363771ef6284dd4bbb9f7ed6 Mon Sep 17 00:00:00 2001 From: Gaetan Delannay Date: Thu, 5 Sep 2013 10:42:19 +0200 Subject: [PATCH] [bin] backup.py: remove Data.fs.old before packing the ZODB to avoid disk space problems; [checkldap] added a param to define the scope of the LDAP query (base, onelevel or subtree); [shared] xml_parser: convert nbsp entity to the equivalent utf-8 char. --- bin/backup.py | 17 +++++++++++++++++ bin/checkldap.py | 15 ++++++++++----- gen/__init__.py | 25 ++++++++++++++++--------- gen/installer.py | 5 ++--- gen/ldap.py | 33 +++++++++++++++++++++++++++++++-- gen/mixins/ToolMixin.py | 1 + pod/xhtml2odt.py | 2 +- shared/xml_parser.py | 7 +++++-- 8 files changed, 83 insertions(+), 22 deletions(-) diff --git a/bin/backup.py b/bin/backup.py index 1e77a11..a9a01ba 100644 --- a/bin/backup.py +++ b/bin/backup.py @@ -61,6 +61,21 @@ class ZodbBackuper: fileName = self.storageLocation + fileSuffix os.system('chown %s %s' % (self.zopeUser, fileName)) + def removeDataFsOld(self): + '''Removes the file Data.fs.old if it exists. + + In the process of packing the ZODB, an additional file Data.fs.pack + is created, and renamed to Data.fs once finished. It means that, when + we pack the ZODB, 3 copies of the DB can be present at the same time: + Data.fs, Data.fs.old and Data.fs.pack. We prefer to remove the + Data.fs.old copy to avoid missing disk space if the DB is big. + ''' + old = self.storageLocation + '.old' + if os.path.exists(old): + self.log('Removing %s...' % old) + os.remove(old) + self.log('Done.') + folderCreateError = 'Could not create backup folder. Backup of log ' \ 'files will not take place. %s' def backupLogs(self): @@ -190,6 +205,8 @@ class ZodbBackuper: self.executeCommand('%s stop' % self.zopectl) # If we are on the "full backup day", let's pack the ZODB first if time.asctime().startswith(self.options.dayFullBackup): + # As a preamble to packing the ZODB, remove Data.fs.old if present. + self.removeDataFsOld() w('> Day is "%s", packing the ZODB...' % self.options.dayFullBackup) self.packZodb() w('> Make a backup of log files...') diff --git a/bin/checkldap.py b/bin/checkldap.py index 5230f86..4254df1 100644 --- a/bin/checkldap.py +++ b/bin/checkldap.py @@ -12,18 +12,22 @@ class LdapTester: attrs is a comma-separated list of attrs we will retrieve in the LDAP, ie "uid,login" filter is the query filter, ie "(&(attr1=Geez*)(status=OK))" + scope is the scope of the search, and can be: + BASE To search the object itself on base + ONELEVEL To search base's immediate children + SUBTREE To search base and all its descendants ''' def __init__(self): # Get params from shell args. - if len(sys.argv) != 7: + if len(sys.argv) != 8: print(LdapTester.__doc__) sys.exit(0) s = self - s.uri, s.login, s.password, s.base, s.attrs, s.filter = sys.argv[1:] + s.uri,s.login,s.password,s.base,s.attrs,s.filter,s.scope = sys.argv[1:] self.attrs = self.attrs.split(',') self.tentatives = 5 self.timeout = 5 - self.attrList = ['cfwbV2cn', 'logindisabled'] + self.attrList = ['cn'] self.ssl = False def test(self): @@ -38,8 +42,9 @@ class LdapTester: for i in range(self.tentatives): try: print('Done. Performing a simple query on %s...'% self.base) - res = server.search_st( - self.base, ldap.SCOPE_ONELEVEL, filterstr=self.filter, + res = server.search_st(self.base, + getattr(ldap, 'SCOPE_%s' % self.scope), + filterstr=self.filter, attrlist=self.attrs, timeout=5) print('Got %d entries' % len(res)) break diff --git a/gen/__init__.py b/gen/__init__.py index fa4e184..8b0dcdf 100644 --- a/gen/__init__.py +++ b/gen/__init__.py @@ -369,15 +369,22 @@ class User(Model): # ------------------------------------------------------------------------------ class LdapConfig: '''Parameters for authenticating users to an external LDAP.''' - server = '' # Name of the LDAP server - port = None # Port for this server. - # Login and password of the technical power user that the Appy application - # will use to connect to the LDAP. - adminLogin = '' - adminPassword = '' - # LDAP attribute to use as login for authenticating users. - loginAttribute = 'dn' # Can also be "mail", "sAMAccountName", "cn" - baseDn = '' # Base distinguished name where to find users in the LDAP. + def __init__(self): + self.server = '' # Name of the LDAP server + self.port = None # Port for this server. + # Login and password of the technical power user that the Appy + # application will use to connect to the LDAP. + self.adminLogin = '' + self.adminPassword = '' + # LDAP attribute to use as login for authenticating users. + self.loginAttribute = 'dn' # Can also be "mail", "sAMAccountName", "cn" + self.baseDn = '' # Base distinguished name where to find users. + + def getServerUri(self): + '''Returns the complete URI for accessing the LDAP, ie + "ldap://some.ldap.server:389".''' + port = self.port or 389 + return 'ldap://%s:%d' % (self.server, port) # ------------------------------------------------------------------------------ class Config: diff --git a/gen/installer.py b/gen/installer.py index b29147c..f61c02a 100644 --- a/gen/installer.py +++ b/gen/installer.py @@ -25,12 +25,11 @@ def traverseWrapper(self, path, response=None, validated_hook=None): '''This function is called every time a users gets a URL, this is used for tracking user activity. self is a BaseRequest''' res = originalTraverse(self, path, response, validated_hook) - t = time.time() if os.path.splitext(path)[-1].lower() not in doNotTrack: - # Do nothing when the user gets non-pages + # Do nothing when the user gets non-pages. userId, dummy = gutils.readCookie(self) if userId: - loggedUsers[userId] = t + loggedUsers[userId] = time.time() # "Touch" the SESSION object. Else, expiration won't occur. session = self.SESSION return res diff --git a/gen/ldap.py b/gen/ldap.py index 75d08d7..d8f0ce0 100644 --- a/gen/ldap.py +++ b/gen/ldap.py @@ -1,5 +1,34 @@ +# ------------------------------------------------------------------------------ +try: + import ldap +except ImportError: + # For people that do not care about ldap. + ldap = None + +# ------------------------------------------------------------------------------ +def connect(serverUri, login, password): + '''Tries to connect to some LDAP server whose UIR is p_serverUri, using + p_login and p_password as credentials.''' + try: + server = ldap.initialize(serverUri) + server.simple_bind(login, password) + return True, server, None + except ldap.LDAPError, le: + return False, None, str(le) + # ------------------------------------------------------------------------------ def authenticate(login, password, ldapConfig, tool): - '''Tries to authenticate user p_login in the LDAP.''' - return + '''Tries to authenticate user p_login in the LDAP.''' + # Connect to the ldap server. + serverUri = cfg.getServerUri() + success, server, msg = connect(serverUri, cfg.adminLogin, cfg.adminPassword) + # Manage a connection error. + if not success: + tool.log('%s: connect error (%s).' % (serverUri, msg)) + return + # Do p_login and p_password correspond to a user in the LDAP? + try: + pass + except: + pass # ------------------------------------------------------------------------------ diff --git a/gen/mixins/ToolMixin.py b/gen/mixins/ToolMixin.py index 0a11e04..4654ad6 100644 --- a/gen/mixins/ToolMixin.py +++ b/gen/mixins/ToolMixin.py @@ -1051,6 +1051,7 @@ class ToolMixin(BaseMixin): # a is the object the object was accessed through # c is the physical container of the object a, c, n, v = self._getobcontext(v, request) + print c # Try to get user name and password from basic authentication login, password = self.identify(auth) if not login: diff --git a/pod/xhtml2odt.py b/pod/xhtml2odt.py index a03fd61..d9797d9 100644 --- a/pod/xhtml2odt.py +++ b/pod/xhtml2odt.py @@ -330,7 +330,7 @@ class XhtmlEnvironment(XmlEnvironment): '''Dumps content that was temporarily stored in self.currentContent into the result.''' contentSize = 0 - if self.currentContent.strip(): + if self.currentContent.strip(' \n\r\t'): # NBSP must not be in this list # Manage missing elements currentElem = self.getCurrentElement() if self.anElementIsMissing(currentElem, None): diff --git a/shared/xml_parser.py b/shared/xml_parser.py index 6b749ac..cce7014 100644 --- a/shared/xml_parser.py +++ b/shared/xml_parser.py @@ -61,7 +61,7 @@ HTML_ENTITIES = { 'ntilde':'ñ', 'ograve':'ò', 'oacute':'ó', 'ocirc':'ô', 'otilde':'õ', 'ouml':'ö', 'divide':'÷', 'oslash':'ø', 'ugrave':'ù', 'uacute':'ú', 'ucirc':'û', 'uuml':'ü', 'yacute':'ý', 'thorn':'þ', 'yuml':'ÿ', - 'euro':'€', 'nbsp':' ', "rsquo":"'", "lsquo":"'", "ldquo":"'", + 'euro':'€', 'nbsp':' ', "rsquo":"'", "lsquo":"'", "ldquo":"'", "rdquo":"'", 'ndash': '—', 'mdash': '—', 'oelig':'oe', 'quot': "'", 'mu': 'µ'} import htmlentitydefs @@ -1135,7 +1135,10 @@ class XhtmlCleaner(XmlParser): # between tags. if not self.env.currentContent or \ self.env.currentContent[-1] in ('\n', ' '): - toAdd = content.lstrip() + # I give here to lstrip an explicit list of what is to be considered + # as blank chars, because I do not want unicode NBSP chars to be in + # this list. + toAdd = content.lstrip(u' \n\r\t') else: toAdd = content # Re-transform XML special chars to entities.