source: trunk/server/www/vendors/simpletest/docs/en/authentication_documentation.html @ 6

Last change on this file since 6 was 6, checked in by sander, 12 years ago

Added SimpleTest? test framework

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