Authentication and authorization is something every framework must manage. Because they're boring and frameworks exist to manage boring things.
When a user can be considered authenticated? The easiest way to check it it searching is its session for a user key with some interesting value. Is this secure? Enough, because we're talking about server-side session, something a user can't touch, we have to trust it well... until a new security flaw is found in one of the layer of our system.
The "auth" thing is a pattern well known in the web world and you can find many solutions for it, looking for "Dancer::Plugin::Auth" you'll find a lot of stuff. Unfortunately Dancer2 is still a young project so the only tool we have is the Dancer2::Plugin::Auth::Tiny from Golden.
We'll not use it. Ok, the plugin is a very good piece of software but its main purpose is define which routes need login to redirect on login page the not authenticated users. I think that this is made useless by the Dancer2 App scope.
In a situation where routes for logged users and routes for not-logged users are mixed up probably D2::P::A::Tiny is the right solution, but here we have an app where every route needs auth so we'll solve our problem just with a before hook defined in the app itself.
But we have to start from: how to open the doors to a logged user? Login went well, user and password were right, what the server will do? As I said before, it will write in the user session the username.
session 'user' => $the_username;
This is what our hook will check:
Yes, just this. The most common mistake is forgetting that the login route has to skip the check because is the only page allowed for not logged users. In every other case the hook does NOTHING for logged users and redirect to the login page the guests.
I made it more longer than what is needed because I like to talk and write and because sometimes I think that also obvious things need some explanation, somewhere. Conclusion is that using the snippets from the dump code festival you have the start situation for an admin tool you can use for every purpose. Probably, working on it, you'll find a lot o things to improve it or make it smarter. But this is just the first step, the dump step, so it's right as it is.
Who looks at a screwdriver and thinks, "Ooh, this could be a little more sonic"?
Showing posts with label before hook. Show all posts
Showing posts with label before hook. Show all posts
Sunday, 28 July 2013
Wednesday, 26 June 2013
Dancer2::Test. undiscovered countries
If you want to do serious developement, talk with serious people and produce serious packages you have to use testing scripts A LOT. Dancer2 comes with its Dancer2::Test library, a lot of good features... and few gloomy mysteries.
Let me start with a little tip: whenever you start testing import Dancer2::Test and specify which apps you're going to use:
use Dancer2::Test apps => ['app1', 'app2'];
Probably you already knew it, but I had to dig up a little in Dancer2 docs to find that this is a "must have" for Dancer2::Test and everytime I spend time to find something I think that what I find deserves few words on this blog. When you'll search for it like me, I'll be there, on the first page of Google, waiting for you.
But I was talking about gloomy mysteries so now we'll talk about strage behaviours of Dancer2::Test, triggered by nasty (but legit) coding choices.
In my first article I talked about before hooks and forward.
See this example:
hook before => sub {
my $context = shift;
if (request->path_info eq '/ghost_path' )
{
$context->response( forward('/solid_path' ));
$context->response->halt;
}
}
You obviously defined a dispatcher for '/solid_path' but there's no reason you have to define a dispatcher for '/ghost_path'. Still, calling '/ghost_path' will return a page (HTTP code 200! Win!).
Now we'll write a test for this:
response_status_is ['GET' => '/ghost_path'], 200;
Result is:
# got: '404'
# expected: '200'
What? Aaaargh, Wotan worshipper rises is axe!
Dancer2::Test does a bit of introspection in your Dancer2 App. When you ask for a path it simply tries to find it in the defined dispatchers, where there isn't. It can't deduce that all will go well thanks to the before hook so the test fails.
But. Wotan. Worshipper. Wants. To. Test!
Well, servers never lie. Never. If the test could be done with a real server and not just... playing with pms...
Here comes Test::TCP.
use strict;
use warnings;
use Test::More;
use Test::TCP;
use LWP::UserAgent;
Test::TCP::test_tcp(
client => sub {
my $port = shift;
my $ua = LWP::UserAgent->new;
my $res = $ua->get("http://127.0.0.1:$port/ghost_path/");
ok($res->is_success);
},
server => sub {
my $port = shift;
use Dancer2;
use multilang;
set(show_errors => 1,
startup_info => 0,
environment => 'developement',
port => $port
);
Dancer2->runner->server->port($port);
start;
},
);
done_testing;
This is a real server launched by a script and reached by a real client. It's just as the actual site. And the test goes well...
What we learned today? Running a server and click in a browser is the only reliable way to test something. Let a machine doing it by itself without all the boring part (open a browser... digit the url... go on Facebook while loading...) is good.
Obviously, in many many cases Dancer2::Test, plain and simple, is enough and it's the right way to do things, because it's a bit less complex working with it. But keep an eye on exceptions...
Let me start with a little tip: whenever you start testing import Dancer2::Test and specify which apps you're going to use:
use Dancer2::Test apps => ['app1', 'app2'];
Probably you already knew it, but I had to dig up a little in Dancer2 docs to find that this is a "must have" for Dancer2::Test and everytime I spend time to find something I think that what I find deserves few words on this blog. When you'll search for it like me, I'll be there, on the first page of Google, waiting for you.
But I was talking about gloomy mysteries so now we'll talk about strage behaviours of Dancer2::Test, triggered by nasty (but legit) coding choices.
In my first article I talked about before hooks and forward.
See this example:
hook before => sub {
my $context = shift;
if (request->path_info eq '/ghost_path' )
{
$context->response( forward('/solid_path' ));
$context->response->halt;
}
}
You obviously defined a dispatcher for '/solid_path' but there's no reason you have to define a dispatcher for '/ghost_path'. Still, calling '/ghost_path' will return a page (HTTP code 200! Win!).
Now we'll write a test for this:
response_status_is ['GET' => '/ghost_path'], 200;
Result is:
# got: '404'
# expected: '200'
What? Aaaargh, Wotan worshipper rises is axe!
Dancer2::Test does a bit of introspection in your Dancer2 App. When you ask for a path it simply tries to find it in the defined dispatchers, where there isn't. It can't deduce that all will go well thanks to the before hook so the test fails.
But. Wotan. Worshipper. Wants. To. Test!
Well, servers never lie. Never. If the test could be done with a real server and not just... playing with pms...
Here comes Test::TCP.
use strict;
use warnings;
use Test::More;
use Test::TCP;
use LWP::UserAgent;
Test::TCP::test_tcp(
client => sub {
my $port = shift;
my $ua = LWP::UserAgent->new;
my $res = $ua->get("http://127.0.0.1:$port/ghost_path/");
ok($res->is_success);
},
server => sub {
my $port = shift;
use Dancer2;
use multilang;
set(show_errors => 1,
startup_info => 0,
environment => 'developement',
port => $port
);
Dancer2->runner->server->port($port);
start;
},
);
done_testing;
This is a real server launched by a script and reached by a real client. It's just as the actual site. And the test goes well...
What we learned today? Running a server and click in a browser is the only reliable way to test something. Let a machine doing it by itself without all the boring part (open a browser... digit the url... go on Facebook while loading...) is good.
Obviously, in many many cases Dancer2::Test, plain and simple, is enough and it's the right way to do things, because it's a bit less complex working with it. But keep an eye on exceptions...
Labels:
before hook,
Dancer2,
Dancer2::Test,
forward,
Test::TCP,
testing
Monday, 17 June 2013
Dancer2: changing path_info in a before hook
Dancer2 is a great piece of software, easy to use and smart. But it's still evolving and working with it could be a little rude. Easy tasks could take a lot of time (and patience) with no possibility to straight google your solution out of few keywords.
I wanted to preprocess the calls my Dancer2 site receives in a before hook and change them to have a cleaner dispatching. For example I wanted to receive
It's a standard task and this page has an example about exactly what i want. An example that... well... doesn't work.
I admit I went a bit on rage, howling against all the Dancer2 Gods when I understood that something was wrong but when the angry moment finished I started digging in the documentation finding this issue.
"Argh!" said the blood thirsy Wotan worshipper inside me "They knew it. They fixed it. And It's still not working!"
Probability that the fix wasn't in the CPAN package I downloaded was high, as I said, Dancer2 is still evolving, it' version is still starting with a double 0 and we're all beta-testers, but the time of the pull request said otherwise so I went on digging.
We're lucky people because we live in a test-driven world. Let us see how the test for this fix is written...:
I wanted to preprocess the calls my Dancer2 site receives in a before hook and change them to have a cleaner dispatching. For example I wanted to receive
/foo/barkeep /foo for me and make the call processed by
get '/bar' => sub(i'll tell you why another day, but you can easly guess).
It's a standard task and this page has an example about exactly what i want. An example that... well... doesn't work.
I admit I went a bit on rage, howling against all the Dancer2 Gods when I understood that something was wrong but when the angry moment finished I started digging in the documentation finding this issue.
"Argh!" said the blood thirsy Wotan worshipper inside me "They knew it. They fixed it. And It's still not working!"
Probability that the fix wasn't in the CPAN package I downloaded was high, as I said, Dancer2 is still evolving, it' version is still starting with a double 0 and we're all beta-testers, but the time of the pull request said otherwise so I went on digging.
We're lucky people because we live in a test-driven world. Let us see how the test for this fix is written...:
hook before => sub {my $context = shift;};
return if $context->request->path eq '/default';
$context->response( forward('/default') );
$context->response->halt;
...and that's the way to do it! I hope someone will find this post before they have to start the voyage I did to find the solution. This will keep the Wotan worshipper calm a bit more...
Subscribe to:
Posts (Atom)