Completed
Push — master ( 089c32...0d9ae4 )
by Tobias
11:36 queued 09:40
created

DigestAuthMiddleware::setNonce()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Buzz\Middleware;
6
7
use Psr\Http\Message\RequestInterface;
8
use Psr\Http\Message\ResponseInterface;
9
10
class DigestAuthMiddleware implements MiddlewareInterface
11
{
12
    private $username;
13
14
    private $password;
15
16
    private $realm;
17
18
    private $algorithm;
19
20
    private $authenticationMethod;
21
22
    private $clientNonce;
23
24
    private $domain;
25
26
    private $entityBody;
27
28
    private $method;
29
30
    private $nonce;
31
32
    private $nonceCount;
33
34
    private $opaque;
35
36
    private $uri;
37
38
    /** @var string[] Quality of Protection */
39
    private $qop = [];
40
41
    /**
42
     * QOP options: Only one of the following can be set at any time. setOptions will throw an exception otherwise.
43
     * OPTION_QOP_AUTH_INT       - Always use auth-int   (if available)
44
     * OPTION_QOP_AUTH           - Always use auth       (even if auth-int available).
45
     */
46
    const OPTION_QOP_AUTH_INT = 1;
47
48
    const OPTION_QOP_AUTH = 2;
49
50
    /**
51
     * Ignore server request to downgrade authentication from Digest to Basic.
52
     * Breaks RFC compatibility, but ensures passwords are never sent using base64 which is trivial for an attacker to decode.
53
     */
54
    const OPTION_IGNORE_DOWNGRADE_REQUEST = 4;
55
56
    /**
57
     * Discard Client Nonce on each request.
58
     */
59
    const OPTION_DISCARD_CLIENT_NONCE = 8;
60
61
    private $options;
62
63
    /**
64
     * Set OPTION_QOP_BEST_AVAILABLE and OPTION_DISCARD_CLIENT_NONCE by default.
65
     */
66 1
    public function __construct(string $username = null, string $password = null, string $realm = null)
67
    {
68 1
        $this->setUsername($username);
69 1
        $this->setPassword($password);
70 1
        $this->setRealm($realm);
71 1
        $this->setOptions(self::OPTION_QOP_AUTH_INT & self::OPTION_DISCARD_CLIENT_NONCE);
72 1
    }
73
74
    /**
75
     * Populates uri, method and entityBody used to generate the Authentication header using the specified request object.
76
     * Appends the Authentication header if it is present and has been able to be calculated.
77
     */
78 1
    public function handleRequest(RequestInterface $request, callable $next)
79
    {
80 1
        $this->setUri($request->getUri()->getPath());
81 1
        $this->setMethod(strtoupper($request->getMethod()));
82 1
        $this->setEntityBody($request->getBody()->__toString());
83
84 1
        if (null !== $header = $this->getHeader()) {
85 1
            $request = $request->withHeader('Authorization', $header);
86
        }
87
88 1
        return $next($request);
89
    }
90
91
    /**
92
     * Passes the returned server headers to parseServerHeaders() to check if any authentication variables need to be set.
93
     * Inteprets the returned status code and attempts authentication if status is 401 (Authentication Required) by resending
94
     * the last request with an Authentication header.
95
     */
96 1
    public function handleResponse(RequestInterface $request, ResponseInterface $response, callable $next)
97
    {
98 1
        $this->parseServerHeaders($response);
99
100 1
        return $next($request, $response);
101
    }
102
103
    /**
104
     * Sets the password to be used to authenticate the client.
105
     *
106
     * @param string $password The password
107
     */
108 1
    public function setPassword(?string $password): void
109
    {
110 1
        $this->password = $password;
111 1
    }
112
113
    /**
114
     * Sets the realm to be used to authenticate the client.
115
     *
116
     * @param string $realm The realm
117
     */
118 1
    public function setRealm(?string $realm): void
119
    {
120 1
        $this->realm = $realm;
121 1
    }
122
123
    /**
124
     * Sets the username to be used to authenticate the client.
125
     *
126
     * @param string $username The username
127
     */
128 1
    public function setUsername(?string $username): void
129
    {
130 1
        $this->username = $username;
131 1
    }
132
133
    /**
134
     * Sets the options to be used by this class.
135
     *
136
     * @param mixed $options a bitmask of the constants defined in this class
137
     */
138 1
    public function setOptions($options): void
139
    {
140 1
        if ($options & self::OPTION_QOP_AUTH_INT) {
141
            if ($options & self::OPTION_QOP_AUTH) {
142
                throw new \InvalidArgumentException('DigestAuthMiddleware: Only one value of OPTION_QOP_AUTH_INT or OPTION_QOP_AUTH may be set.');
143
            }
144
            $this->options = $this->options | self::OPTION_QOP_AUTH_INT;
145 1
        } elseif ($options & self::OPTION_QOP_AUTH) {
146
            $this->options = $this->options | self::OPTION_QOP_AUTH;
147
        }
148
149 1
        if ($options & self::OPTION_IGNORE_DOWNGRADE_REQUEST) {
150
            $this->options = $this->options | self::OPTION_IGNORE_DOWNGRADE_REQUEST;
151
        }
152
153 1
        if ($options & self::OPTION_DISCARD_CLIENT_NONCE) {
154
            $this->options = $this->options | self::OPTION_DISCARD_CLIENT_NONCE;
155
        }
156 1
    }
157
158
    /**
159
     * Discards the Client Nonce forcing the generation of a new Client Nonce on the next request.
160
     */
161
    private function discardClientNonce(): void
162
    {
163
        $this->clientNonce = null;
164
    }
165
166
    /**
167
     * Returns the hashing algorithm to be used to generate the digest value. Currently only returns MD5.
168
     *
169
     * @return string the hashing algorithm to be used
170
     */
171 1
    private function getAlgorithm(): ?string
172
    {
173 1
        if (null == $this->algorithm) {
174
            $this->algorithm = 'MD5';
175
        }
176
177 1
        return $this->algorithm;
178
    }
179
180
    /**
181
     * Returns the authentication method requested by the server.
182
     * If OPTION_IGNORE_DOWNGRADE_REQUEST is set this will always return "Digest".
183
     *
184
     * @return string returns either "Digest" or "Basic"
185
     */
186 1
    private function getAuthenticationMethod(): ?string
187
    {
188 1
        if ($this->options & self::OPTION_IGNORE_DOWNGRADE_REQUEST) {
189
            return 'Digest';
190
        }
191
192 1
        return $this->authenticationMethod;
193
    }
194
195
    /**
196
     * Returns either the current value of clientNonce or generates a new value if clientNonce is null.
197
     * Also increments nonceCount.
198
     *
199
     * @return string Returns either the current value of clientNonce the newly generated clientNonce;
200
     */
201
    private function getClientNonce(): ?string
202
    {
203
        if (null == $this->clientNonce) {
204
            $this->clientNonce = uniqid();
205
206
            if (null == $this->nonceCount) {
207
                // If nonceCount is not set then set it to 00000001.
208
                $this->nonceCount = '00000001';
209
            } else {
210
                // If it is set then increment it.
211
                ++$this->nonceCount;
212
                // Ensure nonceCount is zero-padded at the start of the string to a length of 8
213
                while (strlen($this->nonceCount) < 8) {
214
                    $this->nonceCount = '0'.$this->nonceCount;
215
                }
216
            }
217
        }
218
219
        return $this->clientNonce;
220
    }
221
222
    /**
223
     * Returns a space separated list of uris that the server nonce can be used to generate an authentication response against.
224
     *
225
     * @return string space separated list of uris
226
     */
227
    private function getDomain(): ?string
0 ignored issues
show
Unused Code introduced by
The method getDomain() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
228
    {
229
        return $this->domain;
230
    }
231
232
    /**
233
     * Returns the entity body of the current request.
234
     * The entity body is the request before it has been encoded with the content-encoding and minus the request headers.
235
     *
236
     * @return string the full entity-body
237
     */
238
    private function getEntityBody(): ?string
239
    {
240
        return (string) $this->entityBody;
241
    }
242
243
    /**
244
     * Calculates the value of HA1 according to RFC 2617 and RFC 2069.
245
     *
246
     * @return string|null The value of HA1
247
     */
248 1
    private function getHA1(): ?string
249
    {
250 1
        $username = $this->getUsername();
251 1
        $password = $this->getPassword();
252 1
        $realm = $this->getRealm();
253
254 1
        if (($username) && ($password) && ($realm)) {
255 1
            $algorithm = $this->getAlgorithm();
256
257 1
            if ('MD5' === $algorithm) {
258 1
                $A1 = "{$username}:{$realm}:{$password}";
259
260 1
                return $this->hash($A1);
261
            } elseif ('MD5-sess' === $algorithm) {
262
                $nonce = $this->getNonce();
263
                $cnonce = $this->getClientNonce();
264
                if (($nonce) && ($cnonce)) {
265
                    $A1 = $this->hash("{$username}:{$realm}:{$password}").":{$nonce}:{$cnonce}";
266
267
                    return $this->hash($A1);
268
                }
269
            }
270
        }
271
272
        return null;
273
    }
274
275
    /**
276
     * Calculates the value of HA2 according to RFC 2617 and RFC 2069.
277
     *
278
     * @return string The value of HA2
279
     */
280 1
    private function getHA2(): ?string
281
    {
282 1
        $method = $this->getMethod();
283 1
        $uri = $this->getUri();
284
285 1
        if (($method) && ($uri)) {
286 1
            $qop = $this->getQOP();
287
288 1
            if (null === $qop || 'auth' === $qop) {
289 1
                $A2 = "{$method}:{$uri}";
290
            } elseif ('auth-int' === $qop) {
291
                $entityBody = (string) $this->getEntityBody();
292
                $A2 = "{$method}:{$uri}:".(string) $this->hash($entityBody);
293
            } else {
294
                return null;
295
            }
296
297 1
            $HA2 = $this->hash($A2);
298
299 1
            return $HA2;
300
        }
301
302
        return null;
303
    }
304
305
    /**
306
     * Returns the full Authentication header for use in authenticating the client with either Digest or Basic authentication.
307
     *
308
     * @return string the Authentication header to be sent to the server
309
     */
310 1
    private function getHeader(): ?string
311
    {
312 1
        if ('Digest' == $this->getAuthenticationMethod()) {
313 1
            $username = $this->getUsername();
314 1
            $realm = $this->getRealm();
315 1
            $nonce = $this->getNonce();
316 1
            $response = $this->getResponse();
317 1
            if (($username) && ($realm) && ($nonce) && ($response)) {
318 1
                $uri = $this->getUri();
319 1
                $opaque = $this->getOpaque();
320 1
                $qop = $this->getQOP();
321
322 1
                $header = 'Digest';
323 1
                $header .= ' username="'.$username.'",';
324 1
                $header .= ' realm="'.$realm.'",';
325 1
                $header .= ' nonce="'.$nonce.'",';
326 1
                $header .= ' response="'.$response.'",';
327
328 1
                if ($uri) {
329 1
                    $header .= ' uri="'.$uri.'",';
330
                }
331 1
                if ($opaque) {
332
                    $header .= ' opaque="'.$opaque.'",';
333
                }
334
335 1
                if ($qop) {
336
                    $header .= ' qop='.$qop.',';
337
338
                    $cnonce = $this->getClientNonce();
339
                    $nc = $this->getNonceCount();
340
341
                    if ($cnonce) {
342
                        $header .= ' nc='.$nc.',';
343
                    }
344
                    if ($cnonce) {
345
                        $header .= ' cnonce="'.$cnonce.'",';
346
                    }
347
                }
348
349
                // Remove the last comma from the header
350 1
                $header = substr($header, 0, strlen($header) - 1);
351
                // Discard the Client Nonce if OPTION_DISCARD_CLIENT_NONCE is set.
352 1
                if ($this->options & self::OPTION_DISCARD_CLIENT_NONCE) {
353
                    $this->discardClientNonce();
354
                }
355
356 1
                return $header;
357
            }
358
        }
359 1
        if ('Basic' == $this->getAuthenticationMethod()) {
360
            $username = $this->getUsername();
361
            $password = $this->getPassword();
362
            if (($username) && ($password)) {
363
                $header = 'Basic '.base64_encode("{$username}:{$password}");
364
365
                return $header;
366
            }
367
        }
368
369 1
        return null;
370
    }
371
372
    /**
373
     * Returns the HTTP method used in the current request.
374
     *
375
     * @return string one of GET,POST,PUT,DELETE or HEAD
376
     */
377 1
    private function getMethod(): ?string
378
    {
379 1
        return $this->method;
380
    }
381
382
    /**
383
     * Returns the value of nonce we have received in the server headers.
384
     *
385
     * @return string the value of the server nonce
386
     */
387 1
    private function getNonce(): ?string
388
    {
389 1
        return $this->nonce;
390
    }
391
392
    /**
393
     * Returns the current nonce counter for the client nonce.
394
     *
395
     * @return string an eight digit zero-padded string which reflects the number of times the clientNonce has been generated
396
     */
397
    private function getNonceCount(): ?string
398
    {
399
        return $this->nonceCount;
400
    }
401
402
    /**
403
     * Returns the opaque value that was sent to us from the server.
404
     *
405
     * @return string the value of opaque
406
     */
407 1
    private function getOpaque(): ?string
408
    {
409 1
        return $this->opaque;
410
    }
411
412
    /**
413
     * Returns the plaintext password for the client.
414
     *
415
     * @return string the value of password
416
     */
417 1
    private function getPassword(): ?string
418
    {
419 1
        return $this->password;
420
    }
421
422
    /**
423
     * Returns either the realm specified by the client, or the realm specified by the server.
424
     * If the server set the value of realm then anything set by our client is overwritten.
425
     *
426
     * @return string the value of realm
427
     */
428 1
    private function getRealm(): ?string
429
    {
430 1
        return $this->realm;
431
    }
432
433
    /**
434
     * Calculates the value of response according to RFC 2617 and RFC 2069.
435
     *
436
     * @return string The value of response
437
     */
438 1
    private function getResponse(): ?string
439
    {
440 1
        $HA1 = $this->getHA1();
441 1
        $nonce = $this->getNonce();
442 1
        $HA2 = $this->getHA2();
443
444 1
        if (null !== $HA1 && ($nonce) && null !== $HA2) {
445 1
            $qop = $this->getQOP();
446
447 1
            if (empty($qop)) {
448 1
                $response = $this->hash("{$HA1}:{$nonce}:{$HA2}");
449
450 1
                return $response;
451
            }
452
453
            $cnonce = $this->getClientNonce();
454
            $nc = $this->getNonceCount();
455
            if (($cnonce) && ($nc)) {
456
                $response = $this->hash("{$HA1}:{$nonce}:{$nc}:{$cnonce}:{$qop}:{$HA2}");
457
458
                return $response;
459
            }
460
        }
461
462
        return null;
463
    }
464
465
    /**
466
     * Returns the Quality of Protection to be used when authenticating with the server.
467
     *
468
     * @return string this will either be auth-int or auth
469
     */
470 1
    private function getQOP(): ?string
471
    {
472
        // Has the server specified any options for Quality of Protection
473 1
        if (count($this->qop) > 0) {
474 1
            if ($this->options & self::OPTION_QOP_AUTH_INT) {
475
                if (in_array('auth-int', $this->qop)) {
476
                    return 'auth-int';
477
                }
478
                if (in_array('auth', $this->qop)) {
479
                    return 'auth';
480
                }
481
            }
482 1
            if ($this->options & self::OPTION_QOP_AUTH) {
483
                if (in_array('auth', $this->qop)) {
484
                    return 'auth';
485
                }
486
                if (in_array('auth-int', $this->qop)) {
487
                    return 'auth-int';
488
                }
489
            }
490
        }
491
        // Server has not specified any value for Quality of Protection so return null
492 1
        return null;
493
    }
494
495
    /**
496
     * Returns the username set by the client to authenticate with the server.
497
     *
498
     * @return string The value of username
499
     */
500 1
    private function getUsername(): ?string
501
    {
502 1
        return $this->username;
503
    }
504
505
    /**
506
     * Returns the uri that we are requesting access to.
507
     *
508
     * @return string The value of uri
509
     */
510 1
    private function getUri(): ?string
511
    {
512 1
        return $this->uri;
513
    }
514
515
    /**
516
     * Calculates the hash for a given value using the algorithm specified by the server.
517
     *
518
     * @param string $value The value to be hashed
519
     *
520
     * @return string the hashed value
521
     */
522 1
    private function hash($value): ?string
523
    {
524 1
        $algorithm = $this->getAlgorithm();
525 1
        if (('MD5' == $algorithm) || ('MD5-sess' == $algorithm)) {
526 1
            return hash('md5', $value);
527
        }
528
529
        return null;
530
    }
531
532
    /**
533
     * Parses the Authentication-Info header received from the server and calls the relevant setter method on each variable received.
534
     *
535
     * @param string $authenticationInfo the full Authentication-Info header
536
     */
537
    private function parseAuthenticationInfoHeader(string $authenticationInfo): void
538
    {
539
        $nameValuePairs = $this->parseNameValuePairs($authenticationInfo);
540
        foreach ($nameValuePairs as $name => $value) {
541
            switch ($name) {
542
                case 'message-qop':
543
544
                    break;
545
                case 'nextnonce':
546
                    // This function needs to only set the Nonce once the rspauth has been verified.
547
                    $this->setNonce($value);
548
549
                    break;
550
                case 'rspauth':
551
                    // Check server rspauth value
552
                    break;
553
            }
554
        }
555
    }
556
557
    /**
558
     * Parses a string of name=value pairs separated by commas and returns and array with the name as the index.
559
     *
560
     * @param string $nameValuePairs the string containing the name=value pairs
561
     *
562
     * @return array an array with the name used as the index and the values stored within
563
     */
564 1
    private function parseNameValuePairs(string $nameValuePairs): array
565
    {
566 1
        $parsedNameValuePairs = [];
567 1
        $nameValuePairs = explode(',', $nameValuePairs);
568 1
        foreach ($nameValuePairs as $nameValuePair) {
569
            // Trim the Whitespace from the start and end of the name value pair string
570 1
            $nameValuePair = trim($nameValuePair);
571
            // Split $nameValuePair (name=value) into $name and $value
572 1
            list($name, $value) = explode('=', $nameValuePair, 2);
573
            // Remove quotes if the string is quoted
574 1
            $value = $this->unquoteString($value);
575
            // Add pair to array[name] => value
576 1
            $parsedNameValuePairs[$name] = $value;
577
        }
578
579 1
        return $parsedNameValuePairs;
580
    }
581
582
    /**
583
     * Parses the server headers received and checks for WWW-Authenticate and Authentication-Info headers.
584
     * Calls parseWwwAuthenticateHeader() and parseAuthenticationInfoHeader() respectively if either of these headers are present.
585
     *
586
     * @param ResponseInterface $response
587
     */
588 1
    private function parseServerHeaders(ResponseInterface $response): void
589
    {
590
        // Check to see if the WWW-Authenticate header is present and if so set $authHeader
591 1
        if (!empty($header = $response->getHeaderLine('www-authenticate'))) {
592 1
            $this->parseWwwAuthenticateHeader($header);
593
        }
594
595
        // Check to see if the Authentication-Info header is present and if so set $authInfo
596 1
        if (!empty($header = $response->getHeaderLine('authentication-info'))) {
597
            $this->parseAuthenticationInfoHeader($header);
598
        }
599 1
    }
600
601
    /**
602
     * Parses the WWW-Authenticate header received from the server and calls the relevant setter method on each variable received.
603
     *
604
     * @param string $wwwAuthenticate the full WWW-Authenticate header
605
     */
606 1
    private function parseWwwAuthenticateHeader(string $wwwAuthenticate): void
607
    {
608 1
        if ('Digest ' == substr($wwwAuthenticate, 0, 7)) {
609 1
            $this->setAuthenticationMethod('Digest');
610
            // Remove "Digest " from start of header
611 1
            $wwwAuthenticate = substr($wwwAuthenticate, 7, strlen($wwwAuthenticate) - 7);
612
613 1
            $nameValuePairs = $this->parseNameValuePairs($wwwAuthenticate);
614
615 1
            foreach ($nameValuePairs as $name => $value) {
616
                switch ($name) {
617 1
                    case 'algorithm':
618 1
                        $this->setAlgorithm($value);
619
620 1
                        break;
621 1
                    case 'domain':
622 1
                        $this->setDomain($value);
623
624 1
                        break;
625 1
                    case 'nonce':
626 1
                        $this->setNonce($value);
627
628 1
                        break;
629 1
                    case 'realm':
630 1
                        $this->setRealm($value);
631
632 1
                        break;
633 1
                    case 'opaque':
634
                        $this->setOpaque($value);
635
636
                        break;
637 1
                    case 'qop':
638 1
                        $this->setQOP(explode(',', $value));
639
640 1
                        break;
641
                }
642
            }
643
        }
644 1
        if ('Basic ' == substr($wwwAuthenticate, 0, 6)) {
645
            $this->setAuthenticationMethod('Basic');
646
            // Remove "Basic " from start of header
647
            $wwwAuthenticate = substr($wwwAuthenticate, 6, strlen($wwwAuthenticate) - 6);
648
649
            $nameValuePairs = $this->parseNameValuePairs($wwwAuthenticate);
650
651
            foreach ($nameValuePairs as $name => $value) {
652
                switch ($name) {
653
                    case 'realm':
654
                        $this->setRealm($value);
655
656
                        break;
657
                }
658
            }
659
        }
660 1
    }
661
662
    /**
663
     * Sets the hashing algorithm to be used. Currently only uses MD5 specified by either MD5 or MD5-sess.
664
     * RFCs are currently in draft stage for the proposal of SHA-256 and SHA-512-256.
665
     * Support will be added once the RFC leaves the draft stage.
666
     *
667
     * @param string $algorithm the algorithm the server has requested to use
668
     *
669
     * @throws \InvalidArgumentException if $algorithm is set to anything other than MD5 or MD5-sess
670
     */
671 1
    private function setAlgorithm(string $algorithm): void
672
    {
673 1
        if (('MD5' == $algorithm) || ('MD5-sess' == $algorithm)) {
674 1
            $this->algorithm = $algorithm;
675
        } else {
676
            throw new \InvalidArgumentException('DigestAuthMiddleware: Only MD5 and MD5-sess algorithms are currently supported.');
677
        }
678 1
    }
679
680
    /**
681
     * Sets authentication method to be used. Options are "Digest" and "Basic".
682
     * If the server and the client are unable to authenticate using Digest then the RFCs state that the server should attempt
683
     * to authenticate the client using Basic authentication. This ensures that we adhere to that behaviour.
684
     * This does however create the possibilty of a downgrade attack so it may be an idea to add a way of disabling this functionality
685
     * as Basic authentication is trivial to decrypt and exposes the username/password to a man-in-the-middle attack.
686
     *
687
     * @param string $authenticationMethod the authentication method requested by the server
688
     *
689
     * @throws \InvalidArgumentException If $authenticationMethod is set to anything other than Digest or Basic
690
     */
691 1
    private function setAuthenticationMethod(string $authenticationMethod): void
692
    {
693 1
        if ('Digest' === $authenticationMethod || 'Basic' === $authenticationMethod) {
694 1
            $this->authenticationMethod = $authenticationMethod;
695
        } else {
696
            throw new \InvalidArgumentException('DigestAuthMiddleware: Only Digest and Basic authentication methods are currently supported.');
697
        }
698 1
    }
699
700
    /**
701
     * Sets the domain to be authenticated against. THIS IS NOT TO BE CONFUSED WITH THE HOSTNAME/DOMAIN.
702
     * This is specified by the RFC to be a list of uris separated by spaces that the client will be allowed to access.
703
     * An RFC in draft stage is proposing the removal of this functionality, it does not seem to be in widespread use.
704
     *
705
     * @param string $domain the list of uris separated by spaces that the client will be able to access upon successful authentication
706
     */
707 1
    private function setDomain(string $domain): void
708
    {
709 1
        $this->domain = $domain;
710 1
    }
711
712
    /**
713
     * Sets the Entity Body of the Request for use with qop=auth-int.
714
     *
715
     * @param string $entityBody the body of the entity (The unencoded request minus the headers)
716
     */
717 1
    private function setEntityBody(string $entityBody = null): void
718
    {
719 1
        $this->entityBody = $entityBody;
720 1
    }
721
722
    /**
723
     * Sets the HTTP method being used for the request.
724
     *
725
     * @param string $method The HTTP method
726
     *
727
     * @throws \InvalidArgumentException if $method is set to anything other than GET,POST,PUT,DELETE or HEAD
728
     */
729 1
    private function setMethod(string $method = null): void
730
    {
731 1
        if ('GET' == $method) {
732 1
            $this->method = 'GET';
733
734 1
            return;
735
        }
736
        if ('POST' == $method) {
737
            $this->method = 'POST';
738
739
            return;
740
        }
741
        if ('PUT' == $method) {
742
            $this->method = 'PUT';
743
744
            return;
745
        }
746
        if ('DELETE' == $method) {
747
            $this->method = 'DELETE';
748
749
            return;
750
        }
751
        if ('HEAD' == $method) {
752
            $this->method = 'HEAD';
753
754
            return;
755
        }
756
757
        throw new \InvalidArgumentException('DigestAuthMiddleware: Only GET,POST,PUT,DELETE,HEAD HTTP methods are currently supported.');
758
    }
759
760
    /**
761
     * Sets the value of nonce.
762
     *
763
     * @param string $nonce The server nonce value
764
     */
765 1
    private function setNonce(string $nonce = null): void
766
    {
767 1
        $this->nonce = $nonce;
768 1
    }
769
770
    /**
771
     * Sets the value of opaque.
772
     *
773
     * @param string $opaque The opaque value
774
     */
775
    private function setOpaque(string $opaque): void
776
    {
777
        $this->opaque = $opaque;
778
    }
779
780
    /**
781
     * Sets the acceptable value(s) for the quality of protection used by the server. Supported values are auth and auth-int.
782
     * TODO: This method should give precedence to using qop=auth-int first as this offers integrity protection.
783
     *
784
     * @param array $qop an array with the values of qop that the server has specified it will accept
785
     *
786
     * @throws \InvalidArgumentException if $qop contains any values other than auth-int or auth
787
     */
788 1
    private function setQOP(array $qop = []): void
789
    {
790 1
        $this->qop = [];
791 1
        foreach ($qop as $protection) {
792 1
            $protection = trim($protection);
793 1
            if ('auth-int' == $protection) {
794
                $this->qop[] = 'auth-int';
795 1
            } elseif ('auth' == $protection) {
796 1
                $this->qop[] = 'auth';
797
            } else {
798 1
                throw new \InvalidArgumentException('DigestAuthMiddleware: Only auth-int and auth are supported Quality of Protection mechanisms.');
799
            }
800
        }
801 1
    }
802
803
    /**
804
     * Sets the value of uri.
805
     *
806
     * @param string $uri The uri
807
     */
808 1
    private function setUri(string $uri = null): void
809
    {
810 1
        $this->uri = $uri;
811 1
    }
812
813
    /**
814
     * If a string contains quotation marks at either end this function will strip them. Otherwise it will remain unchanged.
815
     *
816
     * @param string $str the string to be stripped of quotation marks
817
     *
818
     * @return string returns the original string without the quotation marks at either end
819
     */
820 1
    private function unquoteString(string $str = null): ?string
821
    {
822 1
        if ($str) {
823 1
            if ('"' == substr($str, 0, 1)) {
824 1
                $str = substr($str, 1, strlen($str) - 1);
825
            }
826 1
            if ('"' == substr($str, strlen($str) - 1, 1)) {
827 1
                $str = substr($str, 0, strlen($str) - 1);
828
            }
829
        }
830
831 1
        return $str;
832
    }
833
}
834