Login | Register For Free | Help
Search for: (Advanced)

Mailing List Archive: Catalyst: Users

Calling Controller Methods from inside begin or auto.

 

 

Catalyst users RSS feed   Index | Next | Previous | View Threaded


dwueppel at gmail

Sep 29, 2009, 5:34 AM

Post #1 of 15 (2108 views)
Permalink
Calling Controller Methods from inside begin or auto.

Hello,

So I have a site I'm building that requires authentication, but only to
selected pages. By default I want to require that an authenticated user
is found, but in certain controllers I want to disable this. I was
originally thinking about doing this by creating a auth_required()
method call in a base controller class and overriding this as needed.
Then I would write my authentication routine inside the Root controller.
Something like this:

sub begin : Private {
my ($self, $c) = @_;

# I created the get_user method.
my $u = $c->get_user()

if ($self->auth_required()) {
# Do auth stuff.
}
}

However the issue here is that when calling $self->auth_required() this
will always call the Root.pm's auth_required method instead of the
specific controllers.

Is there a way from inside a begin method to get the controller that is
actually going to be used to display the page? If not is there another
way to do the above without having to write the auth handling at the top
of every method used to display pages?

--
Derek Wueppelmann


_______________________________________________
List: Catalyst [at] lists
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst [at] lists/
Dev site: http://dev.catalyst.perl.org/


pagaltzis at gmx

Sep 29, 2009, 5:39 AM

Post #2 of 15 (2036 views)
Permalink
Re: Calling Controller Methods from inside begin or auto. [In reply to]

* monkey <dwueppel [at] gmail> [2009-09-29 14:35]:
> Is there another way to do the above without having to write
> the auth handling at the top of every method used to display
> pages?

Chained dispatch. Do an auth check early in the chain, then the
actions down the chain don’t need to do it.

Regards,
--
Aristotle Pagaltzis // <http://plasmasturm.org/>

_______________________________________________
List: Catalyst [at] lists
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst [at] lists/
Dev site: http://dev.catalyst.perl.org/


dwueppel at gmail

Sep 29, 2009, 6:16 AM

Post #3 of 15 (2029 views)
Permalink
Re: Re: Calling Controller Methods from inside begin or auto. [In reply to]

On Tue, 2009-09-29 at 14:39 +0200, Aristotle Pagaltzis wrote:
> * monkey <dwueppel [at] gmail> [2009-09-29 14:35]:
> > Is there another way to do the above without having to write
> > the auth handling at the top of every method used to display
> > pages?
>
> Chained dispatch. Do an auth check early in the chain, then the
> actions down the chain don’t need to do it.

Thanks, I was hopeing for something that wouldn't require adjusting the
existing methods I currently have. It may not be possible, but that's
what I was hoping for.

--
Derek Wueppelmann


_______________________________________________
List: Catalyst [at] lists
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst [at] lists/
Dev site: http://dev.catalyst.perl.org/


catalyst4 at augensalat

Sep 29, 2009, 6:38 AM

Post #4 of 15 (2034 views)
Permalink
Re: Re: Calling Controller Methods from inside begin or auto. [In reply to]

Aristotle Pagaltzis schrieb:
> * monkey <dwueppel [at] gmail> [2009-09-29 14:35]:
>> Is there another way to do the above without having to write
>> the auth handling at the top of every method used to display
>> pages?
>
> Chained dispatch. Do an auth check early in the chain, then the
> actions down the chain don’t need to do it.

Here I have a question:

What is the recommended way to leave a chain - eg. to show a login form?

detach?

Exceptions (with "die") don't work, since all Catalyst actions are
wrapped in evals.

_______________________________________________
List: Catalyst [at] lists
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst [at] lists/
Dev site: http://dev.catalyst.perl.org/


pagaltzis at gmx

Sep 30, 2009, 12:03 AM

Post #5 of 15 (2010 views)
Permalink
Re: Calling Controller Methods from inside begin or auto. [In reply to]

* Bernhard Graf <catalyst4 [at] augensalat> [2009-09-29 15:45]:
> What is the recommended way to leave a chain - eg. to show a login form?
>
> detach?

Yup.

Regards,
--
Aristotle Pagaltzis // <http://plasmasturm.org/>

_______________________________________________
List: Catalyst [at] lists
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst [at] lists/
Dev site: http://dev.catalyst.perl.org/


dwueppel at gmail

Sep 30, 2009, 5:23 AM

Post #6 of 15 (2005 views)
Permalink
Re: Re: Calling Controller Methods from inside begin or auto. [In reply to]

On Tue, 2009-09-29 at 14:39 +0200, Aristotle Pagaltzis wrote:
> * monkey <dwueppel [at] gmail> [2009-09-29 14:35]:
> > Is there another way to do the above without having to write
> > the auth handling at the top of every method used to display
> > pages?
>
> Chained dispatch. Do an auth check early in the chain, then the
> actions down the chain don’t need to do it.

So I found a different way to do this. It's pretty close to my original
method I had mentioned, but instead of calling $self->auth_required I
changed it to:

$c->action->class->auth_required()

Which has the desired effect. Now all I need to do is if a controller
does not require authentication in order to be viewed I override the
auth_required method in that controller to return 0 instead of the
default 1.

--
o) Derek Wueppelmann (o
(D . dwueppel [at] gmail D).
((` http://www.monkeynet.ca ( ) `



_______________________________________________
List: Catalyst [at] lists
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst [at] lists/
Dev site: http://dev.catalyst.perl.org/


moseley at hank

Sep 30, 2009, 6:53 AM

Post #7 of 15 (1999 views)
Permalink
Re: Re: Calling Controller Methods from inside begin or auto. [In reply to]

On Wed, Sep 30, 2009 at 5:23 AM, Derek Wueppelmann <dwueppel [at] gmail>wrote:

> On Tue, 2009-09-29 at 14:39 +0200, Aristotle Pagaltzis wrote:
> > * monkey <dwueppel [at] gmail> [2009-09-29 14:35]:
> > > Is there another way to do the above without having to write
> > > the auth handling at the top of every method used to display
> > > pages?
> >
> > Chained dispatch. Do an auth check early in the chain, then the
> > actions down the chain don’t need to do it.
>
> So I found a different way to do this. It's pretty close to my original
> method I had mentioned, but instead of calling $self->auth_required I
> changed it to:
>
> $c->action->class->auth_required()
>
> Which has the desired effect. Now all I need to do is if a controller
> does not require authentication in order to be viewed I override the
> auth_required method in that controller to return 0 instead of the
> default 1.
>

Does that approach provide you with enough fine-grained access control?
I suppose you can check the action name in auth_required().

There are a number of existing modules to consider, for example:

Catalyst::Action::Role::ACL
Catalyst::Plugin::Authorization::ACL

I've also used an approach where I check for roles in each controller's auto
method, and I've also used method attributes to indicate the access level
required for each action (which has the benefit where I can require *every*
dispatched action to have an access level specified or be blocked).

I also do not detach to a login page, rather I always redirect. Not sure I
remember the details of that choice, but one reason might have been I didn't
want a URL for one resource to return a 200 yet not return the response for
that URL and instead return a login form.



--
Bill Moseley
moseley [at] hank


dwueppel at gmail

Sep 30, 2009, 7:30 AM

Post #8 of 15 (2001 views)
Permalink
Re: Re: Calling Controller Methods from inside begin or auto. [In reply to]

On Wed, 2009-09-30 at 06:53 -0700, Bill Moseley wrote:

> Does that approach provide you with enough fine-grained access
> control?
> I suppose you can check the action name in auth_required().

It actually does. Basically either the entire class requires auth or
not, and if I need to occasionally require auth to specific methods
that's easy enough to take care of on a case by case basis.

> There are a number of existing modules to consider, for example:
>
> Catalyst::Action::Role::ACL
> Catalyst::Plugin::Authorization::ACL

In order to use these I would have to rewrite significant portions of
the code. At this point it's not worth while doing.

> I've also used an approach where I check for roles in each
> controller's auto method, and I've also used method attributes to
> indicate the access level required for each action (which has the
> benefit where I can require *every* dispatched action to have an
> access level specified or be blocked).
>
> I also do not detach to a login page, rather I always redirect. Not
> sure I remember the details of that choice, but one reason might have
> been I didn't want a URL for one resource to return a 200 yet not
> return the response for that URL and instead return a login form.

I'm actually doing forwards to my login page right now. So that when a
user logs in they can still see the page they were originally trying to
view. I capture the URL they were attempting to view in the login
process.

--
o) Derek Wueppelmann (o
(D . dwueppel [at] gmail D).
((` http://www.monkeynet.ca ( ) `



_______________________________________________
List: Catalyst [at] lists
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst [at] lists/
Dev site: http://dev.catalyst.perl.org/


moseley at hank

Sep 30, 2009, 8:09 AM

Post #9 of 15 (2000 views)
Permalink
Re: Re: Calling Controller Methods from inside begin or auto. [In reply to]

On Wed, Sep 30, 2009 at 7:30 AM, Derek Wueppelmann <dwueppel [at] gmail>wrote:

> I'm actually doing forwards to my login page right now. So that when a
> user logs in they can still see the page they were originally trying to
> view. I capture the URL they were attempting to view in the login
> process.
>

And then redirect back to that original page after login?

I pass that data via the cache or session.

$c->cache->set( $key, {
orig_url => $url,
message => 'Auhorization is required',
});

$c->res->redirect( $c->uri_for( '/login', { info => $key } ) );


Catalyst docs show an example using auto. BTW - shouldn't the redirect be
an absolute-URI?

sub auto : Private {
my ( $self, $c ) = @_;
if ( !$c->user_exists ) { # Catalyst::Plugin::Authentication
$c->res->redirect( '/login' ); # require login
return 0; # abort request and go immediately to end()
}
return 1; # success; carry on to next action
}

RFC3986 has:
absolute-URI = scheme ":" hier-part [ "?" query ]

And 2616:
Location = "Location" ":" absoluteURI


--
Bill Moseley
moseley [at] hank


jshirley at gmail

Sep 30, 2009, 8:53 AM

Post #10 of 15 (1993 views)
Permalink
Re: Re: Calling Controller Methods from inside begin or auto. [In reply to]

On Wed, Sep 30, 2009 at 8:09 AM, Bill Moseley <moseley [at] hank> wrote:

>
>
> On Wed, Sep 30, 2009 at 7:30 AM, Derek Wueppelmann <dwueppel [at] gmail>wrote:
>
>> I'm actually doing forwards to my login page right now. So that when a
>> user logs in they can still see the page they were originally trying to
>> view. I capture the URL they were attempting to view in the login
>> process.
>>
>
> And then redirect back to that original page after login?
>
> I pass that data via the cache or session.
>
> $c->cache->set( $key, {
> orig_url => $url,
> message => 'Auhorization is required',
> });
>
> $c->res->redirect( $c->uri_for( '/login', { info => $key } ) );
>
>
> Catalyst docs show an example using auto. BTW - shouldn't the redirect be
> an absolute-URI?
>
> sub auto : Private {
> my ( $self, $c ) = @_;
> if ( !$c->user_exists ) { # Catalyst::Plugin::Authentication
> $c->res->redirect( '/login' ); # require login
> return 0; # abort request and go immediately to end()
> }
> return 1; # success; carry on to next action
> }
>


My typical recipe is via parameter, rather than session. This is more
flexible, and allows me to pass URLs to people with more definitive results.

You do, however, have to whitelist the URLs prior to redirection. The very
basic recipe is just something to compare the URI base:

my $redir = URI->new( $redir_url );
$redir->base eq $c->req->uri->base;

-J


alexander.hartmaier at t-systems

Sep 30, 2009, 9:19 AM

Post #11 of 15 (1997 views)
Permalink
Re: Re: Calling Controller Methods from inside begin or auto. [In reply to]

Am Mittwoch, den 30.09.2009, 16:30 +0200 schrieb Derek Wueppelmann:
> On Wed, 2009-09-30 at 06:53 -0700, Bill Moseley wrote:
>
> > Does that approach provide you with enough fine-grained access
> > control?
> > I suppose you can check the action name in auth_required().
>
> It actually does. Basically either the entire class requires auth or
> not, and if I need to occasionally require auth to specific methods
> that's easy enough to take care of on a case by case basis.
>
> > There are a number of existing modules to consider, for example:
> >
> > Catalyst::Action::Role::ACL
> > Catalyst::Plugin::Authorization::ACL
>
> In order to use these I would have to rewrite significant portions of
> the code. At this point it's not worth while doing.
>
> > I've also used an approach where I check for roles in each
> > controller's auto method, and I've also used method attributes to
> > indicate the access level required for each action (which has the
> > benefit where I can require *every* dispatched action to have an
> > access level specified or be blocked).
> >
> > I also do not detach to a login page, rather I always redirect. Not
> > sure I remember the details of that choice, but one reason might have
> > been I didn't want a URL for one resource to return a 200 yet not
> > return the response for that URL and instead return a login form.
>
> I'm actually doing forwards to my login page right now. So that when a
> user logs in they can still see the page they were originally trying to
> view. I capture the URL they were attempting to view in the login
> process.

You should redirect to your login page rather than displaying it under a
different url.
I store the previous url in the session and redirect to it after a
successful login, works like a charm.

--
best regards, Alex


*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*
T-Systems Austria GesmbH Rennweg 97-99, 1030 Wien
Handelsgericht Wien, FN 79340b
*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*
Notice: This e-mail contains information that is confidential and may be privileged.
If you are not the intended recipient, please notify the sender and then
delete this e-mail immediately.
*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*

_______________________________________________
List: Catalyst [at] lists
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst [at] lists/
Dev site: http://dev.catalyst.perl.org/


pagaltzis at gmx

Sep 30, 2009, 10:13 AM

Post #12 of 15 (1990 views)
Permalink
Re: Calling Controller Methods from inside begin or auto. [In reply to]

* Bill Moseley <moseley [at] hank> [2009-09-30 16:00]:
> I also do not detach to a login page, rather I always redirect.
> Not sure I remember the details of that choice, but one reason
> might have been I didn't want a URL for one resource to return
> a 200 yet not return the response for that URL and instead
> return a login form.

I detach. My login action sets status 403 and pragma no-cache
(etc) when it’s not requested directly. I’d love to be able to
just send 401 instead and let the user agent take care of
everything (which would transparently and securely deal with
POSTs sent with expired auth credentials) – unfortunately the
HTTP Auth UI in browsers is universally shoddy. If I felt the
need, I could also check for browser vs automated agent and send
either form + 403 to browsers and just a 401 to other clients.

Regards,
--
Aristotle Pagaltzis // <http://plasmasturm.org/>

_______________________________________________
List: Catalyst [at] lists
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst [at] lists/
Dev site: http://dev.catalyst.perl.org/


moseley at hank

Sep 30, 2009, 11:06 AM

Post #13 of 15 (1991 views)
Permalink
Re: Re: Calling Controller Methods from inside begin or auto. [In reply to]

On Wed, Sep 30, 2009 at 10:13 AM, Aristotle Pagaltzis <pagaltzis [at] gmx>wrote:

>
>
>
> I detach. My login action sets status 403 and pragma no-cache
> (etc) when it’s not requested directly. I’d love to be able to
> just send 401 instead and let the user agent take care of
> everything (which would transparently and securely deal with
> POSTs sent with expired auth credentials) – unfortunately the
> HTTP Auth UI in browsers is universally shoddy. If I felt the
> need, I could also check for browser vs automated agent and send
> either form + 403 to browsers and just a 401 to other clients.
>

Looking at my code I also return 401 for API requests. I have a note in the
code that there was problems returning 403 on some browsers although maybe
that was just old IE when the content body was too short and it would
display its own message.

And looking at old svn logs I found another reason I redirect -- which might
be how my code grew over time.

At one point I added a "remember me" feature. So I have code in the
controller's auto() which is basically "return unless $c->test_role( @roles
);" That test_role() method first checks if logged in, and if not logged in
then does the redirect. But, before doing the redirect it attempts an
automatic login via the "remember me" feature.

The problem there was then the action's auto() and begin() methods were not
run with the user logged in, which was important for other reasons.

I would have expected to do the "remember me" login earlier (e.g. in root's
auto() ) and avoided that issue, but mabye I had a reason otherwise.
Anyway, the redirect does allow the login action to be called as a normal
request (with login's auto and begin methods run).

Granted, now there's $c->go to do a full dispatch, but redirect seems
cleaner at the expense of a redirect.




--
Bill Moseley
moseley [at] hank


pagaltzis at gmx

Sep 30, 2009, 8:11 PM

Post #14 of 15 (1969 views)
Permalink
Re: Calling Controller Methods from inside begin or auto. [In reply to]

* Bill Moseley <moseley [at] hank> [2009-09-30 20:10]:
> The problem there was then the action's auto() and begin()
> methods were not run with the user logged in, which was
> important for other reasons.

Ah. The only `begin`, `end` and `default` actions I have are in
my root controller, and the only responsibilities they have are
setting up and tearing down auth and rendering the stash.

Everything else is done via Chained: all other controllers have

sub base : Chained PathPart('whatever') ... { ... }

even if it is empty (which is the case as often as not), and all
actions in each controller chain off its `base` action. That
`base` may itself chain off another action in another controller
instead of `/`.

With this structure, having `auto` etc run before or after auth
or whatever is never an issue. I also do all my auth checks
piecemeal along the chain, thus, without complicated chains of
conditionals in my code, dispatch determines for me how
specifically to perform a complex auth check. And if I need to
extract logic from several actions without affecting the URI
structure I can add a `PathPart('') CaptureArgs(0)` action into
the chain and voilà.

I really enjoy working with this structure. It’s declarative and
very DRY, no hairy logic, clear, simple, explicit. I ♥ Chained.

Regards,
--
Aristotle Pagaltzis // <http://plasmasturm.org/>

_______________________________________________
List: Catalyst [at] lists
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst [at] lists/
Dev site: http://dev.catalyst.perl.org/


moseley at hank

Oct 1, 2009, 7:19 AM

Post #15 of 15 (1960 views)
Permalink
Re: Re: Calling Controller Methods from inside begin or auto. [In reply to]

I move this off list because I'm not sure you want to show

On Wed, Sep 30, 2009 at 8:11 PM, Aristotle Pagaltzis <pagaltzis [at] gmx>wrote:

> With this structure, having `auto` etc run before or after auth
> or whatever is never an issue. I also do all my auth checks
> piecemeal along the chain, thus, without complicated chains of
> conditionals in my code, dispatch determines for me how
> specifically to perform a complex auth check. And if I need to
> extract logic from several actions without affecting the URI
> structure I can add a `PathPart('') CaptureArgs(0)` action into
> the chain and voilà.
>
> I really enjoy working with this structure. It’s declarative and
> very DRY, no hairy logic, clear, simple, explicit. I ♥ Chained.
>

Shifting topics here. I'm not quiet so in love with the chained because it
is sometimes confusing for new programmers and is a bit more challenging to
build urls in the templates. We do used chained actions as I believe you
describe, but not everywhere. Often the more complex auth checks happen at
the end of the action chain so it doesn't alway buy us that much. But, that
may be just the way our URLs work.

That said, I value your comments. Could you show an example of what you
describe -- maybe one that has more complex auth requirements?

What I have liked is a combination of using auto() in the root and
attributes on the actions. Every dispatch method runs auto, so it's a way
to ensure that every action is tested. It's not a big conditional check as
the action attributes link to methods that handle the tests. Someone
can't throw in a : Local method any bypass checks. But, it's not perfect
by any means (there's always ways to mess up the auth checks). So, If you
♥ chained that much I'll belive you have something great and I'd love to
see more real-world examples.

Thanks,





--
Bill Moseley
moseley [at] hank

Catalyst users RSS feed   Index | Next | Previous | View Threaded
 
 


Interested in having your list archived? Contact Gossamer Threads
 
  Web Applications & Managed Hosting Powered by Gossamer Threads Inc.