[Dancer-users] Dancer - new suggestion - "route namespace"

Perlover perlover at perlover.com
Sun Nov 20 15:40:43 CET 2011


Good day!

I found a buggy behavior of Dancer with multiply application
configuration and routes.

Please to see example in current Dancer::Deployment -> Running
multiple apps with Plack::Builder

[...]
           builder {
               mount "/app1" => builder {$app1};
               mount "/app2" => builder {$app2};
           };

Here 'mount' through Plack::App::URLMap->map will call our $app1 &
$app2 BUT /app1 & /app2 will be removed from $request->path_info!

Our applications will get a new path_info paths. Imagine a following example:
           builder {
               mount "/app1" => builder {$app1};
               mount "/app2" => builder { 	enable "Auth::Basic",
authenticator => \&authen_cb; $app2};
           };

Here $app2 is protected by Plack::Middleware::Auth::Basic and it has
some /admin/* routes (for /app2/admin/... urls)
But $app has only one root routes for example as '/' (and may be other routes)

Now please to see the source of module Dancer::App, lines 104-106:

....
sub find_route_through_apps {
    my ($class, $request) = @_;
    for my $app (Dancer::App->current, Dancer::App->applications) {
        my $route = $app->find_route($request);
....


Here we see that searching is going until the target route will be
found in all applications!
Imagine that we requested the url /app1/admin/... What happens?
The $app1 will get path_info as /admin/... after Plack::App::URLMap
and Dancer will start a searching in find_route_through_apps and will
find a route of $app2!
And it will call it code without middleware Auth! And protected and
security data will be given for remote user without protection!

What is result? The /app1/ URLs will sometimes run app2's routes, the
/app2/ URLs will sometimes run a routes of app1 and so on.
And in above example we will get a security hole additional...

---------------------------------------

What i suggest ?

I think Dancer should have:

1) For get & post now we can pass an options hashref before code like
this get '/' => {option1 =>..., ...}, sub {} (i see this in code of
Dancer but i didn't find it in docs) . I think there should be option
'route_namespace' for example. If it defined it consists a namespace
of this route. If not defined it has a namespace defined in
route_namespace command (point 2 here)

2) Should be command like 'prefix' but named as 'route_namespace' fro
setting up namespace of routes of this application. The prefix option
for real pieces in $request->path_info but this route_namespace for
virtual because we cannot get a namespace from path_info (only from
$request->script_name but i want to make this behavior flexible for
'route_namespace')

3) I think the object Dancer::Request should have a method
(getter/setter) route_namespace. Through this method we can setup a
namespace of current request for searching of routes. If we set up for
example $request->route_namespace('app1') before
Dancer->dance($request) (example in Dancer::Deployment -> Running
multiple apps with Plack::Builder) for example then the
find_route_through_apps should search through all applications all
routes BUT with same namespace only. If route_namespace is not defined
for current $request a behavior is as now (backward compatible)

So after this patches our example in Dancer::Deployment will look like:

           use Dancer ':syntax';
           use Plack::Builder;

           setting apphandler => 'PSGI';

           my $app1 = sub {
               my $env = shift;
               local $ENV{DANCER_APPDIR} = '/Users/franck/tmp/app1';

               load_app "app1"; # In the app1 module we have command:
route_namespace "app1" before routes
                                        # May be here we should be
able to setup too like: load_app "app1", route_namespace => 'app1';

               Dancer::App->set_running_app('app1');
               setting appdir => '/Users/franck/tmp/app1';
               Dancer::Config->load;

               my $request = Dancer::Request->new( env => $env
)->route_namespace('app1'); # route_namespace as setter returns an
instance of Dancer::Request

               Dancer->dance($request); # Now the Dancer knows in the
find_route_through_apps what we want (only routes from "app1"
namespace)
           };

           my $app2 = sub {
               my $env = shift;
               local $ENV{DANCER_APPDIR} = '/Users/franck/tmp/app2';

               load_app "app2"; # In the app2 module we have command:
route_namespace "app2" before routes

               Dancer::App->set_running_app('app2');
               setting appdir => '/Users/franck/tmp/app2';
               Dancer::Config->load;

               my $request = Dancer::Request->new( env => $env
)->route_namespace('app2'); # route_namespace as setter returns an
instance of Dancer::Request;

               Dancer->dance($request); # Now the Dancer knows in the
find_route_through_apps what we want (only routes from "app2"
namespace)
           };

           builder {
               mount "/app1" => builder {$app1};
               mount "/app2" => builder { 	enable "Auth::Basic",
authenticator => \&authen_cb; $app2};
           };

Your opinions?

I can make all this patches in Dancer for devel branch at github
But before i want to listen from you some suggestions and opinions

Thanks!
Perlover


More information about the Dancer-users mailing list