Skip to content

Commit

Permalink
Add the ability to perform checks based on values assigned to attribu…
Browse files Browse the repository at this point in the history
…tes, deletion of attributes and operands of in-place mutation operations.
  • Loading branch information
mitchellrj committed Oct 9, 2013
1 parent d7b1b26 commit e44b31a
Show file tree
Hide file tree
Showing 6 changed files with 237 additions and 29 deletions.
6 changes: 5 additions & 1 deletion CHANGES.rst
Expand Up @@ -5,7 +5,11 @@ CHANGES
4.0.1 (unreleased)
------------------

- TBD
- Added ability to perform checks based on values assigned to
attributes, or operands of in-place mutation operations.

- Added ability to check for attribute deletion separately from
attribute assignment.

4.0.0 (2013-07-09)
------------------
Expand Down
4 changes: 4 additions & 0 deletions docs/api/interfaces.rst
Expand Up @@ -47,6 +47,10 @@ Utilities
:members:
:member-order: bysource

.. autointerface:: IValueBasedChecker
:members:
:member-order: bysource

.. autointerface:: ISecurityPolicy
:members:
:member-order: bysource
Expand Down
82 changes: 56 additions & 26 deletions src/zope/security/_proxy.c
Expand Up @@ -72,8 +72,11 @@ static PyObject *__class__str = 0, *__name__str = 0, *__module__str = 0;
DECLARE_STRING(__3pow__);
DECLARE_STRING(__call__);
DECLARE_STRING(check);
DECLARE_STRING(check_with_value);
DECLARE_STRING(check_getattr);
DECLARE_STRING(check_setattr);
DECLARE_STRING(check_setattr_with_value);
DECLARE_STRING(check_delattr);
DECLARE_STRING(__cmp__);
DECLARE_STRING(__coerce__);
DECLARE_STRING(__contains__);
Expand Down Expand Up @@ -161,7 +164,7 @@ static PyTypeObject SecurityProxyType;
*/

static int
check(SecurityProxy *self, PyObject *meth, PyObject *name)
check(SecurityProxy *self, PyObject *meth, PyObject *name, PyObject *value)
{
PyObject *r;

Expand All @@ -177,9 +180,33 @@ check(SecurityProxy *self, PyObject *meth, PyObject *name)
return self->proxy_checker->ob_type->tp_as_mapping->
mp_ass_subscript(self->proxy_checker, self->proxy.proxy_object, name);

r = PyObject_CallMethodObjArgs(self->proxy_checker, meth,
self->proxy.proxy_object, name,
NULL);
if (value != NULL
&& meth == str_check_setattr
&& PyObject_HasAttr(self->proxy_checker, str_check_setattr_with_value) == 1)
// value is NULL if this is really a delattr call
r = PyObject_CallMethodObjArgs(self->proxy_checker,
str_check_setattr_with_value,
self->proxy.proxy_object, name,
value, NULL);
else if (value != NULL
&& meth == str_check
&& PyObject_HasAttr(self->proxy_checker, str_check_with_value) == 1)
// value is NULL if this is really a delattr call
r = PyObject_CallMethodObjArgs(self->proxy_checker,
str_check_with_value,
self->proxy.proxy_object, name,
value, NULL);
else if (value == NULL
&& meth == str_check_setattr
&& PyObject_HasAttr(self->proxy_checker, str_check_delattr) == 1)
r = PyObject_CallMethodObjArgs(self->proxy_checker,
str_check_delattr,
self->proxy.proxy_object, name,
value, NULL);
else
r = PyObject_CallMethodObjArgs(self->proxy_checker, meth,
self->proxy.proxy_object, name,
NULL);
if (r == NULL)
return -1;

Expand Down Expand Up @@ -214,7 +241,7 @@ check1(SecurityProxy *self, PyObject *opname, function1 operation)
{
PyObject *result = NULL;

if (check(self, str_check, opname) >= 0) {
if (check(self, str_check, opname, (PyObject *)NULL) >= 0) {
result = operation(self->proxy.proxy_object);
PROXY_RESULT(self, result);
}
Expand All @@ -229,7 +256,7 @@ check2(PyObject *self, PyObject *other,

if (Proxy_Check(self))
{
if (check((SecurityProxy*)self, str_check, opname) >= 0)
if (check((SecurityProxy*)self, str_check, opname, (PyObject *)NULL) >= 0)
{
result = operation(((SecurityProxy*)self)->proxy.proxy_object,
other);
Expand All @@ -238,7 +265,7 @@ check2(PyObject *self, PyObject *other,
}
else if (Proxy_Check(other))
{
if (check((SecurityProxy*)other, str_check, ropname) >= 0)
if (check((SecurityProxy*)other, str_check, ropname, (PyObject *)NULL) >= 0)
{
result = operation(self,
((SecurityProxy*)other)->proxy.proxy_object);
Expand All @@ -261,7 +288,7 @@ check2i(SecurityProxy *self, PyObject *other,
{
PyObject *result = NULL;

if (check(self, str_check, opname) >= 0)
if (check(self, str_check, opname, other) >= 0)
{
result = operation(self->proxy.proxy_object, other);
if (result == self->proxy.proxy_object)
Expand Down Expand Up @@ -372,7 +399,7 @@ proxy_iter(SecurityProxy *self)
{
PyObject *result = NULL;

if (check(self, str_check, str___iter__) >= 0)
if (check(self, str_check, str___iter__, (PyObject *)NULL) >= 0)
{
result = PyObject_GetIter(self->proxy.proxy_object);
PROXY_RESULT(self, result);
Expand All @@ -385,7 +412,7 @@ proxy_iternext(SecurityProxy *self)
{
PyObject *result = NULL;

if (check(self, str_check_getattr, str_next) >= 0)
if (check(self, str_check_getattr, str_next, (PyObject *)NULL) >= 0)
{
result = PyIter_Next(self->proxy.proxy_object);
PROXY_RESULT(self, result);
Expand All @@ -398,7 +425,7 @@ proxy_getattro(SecurityProxy *self, PyObject *name)
{
PyObject *result = NULL;

if (check(self, str_check_getattr, name) >= 0)
if (check(self, str_check_getattr, name, (PyObject *)NULL) >= 0)
{
result = PyObject_GetAttr(self->proxy.proxy_object, name);
PROXY_RESULT(self, result);
Expand All @@ -409,7 +436,7 @@ proxy_getattro(SecurityProxy *self, PyObject *name)
static int
proxy_setattro(SecurityProxy *self, PyObject *name, PyObject *value)
{
if (check(self, str_check_setattr, name) >= 0)
if (check(self, str_check_setattr, name, value) >= 0)
return PyObject_SetAttr(self->proxy.proxy_object, name, value);
return -1;
}
Expand Down Expand Up @@ -458,7 +485,7 @@ proxy_str(SecurityProxy *self)
{
PyObject *result = NULL;

if (check(self, str_check, str___str__) >= 0)
if (check(self, str_check, str___str__, (PyObject *)NULL) >= 0)
{
result = PyObject_Str(self->proxy.proxy_object);
}
Expand All @@ -475,7 +502,7 @@ proxy_repr(SecurityProxy *self)
{
PyObject *result = NULL;

if (check(self, str_check, str___repr__) >= 0) {
if (check(self, str_check, str___repr__, (PyObject *)NULL) >= 0) {
result = PyObject_Repr(self->proxy.proxy_object);
}
else {
Expand Down Expand Up @@ -504,7 +531,7 @@ proxy_call(SecurityProxy *self, PyObject *args, PyObject *kwds)
{
PyObject *result = NULL;

if (check(self, str_check, str___call__) >= 0)
if (check(self, str_check, str___call__, (PyObject *)NULL) >= 0)
{
result = PyObject_Call(self->proxy.proxy_object, args, kwds);
PROXY_RESULT(self, result);
Expand Down Expand Up @@ -560,7 +587,7 @@ proxy_pow(PyObject *self, PyObject *other, PyObject *modulus)

if (Proxy_Check(self))
{
if (check((SecurityProxy*)self, str_check, str___pow__) >= 0)
if (check((SecurityProxy*)self, str_check, str___pow__, (PyObject *)NULL) >= 0)
{
result = PyNumber_Power(((SecurityProxy*)self)->proxy.proxy_object,
other, modulus);
Expand All @@ -569,7 +596,7 @@ proxy_pow(PyObject *self, PyObject *other, PyObject *modulus)
}
else if (Proxy_Check(other))
{
if (check((SecurityProxy*)other, str_check, str___rpow__) >= 0)
if (check((SecurityProxy*)other, str_check, str___rpow__, (PyObject *)NULL) >= 0)
{
result = PyNumber_Power(self,
((SecurityProxy*)other)->proxy.proxy_object,
Expand All @@ -579,7 +606,7 @@ proxy_pow(PyObject *self, PyObject *other, PyObject *modulus)
}
else if (modulus != NULL && Proxy_Check(modulus))
{
if (check((SecurityProxy*)modulus, str_check, str___3pow__) >= 0)
if (check((SecurityProxy*)modulus, str_check, str___3pow__, (PyObject *)NULL) >= 0)
{
result = PyNumber_Power(self, other,
((SecurityProxy*)modulus)->proxy.proxy_object);
Expand Down Expand Up @@ -608,7 +635,7 @@ proxy_coerce(PyObject **p_self, PyObject **p_other)

assert(Proxy_Check(self));

if (check((SecurityProxy*)self, str_check, str___coerce__) >= 0)
if (check((SecurityProxy*)self, str_check, str___coerce__, (PyObject *)NULL) >= 0)
{
PyObject *left = ((SecurityProxy*)self)->proxy.proxy_object;
PyObject *right = other;
Expand Down Expand Up @@ -692,7 +719,7 @@ INPLACE(truediv, PyNumber_InPlaceTrueDivide)
static Py_ssize_t
proxy_length(SecurityProxy *self)
{
if (check(self, str_check, str___len__) >= 0)
if (check(self, str_check, str___len__, (PyObject *)NULL) >= 0)
return PyObject_Length(self->proxy.proxy_object);
return -1;
}
Expand Down Expand Up @@ -733,7 +760,7 @@ proxy_slice(SecurityProxy *self, Py_ssize_t start, Py_ssize_t end)
{
PyObject *result = NULL;

if (check(self, str_check, str___getslice__) >= 0) {
if (check(self, str_check, str___getslice__, (PyObject *)NULL) >= 0) {
result = PySequence_GetSlice(self->proxy.proxy_object, start, end);
PROXY_RESULT(self, result);
}
Expand All @@ -743,15 +770,15 @@ proxy_slice(SecurityProxy *self, Py_ssize_t start, Py_ssize_t end)
static int
proxy_ass_slice(SecurityProxy *self, Py_ssize_t i, Py_ssize_t j, PyObject *value)
{
if (check(self, str_check, str___setslice__) >= 0)
if (check(self, str_check, str___setslice__, value) >= 0)
return PySequence_SetSlice(self->proxy.proxy_object, i, j, value);
return -1;
}

static int
proxy_contains(SecurityProxy *self, PyObject *value)
{
if (check(self, str_check, str___contains__) >= 0)
if (check(self, str_check, str___contains__, (PyObject *)NULL) >= 0)
return PySequence_Contains(self->proxy.proxy_object, value);
return -1;
}
Expand All @@ -765,7 +792,7 @@ proxy_getitem(SecurityProxy *self, PyObject *key)
{
PyObject *result = NULL;

if (check(self, str_check, str___getitem__) >= 0)
if (check(self, str_check, str___getitem__, (PyObject *)NULL) >= 0)
{
result = PyObject_GetItem(self->proxy.proxy_object, key);
PROXY_RESULT(self, result);
Expand All @@ -777,11 +804,11 @@ static int
proxy_setitem(SecurityProxy *self, PyObject *key, PyObject *value)
{
if (value == NULL) {
if (check(self, str_check, str___delitem__) >= 0)
if (check(self, str_check, str___delitem__, (PyObject *)NULL) >= 0)
return PyObject_DelItem(self->proxy.proxy_object, key);
}
else {
if (check(self, str_check, str___setitem__) >= 0)
if (check(self, str_check, str___setitem__, value) >= 0)
return PyObject_SetItem(self->proxy.proxy_object, key, value);
}
return -1;
Expand Down Expand Up @@ -999,8 +1026,11 @@ if((str_op_##S = INTERN("__" #S "__")) == NULL) return MOD_ERROR_VAL
INIT_STRING(__3pow__);
INIT_STRING(__call__);
INIT_STRING(check);
INIT_STRING(check_with_value);
INIT_STRING(check_getattr);
INIT_STRING(check_setattr);
INIT_STRING(check_setattr_with_value);
INIT_STRING(check_delattr);
INIT_STRING(__cmp__);
INIT_STRING(__coerce__);
INIT_STRING(__contains__);
Expand Down
22 changes: 22 additions & 0 deletions src/zope/security/interfaces.py
Expand Up @@ -149,6 +149,28 @@ def proxy(value):
"""


class IValueBasedChecker(IChecker):
"""Security checker that also checks the operands of attribute
assignment and in-place mutation operations.
"""

def check_delattr(ob, name):
"""As ``check_delattr`` but only applies when the attribute
is being deleted via ``del`` or ``delattr``.
"""

def check_setattr_with_value(ob, name, value):
"""As ``check_setattr`` but also includes the value to be
assigned.
"""

def check_with_value(ob, name, value):
"""As ``check`` but also includes the operand.
Only called for in-place mutation operations.
"""


class INameBasedChecker(IChecker):
"""Security checker that uses permissions to check attribute access."""

Expand Down
10 changes: 8 additions & 2 deletions src/zope/security/proxy.py
Expand Up @@ -101,15 +101,21 @@ def __setattr__(self, name, value):
return super(PyProxyBase, self).__setattr__(name, value)
wrapped = super(PyProxyBase, self).__getattribute__('_wrapped')
checker = super(PyProxyBase, self).__getattribute__('_checker')
checker.check_setattr(wrapped, name)
if getattr(checker, 'check_setattr_with_value', None) is not None:
checker.check_setattr_with_value(wrapped, name, value)
else:
checker.check_setattr(wrapped, name)
setattr(wrapped, name, value)

def __delattr__(self, name):
if name in ('_wrapped', '_checker'):
raise AttributeError()
wrapped = super(PyProxyBase, self).__getattribute__('_wrapped')
checker = super(PyProxyBase, self).__getattribute__('_checker')
checker.check_setattr(wrapped, name)
if getattr(checker, 'check_delattr', None) is not None:
checker.check_delattr(wrapped, name)
else:
checker.check_setattr(wrapped, name)
delattr(wrapped, name)

@_check_name
Expand Down

0 comments on commit e44b31a

Please sign in to comment.