Secure RootController

6 views
Skip to first unread message

Qwait

unread,
Feb 17, 2009, 6:23:30 PM2/17/09
to TurboGears Trunk
I am writing an application that needs to exist behind a secure
controller. When putting the "allow_only", firefox goes into a endless
loop, thus not allowing me to login. I suspect this is because the /
login method is also protected there for redirecting to the login
page, which is protected. The secure controller appears to work on sub
controllers or if I decorate each of the exposed methods. I am running
TG2b5, python 2.5, in a virtual environment.

Gustavo Narea

unread,
Feb 17, 2009, 7:17:53 PM2/17/09
to turbogea...@googlegroups.com, Qwait
That's right, you can only secure subcontrollers, not the whole root
controller.

That causes a cyclic link because the whole application is protected,
including the controller actions where the login form is displayed and
processed, respectively. This is, authorization is denied, then you're
redirected to the login form, but the login form is secured, so you're
redirected to the login form, but the login form is secured... and so on :)

Use the following repoze.what predicate if you're using the default
login/logout URLs:
from repoze.what.predicates import Predicate

class is_login(Predicate):
"""The current page doesn't handle authentication"""
message = "%(path_info)s is does not handle logins or logouts"

white_list = ['/login', '/login_handler', '/post_login_handler',
'/logout', '/post_logout_handler']

def evaluate(self, environ, credentials):
path_info = environ.get('PATH_INFO', '')
if path_info not in self.white_list:
self.unmet(path_info=path_info)

Then you can define your root controller as:
from yourapplication.lib.auth import is_login
class RootController(BaseController):
allow_only = Any(not_anonymous(), is_login())
# ...

Unfortunately, we can't provide a generic mechanism which does the above for
you, because it depends on the repoze.who challenger(s) you're using; for
example, if you were using the built-in FormPlugin, there would be nothing to
do.

We can document it as a recipe, though.

Cheers.
--
Gustavo Narea <http://gustavonarea.net/>.

Get rid of unethical constraints! Get freedomware:
http://www.getgnulinux.org/

cd34

unread,
Feb 17, 2009, 11:11:06 PM2/17/09
to TurboGears Trunk
Is there ever a situation when a login method needs to be behind an
authenticated area? Perhaps if a user logs in as an editor, then
needs to log in as a manager, perhaps you would have that double
authorization request. From what I see, repoze actually would replace
the current credentials with the new privileges and starting a new
auth session rather than adding the extra group membership.

The login page should clear any existing credentials and prompt the
user for login information and shouldn't be password protected.
Otherwise you have to decorate every method in your RootController
with:

@require(predicates.has_permission('manage', msg=_('Only for
managers')))

except for the login and logout?

So, if someone needs to protect an application, they protect the
subcontrollers with one method, and the defined pages in the root
controller a separate way?

perhaps something like:

allowed_request_url = ['/login', '/login_handler', '/
post_login_handler', '/logout', '/post_logout_handler']

if pylons.request.path in allowed_request_url:
return True

or a proper fix.

The current implementation seems to stray from the DRY method.

Gustavo Narea

unread,
Feb 18, 2009, 7:19:33 AM2/18/09
to turbogea...@googlegroups.com
Hello,

This is a consequence of using RedirectingFormPlugin, the only repoze.who
challenger which has to reach the application to challenge (this is, display
the login form). While using this plugin there's *nothing* else you can do --
you are protecting the whole application, how is repoze.who supposed to reach
the controller action that displays the login form then?

If you don't like the workaround, you have to change the plugin (use something
like the built-in FormPlugin or write your own).

But keep in mind that in spite of this drawback, RedirectingFormPlugin-like
forms are developer-friendlier than others.

Cheers.

On Wednesday February 18, 2009 05:11:06 cd34 wrote:
> Is there ever a situation when a login method needs to be behind an
> authenticated area?  Perhaps if a user logs in as an editor, then
> needs to log in as a manager, perhaps you would have that double
> authorization request.  From what I see, repoze actually would replace
> the current credentials with the new privileges and starting a new
> auth session rather than adding the extra group membership.
>
> The login page should clear any existing credentials and prompt the
> user for login information and shouldn't be password protected.
> Otherwise you have to decorate every method in your RootController
> with:
>
> @require(predicates.has_permission('manage', msg=_('Only for
> managers')))
>
> except for the login and logout?
>
> So, if someone needs to protect an application, they protect the
> subcontrollers with one method, and the defined pages in the root
> controller a separate way?
>
> perhaps something like:
>
>  allowed_request_url = ['/login', '/login_handler', '/
> post_login_handler', '/logout', '/post_logout_handler']
>
> if pylons.request.path in allowed_request_url:
>  return True
>
> or a proper fix.
>
> The current implementation seems to stray from the DRY method.

--

cd34

unread,
Feb 18, 2009, 10:20:16 AM2/18/09
to TurboGears Trunk
Just so I'm clear, if you have to protect the RootController, you have
to put @require on each of the root controller paths that needs to be
protected, then protect each subcontroller individually?

So the TurboGears development team suggests the following code
structure:

class RootController(BaseController):
admin = SecuredCatwalk(DBSession, metadata)
secc = SecureController()
error = ErrorController()
docs = DocsController()
billing = BillingController()
clients = ClientController()
databases = DatabaseController()

@expose('wiki.templates.template')
@require(predicates.has_permission('manage', msg=_('Only for
managers')))
def test(self):
return dict(page='index',template='form')

@expose('wiki.templates.index')
@require(predicates.has_permission('manage', msg=_('Only for
managers')))
def index(self):
return dict(page='index')

@expose('wiki.templates.about')
@require(predicates.has_permission('manage', msg=_('Only for
managers')))
def about(self):
return dict(page='about')

@expose('wiki.templates.authentication')
@require(predicates.has_permission('manage', msg=_('Only for
managers')))
def auth(self):
return dict(page='auth')

@expose('wiki.templates.index')
@require(predicates.has_permission('manage', msg=_('Only for
managers')))
def manage_permission_only(self, **kw):
return dict(page='managers stuff')

@expose('wiki.templates.index')
@require(predicates.is_user('editor', msg=_('Only for the
editor')))
def editor_user_only(self, **kw):
return dict(page='editor stuff')

@expose('wiki.templates.login')
def login(self, **kw):
came_from = kw.get('came_from', url('/'))
return dict(page='login', header=lambda *arg: None,
footer=lambda *arg: None, came_from=came_from)

and then in each of the sub controllers, I can then protect it with:

allow_only = in_group('manage')

which seems quite a bit more complex than including the above line
once.

Which of the plugins from here:

http://static.repoze.org/whodocs/narr.html#module-repoze.who.plugins.sql

should I be using where I can specify the allow_only on the root
controller and have the entire application protected?

Are there other frameworks that put the login page behind an AAA
method?

Gustavo Narea

unread,
Feb 18, 2009, 10:46:22 AM2/18/09
to turbogea...@googlegroups.com, cd34
On Wednesday February 18, 2009 16:20:16 cd34 wrote:
> Just so I'm clear, if you have to protect the RootController, you have
> to put @require on each of the root controller paths that needs to be
> protected, then protect each subcontroller individually?
>
> So the TurboGears development team suggests the following code
> structure:
>
> <snip>

>
> and then in each of the sub controllers, I can then protect it with:
>
> allow_only = in_group('manage')

When did any of us suggested such thing?


> which seems quite a bit more complex than including the above line
> once.
>
> Which of the plugins from here:
>
> http://static.repoze.org/whodocs/narr.html#module-repoze.who.plugins.sql
>
> should I be using where I can specify the allow_only on the root
> controller and have the entire application protected?

I already said that if you do want to protect the whole application and don't
want the simple and clear workaround I suggested, then you can use *any*
repoze.who challenger, except those based on the RedirectingFormPlugin.


> Are there other frameworks that put the login page behind an AAA
> method?

You don't seem to understand what is going on:
repoze.who is a WSGI middleware for authentication, used by default in TG2
applications. Because of the way TG2 configures authentication by default, for
the user to be authenticated, one of the application's controller actions must
be called. So, when authorization is denied and the user is anonymous,
repoze.who middleware catches the authorization denial exception and redirects
the user to the login form... But if you protected the login form too, there's
no way that the user will be able to log in.

*But*, if you use a different repoze.who challenger, when authorization is
denied and the user is anonymous, the repoze.who middleware catches the
authorization denial exception and the middleware *itself* renders the login
form (or an HTTP authentication prompt) -- without ever reaching the WSGI
application, so it doesn't matter if it's totally protected.

Cheers.

cd34

unread,
Feb 18, 2009, 10:55:20 AM2/18/09
to TurboGears Trunk


On Feb 18, 10:46 am, Gustavo Narea <m...@gustavonarea.net> wrote:
> You don't seem to understand what is going on:
> repoze.who is a WSGI middleware for authentication, used by default in TG2
> applications. Because of the way TG2 configures authentication by default, for
> the user to be authenticated, one of the application's controller actions must
> be called. So, when authorization is denied and the user is anonymous,
> repoze.who middleware catches the authorization denial exception and redirects
> the user to the login form... But if you protected the login form too, there's
> no way that the user will be able to log in.
>
> *But*, if you use a different repoze.who challenger, when authorization is
> denied and the user is anonymous, the repoze.who middleware catches the
> authorization denial exception and the middleware *itself* renders the login
> form (or an HTTP authentication prompt) -- without ever reaching the WSGI
> application, so it doesn't matter if it's totally protected.

I need to protect an entire application behind a username/password
prompt.

What middleware do I use?

Gustavo Narea

unread,
Feb 18, 2009, 11:13:52 AM2/18/09
to turbogea...@googlegroups.com, cd34
On Wednesday February 18, 2009 16:55:20 cd34 wrote:
> I need to protect an entire application behind a username/password
> prompt.
>
> What middleware do I use?

repoze.who is all the middleware you need, the question is what repoze.who
challenger and the answer is: You can use *any* repoze.who challenger (e.g.,
BasicAuthPlugin, FormPlugin, RedirectingFormPlugin).

The only thing to keep in mind is that if you want to use a
RedirectingFormPlugin-based challenger, your application-wide access rule must
always grant access to authentication-related URLs.

cd34

unread,
Feb 18, 2009, 12:29:44 PM2/18/09
to TurboGears Trunk
perhaps I am not phrasing it correctly.

I need to protect the root controller and all subcontrollers.

What method in Turbogears 2.0 will allow me to do that?

Currently, I must use @require(predicates.has_permission('manage',
msg=_('Only for managers'))) for each page in the Root Controller and
then set allow_only = in_group('manage') in the subcontrollers.

What method will allow me to use:

allow_only = in_group('manage')

in the RootController to protect my entire application?

Where might this be documented?

Mark Ramm

unread,
Feb 18, 2009, 12:41:44 PM2/18/09
to turbogea...@googlegroups.com
You just need to adjust the way login forms are done so that the login
is done by the repoze.who middleware, not forwarded back to your
protected conroller.

For example you could use a basic auth plugin, that never renders a
page, or you could use another form plugin that does not redirect to
your protected tg controller.

If you don't want to do either of those things, and your want your
login form in your main app, and you want everything protected
(there's no public section of the site) then yea, you'll have to
protect all the resources seprately. But there are other options.

They may not be documented that well yet, but we're still in beta,
give us a chance here ;)
--
Mark Ramm-Christensen
email: mark at compoundthinking dot com
blog: www.compoundthinking.com/blog

Gustavo Narea

unread,
Feb 18, 2009, 12:53:03 PM2/18/09
to turbogea...@googlegroups.com
On Wednesday February 18, 2009 18:41:44 Mark Ramm wrote:
> They may not be documented that well yet, but we're still in beta,
> give us a chance here ;)

I just created a ticket so that we won't forget:
http://trac.turbogears.org/ticket/2218

Reply all
Reply to author
Forward
0 new messages