• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zopefoundation / Products.PluggableAuthService / 5303493172

pending completion
5303493172

push

github

web-flow
Drop support for Python 2.7, 3.5, 3.6. (#116)

* Drop zserver extra in setup.py. Thus dropping FTP support.
* Drop support for Zope < 5.
Co-authored-by: Jens Vagelpohl <jens@plyp.com>

1288 of 1745 branches covered (73.81%)

Branch coverage included in aggregate %.

127 of 127 new or added lines in 30 files covered. (100.0%)

9619 of 10349 relevant lines covered (92.95%)

0.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.74
/src/Products/PluggableAuthService/plugins/exportimport.py
1
##############################################################################
2
#
3
# Copyright (c) 2005 Zope Foundation and Contributors
4
#
5
# This software is subject to the provisions of the Zope Public License,
6
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this
7
# distribution.
8
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11
# FOR A PARTICULAR PURPOSE.
12
#
13
##############################################################################
14
""" Export / import adapters for stock PAS plugins.
1✔
15
"""
16

17
import os
1✔
18
import sys
1✔
19
from xml.dom.minidom import parseString
1✔
20

21
from Acquisition import Implicit
1✔
22
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
1✔
23
from zope.interface import implementer
1✔
24

25
from Products.GenericSetup.content import DAVAwareFileAdapter
1✔
26
from Products.GenericSetup.content import FolderishExporterImporter
1✔
27
from Products.GenericSetup.interfaces import IFilesystemExporter
1✔
28
from Products.GenericSetup.interfaces import IFilesystemImporter
1✔
29

30

31
def getPackagePath(instance):
1✔
32
    module = sys.modules[instance.__module__]
1✔
33
    return os.path.dirname(module.__file__)
1✔
34

35

36
@implementer(IFilesystemExporter, IFilesystemImporter)
1✔
37
class SimpleXMLExportImport(Implicit):
1✔
38
    """ Base for plugins whose configuration can be dumped to an XML file.
39

40
    o Derived classes must define:
41

42
      '_FILENAME' -- a class variable naming the export template
43

44
      '_getExportInfo' --  a method returning a mapping which will be passed
45
       to the template as 'info'.
46

47
      '_ROOT_TAGNAME' -- the name of the root tag in the XML (for sanity check)
48

49
      '_purgeContext' -- a method which clears our context.
50

51
      '_updateFromDOM' -- a method taking the root node of the DOM.
52
    """
53
    encoding = None
1✔
54

55
    def __init__(self, context):
1✔
56
        self.context = context
1✔
57

58
    def export(self, export_context, subdir, root=False):
1✔
59
        """ See IFilesystemExporter.
60
        """
61
        package_path = getPackagePath(self)
1✔
62
        template = PageTemplateFile('xml_templates/%s' % self._FILENAME,
1✔
63
                                    package_path).__of__(self.context)
64
        info = self._getExportInfo()
1✔
65
        export_context.writeDataFile('%s.xml' % self.context.getId(),
1✔
66
                                     template(info=info).encode('utf-8'),
67
                                     'text/xml',
68
                                     subdir)
69

70
    def listExportableItems(self):
1✔
71
        """ See IFilesystemExporter.
72
        """
73
        return ()
1✔
74

75
    def import_(self, import_context, subdir, root=False):
1✔
76
        """ See IFilesystemImporter
77
        """
78
        self.encoding = import_context.getEncoding()
1✔
79

80
        if import_context.shouldPurge():
1✔
81
            self._purgeContext()
1✔
82

83
        data = import_context.readDataFile('%s.xml' % self.context.getId(),
1✔
84
                                           subdir)
85

86
        if data is not None:
1!
87

88
            dom = parseString(data)
1✔
89
            root = dom.firstChild
1✔
90
            assert root.tagName == self._ROOT_TAGNAME
1✔
91

92
            self.context.title = self._getNodeAttr(root, 'title', None)
1✔
93
            self._updateFromDOM(root)
1✔
94

95
    def _getNodeAttr(self, node, attrname, default=None):
1✔
96
        attr = node.attributes.get(attrname)
1✔
97
        if attr is None:
1✔
98
            return default
1✔
99
        value = attr.value
1✔
100
        return value
1✔
101

102

103
@implementer(IFilesystemExporter, IFilesystemImporter)
1✔
104
class ZODBUserManagerExportImport(SimpleXMLExportImport):
1✔
105
    """ Adapter for dumping / loading ZODBUSerManager to an XML file.
106
    """
107

108
    _FILENAME = 'zodbusers.xml'
1✔
109
    _ROOT_TAGNAME = 'zodb-users'
1✔
110

111
    def _purgeContext(self):
1✔
112
        self.context.__init__(self.context.id, self.context.title)
1✔
113

114
    def _updateFromDOM(self, root):
1✔
115
        for user in root.getElementsByTagName('user'):
1✔
116
            user_id = self._getNodeAttr(user, 'user_id', None)
1✔
117
            login_name = self._getNodeAttr(user, 'login_name', None)
1✔
118
            password_hash = self._getNodeAttr(user, 'password_hash', None)
1✔
119

120
            if user_id is None or login_name is None or password_hash is None:
1!
121
                raise ValueError('Invalid user record')
×
122

123
            self.context.addUser(user_id, login_name, 'x')
1✔
124
            self.context._user_passwords[user_id] = password_hash
1✔
125

126
    def _getExportInfo(self):
1✔
127
        user_info = []
1✔
128

129
        for uinfo in self.context.listUserInfo():
1✔
130
            user_id = uinfo['user_id']
1✔
131

132
            info = {'user_id': user_id,
1✔
133
                    'login_name': uinfo['login_name'],
134
                    'password_hash': self.context._user_passwords[user_id]}
135

136
            user_info.append(info)
1✔
137

138
        return {'title': self.context.title, 'users': user_info}
1✔
139

140

141
class ZODBGroupManagerExportImport(SimpleXMLExportImport):
1✔
142
    """ Adapter for dumping / loading ZODBGroupManager to an XML file.
143
    """
144
    _FILENAME = 'zodbgroups.xml'
1✔
145
    _ROOT_TAGNAME = 'zodb-groups'
1✔
146

147
    def _purgeContext(self):
1✔
148
        self.context.__init__(self.context.id, self.context.title)
1✔
149

150
    def _updateFromDOM(self, root):
1✔
151

152
        for group in root.getElementsByTagName('group'):
1✔
153
            group_id = self._getNodeAttr(group, 'group_id', None)
1✔
154
            title = self._getNodeAttr(group, 'title', None)
1✔
155
            description = self._getNodeAttr(group, 'description', None)
1✔
156

157
            self.context.addGroup(group_id, title, description)
1✔
158

159
            for principal in group.getElementsByTagName('principal'):
1✔
160
                principal_id = self._getNodeAttr(principal, 'principal_id',
1✔
161
                                                 None)
162
                self.context.addPrincipalToGroup(principal_id, group_id)
1✔
163

164
    def _getExportInfo(self):
1✔
165
        group_info = []
1✔
166
        for ginfo in self.context.listGroupInfo():
1✔
167
            group_id = ginfo['id']
1✔
168
            info = {'group_id': group_id,
1✔
169
                    'title': ginfo['title'],
170
                    'description': ginfo['description']}
171
            info['principals'] = self._listGroupPrincipals(group_id)
1✔
172
            group_info.append(info)
1✔
173
        return {'title': self.context.title, 'groups': group_info}
1✔
174

175
    def _listGroupPrincipals(self, group_id):
1✔
176
        """ List the principal IDs of the group's members.
177
        """
178
        result = []
1✔
179
        for k, v in self.context._principal_groups.items():
1✔
180
            if group_id in v:
1✔
181
                result.append(k)
1✔
182
        return tuple(result)
1✔
183

184

185
class ZODBRoleManagerExportImport(SimpleXMLExportImport):
1✔
186
    """ Adapter for dumping / loading ZODBGroupManager to an XML file.
187
    """
188
    _FILENAME = 'zodbroles.xml'
1✔
189
    _ROOT_TAGNAME = 'zodb-roles'
1✔
190

191
    def _purgeContext(self):
1✔
192
        self.context.__init__(self.context.id, self.context.title)
1✔
193

194
    def _updateFromDOM(self, root):
1✔
195
        for role in root.getElementsByTagName('role'):
1✔
196
            role_id = self._getNodeAttr(role, 'role_id', None)
1✔
197
            title = self._getNodeAttr(role, 'title', None)
1✔
198
            description = self._getNodeAttr(role, 'description', None)
1✔
199

200
            try:
1✔
201
                self.context.addRole(role_id, title, description)
1✔
202
            except KeyError:
×
203
                # it's fine if the role already exists
204
                pass
×
205

206
            for principal in role.getElementsByTagName('principal'):
1✔
207
                principal_id = self._getNodeAttr(principal, 'principal_id',
1✔
208
                                                 None)
209
                self.context.assignRoleToPrincipal(role_id, principal_id)
1✔
210

211
    def _getExportInfo(self):
1✔
212
        role_info = []
1✔
213

214
        for rinfo in self.context.listRoleInfo():
1✔
215
            role_id = rinfo['id']
1✔
216
            info = {'role_id': role_id,
1✔
217
                    'title': rinfo['title'],
218
                    'description': rinfo['description']}
219
            info['principals'] = self._listRolePrincipals(role_id)
1✔
220
            role_info.append(info)
1✔
221

222
        return {'title': self.context.title, 'roles': role_info}
1✔
223

224
    def _listRolePrincipals(self, role_id):
1✔
225
        """ List the principal IDs of the group's members.
226
        """
227
        result = []
1✔
228
        for k, v in self.context._principal_roles.items():
1✔
229
            if role_id in v:
1✔
230
                result.append(k)
1✔
231
        return tuple(result)
1✔
232

233

234
class CookieAuthHelperExportImport(SimpleXMLExportImport):
1✔
235
    """ Adapter for dumping / loading CookieAuthHelper to an XML file.
236
    """
237
    _FILENAME = 'cookieauth.xml'
1✔
238
    _ROOT_TAGNAME = 'cookie-auth'
1✔
239

240
    def _purgeContext(self):
1✔
241
        pass
1✔
242

243
    def _updateFromDOM(self, root):
1✔
244
        cookie_name = self._getNodeAttr(root, 'cookie_name', None)
1✔
245
        if cookie_name is not None:
1!
246
            self.context.cookie_name = cookie_name
1✔
247
        else:
248
            try:
×
249
                del self.context.cookie_name
×
250
            except AttributeError:
×
251
                pass
×
252

253
        login_path = self._getNodeAttr(root, 'login_path', None)
1✔
254
        if login_path is not None:
1!
255
            self.context.login_path = login_path
1✔
256
        else:
257
            try:
×
258
                del self.context.login_path
×
259
            except AttributeError:
×
260
                pass
×
261

262
    def _getExportInfo(self):
1✔
263
        return {'title': self.context.title,
1✔
264
                'cookie_name': self.context.cookie_name,
265
                'login_path': self.context.login_path}
266

267

268
class DomainAuthHelperExportImport(SimpleXMLExportImport):
1✔
269
    """ Adapter for dumping / loading DomainAuthHelper to an XML file.
270
    """
271
    _FILENAME = 'domainauth.xml'
1✔
272
    _ROOT_TAGNAME = 'domain-auth'
1✔
273

274
    def _purgeContext(self):
1✔
275
        self.context.__init__(self.context.id, self.context.title)
1✔
276

277
    def _updateFromDOM(self, root):
1✔
278
        for user in root.getElementsByTagName('user'):
1✔
279
            user_id = self._getNodeAttr(user, 'user_id', None)
1✔
280

281
            for match in user.getElementsByTagName('match'):
1✔
282
                username = self._getNodeAttr(match, 'username', None)
1✔
283
                match_type = self._getNodeAttr(match, 'match_type', None)
1✔
284
                match_string = self._getNodeAttr(match, 'match_string', None)
1✔
285
                role_tokens = self._getNodeAttr(match, 'roles', None)
1✔
286
                roles = role_tokens.split(',')
1✔
287

288
                self.context.manage_addMapping(user_id=user_id,
1✔
289
                                               match_type=match_type,
290
                                               match_string=match_string,
291
                                               username=username,
292
                                               roles=roles)
293

294
    def _getExportInfo(self):
1✔
295
        user_map = {}
1✔
296
        for k, v in self.context._domain_map.items():
1✔
297
            user_map[k] = matches = []
1✔
298
            for match in v:
1✔
299
                match = match.copy()
1✔
300
                match['roles'] = ','.join(match['roles'])
1✔
301
                matches.append(match)
1✔
302

303
        return {'title': self.context.title, 'map': user_map}
1✔
304

305

306
class TitleOnlyExportImport(SimpleXMLExportImport):
1✔
307
    """ Adapter for dumping / loading title-only plugins to an XML file.
308
    """
309
    _FILENAME = 'titleonly.xml'
1✔
310
    _ROOT_TAGNAME = 'plug-in'
1✔
311

312
    def _purgeContext(self):
1✔
313
        pass
1✔
314

315
    def _updateFromDOM(self, root):
1✔
316
        pass
1✔
317

318
    def _getExportInfo(self):
1✔
319
        return {'title': self.context.title}
1✔
320

321

322
class DelegatePathExportImport(SimpleXMLExportImport):
1✔
323
    """ Adapter for dumping / loading plugins with 'delegate' via XML.
324
    """
325
    _FILENAME = 'delegatepath.xml'
1✔
326
    _ROOT_TAGNAME = 'delegating-plugin'
1✔
327

328
    def _purgeContext(self):
1✔
329
        pass
1✔
330

331
    def _updateFromDOM(self, root):
1✔
332
        delegate = self._getNodeAttr(root, 'delegate', None)
1✔
333
        if delegate is not None:
1!
334
            self.context.delegate = delegate
1✔
335
        else:
336
            try:
×
337
                del self.context.delegate
×
338
            except AttributeError:
×
339
                pass
×
340

341
    def _getExportInfo(self):
1✔
342
        return {'title': self.context.title,
1✔
343
                'delegate': self.context.delegate}
344

345

346
class DynamicGroupsPluginExportImport(SimpleXMLExportImport):
1✔
347
    """ Adapter for dumping / loading DynamicGroupsPlugin to an XML file.
348
    """
349
    _FILENAME = 'dynamicgroups.xml'
1✔
350
    _ROOT_TAGNAME = 'dynamic-groups'
1✔
351

352
    def _purgeContext(self):
1✔
353
        for group_id in self.context.listGroupIds():
1✔
354
            self.context.removeGroup(group_id)
1✔
355

356
    def _updateFromDOM(self, root):
1✔
357
        for group in root.getElementsByTagName('group'):
1✔
358
            group_id = self._getNodeAttr(group, 'group_id', None)
1✔
359
            predicate = self._getNodeAttr(group, 'predicate', None)
1✔
360
            title = self._getNodeAttr(group, 'title', None)
1✔
361
            description = self._getNodeAttr(group, 'description', None)
1✔
362
            active = self._getNodeAttr(group, 'active', None)
1✔
363

364
            self.context.addGroup(group_id, predicate, title,
1✔
365
                                  description, active == 'True')
366

367
    def _getExportInfo(self):
1✔
368
        group_info = []
1✔
369

370
        for ginfo in self.context.listGroupInfo():
1✔
371
            group_id = ginfo['id']
1✔
372
            info = {'group_id': group_id,
1✔
373
                    'predicate': ginfo['predicate'],
374
                    'title': ginfo['title'],
375
                    'description': ginfo['description'],
376
                    'active': ginfo['active']}
377
            group_info.append(info)
1✔
378

379
        return {'title': self.context.title, 'groups': group_info}
1✔
380

381

382
class ChallengeProtocolChooserExportImport(SimpleXMLExportImport):
1✔
383
    """ Adapter for dumping / loading ChallengeProtocolChooser to an XML file.
384
    """
385
    _FILENAME = 'chooser.xml'
1✔
386
    _ROOT_TAGNAME = 'challenge-protocol-chooser'
1✔
387

388
    def _purgeContext(self):
1✔
389
        self.context._map.clear()
1✔
390

391
    def _updateFromDOM(self, root):
1✔
392
        for mapping in root.getElementsByTagName('mapping'):
1✔
393
            label = self._getNodeAttr(mapping, 'label', None)
1✔
394
            protocols = self._getNodeAttr(mapping, 'protocols', '').split(',')
1✔
395
            self.context._map[label] = tuple([_f for _f in protocols if _f])
1✔
396

397
    def _getExportInfo(self):
1✔
398
        from .ChallengeProtocolChooser import listRequestTypesLabels
1✔
399

400
        request_type_info = []
1✔
401

402
        for label in sorted(listRequestTypesLabels()):
1✔
403
            protocols = sorted(self.context._map.get(label, []))
1✔
404
            request_type_info.append({'label': label,
1✔
405
                                      'protocols': protocols})
406

407
        return {'title': self.context.title,
1✔
408
                'request_types': request_type_info}
409

410

411
class ScriptablePluginExportImport(FolderishExporterImporter):
1✔
412
    """ Export / import the Scriptable type plugin.
413
    """
414

415
    def export(self, export_context, subdir, root=False):
1✔
416
        """ See IFilesystemExporter.
417
        """
418
        FolderishExporterImporter.export(self, export_context, subdir, root)
×
419

420
    def import_(self, import_context, subdir, root=False):
1✔
421
        """ See IFilesystemImporter.
422
        """
423
        FolderishExporterImporter.import_(self, import_context, subdir, root)
×
424

425

426
class PythonScriptFileAdapter(DAVAwareFileAdapter):
1✔
427
    """File-ish for PythonScript.
428
    """
429

430
    def _getFileName(self):
1✔
431
        """ Return the name under which our file data is stored.
432
        """
433
        return '%s.py' % self.context.getId()
×
434

435

436
class NotCompetent_byRolesExportImport(SimpleXMLExportImport):
1✔
437
    """ Adapter for dumping / loading NCbR plugin.
438
    """
439
    _FILENAME = 'notcompetent.xml'
1✔
440
    _ROOT_TAGNAME = 'not-competent-by-roles'
1✔
441

442
    def _purgeContext(self):
1✔
443
        pass
1✔
444

445
    def _updateFromDOM(self, root):
1✔
446
        roles = []
1✔
447
        for node in root.getElementsByTagName('role'):
1✔
448
            role = node.firstChild.wholeText
1✔
449
            roles.append(role.strip())
1✔
450
        self.context.roles = tuple(roles)
1✔
451

452
    def _getExportInfo(self):
1✔
453
        return {'title': self.context.title, 'roles': self.context.roles}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc