[Bps-public-commit] rtx-rest branch, dev, created. 3aaa5eed1cf55454cadf87630503691f9222926a

Wallace Reis wreis at bestpractical.com
Thu Dec 18 14:19:22 EST 2014


The branch, dev has been created
        at  3aaa5eed1cf55454cadf87630503691f9222926a (commit)

- Log -----------------------------------------------------------------
commit e5748fdc5fe8a3c841e13eb58424186f6907baef
Author: Wallace Reis <wreis at bestpractical.com>
Date:   Mon Dec 15 18:26:16 2014 -0200

    Move Log middleware to its class

diff --git a/lib/RT/Extension/REST2.pm b/lib/RT/Extension/REST2.pm
index 55b00d7..1786b66 100644
--- a/lib/RT/Extension/REST2.pm
+++ b/lib/RT/Extension/REST2.pm
@@ -182,48 +182,48 @@ sub to_psgi_app { shift->to_app(@_) }
 
 sub to_app {
     my $class = shift;
-    return sub {
-        my ($env) = @_;
-        $env->{'psgix.logger'} = sub {
-            my $what = shift;
-            RT->Logger->log(%$what);
-        };
-        # XXX TODO: logging of SQL queries in RT's framework for doing so
+
+    return builder {
+        enable '+RT::Extension::REST2::Middleware::Log';
+
         # XXX TODO: Need a dispatcher?  Or do it inside resources?  Web::Simple?
         RT::ConnectToDatabase();
-        my $dispatch = builder {
-            # XXX TODO: better auth integration
-            enable "Auth::Basic",
-                realm         => RT->Config->Get("rtname") . " API",
-                authenticator => sub {
-                    my ($user, $pass, $env) = @_;
-                    my $cu = RT::CurrentUser->new;
-                    $cu->Load($user);
-
-                    if ($cu->id and $cu->IsPassword($pass)) {
-                        $env->{"rt.current_user"} = $cu;
-                        return 1;
-                    } else {
-                        RT->Logger->error("FAILED LOGIN for $user from $env->{REMOTE_ADDR}");
-                        return 0;
-                    }
-                };
-            for ($class->resources) {
-                (my $path = lc $_) =~ s{::}{/}g;
-                mount "/$path" => resource($_);
-            }
-            mount "/"       => sub { [ 404, ['Content-type' => 'text/plain'], ['Unknown resource'] ] };
-        };
-        $dispatch->(@_);
-    }
+        sub {
+            my ($env) = @_;
+            my $dispatch = builder {
+                # XXX TODO: better auth integration
+                enable "Auth::Basic",
+                    realm         => RT->Config->Get("rtname") . " API",
+                    authenticator => sub {
+                        my ($user, $pass, $env) = @_;
+                        my $cu = RT::CurrentUser->new;
+                        $cu->Load($user);
+
+                        if ($cu->id and $cu->IsPassword($pass)) {
+                            $env->{"rt.current_user"} = $cu;
+                            return 1;
+                        } else {
+                            RT->Logger->error("FAILED LOGIN for $user from $env->{REMOTE_ADDR}");
+                            return 0;
+                        }
+                    };
+                for ($class->resources) {
+                    (my $path = lc $_) =~ s{::}{/}g;
+                    mount "/$path" => resource($_);
+                }
+                mount "/"       => sub { [ 404, ['Content-type' => 'text/plain'], ['Unknown resource'] ] };
+            };
+            $dispatch->(@_);
+        }
+    };
 }
 
 # Called by RT::Interface::Web::Handler->PSGIApp
 sub PSGIWrap {
     my ($class, $app) = @_;
-    builder {
-        mount "/REST/2.0"   => $class->to_app;
-        mount "/"           => $app;
+    return builder {
+        mount '/REST/2.0' => $class->to_app;
+        mount '/' => $app;
     };
 }
 
diff --git a/lib/RT/Extension/REST2/Middleware/Log.pm b/lib/RT/Extension/REST2/Middleware/Log.pm
new file mode 100644
index 0000000..75e95b1
--- /dev/null
+++ b/lib/RT/Extension/REST2/Middleware/Log.pm
@@ -0,0 +1,20 @@
+package RT::Extension::REST2::Middleware::Log;
+
+use strict;
+use warnings;
+
+use base 'Plack::Middleware';
+
+sub call {
+    my ( $self, $env ) = @_;
+
+    # XXX TODO: logging of SQL queries in RT's framework for doing so
+    $env->{'psgix.logger'} = sub {
+        my $what = shift;
+        RT->Logger->log(%$what);
+    };
+
+    return $self->app->($env);
+}
+
+1;

commit 38558c271fbc97c64966ff8d0c3ba770e3c1ca47
Author: Wallace Reis <wreis at bestpractical.com>
Date:   Mon Dec 15 18:35:36 2014 -0200

    Move Auth middleware to its own class

diff --git a/Makefile.PL b/Makefile.PL
index ff9b102..662a183 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -18,6 +18,7 @@ requires 'Plack::Builder';
 requires 'Scalar::Util';
 requires 'Sub::Exporter';
 requires 'Web::Machine' => '0.12';
+requires 'Class::Method::Modifiers';
 
 sign;
 WriteAll;
diff --git a/TODO b/TODO
index f783fad..5fb013e 100644
--- a/TODO
+++ b/TODO
@@ -11,7 +11,6 @@ Find TODOs in the code via `ag TODO`.
   - /scrips
   - /roles
   - /templates
-* Implement Basic HTTP authen
 * Remove any session mocking
 
 XXX TODO: Include Links in record serializations
diff --git a/lib/RT/Extension/REST2.pm b/lib/RT/Extension/REST2.pm
index 1786b66..7f6ca03 100644
--- a/lib/RT/Extension/REST2.pm
+++ b/lib/RT/Extension/REST2.pm
@@ -132,10 +132,9 @@ numbers start at 1.
 
 =head2 Authentication
 
-Currently authentication is limited to internal RT usernames and passwords,
-provided via HTTP Basic auth.  Most HTTP libraries already have a way of
-providing basic auth credentials when making requests.  Using curl, for
-example:
+Authentication is limited to internal RT usernames and passwords, provided via
+HTTP Basic auth. Most HTTP libraries already have a way of providing basic
+auth credentials when making requests.  Using curl, for example:
 
     curl -u username:password …
 
@@ -183,30 +182,15 @@ sub to_psgi_app { shift->to_app(@_) }
 sub to_app {
     my $class = shift;
 
+    RT::ConnectToDatabase();
+
     return builder {
         enable '+RT::Extension::REST2::Middleware::Log';
+        enable '+RT::Extension::REST2::Middleware::Auth';
 
-        # XXX TODO: Need a dispatcher?  Or do it inside resources?  Web::Simple?
-        RT::ConnectToDatabase();
         sub {
             my ($env) = @_;
             my $dispatch = builder {
-                # XXX TODO: better auth integration
-                enable "Auth::Basic",
-                    realm         => RT->Config->Get("rtname") . " API",
-                    authenticator => sub {
-                        my ($user, $pass, $env) = @_;
-                        my $cu = RT::CurrentUser->new;
-                        $cu->Load($user);
-
-                        if ($cu->id and $cu->IsPassword($pass)) {
-                            $env->{"rt.current_user"} = $cu;
-                            return 1;
-                        } else {
-                            RT->Logger->error("FAILED LOGIN for $user from $env->{REMOTE_ADDR}");
-                            return 0;
-                        }
-                    };
                 for ($class->resources) {
                     (my $path = lc $_) =~ s{::}{/}g;
                     mount "/$path" => resource($_);
diff --git a/lib/RT/Extension/REST2/Middleware/Auth.pm b/lib/RT/Extension/REST2/Middleware/Auth.pm
new file mode 100644
index 0000000..18587de
--- /dev/null
+++ b/lib/RT/Extension/REST2/Middleware/Auth.pm
@@ -0,0 +1,29 @@
+package RT::Extension::REST2::Middleware::Auth;
+
+use strict;
+use warnings;
+
+use base 'Plack::Middleware::Auth::Basic';
+
+use Class::Method::Modifiers;
+
+before prepare_app => sub {
+    my $self = shift;
+    $self->realm( RT->Config->Get('rtname') . ' REST API' );
+
+    $self->authenticator(sub {
+        my ($user, $pass, $env) = @_;
+        my $cu = RT::CurrentUser->new;
+        $cu->Load($user);
+        if ($cu->id and $cu->IsPassword($pass)) {
+            $env->{'rt.current_user'} = $cu;
+            return 1;
+        }
+        else {
+            RT->Logger->info("Failed login for $user");
+            return 0;
+        }
+    });
+};
+
+1;

commit d87a2e4cd07bee19ca4bc4877ce30d76b1d2235a
Author: Wallace Reis <wreis at bestpractical.com>
Date:   Mon Dec 15 19:29:25 2014 -0200

    PSGIWrap mount apps' paths
    
    When putting RT somewhere other than at the root of the server, then
    we need to have REST API respect that and serve requests on the correct
    base path. This aim to fix the wrong initial behavior of this extension
    by respecting $WebPath config option.
    
    Additionally, creates the $RESTPath config option to make the relative
    REST API path parametrizable and have a default value not spreaded as
    a magical value on the source code.

diff --git a/lib/RT/Extension/REST2.pm b/lib/RT/Extension/REST2.pm
index 7f6ca03..099734d 100644
--- a/lib/RT/Extension/REST2.pm
+++ b/lib/RT/Extension/REST2.pm
@@ -5,6 +5,7 @@ use 5.010;
 package RT::Extension::REST2;
 
 our $VERSION = '0.01';
+our $DEFAULT_REST_PATH = '/REST/2.0';
 
 use UNIVERSAL::require;
 use Plack::Builder;
@@ -20,7 +21,7 @@ RT-Extension-REST2 - Adds a modern REST API to RT under /REST/2.0/
 
 =head2 Summary
 
-Currently provided endpoints under C</REST/2.0/> are:
+Currently provided endpoints under C<$RESTPath> are:
 
     GET /ticket/:id
     PUT /ticket/:id <JSON body>
@@ -139,7 +140,7 @@ auth credentials when making requests.  Using curl, for example:
     curl -u username:password …
 
 This sort of authentication should B<always> be done over HTTPS/SSL for
-security.  You should only serve up the C</REST/2.0/> endpoint over SSL.
+security.  You should only serve up the C<$RESTPath> endpoint over SSL.
 
 =head2 Conditional requests (If-Modified-Since)
 
@@ -202,21 +203,26 @@ sub to_app {
     };
 }
 
-# Called by RT::Interface::Web::Handler->PSGIApp
-sub PSGIWrap {
-    my ($class, $app) = @_;
-    return builder {
-        mount '/REST/2.0' => $class->to_app;
-        mount '/' => $app;
-    };
-}
+sub rest_path { RT->Config->Get('RESTPath') || $DEFAULT_REST_PATH }
 
 sub base_path {
-    RT->Config->Get("WebPath") . "/REST/2.0"
+    RT->Config->Get('WebPath') . shift->rest_path
 }
 
 sub base_uri {
-    RT->Config->Get("WebBaseURL") . base_path()
+    RT->Config->Get('WebBaseURL') . shift->base_path
+}
+
+# Called by RT::Interface::Web::Handler->PSGIApp
+sub PSGIWrap {
+    my ($class, $app) = @_;
+    my $rt_path = RT->Config->Get('WebPath') || '/';
+    my $rest_path = $rt_path . $class->rest_path;
+       $rest_path =~ s{//}{/};
+    return builder {
+        mount $rt_path => $app;
+        mount $rest_path => $class->to_app;
+    };
 }
 
 =head1 INSTALLATION

commit 2f44aed252cf0df9450f8c5e3ce6e2235d58d229
Author: Wallace Reis <wreis at bestpractical.com>
Date:   Tue Dec 16 14:06:10 2014 -0200

    Documentation changes

diff --git a/lib/RT/Extension/REST2.pm b/lib/RT/Extension/REST2.pm
index 099734d..0647832 100644
--- a/lib/RT/Extension/REST2.pm
+++ b/lib/RT/Extension/REST2.pm
@@ -15,7 +15,49 @@ use Web::Machine;
 
 =head1 NAME
 
-RT-Extension-REST2 - Adds a modern REST API to RT under /REST/2.0/
+RT-Extension-REST2 - Adds a modern REST API to RT
+
+=head1 INSTALLATION
+
+=over
+
+=item C<perl Makefile.PL>
+
+=item C<make>
+
+=item C<make install>
+
+May need root permissions
+
+=item Edit your F</opt/rt4/etc/RT_SiteConfig.pm>
+
+Add this line:
+
+    Plugin('RT::Extension::REST2');
+
+=item Clear your mason cache
+
+    rm -rf /opt/rt4/var/mason_data/obj
+
+=item Restart your webserver
+
+=back
+
+=head1 CONFIGURATION
+
+=over
+
+=item C<$RESTPath>
+
+The relative path from C<$WebPath> where you want to have the REST API being
+served.
+
+C<$RESTPath> requires a leading / but no trailing /, or it can be blank.
+
+Defaults to C</REST/2.0>. Thus, if you have C<$WebPath> set to C</rt> then the
+base REST API URI will be like C<https://example.com/rt/REST/2.0>.
+
+=back
 
 =head1 USAGE
 
@@ -87,7 +129,7 @@ These resources accept a basic JSON structure as the search conditions which
 specifies one or more fields to limit on (using specified operators and
 values).  An example:
 
-    curl -si -u user:pass http://rt.example.com/REST/2.0/queues -XPOST --data-binary '
+    curl -si -u user:pass https://rt.example.com/REST/2.0/queues -XPOST --data-binary '
         [
             { "field":    "Name",
               "operator": "LIKE",
@@ -225,35 +267,9 @@ sub PSGIWrap {
     };
 }
 
-=head1 INSTALLATION
-
-=over
-
-=item C<perl Makefile.PL>
-
-=item C<make>
-
-=item C<make install>
-
-May need root permissions
-
-=item Edit your F</opt/rt4/etc/RT_SiteConfig.pm>
-
-Add this line:
-
-    Plugin('RT::Extension::REST2');
-
-=item Clear your mason cache
-
-    rm -rf /opt/rt4/var/mason_data/obj
-
-=item Restart your webserver
-
-=back
-
 =head1 AUTHOR
 
-Thomas Sibley <trs at bestpractical.com>
+Best Practical Solutions, LLC <modules at bestpractical.com>
 
 =head1 BUGS
 

commit ecd2eda0f62377c57aac243a378a938b598ad407
Author: Wallace Reis <wreis at bestpractical.com>
Date:   Wed Dec 17 16:41:05 2014 -0200

    Use Web::Simple as dispatcher

diff --git a/Makefile.PL b/Makefile.PL
index 662a183..ecad65f 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -18,7 +18,10 @@ requires 'Plack::Builder';
 requires 'Scalar::Util';
 requires 'Sub::Exporter';
 requires 'Web::Machine' => '0.12';
+requires 'Web::Simple';
 requires 'Class::Method::Modifiers';
+requires 'Plack::Middleware::RequestHeaders';
+requires 'Plack::Middleware::ReverseProxyPath';
 
 sign;
 WriteAll;
diff --git a/lib/RT/Extension/REST2.pm b/lib/RT/Extension/REST2.pm
index 0647832..917d6f9 100644
--- a/lib/RT/Extension/REST2.pm
+++ b/lib/RT/Extension/REST2.pm
@@ -9,7 +9,7 @@ our $DEFAULT_REST_PATH = '/REST/2.0';
 
 use UNIVERSAL::require;
 use Plack::Builder;
-use Web::Machine;
+use RT::Extension::REST2::Dispatcher;
 
 =encoding utf-8
 
@@ -202,24 +202,6 @@ handle them appropriately.
 
 # XXX TODO: API doc
 
-sub resources {
-    return qw(
-        Queue
-        Queues
-        Ticket
-        Tickets
-        User
-        Users
-        Download::CF
-    );
-}
-
-sub resource {
-    Web::Machine->new(
-        resource => "RT::Extension::REST2::Resource::$_[0]",
-    )->to_app;
-}
-
 sub to_psgi_app { shift->to_app(@_) }
 
 sub to_app {
@@ -227,21 +209,18 @@ sub to_app {
 
     RT::ConnectToDatabase();
 
+    my $rest_path = $class->rest_path;
+
     return builder {
         enable '+RT::Extension::REST2::Middleware::Log';
         enable '+RT::Extension::REST2::Middleware::Auth';
-
-        sub {
-            my ($env) = @_;
-            my $dispatch = builder {
-                for ($class->resources) {
-                    (my $path = lc $_) =~ s{::}{/}g;
-                    mount "/$path" => resource($_);
-                }
-                mount "/"       => sub { [ 404, ['Content-type' => 'text/plain'], ['Unknown resource'] ] };
-            };
-            $dispatch->(@_);
-        }
+        enable 'RequestHeaders',
+            set => [
+                'X-Forwarded-Script-Name' => '/',
+                'X-Traversal-Path' => $rest_path,
+            ];
+        enable 'ReverseProxyPath';
+        RT::Extension::REST2::Dispatcher->to_psgi_app;
     };
 }
 
diff --git a/lib/RT/Extension/REST2/Dispatcher.pm b/lib/RT/Extension/REST2/Dispatcher.pm
new file mode 100644
index 0000000..5bbfd89
--- /dev/null
+++ b/lib/RT/Extension/REST2/Dispatcher.pm
@@ -0,0 +1,24 @@
+package RT::Extension::REST2::Dispatcher;
+
+use strict;
+use warnings;
+use Web::Simple;
+use Web::Machine;
+
+sub dispatch_request {
+    my ($self) = @_;
+    sub (/*/*) {
+        my $resource_name = ucfirst lc $_[1];
+        my $resource = "RT::Extension::REST2::Resource::${resource_name}";
+        if ( $resource->require ) {
+            Web::Machine->new(
+                resource => $resource,
+            )->to_app;
+        }
+        else {
+            return undef;
+        }
+    },
+}
+
+1;
diff --git a/lib/RT/Extension/REST2/Resource/Record.pm b/lib/RT/Extension/REST2/Resource/Record.pm
index 607be90..c1380bf 100644
--- a/lib/RT/Extension/REST2/Resource/Record.pm
+++ b/lib/RT/Extension/REST2/Resource/Record.pm
@@ -36,7 +36,7 @@ sub _build_record_class {
 sub _build_record {
     my $self = shift;
     my $record = $self->record_class->new( $self->current_user );
-    my $id = bind_path('/:id', $self->request->path_info);
+    my ($type, $id) = bind_path('/:type/:id', $self->request->path_info);
     $record->Load($id) if $id;
     return $record;
 }

commit 3aaa5eed1cf55454cadf87630503691f9222926a
Author: Wallace Reis <wreis at bestpractical.com>
Date:   Thu Dec 18 17:18:49 2014 -0200

    RTx() provides both all_from and readme_from

diff --git a/Makefile.PL b/Makefile.PL
index ecad65f..4b11103 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -1,8 +1,6 @@
 use inc::Module::Install;
 
 RTx 'RT-Extension-REST2';
-all_from 'lib/RT/Extension/REST2.pm';
-readme_from 'lib/RT/Extension/REST2.pm';
 license  'gplv2';
 
 requires_rt('4.2.4');

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


More information about the Bps-public-commit mailing list