Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<html>
2
<head>
3
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
4
<title>SimpleTest documentation for testing log-in and authentication</title>
5
<link rel="stylesheet" type="text/css" href="docs.css" title="Styles">
6
</head>
7
<body>
8
<div class="menu_back">
9
<div class="menu">
10
<h2>
11
<a href="index.html">SimpleTest</a>
12
</h2>
13
<ul>
14
<li>
15
<a href="overview.html">Overview</a>
16
</li>
17
<li>
18
<a href="unit_test_documentation.html">Unit tester</a>
19
</li>
20
<li>
21
<a href="group_test_documentation.html">Group tests</a>
22
</li>
23
<li>
24
<a href="mock_objects_documentation.html">Mock objects</a>
25
</li>
26
<li>
27
<a href="partial_mocks_documentation.html">Partial mocks</a>
28
</li>
29
<li>
30
<a href="reporter_documentation.html">Reporting</a>
31
</li>
32
<li>
33
<a href="expectation_documentation.html">Expectations</a>
34
</li>
35
<li>
36
<a href="web_tester_documentation.html">Web tester</a>
37
</li>
38
<li>
39
<a href="form_testing_documentation.html">Testing forms</a>
40
</li>
41
<li>
42
<span class="chosen">Authentication</span>
43
</li>
44
<li>
45
<a href="browser_documentation.html">Scriptable browser</a>
46
</li>
47
</ul>
48
</div>
49
</div>
50
<h1>Authentication documentation</h1>
51
<div class="content">
52
 
53
            <p>
54
                One of the trickiest, and yet most important, areas
55
                of testing web sites is the security.
56
                Testing these schemes is one of the core goals of
57
                the SimpleTest web tester.
58
            </p>
59
 
60
        <p>
61
<a class="target" name="basic">
62
<h2>Basic HTTP authentication</h2>
63
</a>
64
</p>
65
            <p>
66
                If you fetch a page protected by basic authentication then
67
                rather than receiving content, you will instead get a 401
68
                header.
69
                We can illustrate this with this test...
70
<pre>
71
class AuthenticationTest extends WebTestCase {<strong>
72
    function test401Header() {
73
        $this-&gt;get('http://www.lastcraft.com/protected/');
74
        $this-&gt;showHeaders();
75
    }</strong>
76
}
77
</pre>
78
                This allows us to see the challenge header...
79
                <div class="demo">
80
                    <h1>File test</h1>
81
<pre style="background-color: lightgray; color: black">
82
HTTP/1.1 401 Authorization Required
83
Date: Sat, 18 Sep 2004 19:25:18 GMT
84
Server: Apache/1.3.29 (Unix) PHP/4.3.4
85
WWW-Authenticate: Basic realm="SimpleTest basic authentication"
86
Connection: close
87
Content-Type: text/html; charset=iso-8859-1
88
</pre>
89
                    <div style="padding: 8px; margin-top: 1em; background-color: green; color: white;">1/1 test cases complete.
90
                    <strong>0</strong> passes, <strong>0</strong> fails and <strong>0</strong> exceptions.</div>
91
                </div>
92
                We are trying to get away from visual inspection though, and so SimpleTest
93
                allows to make automated assertions against the challenge.
94
                Here is a thorough test of our header...
95
<pre>
96
class AuthenticationTest extends WebTestCase {
97
    function test401Header() {
98
        $this-&gt;get('http://www.lastcraft.com/protected/');<strong>
99
        $this-&gt;assertAuthentication('Basic');
100
        $this-&gt;assertResponse(401);
101
        $this-&gt;assertRealm('SimpleTest basic authentication');</strong>
102
    }
103
}
104
</pre>
105
                Any one of these tests would normally do on it's own depending
106
                on the amount of detail you want to see.
107
            </p>
108
            <p>
109
                One theme that runs through SimpleTest is the ability to use
110
                <span class="new_code">SimpleExpectation</span> objects wherever a simple
111
                match is not enough.
112
                If you want only an approximate match to the realm for
113
                example, you can do this...
114
<pre>
115
class AuthenticationTest extends WebTestCase {
116
    function test401Header() {
117
        $this-&gt;get('http://www.lastcraft.com/protected/');
118
        $this-&gt;assertRealm(<strong>new PatternExpectation('/simpletest/i')</strong>);
119
    }
120
}
121
</pre>
122
                Most of the time we are not interested in testing the
123
                authentication itself, but want to get past it to test
124
                the pages underneath.
125
                As soon as the challenge has been issued we can reply with
126
                an authentication response...
127
<pre>
128
class AuthenticationTest extends WebTestCase {
129
    function testCanAuthenticate() {
130
        $this-&gt;get('http://www.lastcraft.com/protected/');<strong>
131
        $this-&gt;authenticate('Me', 'Secret');</strong>
132
        $this-&gt;assertTitle(...);
133
    }
134
}
135
</pre>
136
                The username and password will now be sent with every
137
                subsequent request to that directory and subdirectories.
138
                You will have to authenticate again if you step outside
139
                the authenticated directory, but SimpleTest is smart enough
140
                to merge subdirectories into a common realm.
141
            </p>
142
            <p>
143
                You can shortcut this step further by encoding the log in
144
                details straight into the URL...
145
<pre>
146
class AuthenticationTest extends WebTestCase {
147
    function testCanReadAuthenticatedPages() {
148
        $this-&gt;get('http://<strong>Me:Secret@</strong>www.lastcraft.com/protected/');
149
        $this-&gt;assertTitle(...);
150
    }
151
}
152
</pre>
153
                If your username or password has special characters, then you
154
                will have to URL encode them or the request will not be parsed
155
                correctly.
156
                Also this header will not be sent on subsequent requests if
157
                you request a page with a fully qualified URL.
158
                If you navigate with relative URLs though, the authentication
159
                information will be preserved.
160
            </p>
161
            <p>
162
                Only basic authentication is currently supported and this is
163
                only really secure in tandem with HTTPS connections.
164
                This is usually enough to protect test server from prying eyes,
165
                however.
166
                Digest authentication and NTLM authentication may be added
167
                in the future.
168
            </p>
169
 
170
        <p>
171
<a class="target" name="cookies">
172
<h2>Cookies</h2>
173
</a>
174
</p>
175
            <p>
176
                Basic authentication doesn't give enough control over the
177
                user interface for web developers.
178
                More likely this functionality will be coded directly into
179
                the web architecture using cookies and complicated timeouts.
180
            </p>
181
            <p>
182
                Starting with a simple log-in form...
183
<pre>
184
&lt;form&gt;
185
    Username:
186
    &lt;input type="text" name="u" value="" /&gt;&lt;br /&gt;
187
    Password:
188
    &lt;input type="password" name="p" value="" /&gt;&lt;br /&gt;
189
    &lt;input type="submit" value="Log in" /&gt;
190
&lt;/form&gt;
191
</pre>
192
                Which looks like...
193
            </p>
194
            <p>
195
                <form class="demo">
196
                    Username:
197
                    <input type="text" name="u" value="">
198
<br>
199
                    Password:
200
                    <input type="password" name="p" value="">
201
<br>
202
                    <input type="submit" value="Log in">
203
                </form>
204
            </p>
205
            <p>
206
                Let's suppose that in fetching this page a cookie has been
207
                set with a session ID.
208
                We are not going to fill the form in yet, just test that
209
                we are tracking the user.
210
                Here is the test...
211
<pre>
212
class LogInTest extends WebTestCase {
213
    function testSessionCookieSetBeforeForm() {
214
        $this-&gt;get('http://www.my-site.com/login.php');<strong>
215
        $this-&gt;assertCookie('SID');</strong>
216
    }
217
}
218
</pre>
219
                All we are doing is confirming that the cookie is set.
220
                As the value is likely to be rather cryptic it's not
221
                really worth testing this with...
222
<pre>
223
class LogInTest extends WebTestCase {
224
    function testSessionCookieIsCorrectPattern() {
225
        $this-&gt;get('http://www.my-site.com/login.php');
226
        $this-&gt;assertCookie('SID', <strong>new PatternExpectation('/[a-f0-9]{32}/i')</strong>);
227
    }
228
}
229
</pre>
230
                The rest of the test would be the same as any other form,
231
                but we might want to confirm that we still have the same
232
                cookie after log-in as before we entered.
233
                We wouldn't want to lose track of this after all.
234
                Here is a possible test for this...
235
<pre>
236
class LogInTest extends WebTestCase {
237
    ...
238
    function testSessionCookieSameAfterLogIn() {
239
        $this-&gt;get('http://www.my-site.com/login.php');<strong>
240
        $session = $this-&gt;getCookie('SID');
241
        $this-&gt;setField('u', 'Me');
242
        $this-&gt;setField('p', 'Secret');
243
        $this-&gt;click('Log in');
244
        $this-&gt;assertText('Welcome Me');
245
        $this-&gt;assertCookie('SID', $session);</strong>
246
    }
247
}
248
</pre>
249
                This confirms that the session identifier is maintained
250
                afer log-in.
251
            </p>
252
            <p>
253
                We could even attempt to spoof our own system by setting
254
                arbitrary cookies to gain access...
255
<pre>
256
class LogInTest extends WebTestCase {
257
    ...
258
    function testSessionCookieSameAfterLogIn() {
259
        $this-&gt;get('http://www.my-site.com/login.php');<strong>
260
        $this-&gt;setCookie('SID', 'Some other session');
261
        $this-&gt;get('http://www.my-site.com/restricted.php');</strong>
262
        $this-&gt;assertText('Access denied');
263
    }
264
}
265
</pre>
266
                Is your site protected from this attack?
267
            </p>
268
 
269
        <p>
270
<a class="target" name="session">
271
<h2>Browser sessions</h2>
272
</a>
273
</p>
274
            <p>
275
                If you are testing an authentication system a critical piece
276
                of behaviour is what happens when a user logs back in.
277
                We would like to simulate closing and reopening a browser...
278
<pre>
279
class LogInTest extends WebTestCase {
280
    ...
281
    function testLoseAuthenticationAfterBrowserClose() {
282
        $this-&gt;get('http://www.my-site.com/login.php');
283
        $this-&gt;setField('u', 'Me');
284
        $this-&gt;setField('p', 'Secret');
285
        $this-&gt;click('Log in');
286
        $this-&gt;assertText('Welcome Me');<strong>
287
 
288
        $this-&gt;restart();
289
        $this-&gt;get('http://www.my-site.com/restricted.php');
290
        $this-&gt;assertText('Access denied');</strong>
291
    }
292
}
293
</pre>
294
                The <span class="new_code">WebTestCase::restart()</span> method will
295
                preserve cookies that have unexpired timeouts, but throw away
296
                those that are temporary or expired.
297
                You can optionally specify the time and date that the restart
298
                happened.
299
            </p>
300
            <p>
301
                Expiring cookies can be a problem.
302
                After all, if you have a cookie that expires after an hour,
303
                you don't want to stall the test for an hour while the
304
                cookie passes it's timeout.
305
            </p>
306
            <p>
307
                To push the cookies over the hour limit you can age them
308
                before you restart the session...
309
<pre>
310
class LogInTest extends WebTestCase {
311
    ...
312
    function testLoseAuthenticationAfterOneHour() {
313
        $this-&gt;get('http://www.my-site.com/login.php');
314
        $this-&gt;setField('u', 'Me');
315
        $this-&gt;setField('p', 'Secret');
316
        $this-&gt;click('Log in');
317
        $this-&gt;assertText('Welcome Me');
318
        <strong>
319
        $this-&gt;ageCookies(3600);</strong>
320
        $this-&gt;restart();
321
        $this-&gt;get('http://www.my-site.com/restricted.php');
322
        $this-&gt;assertText('Access denied');
323
    }
324
}
325
</pre>
326
                After the restart it will appear that cookies are an
327
                hour older and any that pass their expiry will have
328
                disappeared.
329
            </p>
330
 
331
    </div>
332
<div class="copyright">
333
            Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004
334
        </div>
335
</body>
336
</html>