1<?php
2
3namespace Sabre\HTTP\Auth;
4
5use Sabre\HTTP\Request;
6use Sabre\HTTP\Response;
7
8class DigestTest extends \PHPUnit_Framework_TestCase {
9
10    /**
11     * @var Sabre\HTTP\Response
12     */
13    private $response;
14
15    /**
16     * request
17     *
18     * @var Sabre\HTTP\Request
19     */
20    private $request;
21
22    /**
23     * @var Sabre\HTTP\Auth\Digest
24     */
25    private $auth;
26
27    const REALM = 'SabreDAV unittest';
28
29    function setUp() {
30
31        $this->response = new Response();
32        $this->request = new Request();
33        $this->auth = new Digest(self::REALM, $this->request, $this->response);
34
35
36    }
37
38    function testDigest() {
39
40        list($nonce, $opaque) = $this->getServerTokens();
41
42        $username = 'admin';
43        $password = 12345;
44        $nc = '00002';
45        $cnonce = uniqid();
46
47        $digestHash = md5(
48            md5($username . ':' . self::REALM . ':' . $password) . ':' .
49            $nonce . ':' .
50            $nc . ':' .
51            $cnonce . ':' .
52            'auth:' .
53            md5('GET' . ':' . '/')
54        );
55
56        $this->request->setMethod('GET');
57        $this->request->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . self::REALM . '", nonce="' . $nonce . '", uri="/", response="' . $digestHash . '", opaque="' . $opaque . '", qop=auth,nc=' . $nc . ',cnonce="' . $cnonce . '"');
58
59        $this->auth->init();
60
61        $this->assertEquals($username, $this->auth->getUserName());
62        $this->assertEquals(self::REALM, $this->auth->getRealm());
63        $this->assertTrue($this->auth->validateA1(md5($username . ':' . self::REALM . ':' . $password)), 'Authentication is deemed invalid through validateA1');
64        $this->assertTrue($this->auth->validatePassword($password), 'Authentication is deemed invalid through validatePassword');
65
66    }
67
68    function testInvalidDigest() {
69
70        list($nonce, $opaque) = $this->getServerTokens();
71
72        $username = 'admin';
73        $password = 12345;
74        $nc = '00002';
75        $cnonce = uniqid();
76
77        $digestHash = md5(
78            md5($username . ':' . self::REALM . ':' . $password) . ':' .
79            $nonce . ':' .
80            $nc . ':' .
81            $cnonce . ':' .
82            'auth:' .
83            md5('GET' . ':' . '/')
84        );
85
86        $this->request->setMethod('GET');
87        $this->request->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . self::REALM . '", nonce="' . $nonce . '", uri="/", response="' . $digestHash . '", opaque="' . $opaque . '", qop=auth,nc=' . $nc . ',cnonce="' . $cnonce . '"');
88
89        $this->auth->init();
90
91        $this->assertFalse($this->auth->validateA1(md5($username . ':' . self::REALM . ':' . ($password . 'randomness'))), 'Authentication is deemed invalid through validateA1');
92
93    }
94
95    function testInvalidDigest2() {
96
97        $this->request->setMethod('GET');
98        $this->request->setHeader('Authorization', 'basic blablabla');
99
100        $this->auth->init();
101        $this->assertFalse($this->auth->validateA1(md5('user:realm:password')));
102
103    }
104
105
106    function testDigestAuthInt() {
107
108        $this->auth->setQOP(Digest::QOP_AUTHINT);
109        list($nonce, $opaque) = $this->getServerTokens(Digest::QOP_AUTHINT);
110
111        $username = 'admin';
112        $password = 12345;
113        $nc = '00003';
114        $cnonce = uniqid();
115
116        $digestHash = md5(
117            md5($username . ':' . self::REALM . ':' . $password) . ':' .
118            $nonce . ':' .
119            $nc . ':' .
120            $cnonce . ':' .
121            'auth-int:' .
122            md5('POST' . ':' . '/' . ':' . md5('body'))
123        );
124
125        $this->request->setMethod('POST');
126        $this->request->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . self::REALM . '", nonce="' . $nonce . '", uri="/", response="' . $digestHash . '", opaque="' . $opaque . '", qop=auth-int,nc=' . $nc . ',cnonce="' . $cnonce . '"');
127        $this->request->setBody('body');
128
129        $this->auth->init();
130
131        $this->assertTrue($this->auth->validateA1(md5($username . ':' . self::REALM . ':' . $password)), 'Authentication is deemed invalid through validateA1');
132
133    }
134
135    function testDigestAuthBoth() {
136
137        $this->auth->setQOP(Digest::QOP_AUTHINT | Digest::QOP_AUTH);
138        list($nonce, $opaque) = $this->getServerTokens(Digest::QOP_AUTHINT | Digest::QOP_AUTH);
139
140        $username = 'admin';
141        $password = 12345;
142        $nc = '00003';
143        $cnonce = uniqid();
144
145        $digestHash = md5(
146            md5($username . ':' . self::REALM . ':' . $password) . ':' .
147            $nonce . ':' .
148            $nc . ':' .
149            $cnonce . ':' .
150            'auth-int:' .
151            md5('POST' . ':' . '/' . ':' . md5('body'))
152        );
153
154        $this->request->setMethod('POST');
155        $this->request->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . self::REALM . '", nonce="' . $nonce . '", uri="/", response="' . $digestHash . '", opaque="' . $opaque . '", qop=auth-int,nc=' . $nc . ',cnonce="' . $cnonce . '"');
156        $this->request->setBody('body');
157
158        $this->auth->init();
159
160        $this->assertTrue($this->auth->validateA1(md5($username . ':' . self::REALM . ':' . $password)), 'Authentication is deemed invalid through validateA1');
161
162    }
163
164
165    private function getServerTokens($qop = Digest::QOP_AUTH) {
166
167        $this->auth->requireLogin();
168
169        switch ($qop) {
170            case Digest::QOP_AUTH    : $qopstr = 'auth'; break;
171            case Digest::QOP_AUTHINT : $qopstr = 'auth-int'; break;
172            default                  : $qopstr = 'auth,auth-int'; break;
173        }
174
175        $test = preg_match('/Digest realm="' . self::REALM . '",qop="' . $qopstr . '",nonce="([0-9a-f]*)",opaque="([0-9a-f]*)"/',
176            $this->response->getHeader('WWW-Authenticate'), $matches);
177
178        $this->assertTrue($test == true, 'The WWW-Authenticate response didn\'t match our pattern. We received: ' . $this->response->getHeader('WWW-Authenticate'));
179
180        $nonce = $matches[1];
181        $opaque = $matches[2];
182
183        // Reset our environment
184        $this->setUp();
185        $this->auth->setQOP($qop);
186
187        return [$nonce,$opaque];
188
189    }
190
191}
192