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

zopefoundation / Zope / 6263629025

21 Sep 2023 03:12PM UTC coverage: 82.146% (-0.01%) from 82.159%
6263629025

Pull #1164

github

web-flow
[pre-commit.ci lite] apply automatic fixes
Pull Request #1164: Move all linters to pre-commit.

4353 of 6963 branches covered (0.0%)

Branch coverage included in aggregate %.

487 of 487 new or added lines in 186 files covered. (100.0%)

27394 of 31684 relevant lines covered (86.46%)

0.86 hits per line

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

34.21
/src/OFS/userfolder.py
1
##############################################################################
2
#
3
# Copyright (c) 2002 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 distribution.
7
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10
# FOR A PARTICULAR PURPOSE.
11
#
12
##############################################################################
13
"""User folders."""
1✔
14

15
import os
1✔
16

17
from AccessControl import ClassSecurityInfo
1✔
18
from AccessControl import userfolder as accesscontrol_userfolder
1✔
19
from AccessControl.class_init import InitializeClass
1✔
20
from AccessControl.Permissions import manage_users as ManageUsers
1✔
21
from AccessControl.requestmethod import requestmethod
1✔
22
from AccessControl.rolemanager import DEFAULTMAXLISTUSERS
1✔
23
from AccessControl.users import _remote_user_mode
1✔
24
from AccessControl.users import emergency_user
1✔
25
from AccessControl.users import readUserAccessFile
1✔
26
from AccessControl.users import reqattr
1✔
27
from Acquisition import aq_base
1✔
28
from App.Management import Navigation
1✔
29
from App.Management import Tabs
1✔
30
from App.special_dtml import DTMLFile
1✔
31
from OFS.role import RoleManager
1✔
32
from OFS.SimpleItem import Item
1✔
33
from zExceptions import BadRequest
1✔
34

35

36
class BasicUserFolder(
1✔
37
    Navigation,
38
    Tabs,
39
    Item,
40
    RoleManager,
41
    accesscontrol_userfolder.BasicUserFolder
42
):
43
    """Base class for UserFolder-like objects."""
44

45
    security = ClassSecurityInfo()
1✔
46

47
    # Note: use of the '_super' name is deprecated.
48
    _super = emergency_user
1✔
49

50
    manage_options = ((
1✔
51
        {'label': 'Contents', 'action': 'manage_main'},
52
        {'label': 'Properties', 'action': 'manage_userFolderProperties'},
53
    ) + RoleManager.manage_options + Item.manage_options)
54

55
    @security.protected(ManageUsers)
1✔
56
    @requestmethod('POST')
1✔
57
    def userFolderAddUser(self, name, password, roles, domains,
1✔
58
                          REQUEST=None, **kw):
59
        """API method for creating a new user object.
60

61
        Note that not all user folder implementations support dynamic
62
        creation of user objects.
63
        """
64
        if hasattr(self, '_doAddUser'):
1✔
65
            return self._doAddUser(name, password, roles, domains, **kw)
1✔
66
        raise NotImplementedError
67

68
    @security.protected(ManageUsers)
1✔
69
    @requestmethod('POST')
1✔
70
    def userFolderEditUser(
1✔
71
        self,
72
        name,
73
        password,
74
        roles,
75
        domains,
76
        REQUEST=None,
77
        **kw
78
    ):
79
        """API method for changing user object attributes.
80

81
        Note that not all user folder implementations support changing
82
        of user object attributes.
83
        """
84
        if hasattr(self, '_doChangeUser'):
1✔
85
            return self._doChangeUser(name, password, roles, domains, **kw)
1✔
86
        raise NotImplementedError
87

88
    @security.protected(ManageUsers)
1✔
89
    @requestmethod('POST')
1✔
90
    def userFolderDelUsers(self, names, REQUEST=None):
1✔
91
        """API method for deleting one or more user objects.
92

93
        Note that not all user folder implementations support deletion
94
        of user objects.
95
        """
96
        if hasattr(self, '_doDelUsers'):
×
97
            return self._doDelUsers(names)
×
98
        raise NotImplementedError
99

100
    _mainUser = DTMLFile('dtml/mainUser', globals())
1✔
101
    _add_User = DTMLFile('dtml/addUser', globals(),
1✔
102
                         remote_user_mode__=_remote_user_mode)
103
    _editUser = DTMLFile('dtml/editUser', globals(),
1✔
104
                         remote_user_mode__=_remote_user_mode)
105
    manage = manage_main = _mainUser
1✔
106
    manage_main._setName('manage_main')
1✔
107

108
    _userFolderProperties = DTMLFile('dtml/userFolderProps', globals())
1✔
109

110
    def manage_userFolderProperties(
1✔
111
        self,
112
        REQUEST=None,
113
        manage_tabs_message=None
114
    ):
115
        """"""
116
        return self._userFolderProperties(
×
117
            self,
118
            REQUEST,
119
            manage_tabs_message=manage_tabs_message,
120
            management_view='Properties',
121
        )
122

123
    @requestmethod('POST')
1✔
124
    def manage_setUserFolderProperties(
1✔
125
        self,
126
        encrypt_passwords=0,
127
        update_passwords=0,
128
        maxlistusers=DEFAULTMAXLISTUSERS,
129
        REQUEST=None
130
    ):
131
        """Sets the properties of the user folder."""
132
        self.encrypt_passwords = not not encrypt_passwords
×
133
        try:
×
134
            self.maxlistusers = int(maxlistusers)
×
135
        except ValueError:
×
136
            self.maxlistusers = DEFAULTMAXLISTUSERS
×
137
        if encrypt_passwords and update_passwords:
×
138
            changed = 0
×
139
            for u in self.getUsers():
×
140
                pw = u._getPassword()
×
141
                if not self._isPasswordEncrypted(pw):
×
142
                    pw = self._encryptPassword(pw)
×
143
                    self._doChangeUser(u.getId(), pw, u.getRoles(),
×
144
                                       u.getDomains())
145
                    changed = changed + 1
×
146
            if REQUEST is not None:
×
147
                if not changed:
×
148
                    msg = 'All passwords already encrypted.'
×
149
                else:
150
                    msg = 'Encrypted %d password(s).' % changed
×
151
                return self.manage_userFolderProperties(
×
152
                    REQUEST, manage_tabs_message=msg)
153
            else:
154
                return changed
×
155
        else:
156
            if REQUEST is not None:
×
157
                return self.manage_userFolderProperties(
×
158
                    REQUEST, manage_tabs_message='Saved changes.')
159

160
    @requestmethod('POST')
1✔
161
    def _addUser(self, name, password, confirm, roles, domains, REQUEST=None):
1✔
162
        if not name:
×
163
            raise BadRequest('A username must be specified')
×
164
        if not password or not confirm:
×
165
            if not domains:
×
166
                raise BadRequest('Password and confirmation must be specified')
×
167
        em_user = self._emergency_user
×
168
        if self.getUser(name) or (em_user and name == em_user.getUserName()):
×
169
            raise BadRequest('A user with the specified name already exists')
×
170
        if (password or confirm) and (password != confirm):
×
171
            raise BadRequest('Password and confirmation do not match')
×
172

173
        if not roles:
×
174
            roles = []
×
175
        if not domains:
×
176
            domains = []
×
177

178
        if domains and not self.domainSpecValidate(domains):
×
179
            raise BadRequest('Illegal domain specification')
×
180
        self._doAddUser(name, password, roles, domains)
×
181
        if REQUEST:
×
182
            return self._mainUser(self, REQUEST)
×
183

184
    @requestmethod('POST')
1✔
185
    def _changeUser(self, name, password, confirm, roles, domains,
1✔
186
                    REQUEST=None):
187
        if password == 'password' and confirm == 'pconfirm':
×
188
            # Protocol for editUser.dtml to indicate unchanged password
189
            password = confirm = None
×
190
        if not name:
×
191
            raise BadRequest('A username must be specified')
×
192
        if password == confirm == '':
×
193
            if not domains:
×
194
                raise BadRequest('Password and confirmation must be specified')
×
195
        if not self.getUser(name):
×
196
            raise BadRequest('Unknown user')
×
197
        if (password or confirm) and (password != confirm):
×
198
            raise BadRequest('Password and confirmation do not match')
×
199

200
        if not roles:
×
201
            roles = []
×
202
        if not domains:
×
203
            domains = []
×
204

205
        if domains and not self.domainSpecValidate(domains):
×
206
            raise BadRequest('Illegal domain specification')
×
207
        self._doChangeUser(name, password, roles, domains)
×
208
        if REQUEST:
×
209
            return self._mainUser(self, REQUEST)
×
210

211
    @requestmethod('POST')
1✔
212
    def _delUsers(self, names, REQUEST=None):
1✔
213
        if not names:
×
214
            raise BadRequest('No users specified')
×
215
        self._doDelUsers(names)
×
216
        if REQUEST:
×
217
            return self._mainUser(self, REQUEST)
×
218

219
    @security.protected(ManageUsers)
1✔
220
    def manage_users(self, submit=None, REQUEST=None, RESPONSE=None):
1✔
221
        """This method handles operations on users for the web based forms of
222
        the ZMI.
223

224
        Application code (code that is outside of the forms that
225
        implement the UI of a user folder) are encouraged to use
226
        manage_std_addUser
227
        """
228
        if submit == 'Add...':
×
229
            return self._add_User(self, REQUEST)
×
230

231
        if submit == 'Edit':
×
232
            try:
×
233
                user = self.getUser(reqattr(REQUEST, 'name'))
×
234
            except Exception:
×
235
                raise BadRequest('The specified user does not exist')
×
236
            return self._editUser(self, REQUEST, user=user, password=user.__)
×
237

238
        if submit == 'Add':
×
239
            name = reqattr(REQUEST, 'name')
×
240
            password = reqattr(REQUEST, 'password')
×
241
            confirm = reqattr(REQUEST, 'confirm')
×
242
            roles = reqattr(REQUEST, 'roles')
×
243
            domains = reqattr(REQUEST, 'domains')
×
244
            return self._addUser(name, password, confirm, roles,
×
245
                                 domains, REQUEST)
246

247
        if submit == 'Change':
×
248
            name = reqattr(REQUEST, 'name')
×
249
            password = reqattr(REQUEST, 'password')
×
250
            confirm = reqattr(REQUEST, 'confirm')
×
251
            roles = reqattr(REQUEST, 'roles')
×
252
            domains = reqattr(REQUEST, 'domains')
×
253
            return self._changeUser(name, password, confirm, roles,
×
254
                                    domains, REQUEST)
255

256
        if submit == 'Delete':
×
257
            names = reqattr(REQUEST, 'names')
×
258
            return self._delUsers(names, REQUEST)
×
259

260
        return self._mainUser(self, REQUEST)
×
261

262
    def manage_beforeDelete(self, item, container):
1✔
263
        if item is self:
1!
264
            try:
×
265
                del container.__allow_groups__
×
266
            except Exception:
×
267
                pass
×
268

269
    def manage_afterAdd(self, item, container):
1✔
270
        if item is self:
1!
271
            self = aq_base(self)
1✔
272
            container.__allow_groups__ = self
1✔
273

274
    def _setId(self, id):
1✔
275
        if id != self.id:
×
276
            raise ValueError('Cannot change the id of a UserFolder')
×
277

278

279
InitializeClass(BasicUserFolder)
1✔
280

281

282
class UserFolder(accesscontrol_userfolder.UserFolder, BasicUserFolder):
1✔
283
    """Standard UserFolder object.
284

285
    A UserFolder holds User objects which contain information about
286
    users including name, password domain, and roles. UserFolders
287
    function chiefly to control access by authenticating users and
288
    binding them to a collection of roles.
289
    """
290

291
    _ofs_migrated = False
1✔
292

293
    def __init__(self):
1✔
294
        super().__init__()
1✔
295
        self._ofs_migrated = True
1✔
296

297
    def _createInitialUser(self):
1✔
298
        """If there are no users or only one user in this user folder,
299
        populates from the 'inituser' file in the instance home.
300

301
        We have to do this even when there is already a user just in
302
        case the initial user ignored the setup messages. We don't do it
303
        for more than one user to avoid abuse of this mechanism. Called
304
        only by OFS.Application.initialize().
305
        """
306
        if len(self.data) <= 1:
1!
307
            info = readUserAccessFile('inituser')
1✔
308
            if info:
1✔
309
                import App.config
1✔
310
                name, password, domains, remote_user_mode = info
1✔
311
                self._doDelUsers(self.getUserNames())
1✔
312
                self._doAddUser(name, password, ('Manager', ), domains)
1✔
313
                cfg = App.config.getConfiguration()
1✔
314
                try:
1✔
315
                    os.remove(os.path.join(cfg.instancehome, 'inituser'))
1✔
316
                except Exception:
×
317
                    pass
×
318

319

320
InitializeClass(UserFolder)
1✔
321

322

323
def manage_addUserFolder(self, dtself=None, REQUEST=None, **ignored):
1✔
324
    """"""
325
    f = UserFolder()
1✔
326
    self = self.this()
1✔
327
    try:
1✔
328
        self._setObject('acl_users', f)
1✔
329
    except Exception:
×
330
        raise BadRequest('This object already contains a User Folder')
×
331
    self.__allow_groups__ = f
1✔
332
    if REQUEST is not None:
1!
333
        REQUEST.RESPONSE.redirect(self.absolute_url() + '/manage_main')
×
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