File: real.t

package info (click to toggle)
libapache2-authcookie-perl 3.22-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 400 kB
  • ctags: 99
  • sloc: perl: 1,130; makefile: 23
file content (425 lines) | stat: -rwxr-xr-x 12,060 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
# TODO: handle line-endings better.  Perhaps we should just look for an 
# identifying part of each page rather than trying to do an exact match
# of the entire page.  The problem is on win32, some responses come back with
# dos-style line endings (not all of them though).  Not sure what MacOS does
# and I don't have a Mac to test with.  Currently, we just strip CR's out of
# responses to make the tests pass on Unix and Win32.  
use strict;
use warnings FATAL => 'all';
use lib 'lib';

use Apache::Test '-withtestmore';
use Apache::TestUtil;
use Apache::TestRequest qw(GET POST GET_BODY);

Apache::TestRequest::user_agent( reset => 1, requests_redirectable => 0 );

plan tests => 49, need_lwp;

ok 1;  # we loaded.

# TODO: the test descriptions should be things other than 'test #' here.

# check that /docs/index.html works.  If this fails, the test environment did
# not configure properly.
{
    my $url = '/docs/index.html';
    my $data = strip_cr(GET_BODY $url);

    like($data, qr/Get the protected document/s,
         '/docs/index.html seems to work');
}

# test no_cookie failure
{
    my $url = '/docs/protected/get_me.html';
    my $r = GET $url;

    like($r->content, qr/Failure reason: 'no_cookie'/s,
         'no_cookie works');
}

# should succeed with redirect.
{
    my $r = POST('/LOGIN', [
        destination  => '/docs/protected/get_me.html',
        credential_0 => 'programmer',
        credential_1 => 'Hero'
    ]);

    is($r->code, 302, 'login produces redirect');
    is($r->header('Location'), '/docs/protected/get_me.html',
       'redirect header exists, and contains expected url');
}

# get protected document with valid cookie.  Should succeed.
{
    my $uri = '/docs/protected/get_me.html';

    my $r = GET(
        $uri,
        Cookie => 'Sample::AuthCookieHandler_WhatEver=programmer:Hero;'
    );

    is($r->code, '200', 'get protected document');
    like($r->content, qr/Congratulations, you got past AuthCookie/s,
         'check protected document content');
}

# should fail with no_cookie
{
    my $url = '/docs/protected/get_me.html';

    my $dat = strip_cr(GET_BODY($url));

    like($dat, qr/Failure reason: 'no_cookie'/s,
         'test failure reason: no_cookie');
}

# should have a Set-Cookie header that expired at epoch.
{
    my $url = '/docs/logout.pl';

    my $r = GET($url);

    my $data = $r->header('Set-Cookie');
    my $expected = 'Sample::AuthCookieHandler_WhatEver=; expires=Mon, 21-May-1971 00:00:00 GMT; path=/';

    is($data, $expected, 'logout tries to delete the cookie');
}

# check the session key
{
    my $data = GET_BODY(
        '/docs/echo_cookie.pl',
        Cookie => 'Sample::AuthCookieHandler_WhatEver=programmer:Hero;'
    );

    is(strip_cr($data), 'programmer:Hero', 'session key contains expected data');
}

# should fail because of 'require user programmer'
{
    my $r = GET(
        '/docs/protected/get_me.html',
        Cookie => 'Sample::AuthCookieHandler_WhatEver=some-user:duck;'
    );

    is($r->code, '403', 'user "some-user" is not authorized');
}

# Should redirect to /docs/protected/get_me.html
{
    my $r = POST('/LOGIN', [
        destination  => '/docs/protected/get_me.html',
        credential_0 => 'programmer',
        credential_1 => 'Heroo'
    ]);

    is($r->code, 302, 'programmer:Heroo login replies with redirect');

    is($r->header('Location'), '/docs/protected/get_me.html',
       'programmer:Heroo location header contains expected URL');

    is($r->header('Set-Cookie'),
       'Sample::AuthCookieHandler_WhatEver=programmer:Heroo; path=/',
       'programmer:Heroo cookie header contains expected data');
}

# should get the login form back (bad_cookie).
{
    my $data = GET_BODY(
        '/docs/protected/get_me.html',
        Cookie=>'Sample::AuthCookieHandler_WhatEver=programmer:Heroo'
    );

    like($data, qr/Failure reason: 'bad_cookie'/, 'invalid cookie');
}

# should get the login form back (bad_credentials)
{
    my $r = POST('/LOGIN', [
        destination  => '/docs/protected/get_me.html',
        credential_0 => 'fail',
        credential_1 => 'Hero'
    ]);

    like($r->content, qr/Failure reason: 'bad_credentials'/,
         'invalid credentials');
}

# check that the destination is right.
{
    my $r = POST('/LOGIN', [
        destination  => '/docs/authany/get_me.html',
        credential_0 => 'some-user',
        credential_1 => 'mypassword'
    ]);

    is($r->header('Location'), '/docs/authany/get_me.html',
       'Location header is correct');

    is($r->header('Set-Cookie'), 
       'Sample::AuthCookieHandler_WhatEver=some-user:mypassword; path=/',
       'Set-Cookie header is correct');

    is($r->code, 302, 'redirect code is correct');
}

# should fail because all requirements are not met
{
    my $r = GET(
        '/docs/authall/get_me.html',
        Cookie => 'Sample::AuthCookieHandler_WhatEver=some-user:mypassword'
    );

    is($r->code(), 403, 'unauthorized if requirements are not met');
}

# test POST to GET conversion
{
    my $r = POST('/docs/protected/get_me.html', [
        foo => 'bar'
    ]);

    like($r->content, qr#"/docs/protected/get_me\.html\?foo=bar"#,
         'POST -> GET conversion works');
}

# same test at #16, but in GET mode. Should succeed
{
    my $data = GET_BODY('/docs/protected/get_me.html?foo=bar');

    like($data, qr#"/docs/protected/get_me\.html\?foo=bar"#,
         'input query string exists in desintation');
}

# should succeed (any requirement is met)
{
    my $r = GET(
        '/docs/authany/get_me.html',
        Cookie => 'Sample::AuthCookieHandler_WhatEver=some-user:mypassword'
    );

    like($r->content, qr/Congratulations, you got past AuthCookie/,
         'AuthAny access allowed');
}

# any requirement, username=0 works.
{
    my $r = GET(
        '/docs/authany/get_me.html',
        Cookie => 'Sample::AuthCookieHandler_WhatEver=0:mypassword'
    );

    like($r->content, qr/Congratulations, you got past AuthCookie/,
         'username=0 access allowed');
}

# login with username=0 works
{
    my $r = POST('/LOGIN', [
        destination  => '/docs/authany/get_me.html',
        credential_0 => '0',
        credential_1 => 'mypassword'
    ]);

    is($r->code, 302, 'username=0 login produces redirect');
    is($r->header('Location'), '/docs/authany/get_me.html',
       'redirect header exists, and contains expected url');
}

# should fail: AuthAny and NONE of the requirements are met.
{
    my $r = GET(
        '/docs/authany/get_me.html',
        Cookie => 'Sample::AuthCookieHandler_WhatEver=nouser:mypassword'
    );

    is($r->code, 403, 'AuthAny forbidden');
}

# Should succeed and cookie should have HttpOnly attribute
{
    my $r = POST('/LOGIN-HTTPONLY', [
        destination  => '/docs/protected/get_me.html',
        credential_0 => 'programmer',
        credential_1 => 'Heroo'
    ]);

    is($r->header('Location'), '/docs/protected/get_me.html',
       'HttpOnly location header');

    is($r->header('Set-Cookie'),
       'Sample::AuthCookieHandler_WhatEver=programmer:Heroo; path=/; HttpOnly',
       'cookie contains HttpOnly attribute');

    is($r->code, 302, 'check redirect response code');
}

# test SessionTimeout
{
    my $r = GET(
        '/docs/stimeout/get_me.html',
        Cookie => 'Sample::AuthCookieHandler_WhatEver=programmer:Hero'
    );

    like($r->header('Set-Cookie'),
         qr/^Sample::AuthCookieHandler_WhatEver=.*expires=.+/,
         'Set-Cookie contains expires property');
}

# should return bad credentials page, and credentials should be in a comment.
# We are checking here that $r->prev->pnotes('WhatEverCreds') works.
{
    my $r = POST('/LOGIN', [
        destination  => '/docs/protected/get_me.html',
        credential_0 => 'fail',
        credential_1 => 'Hero'
    ]);

    like($r->content, qr/creds: fail Hero/s, 'WhatEverCreds pnotes works');
}

# regression - Apache2::URI::unescape_url() does not handle '+' to ' '
# conversion.
{
    my $r = POST('/LOGIN', [
        destination  => '/docs/protected/get_me.html',
        credential_0 => 'fail',
        credential_1 => 'one two'
    ]);

    like($r->content, qr/creds: fail one two/,
         'read form data handles "+" conversion');
}

# variation of '+' to ' ' regression.  Make sure we do not remove encoded
# '+'
{
    my $r = POST('/LOGIN', [
        destination  => '/docs/protected/get_me.html',
        credential_0 => 'fail',
        credential_1 => 'one+two'
    ]);

    like($r->content, qr/creds: fail one\+two/,
         'read form data handles "+" conversion with encoded +');
}

# XSS attack prevention.  make sure embedded \r, \n, \t is escaped in the destination.
{
    my $r = POST('/LOGIN', [
        destination  => "/docs/protected/get_me.html\r\nX-Test-Bar: True\r\nX-Test-Foo: True\r\n",
        credential_0 => 'programmer',
        credential_1 => 'Hero'
    ]);

    ok(!defined $r->header('X-Test-Foo'), 'anti XSS injection');
    ok(!defined $r->header('X-Test-Bar'), 'anti XSS injection');

    # try with escaped CRLF also.
    $r = POST('/LOGIN', [
        destination  => "/docs/protected/get_me.html%0d%0aX-Test-Foo: True%0d%0aX-Test-Bar: True\r\n",
        credential_0 => 'programmer',
        credential_1 => 'Hero'
    ]);

    ok(!defined $r->header('X-Test-Foo'), 'anti XSS injection with escaped CRLF');
    ok(!defined $r->header('X-Test-Bar'), 'anti XSS injection with escaped CRLF');
}

# embedded html tags in destination
{
    my $r = POST('/LOGIN', [
        destination  => '"><form method="post">Embedded Form</form>'
    ]);

    like $r->content, qr{"%22%3E%3Cform method=%22post%22%3EEmbedded Form%3C/form%3E"};
}

# embedded script tags
{
    my $r = POST('/LOGIN', [
        destination => q{"><script>alert('123')</script>}
    ]);

    ok index($r->content, q{<script>alert('123')</script>}) == -1;
}

# make sure '/' in password is preserved.
{
    my $r = POST('/LOGIN', [
        destination  => '/docs/protected/get_me.html',
        credential_0 => 'fail',
        credential_1 => 'one/two'
    ]);

    like($r->content, qr/creds: fail one\/two/,
         'read form data handles "/" conversion with encoded +');
}

# make sure multi-valued form data is preserved.
{
    my $r = POST('/docs/protected/xyz', [
        one => 'abc',
        one => 'def'
    ]);

    # check and make sure we are at the login form now.
    like($r->content, qr/Failure reason: 'no_cookie'/,
         'login form was returned');

    # check for multi-valued form data.
    like($r->content, qr/one=abc&one=def/,
         'post conversion perserves multi-valued fields');
}

# make sure $ENV{REMOTE_USER} gets set up
{
    my $r = GET('/docs/protected/echo_user.pl',
        Cookie => 'Sample::AuthCookieHandler_WhatEver=programmer:Hero'
    );

    like($r->content, qr/User: programmer/);
}

# test login form response status=OK with SymbianOS
{
    my $orig_agent = Apache::TestRequest::user_agent()->agent;

    # should get a 403 response by default
    my $r = GET('/docs/protected/get_me.html');
    is $r->code, 403;
    like $r->content, qr/\bcredential_0\b/, 'got login form';

    Apache::TestRequest::user_agent()
        ->agent('Mozilla/5.0 (SymbianOS/9.1; U; [en]; Series60/3.0 NokiaE60/4.06.0) AppleWebKit/413 (KHTML, like Gecko) Safari/413');

    # should get a 200 response for SymbianOS
    $r = GET('/docs/protected/get_me.html');
    is $r->code, 200;
    like $r->content, qr/\bcredential_0\b/, 'got login form';

    Apache::TestRequest::user_agent()->agent($orig_agent);
}

{
    # recognize user
    my $body = GET_BODY('/docs/echo-user.pl',
        Cookie => 'Sample::AuthCookieHandler_WhatEver=programmer:Hero');

    is $body, 'programmer';
}

# remove CR's from a string.  Win32 apache apparently does line ending
# conversion, and that can cause test cases to fail because output does not
# match expected because expected has UNIX line endings, and OUTPUT has dos
# style line endings.
sub strip_cr {
    my $data = shift;
    $data =~ s/\r//gs;
    return $data;
}

# vim: ft=perl ts=4 ai et sw=4