Completed
Push — master ( 99c4e8...3c79d6 )
by Alexander
02:19
created

HttpBasicAuth::challenge()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 3
cp 0
crap 2
rs 10
1
<?php
2
namespace Yiisoft\Yii\Web\Auth;
3
4
use Psr\Http\Message\ResponseInterface;
5
use Psr\Http\Message\ServerRequestInterface;
6
use Yiisoft\Yii\Web\User\IdentityInterface;
7
use Yiisoft\Yii\Web\User\IdentityRepositoryInterface;
8
9
/**
10
 * HttpBasicAuth supports the HTTP Basic authentication method.
11
 *
12
 * > Tip: In case authentication does not work like expected, make sure your web server passes
13
 * username and password to `$request->getServerParams()['PHP_AUTH_USER']` and `$request->getServerParams()['PHP_AUTH_PW']` variables.
14
 * If you are using Apache with PHP-CGI, you might need to add this line to your `.htaccess` file:
15
 * ```
16
 * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
17
 * ```
18
 */
19
final class HttpBasicAuth implements AuthInterface
20
{
21
    /**
22
     * @var string the HTTP authentication realm
23
     */
24
    private $realm = 'api';
25
    /**
26
     * @var callable a PHP callable that will authenticate the user with the HTTP basic auth information.
27
     * The callable receives a username and a password as its parameters. It should return an identity object
28
     * that matches the username and password. Null should be returned if there is no such identity.
29
     * The callable will be called only if current user is not authenticated.
30
     *
31
     * If this property is not set, the username information will be considered as an access token
32
     * while the password information will be ignored. The {@see \Yiisoft\Yii\Web\User\IdentityRepositoryInterface::findIdentityByToken()}
33
     * method will be called to authenticate and login the user.
34
     */
35
    private $auth;
36
    /**
37
     * @var IdentityRepositoryInterface
38
     */
39
    private $identityRepository;
40
41
    public function __construct(IdentityRepositoryInterface $identityRepository)
42
    {
43
        $this->identityRepository = $identityRepository;
44
    }
45
46
    public function authenticate(ServerRequestInterface $request): ?IdentityInterface
47
    {
48
        [$username, $password] = $this->getAuthCredentials($request);
49
50
        if ($this->auth) {
51
            if ($username !== null || $password !== null) {
52
                $identity = \call_user_func($this->auth, $username, $password);
53
54
                return $identity;
55
            }
56
        } elseif ($username !== null) {
57
            $identity = $this->identityRepository->findIdentityByToken($username, get_class($this));
58
59
            return $identity;
60
        }
61
62
        return null;
63
    }
64
65
    public function challenge(ResponseInterface $response): ResponseInterface
66
    {
67
        return $response->withHeader('WWW-Authenticate', "Basic realm=\"{$this->realm}\"");
68
    }
69
70
    public function setAuth(callable $auth): void
71
    {
72
        $this->auth = $auth;
73
    }
74
75
    public function setRealm(string $realm): void
76
    {
77
        $this->realm = $realm;
78
    }
79
80
    private function getAuthCredentials(ServerRequestInterface $request): array
81
    {
82
        $username = $request->getServerParams()['PHP_AUTH_USER'] ?? null;
83
        $password = $request->getServerParams()['PHP_AUTH_PW'] ?? null;
84
        if ($username !== null || $password !== null) {
85
            return [$username, $password];
86
        }
87
88
        /*
89
         * Apache with php-cgi does not pass HTTP Basic authentication to PHP by default.
90
         * To make it work, add the following line to to your .htaccess file:
91
         *
92
         * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
93
         */
94
        $headers = $request->getHeader('Authorization');
95
        $authToken = !empty($headers)
96
            ? \reset($headers)
97
            : $request->getServerParams()['REDIRECT_HTTP_AUTHORIZATION'] ?? null;
98
        if ($authToken !== null && strncasecmp($authToken, 'basic', 5) === 0) {
99
            $parts = array_map(static function ($value) {
100
                return strlen($value) === 0 ? null : $value;
101
            }, explode(':', base64_decode(mb_substr($authToken, 6)), 2));
102
103
            if (\count($parts) < 2) {
104
                return [$parts[0], null];
105
            }
106
107
            return $parts;
108
        }
109
110
        return [null, null];
111
    }
112
}
113