1<?php
2    /**
3     *	Base include file for SimpleTest
4     *	@package	SimpleTest
5     *	@subpackage	WebTester
6     *	@version	$Id: authentication.php,v 1.1 2005/11/09 23:41:18 gsmet Exp $
7     */
8    /**
9     *	include http class
10     */
11    require_once(dirname(__FILE__) . '/http.php');
12
13    /**
14     *    Represents a single security realm's identity.
15	 *    @package SimpleTest
16	 *    @subpackage WebTester
17     */
18    class SimpleRealm {
19        var $_type;
20        var $_root;
21        var $_username;
22        var $_password;
23
24        /**
25         *    Starts with the initial entry directory.
26         *    @param string $type      Authentication type for this
27         *                             realm. Only Basic authentication
28         *                             is currently supported.
29         *    @param SimpleUrl $url    Somewhere in realm.
30         *    @access public
31         */
32        function SimpleRealm($type, $url) {
33            $this->_type = $type;
34            $this->_root = $url->getBasePath();
35            $this->_username = false;
36            $this->_password = false;
37        }
38
39        /**
40         *    Adds another location to the realm.
41         *    @param SimpleUrl $url    Somewhere in realm.
42         *    @access public
43         */
44        function stretch($url) {
45            $this->_root = $this->_getCommonPath($this->_root, $url->getPath());
46        }
47
48        /**
49         *    Finds the common starting path.
50         *    @param string $first        Path to compare.
51         *    @param string $second       Path to compare.
52         *    @return string              Common directories.
53         *    @access private
54         */
55        function _getCommonPath($first, $second) {
56            $first = explode('/', $first);
57            $second = explode('/', $second);
58            for ($i = 0; $i < min(count($first), count($second)); $i++) {
59                if ($first[$i] != $second[$i]) {
60                    return implode('/', array_slice($first, 0, $i)) . '/';
61                }
62            }
63            return implode('/', $first) . '/';
64        }
65
66        /**
67         *    Sets the identity to try within this realm.
68         *    @param string $username    Username in authentication dialog.
69         *    @param string $username    Password in authentication dialog.
70         *    @access public
71         */
72        function setIdentity($username, $password) {
73            $this->_username = $username;
74            $this->_password = $password;
75        }
76
77        /**
78         *    Accessor for current identity.
79         *    @return string        Last succesful username.
80         *    @access public
81         */
82        function getUsername() {
83            return $this->_username;
84        }
85
86        /**
87         *    Accessor for current identity.
88         *    @return string        Last succesful password.
89         *    @access public
90         */
91        function getPassword() {
92            return $this->_password;
93        }
94
95        /**
96         *    Test to see if the URL is within the directory
97         *    tree of the realm.
98         *    @param SimpleUrl $url    URL to test.
99         *    @return boolean          True if subpath.
100         *    @access public
101         */
102        function isWithin($url) {
103            return (strpos($url->getBasePath(), $this->_root) === 0);
104        }
105    }
106
107    /**
108     *    Manages security realms.
109	 *    @package SimpleTest
110	 *    @subpackage WebTester
111     */
112    class SimpleAuthenticator {
113        var $_realms;
114
115        /**
116         *    Clears the realms.
117         *    @access public
118         */
119        function SimpleAuthenticator() {
120            $this->restartSession();
121        }
122
123        /**
124         *    Starts with no realms set up.
125         *    @access public
126         */
127        function restartSession() {
128            $this->_realms = array();
129        }
130
131        /**
132         *    Adds a new realm centered the current URL.
133         *    Browsers vary wildly on their behaviour in this
134         *    regard. Mozilla ignores the realm and presents
135         *    only when challenged, wasting bandwidth. IE
136         *    just carries on presenting until a new challenge
137         *    occours. SimpleTest tries to follow the spirit of
138         *    the original standards committee and treats the
139         *    base URL as the root of a file tree shaped realm.
140         *    @param SimpleUrl $url    Base of realm.
141         *    @param string $type      Authentication type for this
142         *                             realm. Only Basic authentication
143         *                             is currently supported.
144         *    @param string $realm     Name of realm.
145         *    @access public
146         */
147        function addRealm($url, $type, $realm) {
148            $this->_realms[$url->getHost()][$realm] = new SimpleRealm($type, $url);
149        }
150
151        /**
152         *    Sets the current identity to be presented
153         *    against that realm.
154         *    @param string $host        Server hosting realm.
155         *    @param string $realm       Name of realm.
156         *    @param string $username    Username for realm.
157         *    @param string $password    Password for realm.
158         *    @access public
159         */
160        function setIdentityForRealm($host, $realm, $username, $password) {
161            if (isset($this->_realms[$host][$realm])) {
162                $this->_realms[$host][$realm]->setIdentity($username, $password);
163            }
164        }
165
166        /**
167         *    Finds the name of the realm by comparing URLs.
168         *    @param SimpleUrl $url        URL to test.
169         *    @return SimpleRealm          Name of realm.
170         *    @access private
171         */
172        function _findRealmFromUrl($url) {
173            if (! isset($this->_realms[$url->getHost()])) {
174                return false;
175            }
176            foreach ($this->_realms[$url->getHost()] as $name => $realm) {
177                if ($realm->isWithin($url)) {
178                    return $realm;
179                }
180            }
181            return false;
182        }
183
184        /**
185         *    Presents the appropriate headers for this location.
186         *    @param SimpleHttpRequest $request  Request to modify.
187         *    @param SimpleUrl $url              Base of realm.
188         *    @access public
189         */
190        function addHeaders(&$request, $url) {
191            if ($url->getUsername() && $url->getPassword()) {
192                $username = $url->getUsername();
193                $password = $url->getPassword();
194            } elseif ($realm = $this->_findRealmFromUrl($url)) {
195                $username = $realm->getUsername();
196                $password = $realm->getPassword();
197            } else {
198                return;
199            }
200            $this->addBasicHeaders($request, $username, $password);
201        }
202
203        /**
204         *    Presents the appropriate headers for this
205         *    location for basic authentication.
206         *    @param SimpleHttpRequest $request  Request to modify.
207         *    @param string $username            Username for realm.
208         *    @param string $password            Password for realm.
209         *    @access public
210         *    @static
211         */
212        function addBasicHeaders(&$request, $username, $password) {
213            if ($username && $password) {
214                $request->addHeaderLine(
215                        'Authorization: Basic ' . base64_encode("$username:$password"));
216            }
217        }
218    }
219?>