In the last installment, we created Jacquard::Schema::User to describe a Jacquard user, and used Jacquand::Model::KiokuDB (and a helper script) to persist User objects to storage. This time around, we’re going to create our Catalyst app and hook up authentication.

So, first step: create the Catalyst application. The catalyst.pl helper makes this trivial:

$ catalyst.pl Jacquard::Web
created "Jacquard-Web"
created "Jacquard-Web/script"
created "Jacquard-Web/lib"
created "Jacquard-Web/root"
[ couple dozen more lines elided ]

Unfortunately, there’s no easy way to tell it “I’m creating this Cat app as a part of a bigger project” — so after creating it, you have to manually shuffle the files around to get them into the right place. The Catalyst files also come with quite a bit of templated stuff in them that we don’t need (POD skeletons and the like), so cleaning that up and getting the code into line with your personal coding style should be done at this point. Once that’s all done, checkpointing into revision control is a good idea:

$ git ci -m"Create Catalyst application" 
[03-catalyst 3c20771] Create Catalyst application
 11 files changed, 300 insertions(+), 0 deletions(-)
 create mode 100755 jacquard_web.psgi
 create mode 100644 lib/Jacquard/Web.pm
 create mode 100644 lib/Jacquard/Web/Controller/Root.pm
 create mode 100644 root/favicon.ico
 create mode 100644 root/static/images/btn_88x31_built.png
 create mode 100755 script/jacquard_web_cgi.pl
 create mode 100755 script/jacquard_web_create.pl
 create mode 100755 script/jacquard_web_fastcgi.pl
 create mode 100755 script/jacquard_web_server.pl
 create mode 100755 script/jacquard_web_test.pl

Next, we’re going to use CatalystX::SimpleLogin to add authentication to the application, following the outline in the manual. First up, add a number of plugins to the application (in lib/Jacquard/Web.pm):

use Catalyst qw/
    -Debug
    ConfigLoader
    +CatalystX::SimpleLogin
    Authentication
    Session
    Session::Store::File
    Session::State::Cookie
    Static::Simple
/;

Create a default View class by running:

./script/jacquard_web_create.pl view TT TT

Then create the Catalyst Model class that will wrap Jacquard::Model::KiokuDB:

package Jacquard::Web::Model::KiokuDB;
use Moose;

use Jacquard::Model::KiokuDB;

BEGIN { extends qw(Catalyst::Model::KiokuDB) }

has '+model_args'  => ( default => sub { { extra_args => { create => 1 }}});
has '+model_class' => ( default => 'Jacquard::Model::KiokuDB' );

__PACKAGE__->meta->make_immutable;
1;

We also need to add the configuration for these plugins to the application config file:

---
name: Jacquard::Web
Model::KiokuDB:
  dsn: dbi:SQLite:dbname=db/jacquard.db
Plugin::Authentication:
  default:
    credential:
      class: Password
      password_type: self_check
    store:
      class: Model::KiokuDB
      model_name: kiokudb

Finally, we can add a method requiring authentication to the root Controller (which is at lib/Jacquard/Web/Controller/Root.pm):

sub hello_user :Local :Does('NeedsLogin') {
  my( $self , $c ) = @_;

  my $name = $c->user->id;

  $c->response->body( "

Hello, $name!

" ); }

(Don’t forget that you need to change the parent class of the controller as well:

BEGIN { extends 'Catalyst::Controller::ActionRole' }

If things aren’t working, make sure you didn’t forget this.)

With all this in place, you should be able to start up the application, by either running ./script/jacquard_web_server.pl or, if you want to get all Plack-y and modern, plackup jacquard_web.psgi, and then browse to the URL the server reports it’s running on. You should see the Catalyst welcome page at that point. Add ‘/hello_user’ to the end of the URL, and you should get prompted for a username and password. Assuming you created an account using the helper script from the last installment, you should be able to give those same values and see a page that says ‘Hello’ and your test account username.

Now, are we done? Not quite, as we don’t have any tests of the authentication code! The following goes in t/lib/Test/Jacquard/Web/Controller/Root.pm:

package Test::Jacquard::Web::Controller::Root;
use parent 'Test::BASE';

use strict;
use warnings;

use Test::Most;
use Test::WWW::Mechanize::Catalyst;

sub fixtures :Tests(startup) {
  my $test = shift;

  # load test config
  $ENV{CATALYST_CONFIG_LOCAL_SUFFIX} = 'test';

  $test->{mech} = Test::WWW::Mechanize::Catalyst->new( catalyst_app => 'Jacquard::Web' );
}

sub auth_test :Tests() {
  my $test = shift;
  my $mech = $test->{mech};

  $mech->get_ok( '/' , 'basic request works' );
  is( $mech->uri->path , '/' , 'at top page' );

  $mech->get_ok( '/hello_user' , 'request hello_user' );
  is( $mech->uri->path , '/login' , 'redirect to login' );

  $mech->submit_form_ok({
    form_id => 'login_form' ,
    fields  => {
      username => 'test_user' ,
      password => 'test_password' ,
    } ,
    button => 'submit' ,
  } , 'login' );

  is( $mech->uri->path , '/hello_user' , 'redirect to /hello_user' );
  $mech->text_contains( 'Hello, test_user!' , 'see expected greeting' );

  $mech->get_ok( '/' , 'basic request works' );
  is( $mech->uri->path , '/' , 'at home page' );

  $mech->get_ok( '/hello_user' , 'request hello_user' );
  is( $mech->uri->path , '/hello_user' , 'go directly to hello_user' );
  $mech->text_contains( 'Hello, test_user!' , 'still see expected greeting' );

  $mech->get_ok( '/logout' );
  is( $mech->uri->path , '/' , 'back at home page' );

  $mech->get_ok( '/hello_user' , 'request hello_user' );
  is( $mech->uri->path , '/login' , 'redirect to login' );
}


1;

If you’re not familiar with Test::Class-style testing, you should review the links I gave in the post on the Jacquard project infrastructure. Even if you’re not that versed in Test::Class, hopefully the sequence above is fairly self-explanatory: we try to access a restricted URL, verify we get bounced to the login URL, then fill out and submit the login form, and verify we get re-directed to the original request. Following that, we logout and verify that we’re once again unable to get to the restricted resource. Fairly simple.

In order to make this work, we need a distinct application config for testing, one that defines a test user and password for us. Catalyst, of course, provides a way to do this — that’s what that line that sets the CATALYST_CONFIG_LOCAL_SUFFIX environment variable is about. The testing config goes into jacquard_test.yaml, and looks like this:

---
Plugin::Authentication:
  default:
    credential:
      class: Password
      password_field: password
      password_type: clear
    store:
      class: Minimal
      users:
        test_user:
          password: 'test_password'

With that test file and test config in place, we can run the tests:

$ prove -l -I./t/lib ./t/lib/Test/Jacquard/Web/Controller/Root.pm -v
./t/lib/Test/Jacquard/Web/Controller/Root.pm .. # 
# Test::Jacquard::Web::Controller::Root->auth_test

ok 1 - basic request works
ok 2 - at top page
ok 3 - request hello_user
ok 4 - redirect to login
ok 5 - login
ok 6 - redirect to /hello_user
ok 7 - see expected greeting
ok 8 - basic request works
ok 9 - at home page
ok 10 - request hello_user
ok 11 - go directly to hello_user
ok 12 - still see expected greeting
ok 13 - GET /logout
ok 14 - back at home page
ok 15 - request hello_user
ok 16 - redirect to login
1..16
ok
All tests successful.
Files=1, Tests=16,  4 wallclock secs ( 0.04 usr  0.00 sys +  3.67 cusr  0.20 csys =  3.91 CPU)
Result: PASS

and verify they pass. This is also a good point to rerun the whole test suite:

$ prove -l
t/01-run.t .. ok    
All tests successful.
Files=1, Tests=26,  6 wallclock secs ( 0.03 usr  0.01 sys +  4.16 cusr  0.27 csys =  4.47 CPU)
Result: PASS

So, at this point, we have a basic data model (which only models users — but that’s about to change!), a KiokuDB-based persistence framework, and we’ve got all that hooked into a Catalyst-based web application and can use the info in our User object to authenticate to the web app. Sounds like a good place to take a break! In our next installment, we’ll pull back from the coding just a bit and think about the best way to structure the data involved in the services we’re going to connect to, and the posts we’re going to read and write to those connections.

As always, the code for Jacquard is available on Github. Patches are welcome; share and enjoy.

Update: The Jacquard story continues with “Extending The Data Model”.

3 Comments

A few notes following along at home:

after git clone https://github.com/genehack/Jacquard, do "cd Jacquard; mkdir db".

CPAN modules I had to install (I haven't tried to pare these down to their actual modules, just ran the install when things failed): Catalyst CatalystX::SimpleLogin Catalyst::Model::KiokuDB KiokuDB::Backend::DBI MooseX::Types::Email Test::Class

It's 4:40 in the morning here, so I'm not thinking totally clearly, but there's some clean-up or strategy that should be addressed on interpreter invocation, maybe "#!/usr/bin/env perl"

And finally, "prove -l" is giving me a bunch of:

Calling Catalyst::Test::local_request() directly is deprecated.

Please import Catalyst::Test into your namespace and use the provided request() function instead.
at /usr/share/perl5/Test/WWW/Mechanize/Catalyst.pm line 158

warnings. (Apologies for the horrid formatting, I'm not seeing how to do better...)

Aha, that last problem was solved by making sure my Test::WWW::Mechanize::Catalyst was up to date.

Leave a comment

Please note You're welcome to use this comment form to respond to this post -- but I'd greatly prefer if you instead responded via a post on your own weblog or journal. thanks

No TrackBacks

TrackBack URL: http://genehack.org/mt/mt-tb.cgi/1475