Commit 128c6215 authored by Hanno Schlichting's avatar Hanno Schlichting

Split out ZMI related parts of user folders and move it to OFS

parent bb0df0df
...@@ -34,6 +34,12 @@ from .users import absattr ...@@ -34,6 +34,12 @@ from .users import absattr
from .users import reqattr from .users import reqattr
from .users import UnrestrictedUser as Super from .users import UnrestrictedUser as Super
from .userfolder import BasicUserFolder
from .userfolder import UserFolder from zope.deferredimport import deprecated
from .userfolder import manage_addUserFolder deprecated("User folders are no longer part of AccessControl, please depend "
"on Zope2 and import from OFS.userfolder or use the new minimal "
"user folder classes from AccessControl.userfolder.",
BasicUserFolder = 'OFS.userfolder:BasicUserFolder',
manage_addUserFolder = 'OFS.userfolder:manage_addUserFolder',
UserFolder = 'OFS.userfolder:UserFolder',
)
...@@ -197,6 +197,7 @@ class IRoleManager(IPermissionMappingSupport): ...@@ -197,6 +197,7 @@ class IRoleManager(IPermissionMappingSupport):
'disallowed_permissions' -> all other permissions 'disallowed_permissions' -> all other permissions
""" """
class IStandardUserFolder(Interface): class IStandardUserFolder(Interface):
def getUser(name): def getUser(name):
...@@ -213,6 +214,7 @@ class IStandardUserFolder(Interface): ...@@ -213,6 +214,7 @@ class IStandardUserFolder(Interface):
"""Get a sequence of names of the users which reside in the user folder. """Get a sequence of names of the users which reside in the user folder.
""" """
class ISecurityPolicy(Interface): class ISecurityPolicy(Interface):
"""Plug-in policy for checking access to objects within untrusted code. """Plug-in policy for checking access to objects within untrusted code.
""" """
......
...@@ -197,7 +197,7 @@ class OwnershipChangeTests(unittest.TestCase): ...@@ -197,7 +197,7 @@ class OwnershipChangeTests(unittest.TestCase):
def setUp(self): def setUp(self):
from AccessControl.owner import UnownableOwner from AccessControl.owner import UnownableOwner
from AccessControl.User import UserFolder from AccessControl.userfolder import UserFolder
super(OwnershipChangeTests, self).setUp() super(OwnershipChangeTests, self).setUp()
self.root = FauxRoot() self.root = FauxRoot()
......
...@@ -167,8 +167,8 @@ class ZopeSecurityPolicyTestBase(unittest.TestCase): ...@@ -167,8 +167,8 @@ class ZopeSecurityPolicyTestBase(unittest.TestCase):
uf = UserFolder() uf = UserFolder()
a.acl_users = uf a.acl_users = uf
self.uf = a.acl_users self.uf = a.acl_users
uf._addUser('joe', 'password', 'password', user_roles, ()) uf._doAddUser('joe', 'password', user_roles, ())
uf._addUser('theowner', 'password', 'password', eo_roles, ()) uf._doAddUser('theowner', 'password', eo_roles, ())
user = uf.getUserById('joe') user = uf.getUserById('joe')
self.user = user self.user = user
context = SecurityContext(user) context = SecurityContext(user)
...@@ -307,7 +307,7 @@ class ZopeSecurityPolicyTestBase(unittest.TestCase): ...@@ -307,7 +307,7 @@ class ZopeSecurityPolicyTestBase(unittest.TestCase):
self.a.subobject = ImplictAcqObject() self.a.subobject = ImplictAcqObject()
subobject = self.a.subobject subobject = self.a.subobject
subobject.acl_users = UserFolder() subobject.acl_users = UserFolder()
subobject.acl_users._addUser('theowner', 'password', 'password', subobject.acl_users._doAddUser('theowner', 'password',
eo_roles + sysadmin_roles, ()) eo_roles + sysadmin_roles, ())
subobject.r_item = RestrictedSimpleItem() subobject.r_item = RestrictedSimpleItem()
r_subitem = subobject.r_item r_subitem = subobject.r_item
...@@ -353,7 +353,7 @@ class ZopeSecurityPolicyTestBase(unittest.TestCase): ...@@ -353,7 +353,7 @@ class ZopeSecurityPolicyTestBase(unittest.TestCase):
self.a.subobject = ImplictAcqObject() self.a.subobject = ImplictAcqObject()
subobject = self.a.subobject subobject = self.a.subobject
subobject.acl_users = UserFolder() subobject.acl_users = UserFolder()
subobject.acl_users._addUser('theowner', 'password', 'password', subobject.acl_users._doAddUser('theowner', 'password',
eo_roles + sysadmin_roles, ()) eo_roles + sysadmin_roles, ())
subobject.item = UnprotectedSimpleItem() subobject.item = UnprotectedSimpleItem()
subitem = subobject.item subitem = subobject.item
......
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
""" Unit tests for AccessControl.User
"""
import unittest
# TODO class Test_readUserAccessFile(unittest.TestCase)
# TODO class BasicUserFoldertests(unittest.TestCase)
class UserFolderTests(unittest.TestCase):
def setUp(self):
import transaction
transaction.begin()
def tearDown(self):
import transaction
from AccessControl.SecurityManagement import noSecurityManager
noSecurityManager()
transaction.abort()
def _getTargetClass(self):
from AccessControl.userfolder import UserFolder
return UserFolder
def _makeOne(self):
uf = self._getTargetClass()()
uf._doAddUser('user1', 'secret', ['role1'], [])
return uf
def _makeBasicAuthToken(self, creds='user1:secret'):
import base64
return 'Basic %s' % base64.encodestring(creds)
def _login(self, uf, name):
from AccessControl.SecurityManagement import newSecurityManager
user = uf.getUserById(name)
user = user.__of__(uf)
newSecurityManager(None, user)
def test_class_conforms_to_IStandardUserFolder(self):
from AccessControl.interfaces import IStandardUserFolder
from zope.interface.verify import verifyClass
verifyClass(IStandardUserFolder, self._getTargetClass())
def testGetUser(self):
uf = self._makeOne()
self.failIfEqual(uf.getUser('user1'), None)
def testGetBadUser(self):
uf = self._makeOne()
self.assertEqual(uf.getUser('user2'), None)
def testGetUserById(self):
uf = self._makeOne()
self.failIfEqual(uf.getUserById('user1'), None)
def testGetBadUserById(self):
uf = self._makeOne()
self.assertEqual(uf.getUserById('user2'), None)
def testGetUsers(self):
uf = self._makeOne()
users = uf.getUsers()
self.failUnless(users)
self.assertEqual(users[0].getUserName(), 'user1')
def testGetUserNames(self):
uf = self._makeOne()
names = uf.getUserNames()
self.failUnless(names)
self.assertEqual(names[0], 'user1')
def testIdentify(self):
uf = self._makeOne()
name, password = uf.identify(self._makeBasicAuthToken())
self.assertEqual(name, 'user1')
self.assertEqual(password, 'secret')
def testGetRoles(self):
uf = self._makeOne()
user = uf.getUser('user1')
self.failUnless('role1' in user.getRoles())
def testMaxListUsers(self):
# create a folder-ish thing which contains a roleManager,
# then put an acl_users object into the folde-ish thing
from AccessControl.userfolder import BasicUserFolder
class Folderish(BasicUserFolder):
def __init__(self, size, count):
self.maxlistusers = size
self.users = []
self.acl_users = self
self.__allow_groups__ = self
for i in xrange(count):
self.users.append("Nobody")
def getUsers(self):
return self.users
def user_names(self):
return self.getUsers()
tinyFolderOver = Folderish(15, 20)
tinyFolderUnder = Folderish(15, 10)
assert tinyFolderOver.maxlistusers == 15
assert tinyFolderUnder.maxlistusers == 15
assert len(tinyFolderOver.user_names()) == 20
assert len(tinyFolderUnder.user_names()) == 10
try:
list = tinyFolderOver.get_valid_userids()
assert 0, "Did not raise overflow error"
except OverflowError:
pass
try:
list = tinyFolderUnder.get_valid_userids()
pass
except OverflowError:
assert 0, "Raised overflow error erroneously"
def test__doAddUser_with_not_yet_encrypted_passwords(self):
# See collector #1869 && #1926
from AccessControl.AuthEncoding import pw_validate
USER_ID = 'not_yet_encrypted'
PASSWORD = 'password'
uf = self._makeOne()
uf.encrypt_passwords = True
self.failIf(uf._isPasswordEncrypted(PASSWORD))
uf._doAddUser(USER_ID, PASSWORD, [], [])
user = uf.getUserById(USER_ID)
self.failUnless(uf._isPasswordEncrypted(user.__))
self.failUnless(pw_validate(user.__, PASSWORD))
def test__doAddUser_with_preencrypted_passwords(self):
# See collector #1869 && #1926
from AccessControl.AuthEncoding import pw_validate
USER_ID = 'already_encrypted'
PASSWORD = 'password'
uf = self._makeOne()
uf.encrypt_passwords = True
ENCRYPTED = uf._encryptPassword(PASSWORD)
uf._doAddUser(USER_ID, ENCRYPTED, [], [])
user = uf.getUserById(USER_ID)
self.assertEqual(user.__, ENCRYPTED)
self.failUnless(uf._isPasswordEncrypted(user.__))
self.failUnless(pw_validate(user.__, PASSWORD))
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(UserFolderTests))
return suite
...@@ -10,10 +10,9 @@ ...@@ -10,10 +10,9 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Access control package. """User folders.
""" """
import os
from base64 import decodestring from base64 import decodestring
from Acquisition import aq_base from Acquisition import aq_base
...@@ -25,37 +24,26 @@ from zExceptions import BadRequest ...@@ -25,37 +24,26 @@ from zExceptions import BadRequest
from zExceptions import Unauthorized from zExceptions import Unauthorized
from zope.interface import implements from zope.interface import implements
# TODO dependencies
from App.Management import Navigation
from App.Management import Tabs
from App.special_dtml import DTMLFile
from App.Dialogs import MessageDialog
from OFS.role import RoleManager
from OFS.SimpleItem import Item
from AccessControl import AuthEncoding from AccessControl import AuthEncoding
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from AccessControl.class_init import InitializeClass from AccessControl.class_init import InitializeClass
from AccessControl.interfaces import IStandardUserFolder from AccessControl.interfaces import IStandardUserFolder
from AccessControl.Permissions import manage_users as ManageUsers from AccessControl.Permissions import manage_users as ManageUsers
from AccessControl.requestmethod import requestmethod
from AccessControl.rolemanager import DEFAULTMAXLISTUSERS from AccessControl.rolemanager import DEFAULTMAXLISTUSERS
from AccessControl.rolemanager import RoleManager
from AccessControl.SecurityManagement import getSecurityManager from AccessControl.SecurityManagement import getSecurityManager
from AccessControl.SecurityManagement import newSecurityManager from AccessControl.SecurityManagement import newSecurityManager
from AccessControl.SecurityManagement import noSecurityManager from AccessControl.SecurityManagement import noSecurityManager
from AccessControl.users import User from AccessControl.users import User
from AccessControl.users import readUserAccessFile
from AccessControl.users import _remote_user_mode from AccessControl.users import _remote_user_mode
from AccessControl.users import emergency_user from AccessControl.users import emergency_user
from AccessControl.users import nobody from AccessControl.users import nobody
from AccessControl.users import addr_match from AccessControl.users import addr_match
from AccessControl.users import host_match from AccessControl.users import host_match
from AccessControl.users import reqattr
from AccessControl.ZopeSecurityPolicy import _noroles from AccessControl.ZopeSecurityPolicy import _noroles
class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager, class BasicUserFolder(Implicit, Persistent, RoleManager):
Item):
"""Base class for UserFolder-like objects""" """Base class for UserFolder-like objects"""
meta_type = 'User Folder' meta_type = 'User Folder'
...@@ -67,20 +55,12 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager, ...@@ -67,20 +55,12 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager,
maxlistusers = DEFAULTMAXLISTUSERS maxlistusers = DEFAULTMAXLISTUSERS
encrypt_passwords = 1 encrypt_passwords = 1
security = ClassSecurityInfo() _remote_user_mode = _remote_user_mode
_domain_auth_mode = 0
manage_options=( _emergency_user = emergency_user
( _nobody = nobody
{'label': 'Contents', 'action': 'manage_main'},
{'label': 'Properties', 'action': 'manage_userFolderProperties'},
)
+RoleManager.manage_options
+Item.manage_options
)
# ---------------------------------- security = ClassSecurityInfo()
# Public UserFolder object interface
# ----------------------------------
security.declareProtected(ManageUsers, 'getUserNames') security.declareProtected(ManageUsers, 'getUserNames')
def getUserNames(self): def getUserNames(self):
...@@ -127,60 +107,6 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager, ...@@ -127,60 +107,6 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager,
to do the actual deleting of users.""" to do the actual deleting of users."""
raise NotImplementedError raise NotImplementedError
# As of Zope 2.5, userFolderAddUser, userFolderEditUser and
# userFolderDelUsers offer aliases for the the _doAddUser, _doChangeUser
# and _doDelUsers methods (with the difference that they can be called
# from XML-RPC or untrusted scripting code, given the necessary
# permissions).
#
# Authors of custom user folders don't need to do anything special to
# support these - they will just call the appropriate '_' methods that
# user folder subclasses already implement.
security.declareProtected(ManageUsers, 'userFolderAddUser')
@requestmethod('POST')
def userFolderAddUser(self, name, password, roles, domains,
REQUEST=None, **kw):
"""API method for creating a new user object. Note that not all
user folder implementations support dynamic creation of user
objects."""
if hasattr(self, '_doAddUser'):
return self._doAddUser(name, password, roles, domains, **kw)
raise NotImplementedError
security.declareProtected(ManageUsers, 'userFolderEditUser')
@requestmethod('POST')
def userFolderEditUser(self, name, password, roles, domains,
REQUEST=None, **kw):
"""API method for changing user object attributes. Note that not
all user folder implementations support changing of user object
attributes."""
if hasattr(self, '_doChangeUser'):
return self._doChangeUser(name, password, roles, domains, **kw)
raise NotImplementedError
security.declareProtected(ManageUsers, 'userFolderDelUsers')
@requestmethod('POST')
def userFolderDelUsers(self, names, REQUEST=None):
"""API method for deleting one or more user objects. Note that not
all user folder implementations support deletion of user objects."""
if hasattr(self, '_doDelUsers'):
return self._doDelUsers(names)
raise NotImplementedError
# -----------------------------------
# Private UserFolder object interface
# -----------------------------------
_remote_user_mode=_remote_user_mode
_domain_auth_mode=0
_emergency_user=emergency_user
# Note: use of the '_super' name is deprecated.
_super=emergency_user
_nobody=nobody
def identify(self, auth): def identify(self, auth):
if auth and auth.lower().startswith('basic '): if auth and auth.lower().startswith('basic '):
try: try:
...@@ -389,67 +315,12 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager, ...@@ -389,67 +315,12 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager,
def __len__(self): def __len__(self):
return 1 return 1
_mainUser=DTMLFile('dtml/mainUser', globals())
_add_User=DTMLFile('dtml/addUser', globals(),
remote_user_mode__=_remote_user_mode)
_editUser=DTMLFile('dtml/editUser', globals(),
remote_user_mode__=_remote_user_mode)
manage=manage_main=_mainUser
manage_main._setName('manage_main')
_userFolderProperties = DTMLFile('dtml/userFolderProps', globals())
def manage_userFolderProperties(self, REQUEST=None,
manage_tabs_message=None):
"""
"""
return self._userFolderProperties(
self, REQUEST, manage_tabs_message=manage_tabs_message,
management_view='Properties')
@requestmethod('POST')
def manage_setUserFolderProperties(self, encrypt_passwords=0,
update_passwords=0,
maxlistusers=DEFAULTMAXLISTUSERS,
REQUEST=None):
"""
Sets the properties of the user folder.
"""
self.encrypt_passwords = not not encrypt_passwords
try:
self.maxlistusers = int(maxlistusers)
except ValueError:
self.maxlistusers = DEFAULTMAXLISTUSERS
if encrypt_passwords and update_passwords:
changed = 0
for u in self.getUsers():
pw = u._getPassword()
if not self._isPasswordEncrypted(pw):
pw = self._encryptPassword(pw)
self._doChangeUser(u.getUserName(), pw, u.getRoles(),
u.getDomains())
changed = changed + 1
if REQUEST is not None:
if not changed:
msg = 'All passwords already encrypted.'
else:
msg = 'Encrypted %d password(s).' % changed
return self.manage_userFolderProperties(
REQUEST, manage_tabs_message=msg)
else:
return changed
else:
if REQUEST is not None:
return self.manage_userFolderProperties(
REQUEST, manage_tabs_message='Saved changes.')
def _isPasswordEncrypted(self, pw): def _isPasswordEncrypted(self, pw):
return AuthEncoding.is_encrypted(pw) return AuthEncoding.is_encrypted(pw)
def _encryptPassword(self, pw): def _encryptPassword(self, pw):
return AuthEncoding.pw_encrypt(pw, 'SSHA') return AuthEncoding.pw_encrypt(pw, 'SSHA')
def domainSpecValidate(self, spec): def domainSpecValidate(self, spec):
for ob in spec: for ob in spec:
am = addr_match(ob) am = addr_match(ob)
...@@ -458,168 +329,13 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager, ...@@ -458,168 +329,13 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager,
return 0 return 0
return 1 return 1
@requestmethod('POST')
def _addUser(self, name, password, confirm, roles, domains, REQUEST=None):
if not name:
return MessageDialog(
title='Illegal value',
message='A username must be specified',
action='manage_main')
if not password or not confirm:
if not domains:
return MessageDialog(
title='Illegal value',
message='Password and confirmation must be specified',
action='manage_main')
if self.getUser(name) or (self._emergency_user and
name == self._emergency_user.getUserName()):
return MessageDialog(
title='Illegal value',
message='A user with the specified name already exists',
action='manage_main')
if (password or confirm) and (password != confirm):
return MessageDialog(
title='Illegal value',
message='Password and confirmation do not match',
action='manage_main')
if not roles:
roles = []
if not domains:
domains = []
if domains and not self.domainSpecValidate(domains):
return MessageDialog(
title='Illegal value',
message='Illegal domain specification',
action='manage_main')
self._doAddUser(name, password, roles, domains)
if REQUEST:
return self._mainUser(self, REQUEST)
@requestmethod('POST')
def _changeUser(self, name, password, confirm, roles, domains,
REQUEST=None):
if password == 'password' and confirm == 'pconfirm':
# Protocol for editUser.dtml to indicate unchanged password
password = confirm = None
if not name:
return MessageDialog(
title='Illegal value',
message='A username must be specified',
action='manage_main')
if password == confirm == '':
if not domains:
return MessageDialog(
title='Illegal value',
message='Password and confirmation must be specified',
action='manage_main')
if not self.getUser(name):
return MessageDialog(
title='Illegal value',
message='Unknown user',
action='manage_main')
if (password or confirm) and (password != confirm):
return MessageDialog(
title='Illegal value',
message='Password and confirmation do not match',
action='manage_main')
if not roles:
roles = []
if not domains:
domains = []
if domains and not self.domainSpecValidate(domains):
return MessageDialog(
title='Illegal value',
message='Illegal domain specification',
action='manage_main')
self._doChangeUser(name, password, roles, domains)
if REQUEST:
return self._mainUser(self, REQUEST)
@requestmethod('POST')
def _delUsers(self, names, REQUEST=None):
if not names:
return MessageDialog(
title='Illegal value',
message='No users specified',
action='manage_main')
self._doDelUsers(names)
if REQUEST:
return self._mainUser(self, REQUEST)
security.declareProtected(ManageUsers, 'manage_users')
def manage_users(self, submit=None, REQUEST=None, RESPONSE=None):
"""This method handles operations on users for the web based forms
of the ZMI. Application code (code that is outside of the forms
that implement the UI of a user folder) are encouraged to use
manage_std_addUser"""
if submit=='Add...':
return self._add_User(self, REQUEST)
if submit=='Edit':
try:
user=self.getUser(reqattr(REQUEST, 'name'))
except:
return MessageDialog(
title='Illegal value',
message='The specified user does not exist',
action='manage_main')
return self._editUser(self, REQUEST, user=user, password=user.__)
if submit=='Add':
name = reqattr(REQUEST, 'name')
password = reqattr(REQUEST, 'password')
confirm = reqattr(REQUEST, 'confirm')
roles = reqattr(REQUEST, 'roles')
domains = reqattr(REQUEST, 'domains')
return self._addUser(name, password, confirm, roles,
domains, REQUEST)
if submit=='Change':
name = reqattr(REQUEST, 'name')
password = reqattr(REQUEST, 'password')
confirm = reqattr(REQUEST, 'confirm')
roles = reqattr(REQUEST, 'roles')
domains = reqattr(REQUEST, 'domains')
return self._changeUser(name, password, confirm, roles,
domains, REQUEST)
if submit=='Delete':
names = reqattr(REQUEST, 'names')
return self._delUsers(names, REQUEST)
return self._mainUser(self, REQUEST)
security.declareProtected(ManageUsers, 'user_names') security.declareProtected(ManageUsers, 'user_names')
def user_names(self): def user_names(self):
return self.getUserNames() return self.getUserNames()
def manage_beforeDelete(self, item, container):
if item is self:
try:
del container.__allow_groups__
except:
pass
def manage_afterAdd(self, item, container):
if item is self:
self = aq_base(self)
container.__allow_groups__ = self
def __creatable_by_emergency_user__(self): def __creatable_by_emergency_user__(self):
return 1 return 1
def _setId(self, id):
if id != self.id:
raise MessageDialog(
title='Invalid Id',
message='Cannot change the id of a UserFolder',
action='./manage_main')
# Domain authentication support. This is a good candidate to # Domain authentication support. This is a good candidate to
# become deprecated in future Zope versions. # become deprecated in future Zope versions.
...@@ -640,7 +356,6 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager, ...@@ -640,7 +356,6 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager,
class UserFolder(BasicUserFolder): class UserFolder(BasicUserFolder):
"""Standard UserFolder object """Standard UserFolder object
A UserFolder holds User objects which contain information A UserFolder holds User objects which contain information
...@@ -653,7 +368,6 @@ class UserFolder(BasicUserFolder): ...@@ -653,7 +368,6 @@ class UserFolder(BasicUserFolder):
meta_type = 'User Folder' meta_type = 'User Folder'
id = 'acl_users' id = 'acl_users'
title = 'User Folder' title = 'User Folder'
icon = 'p_/UserFolder'
def __init__(self): def __init__(self):
self.data=PersistentMapping() self.data=PersistentMapping()
...@@ -704,43 +418,4 @@ class UserFolder(BasicUserFolder): ...@@ -704,43 +418,4 @@ class UserFolder(BasicUserFolder):
for name in names: for name in names:
del self.data[name] del self.data[name]
def _createInitialUser(self):
"""
If there are no users or only one user in this user folder,
populates from the 'inituser' file in the instance home.
We have to do this even when there is already a user
just in case the initial user ignored the setup messages.
We don't do it for more than one user to avoid
abuse of this mechanism.
Called only by OFS.Application.initialize().
"""
if len(self.data) <= 1:
info = readUserAccessFile('inituser')
if info:
import App.config
name, password, domains, remote_user_mode = info
self._doDelUsers(self.getUserNames())
self._doAddUser(name, password, ('Manager', ), domains)
cfg = App.config.getConfiguration()
try:
os.remove(os.path.join(cfg.instancehome, 'inituser'))
except:
pass
InitializeClass(UserFolder) InitializeClass(UserFolder)
def manage_addUserFolder(self, dtself=None, REQUEST=None, **ignored):
""" """
f = UserFolder()
self = self.this()
try:
self._setObject('acl_users', f)
except:
return MessageDialog(
title='Item Exists',
message='This object already contains a User Folder',
action='%s/manage_main' % REQUEST['URL1'])
self.__allow_groups__ = f
if REQUEST is not None:
REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main')
...@@ -27,13 +27,13 @@ import transaction ...@@ -27,13 +27,13 @@ import transaction
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from AccessControl.class_init import InitializeClass from AccessControl.class_init import InitializeClass
from AccessControl.Permission import ApplicationDefaultPermissions from AccessControl.Permission import ApplicationDefaultPermissions
from AccessControl.User import UserFolder
from Acquisition import aq_base from Acquisition import aq_base
from App.ApplicationManager import ApplicationManager from App.ApplicationManager import ApplicationManager
from App.config import getConfiguration from App.config import getConfiguration
from App.Product import doInstall from App.Product import doInstall
from DateTime import DateTime from DateTime import DateTime
from HelpSys.HelpSys import HelpSys from HelpSys.HelpSys import HelpSys
from OFS.userfolder import UserFolder
from Persistence import Persistent from Persistence import Persistent
from webdav.NullResource import NullResource from webdav.NullResource import NullResource
from zExceptions import Redirect as RedirectException, Forbidden from zExceptions import Redirect as RedirectException, Forbidden
......
...@@ -10,8 +10,9 @@ ...@@ -10,8 +10,9 @@
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE
# #
############################################################################## ##############################################################################
"""Access control support """Role manager
""" """
from cgi import escape from cgi import escape
from App.Dialogs import MessageDialog from App.Dialogs import MessageDialog
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
""" Unit tests for AccessControl.User """ Unit tests for OFS.userfolder
""" """
import unittest import unittest
...@@ -33,7 +33,7 @@ class UserFolderTests(unittest.TestCase): ...@@ -33,7 +33,7 @@ class UserFolderTests(unittest.TestCase):
transaction.abort() transaction.abort()
def _getTargetClass(self): def _getTargetClass(self):
from AccessControl.User import UserFolder from OFS.userfolder import UserFolder
return UserFolder return UserFolder
def _makeOne(self, app=None): def _makeOne(self, app=None):
...@@ -74,45 +74,6 @@ class UserFolderTests(unittest.TestCase): ...@@ -74,45 +74,6 @@ class UserFolderTests(unittest.TestCase):
from zope.interface.verify import verifyClass from zope.interface.verify import verifyClass
verifyClass(IStandardUserFolder, self._getTargetClass()) verifyClass(IStandardUserFolder, self._getTargetClass())
def testGetUser(self):
uf = self._makeOne()
self.failIfEqual(uf.getUser('user1'), None)
def testGetBadUser(self):
uf = self._makeOne()
self.assertEqual(uf.getUser('user2'), None)
def testGetUserById(self):
uf = self._makeOne()
self.failIfEqual(uf.getUserById('user1'), None)
def testGetBadUserById(self):
uf = self._makeOne()
self.assertEqual(uf.getUserById('user2'), None)
def testGetUsers(self):
uf = self._makeOne()
users = uf.getUsers()
self.failUnless(users)
self.assertEqual(users[0].getUserName(), 'user1')
def testGetUserNames(self):
uf = self._makeOne()
names = uf.getUserNames()
self.failUnless(names)
self.assertEqual(names[0], 'user1')
def testIdentify(self):
uf = self._makeOne()
name, password = uf.identify(self._makeBasicAuthToken())
self.assertEqual(name, 'user1')
self.assertEqual(password, 'secret')
def testGetRoles(self):
uf = self._makeOne()
user = uf.getUser('user1')
self.failUnless('role1' in user.getRoles())
def testGetRolesInContext(self): def testGetRolesInContext(self):
app = self._makeApp() app = self._makeApp()
uf = self._makeOne(app) uf = self._makeOne(app)
...@@ -208,80 +169,6 @@ class UserFolderTests(unittest.TestCase): ...@@ -208,80 +169,6 @@ class UserFolderTests(unittest.TestCase):
app = self._makeApp() app = self._makeApp()
self.assertRaises(Unauthorized, app.restrictedTraverse, 'doc') self.assertRaises(Unauthorized, app.restrictedTraverse, 'doc')
def testMaxListUsers(self):
# create a folder-ish thing which contains a roleManager,
# then put an acl_users object into the folde-ish thing
from AccessControl.User import BasicUserFolder
class Folderish(BasicUserFolder):
def __init__(self, size, count):
self.maxlistusers = size
self.users = []
self.acl_users = self
self.__allow_groups__ = self
for i in xrange(count):
self.users.append("Nobody")
def getUsers(self):
return self.users
def user_names(self):
return self.getUsers()
tinyFolderOver = Folderish(15, 20)
tinyFolderUnder = Folderish(15, 10)
assert tinyFolderOver.maxlistusers == 15
assert tinyFolderUnder.maxlistusers == 15
assert len(tinyFolderOver.user_names()) == 20
assert len(tinyFolderUnder.user_names()) == 10
try:
list = tinyFolderOver.get_valid_userids()
assert 0, "Did not raise overflow error"
except OverflowError:
pass
try:
list = tinyFolderUnder.get_valid_userids()
pass
except OverflowError:
assert 0, "Raised overflow error erroneously"
def test__doAddUser_with_not_yet_encrypted_passwords(self):
# See collector #1869 && #1926
from AccessControl.AuthEncoding import pw_validate
USER_ID = 'not_yet_encrypted'
PASSWORD = 'password'
uf = self._makeOne()
uf.encrypt_passwords = True
self.failIf(uf._isPasswordEncrypted(PASSWORD))
uf._doAddUser(USER_ID, PASSWORD, [], [])
user = uf.getUserById(USER_ID)
self.failUnless(uf._isPasswordEncrypted(user.__))
self.failUnless(pw_validate(user.__, PASSWORD))
def test__doAddUser_with_preencrypted_passwords(self):
# See collector #1869 && #1926
from AccessControl.AuthEncoding import pw_validate
USER_ID = 'already_encrypted'
PASSWORD = 'password'
uf = self._makeOne()
uf.encrypt_passwords = True
ENCRYPTED = uf._encryptPassword(PASSWORD)
uf._doAddUser(USER_ID, ENCRYPTED, [], [])
user = uf.getUserById(USER_ID)
self.assertEqual(user.__, ENCRYPTED)
self.failUnless(uf._isPasswordEncrypted(user.__))
self.failUnless(pw_validate(user.__, PASSWORD))
def test_suite(): def test_suite():
suite = unittest.TestSuite() suite = unittest.TestSuite()
......
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""User folders
"""
import os
from Acquisition import aq_base
from App.Management import Navigation
from App.Management import Tabs
from App.special_dtml import DTMLFile
from App.Dialogs import MessageDialog
from OFS.role import RoleManager
from OFS.SimpleItem import Item
from AccessControl import ClassSecurityInfo
from AccessControl.class_init import InitializeClass
from AccessControl.Permissions import manage_users as ManageUsers
from AccessControl.requestmethod import requestmethod
from AccessControl.rolemanager import DEFAULTMAXLISTUSERS
from AccessControl import userfolder as accesscontrol_userfolder
from AccessControl.users import readUserAccessFile
from AccessControl.users import _remote_user_mode
from AccessControl.users import emergency_user
from AccessControl.users import reqattr
class BasicUserFolder(Navigation, Tabs, Item, RoleManager,
accesscontrol_userfolder.BasicUserFolder):
"""Base class for UserFolder-like objects"""
security = ClassSecurityInfo()
# Note: use of the '_super' name is deprecated.
_super = emergency_user
manage_options=(
(
{'label': 'Contents', 'action': 'manage_main'},
{'label': 'Properties', 'action': 'manage_userFolderProperties'},
)
+RoleManager.manage_options
+Item.manage_options
)
security.declareProtected(ManageUsers, 'userFolderAddUser')
@requestmethod('POST')
def userFolderAddUser(self, name, password, roles, domains,
REQUEST=None, **kw):
"""API method for creating a new user object. Note that not all
user folder implementations support dynamic creation of user
objects."""
if hasattr(self, '_doAddUser'):
return self._doAddUser(name, password, roles, domains, **kw)
raise NotImplementedError
security.declareProtected(ManageUsers, 'userFolderEditUser')
@requestmethod('POST')
def userFolderEditUser(self, name, password, roles, domains,
REQUEST=None, **kw):
"""API method for changing user object attributes. Note that not
all user folder implementations support changing of user object
attributes."""
if hasattr(self, '_doChangeUser'):
return self._doChangeUser(name, password, roles, domains, **kw)
raise NotImplementedError
security.declareProtected(ManageUsers, 'userFolderDelUsers')
@requestmethod('POST')
def userFolderDelUsers(self, names, REQUEST=None):
"""API method for deleting one or more user objects. Note that not
all user folder implementations support deletion of user objects."""
if hasattr(self, '_doDelUsers'):
return self._doDelUsers(names)
raise NotImplementedError
_mainUser=DTMLFile('dtml/mainUser', globals())
_add_User=DTMLFile('dtml/addUser', globals(),
remote_user_mode__=_remote_user_mode)
_editUser=DTMLFile('dtml/editUser', globals(),
remote_user_mode__=_remote_user_mode)
manage=manage_main=_mainUser
manage_main._setName('manage_main')
_userFolderProperties = DTMLFile('dtml/userFolderProps', globals())
def manage_userFolderProperties(self, REQUEST=None,
manage_tabs_message=None):
"""
"""
return self._userFolderProperties(
self, REQUEST, manage_tabs_message=manage_tabs_message,
management_view='Properties')
@requestmethod('POST')
def manage_setUserFolderProperties(self, encrypt_passwords=0,
update_passwords=0,
maxlistusers=DEFAULTMAXLISTUSERS,
REQUEST=None):
"""
Sets the properties of the user folder.
"""
self.encrypt_passwords = not not encrypt_passwords
try:
self.maxlistusers = int(maxlistusers)
except ValueError:
self.maxlistusers = DEFAULTMAXLISTUSERS
if encrypt_passwords and update_passwords:
changed = 0
for u in self.getUsers():
pw = u._getPassword()
if not self._isPasswordEncrypted(pw):
pw = self._encryptPassword(pw)
self._doChangeUser(u.getUserName(), pw, u.getRoles(),
u.getDomains())
changed = changed + 1
if REQUEST is not None:
if not changed:
msg = 'All passwords already encrypted.'
else:
msg = 'Encrypted %d password(s).' % changed
return self.manage_userFolderProperties(
REQUEST, manage_tabs_message=msg)
else:
return changed
else:
if REQUEST is not None:
return self.manage_userFolderProperties(
REQUEST, manage_tabs_message='Saved changes.')
@requestmethod('POST')
def _addUser(self, name, password, confirm, roles, domains, REQUEST=None):
if not name:
return MessageDialog(
title='Illegal value',
message='A username must be specified',
action='manage_main')
if not password or not confirm:
if not domains:
return MessageDialog(
title='Illegal value',
message='Password and confirmation must be specified',
action='manage_main')
if self.getUser(name) or (self._emergency_user and
name == self._emergency_user.getUserName()):
return MessageDialog(
title='Illegal value',
message='A user with the specified name already exists',
action='manage_main')
if (password or confirm) and (password != confirm):
return MessageDialog(
title='Illegal value',
message='Password and confirmation do not match',
action='manage_main')
if not roles:
roles = []
if not domains:
domains = []
if domains and not self.domainSpecValidate(domains):
return MessageDialog(
title='Illegal value',
message='Illegal domain specification',
action='manage_main')
self._doAddUser(name, password, roles, domains)
if REQUEST:
return self._mainUser(self, REQUEST)
@requestmethod('POST')
def _changeUser(self, name, password, confirm, roles, domains,
REQUEST=None):
if password == 'password' and confirm == 'pconfirm':
# Protocol for editUser.dtml to indicate unchanged password
password = confirm = None
if not name:
return MessageDialog(
title='Illegal value',
message='A username must be specified',
action='manage_main')
if password == confirm == '':
if not domains:
return MessageDialog(
title='Illegal value',
message='Password and confirmation must be specified',
action='manage_main')
if not self.getUser(name):
return MessageDialog(
title='Illegal value',
message='Unknown user',
action='manage_main')
if (password or confirm) and (password != confirm):
return MessageDialog(
title='Illegal value',
message='Password and confirmation do not match',
action='manage_main')
if not roles:
roles = []
if not domains:
domains = []
if domains and not self.domainSpecValidate(domains):
return MessageDialog(
title='Illegal value',
message='Illegal domain specification',
action='manage_main')
self._doChangeUser(name, password, roles, domains)
if REQUEST:
return self._mainUser(self, REQUEST)
@requestmethod('POST')
def _delUsers(self, names, REQUEST=None):
if not names:
return MessageDialog(
title='Illegal value',
message='No users specified',
action='manage_main')
self._doDelUsers(names)
if REQUEST:
return self._mainUser(self, REQUEST)
security.declareProtected(ManageUsers, 'manage_users')
def manage_users(self, submit=None, REQUEST=None, RESPONSE=None):
"""This method handles operations on users for the web based forms
of the ZMI. Application code (code that is outside of the forms
that implement the UI of a user folder) are encouraged to use
manage_std_addUser"""
if submit=='Add...':
return self._add_User(self, REQUEST)
if submit=='Edit':
try:
user=self.getUser(reqattr(REQUEST, 'name'))
except:
return MessageDialog(
title='Illegal value',
message='The specified user does not exist',
action='manage_main')
return self._editUser(self, REQUEST, user=user, password=user.__)
if submit=='Add':
name = reqattr(REQUEST, 'name')
password = reqattr(REQUEST, 'password')
confirm = reqattr(REQUEST, 'confirm')
roles = reqattr(REQUEST, 'roles')
domains = reqattr(REQUEST, 'domains')
return self._addUser(name, password, confirm, roles,
domains, REQUEST)
if submit=='Change':
name = reqattr(REQUEST, 'name')
password = reqattr(REQUEST, 'password')
confirm = reqattr(REQUEST, 'confirm')
roles = reqattr(REQUEST, 'roles')
domains = reqattr(REQUEST, 'domains')
return self._changeUser(name, password, confirm, roles,
domains, REQUEST)
if submit=='Delete':
names = reqattr(REQUEST, 'names')
return self._delUsers(names, REQUEST)
return self._mainUser(self, REQUEST)
def manage_beforeDelete(self, item, container):
if item is self:
try:
del container.__allow_groups__
except:
pass
def manage_afterAdd(self, item, container):
if item is self:
self = aq_base(self)
container.__allow_groups__ = self
def _setId(self, id):
if id != self.id:
raise MessageDialog(
title='Invalid Id',
message='Cannot change the id of a UserFolder',
action='./manage_main')
class UserFolder(accesscontrol_userfolder.UserFolder, BasicUserFolder):
"""Standard UserFolder object
A UserFolder holds User objects which contain information
about users including name, password domain, and roles.
UserFolders function chiefly to control access by authenticating
users and binding them to a collection of roles."""
icon = 'p_/UserFolder'
def _createInitialUser(self):
"""
If there are no users or only one user in this user folder,
populates from the 'inituser' file in the instance home.
We have to do this even when there is already a user
just in case the initial user ignored the setup messages.
We don't do it for more than one user to avoid
abuse of this mechanism.
Called only by OFS.Application.initialize().
"""
if len(self.data) <= 1:
info = readUserAccessFile('inituser')
if info:
import App.config
name, password, domains, remote_user_mode = info
self._doDelUsers(self.getUserNames())
self._doAddUser(name, password, ('Manager', ), domains)
cfg = App.config.getConfiguration()
try:
os.remove(os.path.join(cfg.instancehome, 'inituser'))
except:
pass
InitializeClass(UserFolder)
def manage_addUserFolder(self, dtself=None, REQUEST=None, **ignored):
""" """
f = UserFolder()
self = self.this()
try:
self._setObject('acl_users', f)
except:
return MessageDialog(
title='Item Exists',
message='This object already contains a User Folder',
action='%s/manage_main' % REQUEST['URL1'])
self.__allow_groups__ = f
if REQUEST is not None:
REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main')
...@@ -14,7 +14,7 @@ __doc__='''Object system core ...@@ -14,7 +14,7 @@ __doc__='''Object system core
$Id$''' $Id$'''
__version__='$Revision: 1.38 $'[11:-2] __version__='$Revision: 1.38 $'[11:-2]
import OFS.Image, OFS.Folder, AccessControl.User import OFS.Image, OFS.Folder, OFS.userfolder
import OFS.DTMLMethod, OFS.DTMLDocument, OFS.PropertySheets import OFS.DTMLMethod, OFS.DTMLDocument, OFS.PropertySheets
import OFS.OrderedFolder import OFS.OrderedFolder
...@@ -79,10 +79,10 @@ def initialize(context): ...@@ -79,10 +79,10 @@ def initialize(context):
) )
context.registerClass( context.registerClass(
AccessControl.User.UserFolder, OFS.userfolder.UserFolder,
constructors=(AccessControl.User.manage_addUserFolder,), constructors=(OFS.userfolder.manage_addUserFolder,),
icon='images/UserFolder_icon.gif', icon='images/UserFolder_icon.gif',
legacy=(AccessControl.User.manage_addUserFolder,), legacy=(OFS.userfolder.manage_addUserFolder,),
) )
context.registerHelp() context.registerHelp()
......
...@@ -71,7 +71,7 @@ class ZopeTestCase(base.TestCase): ...@@ -71,7 +71,7 @@ class ZopeTestCase(base.TestCase):
def _setupUserFolder(self): def _setupUserFolder(self):
'''Creates the user folder.''' '''Creates the user folder.'''
from AccessControl.User import manage_addUserFolder from OFS.userfolder import manage_addUserFolder
manage_addUserFolder(self.folder) manage_addUserFolder(self.folder)
def _setupUser(self): def _setupUser(self):
......
...@@ -406,7 +406,7 @@ class TestPortalTestCase(ZopeTestCase.PortalTestCase): ...@@ -406,7 +406,7 @@ class TestPortalTestCase(ZopeTestCase.PortalTestCase):
self.assertEqual(lhs, rhs) self.assertEqual(lhs, rhs)
from AccessControl.User import UserFolder from OFS.userfolder import UserFolder
class WrappingUserFolder(UserFolder): class WrappingUserFolder(UserFolder):
'''User folder returning wrapped user objects''' '''User folder returning wrapped user objects'''
......
...@@ -349,7 +349,7 @@ class TestZopeTestCase(ZopeTestCase.ZopeTestCase): ...@@ -349,7 +349,7 @@ class TestZopeTestCase(ZopeTestCase.ZopeTestCase):
self.assertEqual(lhs, rhs) self.assertEqual(lhs, rhs)
from AccessControl.User import UserFolder from OFS.userfolder import UserFolder
from Acquisition import aq_inner, aq_parent, aq_chain from Acquisition import aq_inner, aq_parent, aq_chain
class WrappingUserFolder(UserFolder): class WrappingUserFolder(UserFolder):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment