From 241c94175f8fc1053c372ad8b11081a167b52625 Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Fri, 16 Apr 2010 18:08:16 -0500 Subject: [PATCH] move more stuff into psgi file, rewrite WGAccess middleware --- lib/WebGUI/Middleware/Session.pm | 31 ++--------- lib/WebGUI/Middleware/WGAccess.pm | 91 ++++++++++++++++--------------- var/site.psgi | 18 ++++-- 3 files changed, 63 insertions(+), 77 deletions(-) diff --git a/lib/WebGUI/Middleware/Session.pm b/lib/WebGUI/Middleware/Session.pm index 175790c6f..3d4f4e303 100644 --- a/lib/WebGUI/Middleware/Session.pm +++ b/lib/WebGUI/Middleware/Session.pm @@ -5,15 +5,9 @@ use WebGUI::Config; use WebGUI::Session; use WebGUI::Utility (); use Try::Tiny; -use Plack::Middleware::StackTrace; -use Plack::Middleware::Debug; -use WebGUI::Middleware::Debug::Performance; use WebGUI::Middleware::HTTPExceptions; -use Plack::Middleware::ErrorDocument; use Plack::Middleware::SimpleLogger; -use Scalar::Util qw(weaken); - -use Plack::Util::Accessor qw( config error_docs ); +use Plack::Util::Accessor qw( config ); =head1 NAME @@ -30,13 +24,14 @@ the session out of the PSGI env hash: and not worry about closing it. +It also sets C as appropriate. + =cut sub call { my ( $self, $env ) = @_; my $app = $self->app; - weaken $self->{config}; my $config = $self->config or die 'Mandatory config parameter missing'; @@ -57,27 +52,11 @@ sub call { # We don't have access to a db connection to find out if the user is allowed to see # a verbose error message or not, so resort to a generic Internal Server Error - # (using the error_docs mapping) - if ($self->error_docs) { - return Plack::Middleware::ErrorDocument->wrap( sub { [ 500, [], [] ] }, %{ $self->error_docs } )->($env); - } else { - return [ 500, [ 'Content-Type' => 'text/plain' ], [ 'Internal Server Error' ] ]; - } + return [ 500, [ 'Content-Type' => 'text/plain' ], [ 'Internal Server Error' ] ]; } - # Perhaps I'm being paranoid.. - weaken $session->{_config}; - my $debug = $env->{'webgui.debug'} = $self->canShowDebug($env); - # Turn exceptions into HTTP errors - $app = WebGUI::Middleware::HTTPExceptions->wrap($app); - - # HTTP error document mapping - if ( !$debug && $self->error_docs ) { - $app = Plack::Middleware::ErrorDocument->wrap( $app, %{ $self->error_docs } ); - } - # Run the app my $res = $app->($env); @@ -91,7 +70,7 @@ sub call { $env->{'webgui.session'}->close(); #memory_cycle_ok( $env->{'webgui.session'} ); delete $env->{'webgui.session'}; - + #use Test::Memory::Cycle; #memory_cycle_ok( $env ); } diff --git a/lib/WebGUI/Middleware/WGAccess.pm b/lib/WebGUI/Middleware/WGAccess.pm index ba5d3d179..74e23c169 100644 --- a/lib/WebGUI/Middleware/WGAccess.pm +++ b/lib/WebGUI/Middleware/WGAccess.pm @@ -1,8 +1,9 @@ package WebGUI::Middleware::WGAccess; use strict; -use Plack::App::File; use parent qw(Plack::Middleware); -use Path::Class 'dir'; +use Path::Class::File; +use Scalar::Util; +use JSON (); =head1 NAME @@ -18,52 +19,52 @@ to be serving static files with something a lot faster. =cut -use Plack::Util::Accessor qw( config ); - sub call { my $self = shift; my $env = shift; - my $app = $self->app; - my $config = $self->config or die 'Mandatory config parameter missing'; - my $uploadsPath = $config->get('uploadsPath'); - my $uploadsURL = $config->get('uploadsURL'); - - my $path = $env->{PATH_INFO}; - my $matched = $path =~ s{^\Q$uploadsURL\E/}{}; - return $app->($env) unless $matched; - - my $root = dir($uploadsPath); - my $file = $root->file(File::Spec::Unix->splitpath($path)); - my $wgaccess = File::Spec::Unix->catfile($file->dir, '.wgaccess'); - - if (-e $wgaccess) { - my $fileContents; - open(my $FILE, "<", $wgaccess); - while (my $line = <$FILE>) { - $fileContents .= $line; - } - close($FILE); - my @privs = split("\n", $fileContents); - - unless ($privs[1] eq "7" || $privs[1] eq "1") { - my $session = $env->{'webgui.session'}; - my $hasPrivs = ($session->var->get("userId") eq $privs[0] || $session->user->isInGroup($privs[1]) || $session->user->isInGroup($privs[2])); - warn "has: $hasPrivs"; - warn $session->var->get("userId"); - warn $session->user->isInGroup($privs[1]); - warn $session->user->isInGroup($privs[2]); - if ($hasPrivs) { - $self->{file} ||= Plack::App::File->new; - return $self->{file}->serve_path($env, $file); # serve statically - } - else { - return [403, ['Content-Type' => 'text/plain'], ['Forbidden']]; - } - } + my $session = $env->{'webgui.session'}; + if (! $session) { + my $logger = $env->{'psgix.logger'}; + $logger && $logger->({ level => 'error', message => 'WebGUI session missing!'}); + return [500, ['Content-Type' => 'text/plain'], 'Internal Server Error']; } - - $self->{file} ||= Plack::App::File->new; - return $self->{file}->serve_path($env, $file); # serve statically + + my $r = $self->app->($env); + $self->response_cb($r, sub { + my ($status, $headers, $body) = @$r; + return + unless Scalar::Util::blessed($body) && $body->can('path'); + + my $file = Path::Class::File->new($body->path); + my $wgaccess = $file->dir->file('.wgaccess'); + return + unless -e $wgaccess; + my $contents = $wgaccess->slurp; + my $privs; + if ($contents =~ /\A(\d+|[A-Za-z0-9_-]{22})\n(\d+|[A-Za-z0-9_-]{22})\n(\d+|[A-Za-z0-9_-]{22})/) { + $privs = { + users => [ $1 ], + groups => [ $2, $3 ], + assets => [], + }; + } + else { + $privs = JSON->new->utf8->decode($contents); + } + + require WebGUI::Asset; + my $userId = $session->var->get('userId'); + + return + if grep { $_ eq '1' || $_ eq $userId } @{ $privs->{users} } + or grep { $_ eq '1' || $_ eq '7' } @{ $privs->{groups} } + or grep { $session->user->isInGroup($_) } @{ $privs->{groups} } + or grep { WebGUI::Asset->newById($session, $_)->canView } @{ $privs->{assets} } + ; + + # failed auto, change response into auth failure + @$r = (401, [ 'Content-Type' => 'text/plain' ], [ 'Authorization Required' ]); + }); } -1; \ No newline at end of file +1; diff --git a/var/site.psgi b/var/site.psgi index 83d6d97db..2db3bdf59 100644 --- a/var/site.psgi +++ b/var/site.psgi @@ -1,7 +1,9 @@ use strict; use Plack::Builder; +use Plack::App::File; use WebGUI; use WebGUI::Paths; +use WebGUI::Middleware::Debug::Performance; my $config = $ENV{WEBGUI_CONFIG}; builder { @@ -18,13 +20,14 @@ builder { # Extras fallback (you should be using something else to serve static files in production) my ( $extrasURL, $extrasPath ) = ( $config->get('extrasURL'), $config->get('extrasPath') ); - enable 'Static', root => "$extrasPath/", path => sub {s{^$extrasURL/}{}}; + enable 'Static', root => "$extrasPath/", path => sub {s{^\Q$extrasURL/}{}}; # Open/close the WebGUI::Session at the outer-most onion layer - enable '+WebGUI::Middleware::Session', - config => $config, - error_docs => { 500 => $config->get('maintenancePage') }; + enable '+WebGUI::Middleware::Session', config => $config; + enable '+WebGUI::Middleware::HTTPExceptions'; + + enable_if { ! $_[0]->{'webgui.debug'} } 'ErrorDocument', 500 => $config->get('maintenancePage'); enable_if { $_[0]->{'webgui.debug'} } 'StackTrace'; enable_if { $_[0]->{'webgui.debug'} } 'Debug', panels => [ 'Environment', @@ -40,9 +43,12 @@ builder { ]; # This one uses the Session object, so it comes after WebGUI::Middleware::Session - enable '+WebGUI::Middleware::WGAccess', config => $config; + mount $config->get('uploadsURL') => builder { + enable '+WebGUI::Middleware::WGAccess'; + Plack::App::File->new(root => $config->get('uploadsPath'))->to_app; + }; # Return the app - $wg->to_app; + mount '/' => $wg->to_app; };