[Bps-public-commit] rt-extension-rest2 branch, master, updated. e0ddc9ef455296f8a3fa1c0a1a25b973bcb104a6
Shawn Moore
shawn at bestpractical.com
Fri Jul 7 11:39:31 EDT 2017
The branch, master has been updated
via e0ddc9ef455296f8a3fa1c0a1a25b973bcb104a6 (commit)
from 5159c3fdefab807730854e41250f09ff9e4de0b4 (commit)
Summary of changes:
Makefile.PL | 1 -
lib/RT/Extension/REST2/Middleware/Auth.pm | 114 ++++++++++++++++++++++++++----
t/root.t | 1 -
3 files changed, 102 insertions(+), 14 deletions(-)
- Log -----------------------------------------------------------------
commit e0ddc9ef455296f8a3fa1c0a1a25b973bcb104a6
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Fri Jul 7 15:22:48 2017 +0000
Allow web session and token-based auth
If you have a valid session cookie, we now use it. This makes it
possible to (among other things) use REST2 from JS, instead of having to
write one-off /Helpers/ endpoints.
There's also a new RT::Authen::Token extension which REST2 can use as
well. Since this plugin is likely destined for core, it doesn't check
@Plugins but instead checks to see if the feature is there.
If it looks like you're using a browser, we shuttle you over to / to log
you in rather than provide a 401 response.
Finally, the test demanding that unauthorized requests send a
WWW-Authenticate be present is removed; we don't want to prompt a
browser for basic auth. The 401 status code and response body should
suffice for non-browser clients. A sampling of other APIs shows
WWW-Authenticate isn't widely used.
diff --git a/Makefile.PL b/Makefile.PL
index ab30f79..10cad18 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -18,7 +18,6 @@ requires 'Plack::Builder';
requires 'Scalar::Util';
requires 'Sub::Exporter';
requires 'Web::Machine' => '0.12';
-requires 'Class::Method::Modifiers';
requires 'Plack::Middleware::RequestHeaders';
requires 'Plack::Middleware::ReverseProxyPath';
requires 'Module::Path';
diff --git a/lib/RT/Extension/REST2/Middleware/Auth.pm b/lib/RT/Extension/REST2/Middleware/Auth.pm
index 18587de..3745b6c 100644
--- a/lib/RT/Extension/REST2/Middleware/Auth.pm
+++ b/lib/RT/Extension/REST2/Middleware/Auth.pm
@@ -3,27 +3,117 @@ package RT::Extension::REST2::Middleware::Auth;
use strict;
use warnings;
-use base 'Plack::Middleware::Auth::Basic';
+use base 'Plack::Middleware';
-use Class::Method::Modifiers;
+our @auth_priority = qw(
+ login_from_cookie
+ login_from_authtoken
+ login_from_basicauth
+);
-before prepare_app => sub {
- my $self = shift;
- $self->realm( RT->Config->Get('rtname') . ' REST API' );
+sub call {
+ my ($self, $env) = @_;
+
+ for my $method (@auth_priority) {
+ last if $env->{'rt.current_user'} = $self->$method($env);
+ }
+
+ if ($env->{'rt.current_user'}) {
+ return $self->app->($env);
+ }
+ else {
+ return $self->unauthorized($env);
+ }
+}
+
+sub login_from_cookie {
+ my ($self, $env) = @_;
+
+ # allow reusing authentication from the ordinary web UI so that
+ # among other things our JS can use REST2
+ if ($env->{HTTP_COOKIE}) {
+
+ # this is foul but LoadSessionFromCookie doesn't have a hook for
+ # saying "look up cookie in my $env". this beats duplicating
+ # LoadSessionFromCookie
+ no warnings 'redefine';
+ local *RT::Interface::Web::RequestENV = sub { return $env->{$_[0]} };
+
+ local *HTML::Mason::Commands::session;
+
+ RT::Interface::Web::LoadSessionFromCookie();
+ if (RT::Interface::Web::_UserLoggedIn) {
+ return $HTML::Mason::Commands::session{CurrentUser};
+ }
+ }
+
+ return;
+}
+
+sub login_from_authtoken {
+ my ($self, $env) = @_;
- $self->authenticator(sub {
- my ($user, $pass, $env) = @_;
+ # needs RT::Authen::Token extension
+ return unless RT::AuthToken->can('Create');
+
+ if (($env->{HTTP_AUTHORIZATION}||'') =~ /^token (.*)$/i) {
+ my ($user_obj, $token) = RT::Authen::Token->UserForAuthString($1);
+ return $user_obj;
+ }
+
+ return;
+}
+
+sub login_from_basicauth {
+ my ($self, $env) = @_;
+
+ require MIME::Base64;
+ if (($env->{HTTP_AUTHORIZATION}||'') =~ /^basic (.*)$/i) {
+ my($user, $pass) = split /:/, (MIME::Base64::decode($1) || ":"), 2;
my $cu = RT::CurrentUser->new;
$cu->Load($user);
if ($cu->id and $cu->IsPassword($pass)) {
- $env->{'rt.current_user'} = $cu;
- return 1;
+ return $cu;
}
else {
RT->Logger->info("Failed login for $user");
- return 0;
+ return;
}
- });
-};
+ }
+
+ return;
+}
+
+sub _looks_like_browser {
+ my $self = shift;
+ my $env = shift;
+
+ return 1 if $env->{HTTP_COOKIE};
+ return 1 if $env->{HTTP_USER_AGENT} =~ /Mozilla/;
+ return 0;
+}
+
+sub unauthorized {
+ my $self = shift;
+ my $env = shift;
+
+ if ($self->_looks_like_browser($env)) {
+ my $url = RT->Config->Get('WebPath') . '/';
+ return [
+ 302,
+ [ 'Location' => $url ],
+ [ "Login required" ],
+ ];
+ }
+ else {
+ my $body = 'Authorization required';
+ return [
+ 401,
+ [ 'Content-Type' => 'text/plain',
+ 'Content-Length' => length $body ],
+ [ $body ],
+ ];
+ }
+}
1;
diff --git a/t/root.t b/t/root.t
index be7d640..9ed51e8 100644
--- a/t/root.t
+++ b/t/root.t
@@ -11,7 +11,6 @@ my $rest_base_path = '/REST/2.0';
{
my $res = $mech->get($rest_base_path);
is($res->code, 401, 'Unauthorized');
- is($res->header('www-authenticate'), 'Basic realm="example.com REST API"');
is($mech->json_response->{message}, 'Unauthorized');
}
-----------------------------------------------------------------------
More information about the Bps-public-commit
mailing list