[Catalyst] Three DBIC Authentication/Authorization questions

Yuval Kogman nothingmuch at woobling.org
Sat Jan 7 11:13:52 CET 2006


On Thu, Jan 05, 2006 at 15:40:58 -0800, Dennis Daupert wrote:
> Please forgive my attaching a textfile. I have SBC Yahoo web mail by way of Firefox, and for whatever reason, line feeds get stripped when I copy/paste.
>  
>  Anyhow, 3 ACL/auth issues have wrestled me to the ground. Help on any or all will be vastly appreciated.
>  
>  /dennis
>  

Content-Description: 4192712010-auth-questions.txt
> Three DBIC Authentication/Authorization questions
> 
> I have auth working, to a degree, yay!  I have three general areas: a public area anyone can browse; a Members area requiring registration; and an admin area. I can bring up my app, click login to go to my admin area, and I get authed. But still having problems. To save time, I'll put them all in here.
> 
> (Code details at end)
> 
> PROBLEM 1: (ACCESS DENIED EXCEPTION) When I try logging into /admin area as some non-authorized user, I get debug screen: "Caught exception Access to admin/begin denied by rule CODE(0xe182d0)." I want to simply display a nice "not authorized" message. I can't see what I'm doing wrong.

You'll need to handle the access denied event in the restricted
controller... What you did:

> __PACKAGE__->deny_access_unless( "/admin", [qw/admin/] );
...
> package MyApp2::Controller::Auth;
...
> sub access_denied : Private {
>   my ( $self, $c, $action ) = @_;
>   my @errors = ();
>   $c->stash->{'template'} = 'login.tt';
>   push @errors, 'Sorry, access is not authorized';
> }

This should be in the controller that's getting denied, or the top
level of your app. If it's in your auth controller it will only
handle access denial to the auth controller - but you have no ACL
denying access to this area.

	package MyApp2::Controller::Admin;

	sub access_denied : Private {
		...
	}

or for the whole app:

	package MyApp2;

	sub access_denied : Private {

	}


> PROBLEM 2: (LOGOUT REMAINS) I have a TT wrapper that displays different navigation menus depending on whether the user is an admin or a public user:
> 
>     [% IF c.user.id == "admin" %]
>      [% INCLUDE page/adm_menu.tt %]
>     [% ELSE %]
>      [% INCLUDE page/menu.tt %]
>     [% END %]

This is probably better done with

	[% IF c.check_roles("admin") %]

if you're using roles authz, btw, as possibly several people can be
admins.

> But after invoking logout and being returned to the app home page, I still see the admin menu. I can see in the debug printout messages the session is restored. I thought logout was supposed to kill sessions, also. Is there something else I need to do? 

I'd like to see the full if possible... This sounds like a bug.
And no, sessions are not deleted (use $c->delete_session("reason")
for that) but the user should still be logged out of the session.

One possible reason is race conditions in the session handling
stuff... Are you using AJAX to make the logout? could it be done at
the same time as any other action?

Do you participate in IRC chat? If so i'd like you to ping me there
(i'm nothingmuch) so that we can resolve this efficiently.


> sub logout : Global {
>   my ($self, $c) = @_;
> 
>   if ( $c->user_exists ) {
>     $c->logout;
>     #$c->res->redirect('/');
>     } 
>     else {
>       $c->forward("auth/login");
>       }
> };

This looks correct.



> PROBLEM 3: I need to be able to display the role of the login user as well as the user id. For example
> 
> <b>Admin: [% c.user.id %] Role: [% c.user.role %]</b>
> 
> Of course, [% c.user.role %] doesn't work.

It's "roles", since RBAC is oriented towards many-to-many, and not a
single group per user. This should return a list because each user
can perform any number of roles.


> #-------------------------------------------------
> sub login : Local {  
>   my ( $self, $c ) = @_;
>   my @errors;
> 
>   $c->stash->{'template'} = 'login.tt';

If you use the template suffix option (see the View::TT docs this
template can be chosen automatically for you. This is in to so you
Don't Repeat Yourself (typing login twice *is* repitition, after all
=)

>   if ( $c->req->param('op') eq 'SUBMIT' ) {
>     $c->form(required => [qw/username password/]);
>     # Are field entries OK?
>     if ($c->form->success) {
>     # Are credentials present?
>     if ($c->login( $c->req->param('username'), 
>                    $c->req->param('password') )) {

All this could be just

	if ( $c->login )

because it will look inside $c->req for conventionally named
parameters, and will not cry if params are missing.


>         @errors = map("The field '$_' is required!", $c->form->missing);
>         }
>     } # end if op = SUBMIT
>     push @{$c->stash->{'errors'}}, @errors;


You might want to say

	my $errors = $c->stash->{errors} ||= [];

at the end of your action so that you can just

	push @$errors, "blah blah blah";

which is arguably cleaner =)

-- 
 ()  Yuval Kogman <nothingmuch at woobling.org> 0xEBD27418  perl hacker &
 /\  kung foo master: /me does a karate-chop-flip: neeyah!!!!!!!!!!!!!!




More information about the Catalyst mailing list