bump minimum version for authority config
[catagits/Catalyst-Authentication-Credential-HTTP.git] / t / basic.t
CommitLineData
2022b950 1use strict;
2use warnings;
b5402c9e 3use Test::More tests => 35;
2022b950 4use Test::MockObject::Extends;
5use Test::MockObject;
d9914dd2 6use Test::Exception;
2022b950 7use HTTP::Headers;
8
513d8ab6 9my $m; BEGIN { use_ok($m = "Catalyst::Authentication::Credential::HTTP") }
10can_ok( $m, "authenticate" );
2022b950 11can_ok( $m, "authorization_required_response" );
490754a8 12
2022b950 13my $req = Test::MockObject->new;
14my $req_headers = HTTP::Headers->new;
2022b950 15$req->set_always( headers => $req_headers );
2022b950 16my $res = Test::MockObject->new;
2022b950 17my $status;
18$res->mock(status => sub { $status = $_[1] });
f366138e 19my $content_type;
20$res->mock(content_type => sub { $content_type = $_[1] });
21my $body;
513d8ab6 22my $headers;
f366138e 23$res->mock(body => sub { $body = $_[1] });
2022b950 24my $res_headers = HTTP::Headers->new;
25$res->set_always( headers => $res_headers );
513d8ab6 26my $user = Test::MockObject->new;
b5402c9e 27$user->set_isa('Catalyst::Authentication::User');
490754a8 28$user->mock(get => sub { return shift->{$_[0]} });
29my $find_user_opts;
30my $realm = Test::MockObject->new;
513d8ab6 31$realm->mock( find_user => sub { $find_user_opts = $_[1]; return $user; });
32$realm->mock( name => sub { 'foo' } );
33my $c = Test::MockObject->new;
007935b8 34my $cache = Test::MockObject->new;
35$cache->mock(set => sub { shift->{$_[0]} = $_[1] });
36$cache->mock(get => sub { return shift->{$_[0]} });
c5a1fa88 37my $uri_for_called = 0;
38$c->mock(uri_for => sub { my ($c, $uri) = @_; $uri_for_called++; return 'uri_for:' . $uri} );
007935b8 39$c->mock(cache => sub { $cache });
4e8cbd42 40$c->mock(debug => sub { 0 });
2022b950 41my @login_info;
42$c->mock( login => sub { shift; @login_info = @_; 1 } );
513d8ab6 43my $authenticated = 0;
44$c->mock( set_authenticated => sub { $authenticated++; } );
2022b950 45$c->set_always( config => {} );
46$c->set_always( req => $req );
47$c->set_always( res => $res );
513d8ab6 48$c->set_always( request => $req );
49$c->set_always( response => $res );
490754a8 50
51sub new_self {
52 my $config = { @_ };
53 my $raw_self = $m->new($config, $c, $realm);
54 return Test::MockObject::Extends->new( $raw_self );
55}
56
57# Normal auth, simple as possible.
58# No credentials
59my $self = new_self( type => 'any', password_type => 'clear', password_field => 'password' );
60throws_ok {
61 $self->authenticate( $c, $realm );
62} qr/^ $Catalyst::DETACH $/x, 'Calling authenticate for http auth without header detaches';
63$user->{password} = 'bar';
64
65# Wrong credentials
66$req_headers->authorization_basic( qw/foo quux/ );
67throws_ok {
68 $self->authenticate( $c, $realm );
69} qr/^ $Catalyst::DETACH $/x, 'Calling authenticate for http auth without header detaches';
70
71# Correct credentials
2022b950 72$req_headers->authorization_basic( qw/foo bar/ );
b5402c9e 73{
74 my $user = $self->authenticate($c, $realm);
75 ok($user, "auth successful with header");
76 isa_ok $user, 'Catalyst::Authentication::User';
77}
78is($authenticated, 0, 'Not called set_authenticated');
513d8ab6 79is_deeply( $find_user_opts, { username => 'foo'}, "login delegated");
490754a8 80
81# Test all the headers look good.
2022b950 82$req_headers->clear;
bf399285 83$res_headers->clear;
2022b950 84$c->clear;
d9914dd2 85throws_ok {
513d8ab6 86 $self->authenticate( $c, $realm );
d9914dd2 87} qr/^ $Catalyst::DETACH $/x, "detached on no authorization required with bad auth";
2022b950 88is( $status, 401, "401 status code" );
f366138e 89is( $content_type, 'text/plain' );
90is( $body, 'Authorization required.' );
007935b8 91like( ($res_headers->header('WWW-Authenticate'))[0], qr/^Digest/, "WWW-Authenticate header set: digest");
513d8ab6 92like( ($res_headers->header('WWW-Authenticate'))[0], qr/realm="foo"/, "WWW-Authenticate header set: digest realm");
007935b8 93like( ($res_headers->header('WWW-Authenticate'))[1], qr/^Basic/, "WWW-Authenticate header set: basic");
513d8ab6 94like( ($res_headers->header('WWW-Authenticate'))[1], qr/realm="foo"/, "WWW-Authenticate header set: basic realm");
6afc3665 95
bf399285 96$res_headers->clear;
490754a8 97# Check password_field works
98{
99 my $self = new_self( type => 'any', password_type => 'clear', password_field => 'the_other_password' );
100 local $user->{password} = 'bar';
101 local $user->{the_other_password} = 'the_other_password';
102 $req_headers->authorization_basic( qw/foo the_other_password/ );
103 ok($self->authenticate($c, $realm), "auth successful with header and alternate password field");
104 $c->clear;
105 $req_headers->authorization_basic( qw/foo bar/ );
106 throws_ok {
107 $self->authenticate( $c, $realm );
108 } qr/^ $Catalyst::DETACH $/x, "detached on bad password (different password field)";
109}
110
111$req_headers->clear;
bf399285 112$res_headers->clear;
513d8ab6 113throws_ok {
114 $self->authenticate( $c, $realm, { realm => 'myrealm' }); # Override realm object's name method by doing this.
490754a8 115} qr/^ $Catalyst::DETACH $/x, "detached on no authorization supplied, overridden realm value";
513d8ab6 116is( $status, 401, "401 status code" );
117is( $content_type, 'text/plain' );
118is( $body, 'Authorization required.' );
bf399285 119like( ($res_headers->header('WWW-Authenticate'))[0], qr/realm="myrealm"/, "WWW-Authenticate header set: digest realm overridden");
120like( ($res_headers->header('WWW-Authenticate'))[1], qr/realm="myrealm"/, "WWW-Authenticate header set: basic realm overridden");
121
05512a69 122# Check authorization_required_message works
123$req_headers->clear;
124$res_headers->clear;
125$c->clear;
126{
127 my $self = new_self( type => 'any', password_type => 'clear',
128 authorization_required_message => 'foobar'
129 );
130 throws_ok {
131 $self->authenticate( $c, $realm );
132 } qr/^ $Catalyst::DETACH $/x, "detached";
133 is( $body, 'foobar', 'Body is supplied auth message');
134}
135
f1f73b53 136# Check undef authorization_required_message suppresses crapping in
137# the body.
05512a69 138$req_headers->clear;
139$res_headers->clear;
140$c->clear;
afe44be8 141$body = 'quuux';
05512a69 142{
143 my $self = new_self( type => 'any', password_type => 'clear',
144 authorization_required_message => undef
145 );
146 throws_ok {
147 $self->authenticate( $c, $realm );
148 } qr/^ $Catalyst::DETACH $/x, "detached";
afe44be8 149 is( $body, 'quuux', 'Body is not set - user overrode auth message');
f1f73b53 150}
151
152# Check domain config works
153$req_headers->clear;
154$res_headers->clear;
155$c->clear;
156{
c5a1fa88 157 my $self = new_self( type => 'any', password_type => 'clear');
f1f73b53 158 throws_ok {
159 $self->authenticate( $c, $realm, {domain => [qw/dom1 dom2/]} );
160 } qr/^ $Catalyst::DETACH $/x, "detached";
161 like( ($res_headers->header('WWW-Authenticate'))[0], qr/domain="dom1 dom2"/, "WWW-Authenticate header set: digest domains set");
162 like( ($res_headers->header('WWW-Authenticate'))[1], qr/domain="dom1 dom2"/, "WWW-Authenticate header set: basic domains set");
163}
c5a1fa88 164
165# Check domain config works with use_uri_for option
166$req_headers->clear;
167$res_headers->clear;
168$c->clear;
169{
170 my $self = new_self( type => 'any', password_type => 'clear', use_uri_for => 1);
171 throws_ok {
172 $self->authenticate( $c, $realm, {domain => [qw/dom1 dom2/]} );
173 } qr/^ $Catalyst::DETACH $/x, "detached";
174 like( ($res_headers->header('WWW-Authenticate'))[0], qr/domain="uri_for:dom1 uri_for:dom2"/,
175 "WWW-Authenticate header set: digest domains set with use_uri_for");
176 like( ($res_headers->header('WWW-Authenticate'))[1], qr/domain="uri_for:dom1 uri_for:dom2"/,
177 "WWW-Authenticate header set: basic domains set with use_uri_for");
9af8f4d3 178}