Back to index

plone3  3.1.7
exportimport.py
Go to the documentation of this file.
00001 ##############################################################################
00002 #
00003 # Copyright (c) 2005 Zope Corporation and Contributors. All Rights
00004 # Reserved.
00005 #
00006 # This software is subject to the provisions of the Zope Public License,
00007 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this
00008 # distribution.
00009 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
00010 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00011 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
00012 # FOR A PARTICULAR PURPOSE.
00013 #
00014 ##############################################################################
00015 """ Export / import adapters for stock PAS plugins.
00016 
00017 TODO:
00018 
00019  o Add export / import adapters for all stock plugin types:
00020 
00021    - [X] ChallengeProtocolChooser (TitleOnlyExportImport)
00022 
00023    - [X] CookieAuthHelper (CookieAuthHelperExportImport)
00024 
00025    - [X] DelegatingMultiPlugin (DelegatePathExportImport)
00026 
00027    - [X] DomainAuthHelper (DomainAuthHelperExportImport)
00028 
00029    - [X] DynamicGroupsPlugin (DynamicGroupsPluginExportImport)
00030 
00031    - [X] HTTPBasicAuthHelper (TitleOnlyExportImport)
00032 
00033    - [X] InlineAuthHelper (TitleOnlyExportImport)
00034 
00035    - [X] LocalRolePlugin (TitleOnlyExportImport)
00036 
00037    - [X] RecursiveGroupsPlugin (TitleOnlyExportImport)
00038 
00039    - [X] RequestTypeSniffer (TitleOnlyExportImport)
00040 
00041    - [?] ScriptablePlugin (stock GenericSetup folderish support?)
00042 
00043    - [X] SearchPrincipalsPlugin (DelegatePathExportImport)
00044 
00045    - [X] SessionAuthHelper (TitleOnlyExportImport)
00046 
00047    - [X] ZODBGroupsManager (ZODBGroupManagerExportImport)
00048 
00049    - [X] ZODBRolesManager (ZODBRoleManagerExportImport)
00050 
00051    - [X] ZODBUserManager (ZODBUserManagerExportImport)
00052 
00053  o Review BasePlugin to ensure we haven't left anything out.
00054 
00055 $Id: exportimport.py 70860 2006-10-20 21:39:45Z andrew $
00056 """
00057 from xml.dom.minidom import parseString
00058 
00059 from Acquisition import Implicit
00060 from zope.interface import implements
00061 
00062 from Products.GenericSetup.interfaces import IFilesystemExporter
00063 from Products.GenericSetup.interfaces import IFilesystemImporter
00064 from Products.GenericSetup.content import DAVAwareFileAdapter
00065 from Products.GenericSetup.content import FolderishExporterImporter
00066 
00067 try:
00068     from Products.GenericSetup.utils import PageTemplateResource
00069 except ImportError: # BBB
00070     from Products.PageTemplates.PageTemplateFile \
00071         import PageTemplateFile as PageTemplateResource
00072 
00073 class SimpleXMLExportImport(Implicit):
00074     """ Base for plugins whose configuration can be dumped to an XML file.
00075 
00076     o Derived classes must define:
00077 
00078       '_FILENAME' -- a class variable naming the export template
00079 
00080       '_getExportInfo' --  a method returning a mapping which will be passed
00081        to the template as 'info'.
00082 
00083       '_ROOT_TAGNAME' -- the name of the root tag in the XML (for sanity check)
00084 
00085       '_purgeContext' -- a method which clears our context.
00086 
00087       '_updateFromDOM' -- a method taking the root node of the DOM.
00088     """
00089     implements(IFilesystemExporter, IFilesystemImporter)
00090     encoding = None
00091 
00092     def __init__(self, context):
00093         self.context = context
00094 
00095     def export(self, export_context, subdir, root=False):
00096         """ See IFilesystemExporter.
00097         """
00098         template = PageTemplateResource('xml/%s' % self._FILENAME,
00099                                         globals()).__of__(self.context)
00100         info = self._getExportInfo()
00101         export_context.writeDataFile('%s.xml' % self.context.getId(),
00102                                      template(info=info),
00103                                      'text/xml',
00104                                      subdir,
00105                                     )
00106 
00107     def listExportableItems(self):
00108         """ See IFilesystemExporter.
00109         """
00110         return ()
00111 
00112     def import_(self, import_context, subdir, root=False):
00113         """ See IFilesystemImporter
00114         """
00115         self.encoding = import_context.getEncoding()
00116 
00117         if import_context.shouldPurge():
00118             self._purgeContext()
00119 
00120         data = import_context.readDataFile('%s.xml' % self.context.getId(),
00121                                            subdir)
00122 
00123         if data is not None:
00124 
00125             dom = parseString(data)
00126             root = dom.firstChild
00127             assert root.tagName == self._ROOT_TAGNAME
00128 
00129             self.context.title = self._getNodeAttr(root, 'title', None)
00130             self._updateFromDOM(root)
00131 
00132     def _getNodeAttr(self, node, attrname, default=None):
00133         attr = node.attributes.get(attrname)
00134         if attr is None:
00135             return default
00136         value = attr.value
00137         if isinstance(value, unicode) and self.encoding is not None:
00138             value = value.encode(self.encoding)
00139         return value
00140 
00141 class ZODBUserManagerExportImport(SimpleXMLExportImport):
00142     """ Adapter for dumping / loading ZODBUSerManager to an XML file.
00143     """
00144     implements(IFilesystemExporter, IFilesystemImporter)
00145 
00146     _FILENAME = 'zodbusers.xml'
00147     _ROOT_TAGNAME = 'zodb-users'
00148 
00149     def _purgeContext(self):
00150         self.context.__init__(self.context.id, self.context.title)
00151 
00152     def _updateFromDOM(self, root):
00153         for user in root.getElementsByTagName('user'):
00154             user_id = self._getNodeAttr(user, 'user_id', None)
00155             login_name = self._getNodeAttr(user, 'login_name', None)
00156             password_hash = self._getNodeAttr(user, 'password_hash', None)
00157 
00158             if user_id is None or login_name is None or password_hash is None:
00159                 raise ValueError, 'Invalid user record'
00160 
00161             self.context.addUser(user_id, login_name, 'x')
00162             self.context._user_passwords[user_id] = password_hash
00163 
00164     def _getExportInfo(self):
00165         user_info = []
00166 
00167         for uinfo in self.context.listUserInfo():
00168             user_id = uinfo['user_id']
00169 
00170             info = {'user_id': user_id,
00171                     'login_name': uinfo['login_name'],
00172                     'password_hash': self.context._user_passwords[user_id],
00173                    }
00174 
00175             user_info.append(info)
00176 
00177         return {'title': self.context.title,
00178                 'users': user_info,
00179                }
00180 
00181 
00182 class ZODBGroupManagerExportImport(SimpleXMLExportImport):
00183     """ Adapter for dumping / loading ZODBGroupManager to an XML file.
00184     """
00185     _FILENAME = 'zodbgroups.xml'
00186     _ROOT_TAGNAME = 'zodb-groups'
00187 
00188     def _purgeContext(self):
00189         self.context.__init__(self.context.id, self.context.title)
00190 
00191     def _updateFromDOM(self, root):
00192 
00193         for group in root.getElementsByTagName('group'):
00194             group_id = self._getNodeAttr(group, 'group_id', None)
00195             title = self._getNodeAttr(group, 'title', None)
00196             description = self._getNodeAttr(group, 'description', None)
00197 
00198             self.context.addGroup(group_id, title, description)
00199 
00200             for principal in group.getElementsByTagName('principal'):
00201                 principal_id = self._getNodeAttr(principal, 'principal_id', None)
00202                 self.context.addPrincipalToGroup(principal_id, group_id)
00203 
00204     def _getExportInfo(self):
00205         group_info = []
00206         for ginfo in self.context.listGroupInfo():
00207             group_id = ginfo['id']
00208             info = {'group_id': group_id,
00209                     'title': ginfo['title'],
00210                     'description': ginfo['description'],
00211                    }
00212             info['principals'] = self._listGroupPrincipals(group_id) 
00213             group_info.append(info)
00214         return {'title': self.context.title,
00215                 'groups': group_info,
00216                }
00217 
00218     def _listGroupPrincipals(self, group_id):
00219         """ List the principal IDs of the group's members.
00220         """
00221         result = []
00222         for k, v in self.context._principal_groups.items():
00223             if group_id in v:
00224                 result.append(k)
00225         return tuple(result)
00226 
00227 
00228 
00229 class ZODBRoleManagerExportImport(SimpleXMLExportImport):
00230     """ Adapter for dumping / loading ZODBGroupManager to an XML file.
00231     """
00232     _FILENAME = 'zodbroles.xml'
00233     _ROOT_TAGNAME = 'zodb-roles'
00234 
00235     def _purgeContext(self):
00236         self.context.__init__(self.context.id, self.context.title)
00237 
00238     def _updateFromDOM(self, root):
00239         for role in root.getElementsByTagName('role'):
00240             role_id = self._getNodeAttr(role, 'role_id', None)
00241             title = self._getNodeAttr(role, 'title', None)
00242             description = self._getNodeAttr(role, 'description', None)
00243 
00244             self.context.addRole(role_id, title, description)
00245 
00246             for principal in role.getElementsByTagName('principal'):
00247                 principal_id = self._getNodeAttr(principal, 'principal_id', None)
00248                 self.context.assignRoleToPrincipal(role_id, principal_id)
00249 
00250     def _getExportInfo(self):
00251         role_info = []
00252 
00253         for rinfo in self.context.listRoleInfo():
00254             role_id = rinfo['id']
00255             info = {'role_id': role_id,
00256                     'title': rinfo['title'],
00257                     'description': rinfo['description'],
00258                    }
00259             info['principals'] = self._listRolePrincipals(role_id) 
00260             role_info.append(info)
00261 
00262         return {'title': self.context.title,
00263                 'roles': role_info,
00264                }
00265 
00266     def _listRolePrincipals(self, role_id):
00267         """ List the principal IDs of the group's members.
00268         """
00269         result = []
00270         for k, v in self.context._principal_roles.items():
00271             if role_id in v:
00272                 result.append(k)
00273         return tuple(result)
00274 
00275 class CookieAuthHelperExportImport(SimpleXMLExportImport):
00276     """ Adapter for dumping / loading CookieAuthHelper to an XML file.
00277     """
00278     _FILENAME = 'cookieauth.xml'
00279     _ROOT_TAGNAME = 'cookie-auth'
00280 
00281     def _purgeContext(self):
00282         pass
00283 
00284     def _updateFromDOM(self, root):
00285         cookie_name = self._getNodeAttr(root, 'cookie_name', None)
00286         if cookie_name is not None:
00287             self.context.cookie_name = cookie_name
00288         else:
00289             try:
00290                 del self.context.cookie_name
00291             except AttributeError:
00292                 pass
00293 
00294         login_path = self._getNodeAttr(root, 'login_path', None)
00295         if login_path is not None:
00296             self.context.login_path = login_path
00297         else:
00298             try:
00299                 del self.context.login_path
00300             except AttributeError:
00301                 pass
00302 
00303     def _getExportInfo(self):
00304         return {'title': self.context.title,
00305                 'cookie_name': self.context.cookie_name,
00306                 'login_path': self.context.login_path,
00307                }
00308 
00309 class DomainAuthHelperExportImport(SimpleXMLExportImport):
00310     """ Adapter for dumping / loading DomainAuthHelper to an XML file.
00311     """
00312     _FILENAME = 'domainauth.xml'
00313     _ROOT_TAGNAME = 'domain-auth'
00314 
00315     def _purgeContext(self):
00316         self.context.__init__(self.context.id, self.context.title)
00317 
00318     def _updateFromDOM(self, root):
00319         for user in root.getElementsByTagName('user'):
00320             user_id = self._getNodeAttr(user, 'user_id', None)
00321 
00322             for match in user.getElementsByTagName('match'):
00323                 username = self._getNodeAttr(match, 'username', None)
00324                 match_type = self._getNodeAttr(match, 'match_type', None)
00325                 match_string = self._getNodeAttr(match, 'match_string', None)
00326                 role_tokens = self._getNodeAttr(match, 'roles', None)
00327                 roles = role_tokens.split(',')
00328 
00329                 self.context.manage_addMapping(user_id=user_id,
00330                                                match_type=match_type,
00331                                                match_string=match_string,
00332                                                username=username,
00333                                                roles=roles,
00334                                               )
00335 
00336     def _getExportInfo(self):
00337         user_map = {}
00338         for k, v in self.context._domain_map.items():
00339             user_map[k] = matches = []
00340             for match in v:
00341                 match = match.copy()
00342                 match['roles'] = ','.join(match['roles'])
00343                 matches.append(match)
00344 
00345         return {'title': self.context.title,
00346                 'map': user_map
00347                }
00348 
00349 class TitleOnlyExportImport(SimpleXMLExportImport):
00350     """ Adapter for dumping / loading title-only plugins to an XML file.
00351     """
00352     _FILENAME = 'titleonly.xml'
00353     _ROOT_TAGNAME = 'plug-in'
00354 
00355     def _purgeContext(self):
00356         pass
00357 
00358     def _updateFromDOM(self, root):
00359         pass
00360 
00361     def _getExportInfo(self):
00362         return {'title': self.context.title,
00363                }
00364 
00365 class DelegatePathExportImport(SimpleXMLExportImport):
00366     """ Adapter for dumping / loading plugins with 'delegate' via XML.
00367     """
00368     _FILENAME = 'delegatepath.xml'
00369     _ROOT_TAGNAME = 'delegating-plugin'
00370 
00371     def _purgeContext(self):
00372         pass
00373 
00374     def _updateFromDOM(self, root):
00375         delegate = self._getNodeAttr(root, 'delegate', None)
00376         if delegate is not None:
00377             self.context.delegate = delegate
00378         else:
00379             try:
00380                 del self.context.delegate
00381             except AttributeError:
00382                 pass
00383 
00384     def _getExportInfo(self):
00385         return {'title': self.context.title,
00386                 'delegate': self.context.delegate,
00387                }
00388 
00389 class DynamicGroupsPluginExportImport(SimpleXMLExportImport):
00390     """ Adapter for dumping / loading DynamicGroupsPlugin to an XML file.
00391     """
00392     _FILENAME = 'dynamicgroups.xml'
00393     _ROOT_TAGNAME = 'dynamic-groups'
00394 
00395     def _purgeContext(self):
00396         for group_id in self.context.listGroupIds():
00397             self.context.removeGroup(group_id)
00398 
00399     def _updateFromDOM(self, root):
00400         for group in root.getElementsByTagName('group'):
00401             group_id = self._getNodeAttr(group, 'group_id', None)
00402             predicate = self._getNodeAttr(group, 'predicate', None)
00403             title = self._getNodeAttr(group, 'title', None)
00404             description = self._getNodeAttr(group, 'description', None)
00405             active = self._getNodeAttr(group, 'active', None)
00406 
00407             self.context.addGroup(group_id,
00408                                   predicate,
00409                                   title,
00410                                   description,
00411                                   active == 'True',
00412                                  )
00413     def _getExportInfo(self):
00414         group_info = []
00415 
00416         for ginfo in self.context.listGroupInfo():
00417             group_id = ginfo['id']
00418             info = {'group_id': group_id,
00419                     'predicate': ginfo['predicate'],
00420                     'title': ginfo['title'],
00421                     'description': ginfo['description'],
00422                     'active': ginfo['active'],
00423                    }
00424             group_info.append(info)
00425 
00426         return {'title': self.context.title,
00427                 'groups': group_info,
00428                }
00429 
00430 class ScriptablePluginExportImport(FolderishExporterImporter):
00431     """ Export / import the Scriptable type plugin.
00432     """
00433     def export(self, export_context, subdir, root=False):
00434         """ See IFilesystemExporter.
00435         """
00436         FolderishExporterImporter.export(self, export_context, subdir, root)
00437 
00438     def import_(self, import_context, subdir, root=False):
00439         """ See IFilesystemImporter.
00440         """
00441         FolderishExporterImporter.import_(self, import_context, subdir, root)
00442 
00443 class PythonScriptFileAdapter(DAVAwareFileAdapter):
00444     """File-ish for PythonScript.
00445     """
00446     def _getFileName(self):
00447         """ Return the name under which our file data is stored.
00448         """
00449         return '%s.py' % self.context.getId()