FAQ
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

Search Discussions

  • Aristotle Pagaltzis at Sep 29, 2009 at 12:39 pm

    * monkey [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/>
  • Monkey at Sep 29, 2009 at 1:16 pm

    On Tue, 2009-09-29 at 14:39 +0200, Aristotle Pagaltzis wrote:
    * monkey [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
  • Bernhard Graf at Sep 29, 2009 at 1:38 pm

    Aristotle Pagaltzis schrieb:
    * monkey [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.
  • Aristotle Pagaltzis at Sep 30, 2009 at 7:03 am

    * Bernhard Graf [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/>
  • Derek Wueppelmann at Sep 30, 2009 at 12:23 pm

    On Tue, 2009-09-29 at 14:39 +0200, Aristotle Pagaltzis wrote:
    * monkey [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@gmail.com D).
    ((` http://www.monkeynet.ca ( ) `
  • Bill Moseley at Sep 30, 2009 at 1:53 pm

    On Wed, Sep 30, 2009 at 5:23 AM, Derek Wueppelmann wrote:
    On Tue, 2009-09-29 at 14:39 +0200, Aristotle Pagaltzis wrote:
    * monkey [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@hank.org
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: http://lists.scsys.co.uk/pipermail/catalyst/attachments/20090930/f5e69440/attachment.htm
  • Derek Wueppelmann at Sep 30, 2009 at 2:30 pm

    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@gmail.com D).
    ((` http://www.monkeynet.ca ( ) `
  • Bill Moseley at Sep 30, 2009 at 3:09 pm

    On Wed, Sep 30, 2009 at 7:30 AM, Derek Wueppelmann 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@hank.org
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: http://lists.scsys.co.uk/pipermail/catalyst/attachments/20090930/ae36f72b/attachment.htm
  • Jay Shirley at Sep 30, 2009 at 3:53 pm
    On Wed, Sep 30, 2009 at 8:09 AM, Bill Moseley wrote:
    On Wed, Sep 30, 2009 at 7:30 AM, Derek Wueppelmann 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
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: http://lists.scsys.co.uk/pipermail/catalyst/attachments/20090930/c68e40d3/attachment.htm
  • Alexander Hartmaier at Sep 30, 2009 at 4:19 pm

    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.
    *"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*"*
  • Aristotle Pagaltzis at Sep 30, 2009 at 5:13 pm

    * Bill Moseley [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/>
  • Bill Moseley at Sep 30, 2009 at 6:06 pm

    On Wed, Sep 30, 2009 at 10:13 AM, Aristotle Pagaltzis 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@hank.org
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: http://lists.scsys.co.uk/pipermail/catalyst/attachments/20090930/17a98740/attachment.htm
  • Aristotle Pagaltzis at Oct 1, 2009 at 3:11 am

    * Bill Moseley [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/>
  • Bill Moseley at Oct 1, 2009 at 2:19 pm
    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 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@hank.org
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: http://lists.scsys.co.uk/pipermail/catalyst/attachments/20091001/12daa0b8/attachment.htm

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupcatalyst @
categoriescatalyst, perl
postedSep 29, '09 at 12:34p
activeOct 1, '09 at 2:19p
posts15
users6
websitecatalystframework.org
irc#catalyst

People

Translate

site design / logo © 2022 Grokbase