Commit 86fa36cb authored by Tres Seaver's avatar Tres Seaver

My original patch, with tests.

parent efb30bfa
......@@ -34,6 +34,7 @@ except ImportError:
from AccessControl import SecurityManagement
from AccessControl import Unauthorized
from AccessControl.interfaces import ISecurityPolicy
from AccessControl.interfaces import ISecurityManager
from AccessControl.SimpleObjectPolicies import Containers, _noroles
from AccessControl.ZopeGuards import guarded_getitem
......@@ -199,6 +200,8 @@ from AccessControl.ZopeSecurityPolicy import getRoles # XXX
class ZopeSecurityPolicy:
implements(ISecurityPolicy)
def __init__(self, ownerous=1, authenticated=1, verbose=0):
"""Create a Zope security policy.
......@@ -459,12 +462,28 @@ class ZopeSecurityPolicy:
raise Unauthorized(name, value)
def checkPermission(self, permission, object, context):
# XXX proxy roles and executable owner are not checked
roles = rolesForPermissionOn(permission, object)
if isinstance(roles, basestring):
roles = [roles]
return context.user.allowed(object, roles)
result = context.user.allowed(object, roles)
# check executable owner and proxy roles
stack = context.stack
if stack:
eo = stack[-1]
if self._ownerous:
owner = eo.getOwner()
if (owner is not None) and not owner.allowed(object, roles):
return 0
proxy_roles = getattr(eo, '_proxy_roles', None)
if proxy_roles:
if object is not aq_base(object):
if not owner._check_context(object):
return 0
for r in proxy_roles:
if r in roles:
return 1
return result
# AccessControl.SecurityManager
# -----------------------------
......
......@@ -1292,61 +1292,200 @@ static PyObject *ZopeSecurityPolicy_validate(PyObject *self, PyObject *args) {
*/
static PyObject *ZopeSecurityPolicy_checkPermission(PyObject *self,
PyObject *args) {
PyObject *permission = NULL;
PyObject *object = NULL;
PyObject *context = NULL;
PyObject *roles;
PyObject *result = NULL;
PyObject *user;
/*| def checkPermission(self, permission, object, context)
*/
if (unpacktuple3(args, "checkPermission", 3,
PyObject *args) {
/* return value */
PyObject *rval = NULL;
/* arguments, not increfe'd */
PyObject *permission = NULL;
PyObject *object = NULL;
PyObject *context = NULL;
/* locals, XDECREF at exit */
PyObject *roles = NULL;
PyObject *user = NULL;
PyObject *stack = NULL;
PyObject *eo = NULL;
PyObject *owner = NULL;
PyObject *proxy_roles = NULL;
PyObject *method = NULL;
PyObject *wrappedowner = NULL;
PyObject *objectbase = NULL;
PyObject *incontext = NULL;
int contains = 0;
int iRole;
int length;
/*| def checkPermission(self, permission, object, context)
*/
if (unpacktuple3(args, "checkPermission", 3,
&permission, &object, &context) < 0)
return NULL;
return NULL;
/*| roles = rolesForPermissionOn(permission, object)
*/
/*| roles = rolesForPermissionOn(permission, object)
*/
roles = c_rolesForPermissionOn(permission, object, NULL, NULL);
if (roles == NULL)
roles = c_rolesForPermissionOn(permission, object, NULL, NULL);
if (roles == NULL)
return NULL;
/*| if type(roles) in (StringType, UnicodeType):
**| roles = [roles]
*/
/*| if type(roles) in (StringType, UnicodeType):
**| roles = [roles]
*/
if ( PyString_Check(roles) || PyUnicode_Check(roles) ) {
PyObject *r;
if ( PyString_Check(roles) || PyUnicode_Check(roles) ) {
PyObject *role_list;
r = PyList_New(1);
if (r == NULL) {
Py_DECREF(roles);
return NULL;
}
role_list = PyList_New(1);
if (role_list == NULL) goto cP_done;
/* Note: ref to roles is passed to the list object. */
PyList_SET_ITEM(r, 0, roles);
roles = r;
}
PyList_SET_ITEM(role_list, 0, roles);
roles = role_list;
}
/*| return context.user.allowed(object, roles)
*/
/*| result = context.user.allowed(object, roles)
*/
user = PyObject_GetAttr(context, user_str);
if (user != NULL) {
ASSIGN(user, PyObject_GetAttr(user, allowed_str));
if (user != NULL) {
result = callfunction2(user, object, roles);
Py_DECREF(user);
}
}
user = PyObject_GetAttr(context, user_str);
if (user != NULL) {
ASSIGN(user, PyObject_GetAttr(user, allowed_str));
if (user != NULL) {
rval = callfunction2(user, object, roles);
}
}
Py_DECREF(roles);
/*| # Check executable security
**| stack = context.stack
**| if stack:
*/
stack = PyObject_GetAttr(context, stack_str);
if (stack == NULL) goto cP_done;
if (PyObject_IsTrue(stack)) {
/*| eo = stack[-1]
**| # If the executable had an owner, can it execute?
**| owner = eo.getOwner()
**| if (owner is not None) and not owner.allowed(object, roles)
**| # We don't want someone to acquire if they can't
**| # get an unacquired!
**| return 0
*/
eo = PySequence_GetItem(stack, -1);
if (eo == NULL) goto cP_done;
if (ownerous) {
owner = PyObject_GetAttr(eo, getOwner_str);
if (owner) ASSIGN(owner, PyObject_CallObject(owner, NULL));
if (owner == NULL) goto cP_done;
if (owner != Py_None) {
ASSIGN(owner, PyObject_GetAttr(owner, allowed_str));
if (owner) ASSIGN(owner, callfunction2(owner, object, roles));
if (owner == NULL) goto cP_done;
if (! PyObject_IsTrue(owner)) {
rval = 0;
goto cP_done;
}
}
}
/*| # Proxy roles, which are a lot safer now
**| proxy_roles = getattr(eo, "_proxy_roles", None)
**| if proxy_roles:
**| # Verify that the owner actually can state the proxy role
**| # in the context of the accessed item; users in subfolders
**| # should not be able to use proxy roles to access items
**| # above their subfolder!
**| owner = eo.getWrappedOwner()
**|
**| if owner is not None:
**| if object is not aq_base(object):
**| if not owner._check_context(object):
**| # object is higher up than the owner,
**| # deny access
**| return 0
**|
**| for r in proxy_roles:
**| if r in roles:
**| return 1
**|
**| return result
*/
proxy_roles = PyObject_GetAttr(eo, _proxy_roles_str);
if (proxy_roles == NULL) {
PyErr_Clear();
goto cP_done;
}
return result;
if (PyObject_IsTrue(proxy_roles)) {
method = PyObject_GetAttr(eo, getWrappedOwner_str);
if (method == NULL) goto cP_done;
wrappedowner = PyObject_CallObject(method, NULL);
if (wrappedowner == NULL) goto cP_done;
if (wrappedowner != Py_None) {
objectbase = aq_base(object);
if (object != objectbase) {
incontext = callmethod1(wrappedowner,
_check_context_str, object);
if (incontext == NULL) goto cP_done;
}
if ( ! PyObject_IsTrue(incontext)) goto cP_done;
}
}
if (PyTuple_Check(proxy_roles)) {
PyObject *proxy_role;
length = PyTuple_GET_SIZE(proxy_roles);
for (iRole=0; iRole < length; iRole++) {
proxy_role = PyTuple_GET_ITEM(proxy_roles, iRole);
/* proxy_role is not increfed */
if ((contains = PySequence_Contains(roles, proxy_role)))
break;
}
} else {
PyObject *proxy_role;
length = PySequence_Size(proxy_roles);
if (length < 0) contains = -1;
for (iRole=0; contains == 0 && iRole < length; iRole++) {
proxy_role = PySequence_GetItem(proxy_roles, iRole);
if (proxy_role == NULL) goto cP_done;
/* proxy_role is increfed */
contains = PySequence_Contains(roles, proxy_role);
Py_DECREF(proxy_role);
}
}
if (contains > 0)
rval = PyInt_FromLong(contains);
} /* End of stack check */
cP_done:
Py_XDECREF(roles);
Py_XDECREF(user);
Py_XDECREF(stack);
Py_XDECREF(eo);
Py_XDECREF(owner);
Py_XDECREF(proxy_roles);
Py_XDECREF(method);
Py_XDECREF(wrappedowner);
Py_XDECREF(objectbase);
Py_XDECREF(incontext);
return rval;
}
/*
......
......@@ -17,8 +17,37 @@ from AccessControl.SimpleObjectPolicies import _noroles
from zope.interface import Interface
from zope.interface import Attribute
class ISecurityPolicy(Interface):
"""Plug-in policy for checking access to objects within untrusted code.
"""
def validate(accessed, container, name, value, context, roles=_noroles):
"""Check that the current user (from context) has access.
o Raise Unauthorized if access is not allowed; otherwise, return
a true value.
Arguments:
accessed -- the object that was being accessed
container -- the object the value was found in
name -- The name used to access the value
value -- The value retrieved though the access.
context -- the security context (normally supplied by the security
manager).
roles -- The roles of the object if already known.
"""
def checkPermission(permission, object, context):
"""Check whether the current user has a permission w.r.t. an object.
"""
class ISecurityManager(Interface):
"""Checks access and manages executable context and policies.
"""Check access and manages executable context and policies.
"""
_policy = Attribute(u'Current Security Policy')
......
......@@ -23,7 +23,6 @@ try:
from zExceptions import Unauthorized
except ImportError:
Unauthorized = 'Unauthorized'
from AccessControl.ZopeSecurityPolicy import ZopeSecurityPolicy
from AccessControl.User import UserFolder
from AccessControl.SecurityManagement import SecurityContext
from Acquisition import Implicit, Explicit, aq_base
......@@ -152,9 +151,7 @@ class SimpleClass:
attr = 1
class ZopeSecurityPolicyTests (unittest.TestCase):
policy = ZopeSecurityPolicy()
class ZopeSecurityPolicyTestBase(unittest.TestCase):
def setUp(self):
a = App()
......@@ -174,6 +171,10 @@ class ZopeSecurityPolicyTests (unittest.TestCase):
self.user = user
context = SecurityContext(user)
self.context = context
self.policy = self._makeOne()
def _makeOne(self, *args, **kw):
return self._getTargetClass()(*args, **kw)
def assertPolicyAllows(self, ob, attrname):
res = self.policy.validate(ob, ob, attrname, getattr(ob, attrname),
......@@ -276,6 +277,17 @@ class ZopeSecurityPolicyTests (unittest.TestCase):
v = self.policy.checkPermission('View', r_item, o_context)
self.assert_(v, '_View_Permission should grant access to theowner')
def test_checkPermission_respects_proxy_roles(self):
r_item = self.a.r_item
context = self.context
self.failIf(self.policy.checkPermission('View', r_item, context))
o_context = SecurityContext(self.uf.getUserById('joe'))
# Push an executable with proxy roles on the stack
eo = OwnedSetuidMethod().__of__(r_item)
eo._proxy_roles = eo_roles
context.stack.append(eo)
self.failUnless(self.policy.checkPermission('View', r_item, context))
def testUnicodeRolesForPermission(self):
r_item = self.a.r_item
context = self.context
......@@ -351,6 +363,27 @@ class ZopeSecurityPolicyTests (unittest.TestCase):
self.fail('Policy accepted bad __roles__')
class ISecurityPolicyConformance:
def test_conforms_to_ISecurityPolicy(self):
from AccessControl.interfaces import ISecurityPolicy
from zope.interface.verify import verifyClass
verifyClass(ISecurityPolicy, self._getTargetClass())
class Python_ZSPTests(ZopeSecurityPolicyTestBase,
ISecurityPolicyConformance,
):
def _getTargetClass(self):
from AccessControl.ImplPython import ZopeSecurityPolicy
return ZopeSecurityPolicy
class C_ZSPTests(ZopeSecurityPolicyTestBase,
# ISecurityPolicyConformance, #XXX C version, how?
):
def _getTargetClass(self):
from AccessControl.ImplC import ZopeSecurityPolicy
return ZopeSecurityPolicy
def test_getRoles():
"""
......@@ -445,6 +478,7 @@ def test_getRoles():
def test_zsp_gets_right_roles_for_methods():
"""
>>> from AccessControl.ZopeSecurityPolicy import ZopeSecurityPolicy
>>> zsp = ZopeSecurityPolicy()
>>> from ExtensionClass import Base
>>> class C(Base):
......@@ -499,7 +533,8 @@ from zope.testing.doctest import DocTestSuite
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(ZopeSecurityPolicyTests, 'test'))
suite.addTest(unittest.makeSuite(Python_ZSPTests, 'test'))
suite.addTest(unittest.makeSuite(C_ZSPTests, 'test'))
suite.addTest(DocTestSuite())
return suite
......
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