[PATCH] Check the policy before applying changes

4 views
Skip to first unread message

F. Poirotte

unread,
Feb 14, 2011, 5:38:53 AM2/14/11
to rum-discuss
The following patches make rum check the policy before applying some
of the changes. It also makes it possible to check an instance
globally before accepting a change (eg. some users may have the right
to change attribute A but only if they did not modify attribute B).

The first patch (against RumAlchemy) prevents an update() operation on
an object in RumAlchemy's repository from being applied automatically
on the next session flush (in that regard, update() now works the same
way as create()).
This introduces a slight change in rum too because the repository's
save() method needs to be called after update() [again, this is the
same as for create()].

The next patch (against rum) adds a call to save() after update(). It
also makes rum's controller call self.app.policy.check() more often
(there may be other places in the code where this would also be
useful, but those are the primary places I could think of).

For the RumAlchemy part :

diff -r f2863d570eac rumalchemy/repository.py
--- a/rumalchemy/repository.py Tue Feb 01 13:49:14 2011 +0100
+++ b/rumalchemy/repository.py Mon Feb 14 11:29:58 2011 +0100
@@ -238,7 +238,8 @@
with coerced and validated values from the View.
"""
self._set_attrs(obj, data)
- self._set_dirty()
+ if obj in self.session:
+ self.session.expunge(obj)

def create(self, data):
"""

For rum itself :

diff -r 1ebf21e7aa72 rum/controller.py
--- a/rum/controller.py Wed Jan 26 16:06:38 2011 +0100
+++ b/rum/controller.py Mon Feb 14 11:35:30 2011 +0100
@@ -851,6 +851,7 @@
@resource_action('collection', 'POST')
def create(self):
obj = self.repository.create(self.form_result)
+ self.app.policy.check(obj=obj, action="create", attr=None)
self.flash(_(u'Object %(obj)s was succesfully created') %
{'obj': obj})
self.repository.save(obj)
self.app.redirect_to(action='index', _use_next=True, id=None)
@@ -875,6 +876,8 @@
for k in self.form_result:
self.app.policy.check(obj=self.obj, action="update",
attr=k)
self.repository.update(self.obj, self.form_result)
+ self.app.policy.check(obj=self.obj, action="update",
attr=None)
+ self.repository.save(self.obj)
self.flash(_(u'Object %(obj)s was succesfully updated')
% {'obj':self.obj})
#should include parent here
@@ -890,6 +893,7 @@
N_('delete')
@resource_action('member', 'DELETE')
def delete(self):
+ self.app.policy.check(obj=self.obj, action="delete",
attr=None)
self.flash(_(u'Object %(obj)s was succesfully deleted')
% {'obj':self.obj})
self.repository.delete(self.obj)
@@ -910,6 +914,7 @@
N_('edit')
@resource_action('member', 'GET')
def edit(self):
+ self.app.policy.check(obj=self.obj, action="edit", attr=None)
return {'item': self.obj}

Michael Brickenstein

unread,
Feb 14, 2011, 6:31:19 AM2/14/11
to rum-d...@googlegroups.com, F. Poirotte
Hi François!

Thank you very much, for the patches.
Awesome work!

I'll review them and incorporate them later more carefully,
but might I ask some spontaneous questions come in my mind.

Of course, things should be handled in RUM
very clean. That's the purpose of a framework to have a reliable
implementation of functionality.

So let me ask, some questions, why I personally do not run into that trouble (that
must have been hard to debug, really sorry).

Actually, you touched some parts of Albertos design,
so as usual, I can only guess of any reasons and
whether it's a bug or feature.

Two reasons
- My main app makes sure (in terms of middleware), that the session is removed
- I wrap in my complete app repoze.tm2 middleware

Actually rum has a cleanup procedure
which should resolve the problem at least at the end of the request
in case, it runs as full_stack, otherwise, cleanup might be the duty
of the mounting app (oh, even if that's good documented, it's still a heavy pitfall).

I cite the cleanup procedure from
RUMAlchemy's repository.


def cleanup(self, app=None):
# Cleans up session if app is full_stack or repository is not attached
# to any app.
if not app or app.config['full_stack']:
if hasattr(self.session_factory, 'remove'):
self.session_factory.remove()
transaction.abort()

So, my first question for analysis is whether you disabled RUM to be full_stack?


I still remember that you would like to have an official RUM release.
I think 0.4 is feature complete (actually I cannot remember all new features).
Of course, I would like to be sure
that there are no problems with our policy.

So, did I really miss some policy checks, or has the insertion of policy checks just been safety.
I personally run often enough against my policy (usually as I do note have established any rules)
for my many, many custom actions in my own app.
So, I am quite confident, that the policy is checked (for any controller action of any controller
derrived from CRUDController) by this rule.

rum.controller:

@call_action.around( prio=48)
def _check_security(next_method, self, routes):
check_on=routes["resource"]
obj=getattr(self, "obj", None)
if obj is not None:
check_on=obj
parent=getattr(self, "parent_obj",None)
if not parent is None:
remote_name=routes['remote_name']
self.app.policy.check(obj=parent, action="show", attr=remote_name)
self.app.policy.check(obj=check_on,action=routes["action"])
return next_method(self, routes)

Do, I miss something? That would be frightened.
IMHO, the only thing, which must be manually checked are
column level permissions.

I think the rest of the second patch is an adjustment to the first patch.

By the way, have you seen
https://www.ohloh.net/p/rum/contributors ?
I would love to enter more of your patches to our repository.

Cheers,
Michael

> --
> You received this message because you are subscribed to the Google Groups "rum-discuss" group.
> To post to this group, send email to rum-d...@googlegroups.com.
> To unsubscribe from this group, send email to rum-discuss...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/rum-discuss?hl=en.

-------------------------------------------
Dr. rer. nat. Michael Brickenstein
Mathematisches Forschungsinstitut Oberwolfach gGmbH
Schwarzwaldstr. 9 - 11
77709 Oberwolfach
Tel.: 07834/979-31
Fax: 07834/979-38

F. Poirotte

unread,
Mar 24, 2011, 9:44:52 AM3/24/11
to rum-discuss
Hi Michael,

Sorry it took me so long to answer your questions.
At the time I wrote the patch, I was using rum-0.3dev_20100617. I
wanted to take some time to make sure I could still reproduce this on
the latest release of rum-0.4.

So, I have now set up a working platform with rum-0.4, and I still
have the same issue. Here's some additional information:

Indeed, its seems the policy is already checked on the class itself by
the method you copied.
I don't know why, but the method is never called in my case. I'll try
to write a simple reproducable testcase later.
On the other hand, the checks on individual columns/fields work
perfectly, with my custom policy being called for each column as it
should.
The feature I wanted to implement in my policy was a full object check
(testing the values of several columns together to determine whether
the action on the object as a whole should be allowed or not).
My use case for this is: I have several criteria the depend on the
value of two or more DB columns. Those criteria are complex enough
that they would be impractical to implement at the DBMS level using
CHECK constraints or triggers.

Therefore, the patch makes a second call to the policy *AFTER* the
object as a whole has been created/updated so that my policy can do
whatever complex checks it needs to do before the object in committed
into the database (note: the change in RumAlchemy prevents the object
from being committed immediately after it was updated, so as to make
this possible).

I don't have a very deep knowledge of rum, so there may be another
(better/easier) way to achieve this.
I hope this clarifies things a little.

Cheers,
François

Michael Brickenstein

unread,
Mar 24, 2011, 10:05:22 AM3/24/11
to rum-d...@googlegroups.com, F. Poirotte
Hi!

Am 24.03.2011 um 14:44 schrieb F. Poirotte:

> Hi Michael,
>
> Sorry it took me so long to answer your questions.
> At the time I wrote the patch, I was using rum-0.3dev_20100617. I
> wanted to take some time to make sure I could still reproduce this on
> the latest release of rum-0.4.
>
> So, I have now set up a working platform with rum-0.4, and I still
> have the same issue. Here's some additional information:
>
> Indeed, its seems the policy is already checked on the class itself by
> the method you copied.
> I don't know why, but the method is never called in my case. I'll try
> to write a simple reproducable testcase later.

I do not know about your case.
But create/update really work a little bit differently.
In the first case, the policy is only checked for the
resource ('create an new instance of that resource')
and in the the second
case it can be checked for a particular instance.

This means, in the case of 'create' the resource/class is passed to your
policy checker function.

> On the other hand, the checks on individual columns/fields work
> perfectly, with my custom policy being called for each column as it
> should.
> The feature I wanted to implement in my policy was a full object check
> (testing the values of several columns together to determine whether
> the action on the object as a whole should be allowed or not).
> My use case for this is: I have several criteria the depend on the
> value of two or more DB columns. Those criteria are complex enough
> that they would be impractical to implement at the DBMS level using
> CHECK constraints or triggers.
>
> Therefore, the patch makes a second call to the policy *AFTER* the
> object as a whole has been created/updated so that my policy can do
> whatever complex checks it needs to do before the object in committed
> into the database (note: the change in RumAlchemy prevents the object
> from being committed immediately after it was updated, so as to make
> this possible).
>
> I don't have a very deep knowledge of rum, so there may be another
> (better/easier) way to achieve this.

Hmm, let's talk about *better*. Easier is more difficult.

I am not sure, whether your use case is really security
or some constraints.
In the last case, it would not be right to do it with the policy.
Do you think, that you could express it as chained/compound validators?
http://formencode.org/Validator.html#id7
If you think so, I can help you to
add this validator to the autogenerated form.

Another way might be subclassing the
CRUDController registering a custom controller.
This is actually very easy.

Cheers,
Michael

F. Poirotte

unread,
Mar 24, 2011, 1:35:10 PM3/24/11
to rum-discuss
> Do you think, that you could express it as chained/compound validators?http://formencode.org/Validator.html#id7
> If you think so, I can help you to
> add this validator to the autogenerated form.
>
> Another way might be subclassing the
> CRUDController registering a custom controller.
> This is actually very easy.
>
> Cheers,
> Michael
>

I'm using the policy to do constraint checks.
I'm not sure if compound validators could help me here because the
constraints need to fetch data from several other tables and I see a
simple way to do that using formencode (without having to roll my own
validators).
The subclassing approaching might work just fine however (I recently
started reading the "MoreThanCrud" page on the wiki, and I start to
see how it could be of use here). I'll try it out and keep you posted.

On another note, I got an error when raising a Denial exception which
contains accentuated characters from the policy.
Part of the code seems to expect the Denial's message to be of type
str while others seem to assume unicode.
I'll probably start a new thread and/or file a bug report for that
separate issue though.

Thanks again for your valuable inputs,
François

Michael Brickenstein

unread,
Mar 28, 2011, 5:00:58 AM3/28/11
to rum-d...@googlegroups.com, F.Poirotte
Dear François!

I noticed problems with error display and unicode myself, some
time before.
At that time, I noticed that the bug is not specific to rum but
to the generated error controllers of my Pylons application.
Unfortunately I had to postpone fixing it.

Now, I have reinvestigated the issue.

File "/home/michael/owpdbenv/lib/python2.6/site-packages/Pylons-1.0-py2.6.egg/pylons/controllers/core.py", line 105, in _inspect_call
result = self._perform_call(func, args)
File "/home/michael/owpdbenv/lib/python2.6/site-packages/Pylons-1.0-py2.6.egg/pylons/controllers/core.py", line 57, in _perform_call
return func(**args)
File "/mnt/hgfs/programming/owconf/owconf/controllers/error.py", line 26, in document
content = literal(resp.body) or cgi.escape(request.GET.get('message', ''))
File "/home/michael/owpdbenv/lib/python2.6/site-packages/MarkupSafe-0.11-py2.6-linux-i686.egg/markupsafe/__init__.py", line 71, in __new__
return unicode.__new__(cls, base)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 160: ordinal not in range(128)


Since, that's really a problem of the hosting application, I can only hope
that you have encountered a similar problem and my
following solution helps you.

--- a/owconf/controllers/error.py Tue Mar 15 10:53:39 2011 +0100
+++ b/owconf/controllers/error.py Mon Mar 28 01:52:43 2011 -0700
@@ -4,7 +4,9 @@
from pylons import request
from pylons.controllers.util import forward
from pylons.middleware import error_document_template
-from webhelpers.html.builder import literal
+from webhelpers.html.builder import literal as literal_
+
+

from owconf.lib.base import BaseController

@@ -23,6 +25,12 @@
def document(self):
"""Render the error document"""
resp = request.environ.get('pylons.original_response')
+ def literal(l):
+ if isinstance(l, str):
+ default_charset=getattr(resp, 'default_charset', None)
+ if default_charset:
+ l= l.decode(default_charset)
+ return literal_(l)
content = literal(resp.body) or cgi.escape(request.GET.get('message', ''))
page = error_document_template % \
dict(prefix=request.environ.get('SCRIPT_NAME', ''),

Best regards from not so far from France,
Michael

Am 24.03.2011 um 18:35 schrieb F. Poirotte:

>>
>>
>
> I'm using the policy to do constraint checks.
> I'm not sure if compound validators could help me here because the
> constraints need to fetch data from several other tables and I see a
> simple way to do that using formencode (without having to roll my own
> validators).
> The subclassing approaching might work just fine however (I recently
> started reading the "MoreThanCrud" page on the wiki, and I start to
> see how it could be of use here). I'll try it out and keep you posted.
>
> On another note, I got an error when raising a Denial exception which
> contains accentuated characters from the policy.
> Part of the code seems to expect the Denial's message to be of type
> str while others seem to assume unicode.
> I'll probably start a new thread and/or file a bug report for that
> separate issue though.
>
> Thanks again for your valuable inputs,
> François
>

F. Poirotte

unread,
Mar 28, 2011, 7:57:02 AM3/28/11
to rum-discuss
Hi Michael,

I my case the traceback is a little bit different. When I pass a
unicode object to Denial's __init__ method:

URL: http://localhost:8080/admin/groups/1/edit
File '/home/fpoirott/.buildout/eggs/WebError-0.10.1-py2.5.egg/weberror/
evalexception.py', line 431 in respond
app_iter = self.application(environ, detect_start_response)
File '/home/fpoirott/.buildout/eggs/TurboGears2-2.0.3-py2.5.egg/tg/
configuration.py', line 655 in wrapper
return app(environ, start_response)
File '/home/fpoirott/.buildout/eggs/TurboGears2-2.0.3-py2.5.egg/tg/
configuration.py', line 555 in remover
return app(environ, start_response)
File '/home/fpoirott/.buildout/eggs/repoze.tm2-1.0a5-py2.5.egg/repoze/
tm/__init__.py', line 23 in __call__
result = self.application(environ, save_status_and_headers)
File '/home/fpoirott/.buildout/eggs/ToscaWidgets-0.9.9-py2.5.egg/tw/
core/middleware.py', line 43 in __call__
return self.wsgi_app(environ, start_response)
File '/home/fpoirott/.buildout/eggs/ToscaWidgets-0.9.9-py2.5.egg/tw/
core/middleware.py', line 68 in wsgi_app
resp = req.get_response(self.application)
File '/home/fpoirott/.buildout/eggs/WebOb-1.0-py2.5.egg/webob/
request.py', line 937 in get_response
application, catch_exc_info=False)
File '/home/fpoirott/.buildout/eggs/WebOb-1.0-py2.5.egg/webob/
request.py', line 906 in call_application
app_iter = application(self.environ, start_response)
File '/home/fpoirott/.buildout/eggs/ToscaWidgets-0.9.9-py2.5.egg/tw/
core/resource_injector.py', line 68 in _injector
resp = req.get_response(app)
File '/home/fpoirott/.buildout/eggs/WebOb-1.0-py2.5.egg/webob/
request.py', line 937 in get_response
application, catch_exc_info=False)
File '/home/fpoirott/.buildout/eggs/WebOb-1.0-py2.5.egg/webob/
request.py', line 906 in call_application
app_iter = application(self.environ, start_response)
File '/home/fpoirott/.buildout/eggs/Beaker-1.4-py2.5.egg/beaker/
middleware.py', line 73 in __call__
return self.app(environ, start_response)
File '/home/fpoirott/.buildout/eggs/Beaker-1.4-py2.5.egg/beaker/
middleware.py', line 152 in __call__
return self.wrap_app(environ, session_start_response)
File '/home/fpoirott/.buildout/eggs/Routes-1.11-py2.5.egg/routes/
middleware.py', line 130 in __call__
response = self.app(environ, start_response)
File '/home/fpoirott/.buildout/eggs/Pylons-0.9.7-py2.5.egg/pylons/
wsgiapp.py', line 125 in __call__
response = self.dispatch(controller, environ, start_response)
File '/home/fpoirott/.buildout/eggs/Pylons-0.9.7-py2.5.egg/pylons/
wsgiapp.py', line 324 in dispatch
return controller(environ, start_response)
File '[...]/turbogears/controllers/__init__.py', line 47 in __call__
return TGController.__call__(self, environ, start_response)
File '/home/fpoirott/.buildout/eggs/Pylons-0.9.7-py2.5.egg/pylons/
controllers/core.py', line 221 in __call__
response = self._dispatch_call()
File '/home/fpoirott/.buildout/eggs/Pylons-0.9.7-py2.5.egg/pylons/
controllers/core.py', line 172 in _dispatch_call
response = self._inspect_call(func)
File '/home/fpoirott/.buildout/eggs/Pylons-0.9.7-py2.5.egg/pylons/
controllers/core.py', line 107 in _inspect_call
result = self._perform_call(func, args)
File '/home/fpoirott/.buildout/eggs/TurboGears2-2.0.3-py2.5.egg/tg/
controllers.py', line 857 in _perform_call
self, controller, params, remainder=remainder)
File '/home/fpoirott/.buildout/eggs/TurboGears2-2.0.3-py2.5.egg/tg/
controllers.py', line 172 in _perform_call
output = controller(*remainder, **dict(params))
File '/home/fpoirott/.buildout/eggs/TurboGears2-2.0.3-py2.5.egg/tg/
controllers.py', line 924 in default
return self.delegate(new_req.environ, request.start_response)
File '/home/fpoirott/.buildout/eggs/TgRum-0.3.2dev_20100628-py2.5.egg/
tgrum.py', line 94 in delegate
return super(RumAlchemyController, self).delegate(environ,
start_response)
File '/home/fpoirott/.buildout/eggs/TurboGears2-2.0.3-py2.5.egg/tg/
controllers.py', line 932 in delegate
return self.app(environ, start_response)
File '/home/fpoirott/.buildout/eggs/rum-0.4dev_20110202-py2.5.egg/rum/
wsgiapp.py', line 314 in __call__
return self.wsgi_app(environ, start_response)
File '/home/fpoirott/.buildout/eggs/Paste-1.7.2-py2.5.egg/paste/
registry.py', line 350 in __call__
app_iter = self.application(environ, start_response)
File '/home/fpoirott/.buildout/eggs/rum-0.4dev_20110202-py2.5.egg/rum/
wsgiapp.py', line 325 in wsgi_app
return self.dispatch(request)(environ, start_response)
File '/home/fpoirott/.buildout/eggs/rum-0.4dev_20110202-py2.5.egg/rum/
router.py', line 23 in __call__
return next_app(environ, start_response)
File '/home/fpoirott/.buildout/eggs/rum-0.4dev_20110202-py2.5.egg/rum/
controller.py', line 104 in __call__
output = self.handle_exception(e, routes)
File '<generated code>', line ? in handle_exception
File '/home/fpoirott/.buildout/eggs/PEAK_Rules-0.5a1.dev_r2600-
py2.5.egg/peak/rules/core.py', line 153 in __call__
return self.body(*args, **kw)
File '/home/fpoirott/.buildout/eggs/rum-0.4dev_20110202-py2.5.egg/rum/
controller.py', line 144 in _handle_denial
self.flash(unicode(e), status='alert')
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in
position 19: ordinal not in range(128)

Removing the accentuated characters from the Denial's message prevents
this error from happening.
When passing an str object with accentuated characters (encoded using
iso-8859-15), the exception also occurs, this time in rumpolicy:

URL: http://localhost:8080/admin/groups/1/edit
File '/home/fpoirott/.buildout/eggs/WebError-0.10.1-py2.5.egg/weberror/
evalexception.py', line 431 in respond
app_iter = self.application(environ, detect_start_response)
File '/home/fpoirott/.buildout/eggs/TurboGears2-2.0.3-py2.5.egg/tg/
configuration.py', line 655 in wrapper
return app(environ, start_response)
File '/home/fpoirott/.buildout/eggs/TurboGears2-2.0.3-py2.5.egg/tg/
configuration.py', line 555 in remover
return app(environ, start_response)
File '/home/fpoirott/.buildout/eggs/repoze.tm2-1.0a5-py2.5.egg/repoze/
tm/__init__.py', line 23 in __call__
result = self.application(environ, save_status_and_headers)
File '/home/fpoirott/.buildout/eggs/ToscaWidgets-0.9.9-py2.5.egg/tw/
core/middleware.py', line 43 in __call__
return self.wsgi_app(environ, start_response)
File '/home/fpoirott/.buildout/eggs/ToscaWidgets-0.9.9-py2.5.egg/tw/
core/middleware.py', line 68 in wsgi_app
resp = req.get_response(self.application)
File '/home/fpoirott/.buildout/eggs/WebOb-1.0-py2.5.egg/webob/
request.py', line 937 in get_response
application, catch_exc_info=False)
File '/home/fpoirott/.buildout/eggs/WebOb-1.0-py2.5.egg/webob/
request.py', line 906 in call_application
app_iter = application(self.environ, start_response)
File '/home/fpoirott/.buildout/eggs/ToscaWidgets-0.9.9-py2.5.egg/tw/
core/resource_injector.py', line 68 in _injector
resp = req.get_response(app)
File '/home/fpoirott/.buildout/eggs/WebOb-1.0-py2.5.egg/webob/
request.py', line 937 in get_response
application, catch_exc_info=False)
File '/home/fpoirott/.buildout/eggs/WebOb-1.0-py2.5.egg/webob/
request.py', line 906 in call_application
app_iter = application(self.environ, start_response)
File '/home/fpoirott/.buildout/eggs/Beaker-1.4-py2.5.egg/beaker/
middleware.py', line 73 in __call__
return self.app(environ, start_response)
File '/home/fpoirott/.buildout/eggs/Beaker-1.4-py2.5.egg/beaker/
middleware.py', line 152 in __call__
return self.wrap_app(environ, session_start_response)
File '/home/fpoirott/.buildout/eggs/Routes-1.11-py2.5.egg/routes/
middleware.py', line 130 in __call__
response = self.app(environ, start_response)
File '/home/fpoirott/.buildout/eggs/Pylons-0.9.7-py2.5.egg/pylons/
wsgiapp.py', line 125 in __call__
response = self.dispatch(controller, environ, start_response)
File '/home/fpoirott/.buildout/eggs/Pylons-0.9.7-py2.5.egg/pylons/
wsgiapp.py', line 324 in dispatch
return controller(environ, start_response)
File '[...]/turbogears/controllers/__init__.py', line 47 in __call__
return TGController.__call__(self, environ, start_response)
File '/home/fpoirott/.buildout/eggs/Pylons-0.9.7-py2.5.egg/pylons/
controllers/core.py', line 221 in __call__
response = self._dispatch_call()
File '/home/fpoirott/.buildout/eggs/Pylons-0.9.7-py2.5.egg/pylons/
controllers/core.py', line 172 in _dispatch_call
response = self._inspect_call(func)
File '/home/fpoirott/.buildout/eggs/Pylons-0.9.7-py2.5.egg/pylons/
controllers/core.py', line 107 in _inspect_call
result = self._perform_call(func, args)
File '/home/fpoirott/.buildout/eggs/TurboGears2-2.0.3-py2.5.egg/tg/
controllers.py', line 857 in _perform_call
self, controller, params, remainder=remainder)
File '/home/fpoirott/.buildout/eggs/TurboGears2-2.0.3-py2.5.egg/tg/
controllers.py', line 172 in _perform_call
output = controller(*remainder, **dict(params))
File '/home/fpoirott/.buildout/eggs/TurboGears2-2.0.3-py2.5.egg/tg/
controllers.py', line 924 in default
return self.delegate(new_req.environ, request.start_response)
File '/home/fpoirott/.buildout/eggs/TgRum-0.3.2dev_20100628-py2.5.egg/
tgrum.py', line 94 in delegate
return super(RumAlchemyController, self).delegate(environ,
start_response)
File '/home/fpoirott/.buildout/eggs/TurboGears2-2.0.3-py2.5.egg/tg/
controllers.py', line 932 in delegate
return self.app(environ, start_response)
File '/home/fpoirott/.buildout/eggs/rum-0.4dev_20110202-py2.5.egg/rum/
wsgiapp.py', line 314 in __call__
return self.wsgi_app(environ, start_response)
File '/home/fpoirott/.buildout/eggs/Paste-1.7.2-py2.5.egg/paste/
registry.py', line 350 in __call__
app_iter = self.application(environ, start_response)
File '/home/fpoirott/.buildout/eggs/rum-0.4dev_20110202-py2.5.egg/rum/
wsgiapp.py', line 325 in wsgi_app
return self.dispatch(request)(environ, start_response)
File '/home/fpoirott/.buildout/eggs/rum-0.4dev_20110202-py2.5.egg/rum/
router.py', line 23 in __call__
return next_app(environ, start_response)
File '/home/fpoirott/.buildout/eggs/rum-0.4dev_20110202-py2.5.egg/rum/
controller.py', line 99 in __call__
output = self.call_action(routes)
File '/home/fpoirott/.buildout/eggs/rum-0.4dev_20110202-py2.5.egg/rum/
controller.py', line 205 in call_action
@generic
File '<generated code>', line ? in call_action
File '/home/fpoirott/.buildout/eggs/PEAK_Rules-0.5a1.dev_r2600-
py2.5.egg/peak/rules/core.py', line 152 in __call__
return self.body(self.tail, *args, **kw)
File '/home/fpoirott/.buildout/eggs/rum-0.4dev_20110202-py2.5.egg/rum/
controller.py', line 588 in _initialize_repository
return next_method(self, routes)
File '/home/fpoirott/.buildout/eggs/PEAK_Rules-0.5a1.dev_r2600-
py2.5.egg/peak/rules/core.py', line 152 in __call__
return self.body(self.tail, *args, **kw)
File '/home/fpoirott/.buildout/eggs/rum-0.4dev_20110202-py2.5.egg/rum/
controller.py', line 640 in _query
return next_method(self, routes)
File '/home/fpoirott/.buildout/eggs/PEAK_Rules-0.5a1.dev_r2600-
py2.5.egg/peak/rules/core.py', line 152 in __call__
return self.body(self.tail, *args, **kw)
File '/home/fpoirott/.buildout/eggs/rum-0.4dev_20110202-py2.5.egg/rum/
controller.py', line 676 in _preselect_fields
return next_method(self, routes)
File '/home/fpoirott/.buildout/eggs/PEAK_Rules-0.5a1.dev_r2600-
py2.5.egg/peak/rules/core.py', line 152 in __call__
return self.body(self.tail, *args, **kw)
File '/home/fpoirott/.buildout/eggs/rum-0.4dev_20110202-py2.5.egg/rum/
controller.py', line 575 in _validate_input
return next_method(self, routes)
File '/home/fpoirott/.buildout/eggs/PEAK_Rules-0.5a1.dev_r2600-
py2.5.egg/peak/rules/core.py', line 152 in __call__
return self.body(self.tail, *args, **kw)
File '/home/fpoirott/.buildout/eggs/rum-0.4dev_20110202-py2.5.egg/rum/
controller.py', line 595 in _fetch_resource
return next_method(self, routes)
File '/home/fpoirott/.buildout/eggs/PEAK_Rules-0.5a1.dev_r2600-
py2.5.egg/peak/rules/core.py', line 152 in __call__
return self.body(self.tail, *args, **kw)
File '/home/fpoirott/.buildout/eggs/rum-0.4dev_20110202-py2.5.egg/rum/
controller.py', line 604 in _fetch_parent
return next_method(self, routes)
File '/home/fpoirott/.buildout/eggs/PEAK_Rules-0.5a1.dev_r2600-
py2.5.egg/peak/rules/core.py', line 152 in __call__
return self.body(self.tail, *args, **kw)
File '/home/fpoirott/.buildout/eggs/rum-0.4dev_20110202-py2.5.egg/rum/
controller.py', line 616 in _check_security
self.app.policy.check(obj=check_on,action=routes["action"])
File '/home/fpoirott/.buildout/eggs/rum_policy-0.0.9dev-py2.5.egg/
rumpolicy/__init__.py', line 104 in check
raise self.exception_class(unicode(allowed))
File '/home/fpoirott/.buildout/eggs/rum_policy-0.0.9dev-py2.5.egg/
rumpolicy/__init__.py', line 30 in __unicode__
return unicode(self.msg)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position
19: ordinal not in range(128)

The following patch against rum-0.4dev_20110202-py2.5.egg/rum/
exceptions.py worked for me (while passing an unicode object to
Denial):

--- exceptions.py.orig 2011-03-28 13:47:16.000000000 +0200
+++ exceptions.py 2011-03-28 13:52:20.000000000 +0200
@@ -2,7 +2,12 @@
from formencode.api import Invalid
from rum.genericfunctions import AmbiguousMethods,
NoApplicableMethods

-class RumException(StandardError): pass
+class RumException(StandardError):
+ def __unicode__(self):
+ return unicode(self.args[0]
+ if len(self.args) <= 1
+ else self.args)
+
class ConfigError(RumException): pass

class NoTemplateDefined(LookupError, RumException): pass


The patch is based on work done in PEP 0352 (http://www.python.org/dev/
peps/pep-0352/) to handle exception restructuration (see "Transition
plan"). I tested it on Python 2.5.2 where it's working perfectly. I
assume this will also work on later versions.
I'm not sure if this works on Python 2.4 though and I remember seeing
a ticket on rum's tracker to deal with Python 2.4 compatibility, so
depending on target versions for rum, this may not be an acceptable
solution.

Best regards (from not so far from Germany ;)
François
> > For more options, visit this group athttp://groups.google.com/group/rum-discuss?hl=en.

Michael Brickenstein

unread,
Mar 28, 2011, 9:47:24 AM3/28/11
to rum-d...@googlegroups.com
Dear François,

Passing unicode is definitively the way
to go.
In principle, I did the same thing this morning.

Without more brain twisting, I cannot give any reasons why you ran into trouble, and
why your patch helped.

But it looks very, very reasonable.
I pushed it to our repositories.

http://hg.python-rum.org/rum/rev/cf9a24d8ecb3

Thanks a lot :-),
Michael

> For more options, visit this group at http://groups.google.com/group/rum-discuss?hl=en.

Reply all
Reply to author
Forward
0 new messages