Completed
Pull Request — master (#116)
by Rustam
01:53
created

HttpBasicAuth::authenticate()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 9
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 17
ccs 0
cts 13
cp 0
crap 30
rs 9.6111
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 is an action filter that 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 `$_SERVER['PHP_AUTH_USER']` and `$_SERVER['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
class HttpBasicAuth implements AuthInterface
20
{
21
    /**
22
     * @var string the HTTP authentication realm
23
     */
24
    public $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
     * The following code is a typical implementation of this callable:
32
     *
33
     * ```php
34
     * function ($username, $password) {
35
     *     return \app\models\User::findOne([
36
     *         'username' => $username,
37
     *         'password' => $password,
38
     *     ]);
39
     * }
40
     * ```
41
     *
42
     * If this property is not set, the username information will be considered as an access token
43
     * while the password information will be ignored. The [[\yii\web\User::loginByAccessToken()]]
44
     * method will be called to authenticate and login the user.
45
     */
46
    public $auth;
47
    private $identityRepository;
48
49
    public function __construct(IdentityRepositoryInterface $identityRepository)
50
    {
51
        $this->identityRepository = $identityRepository;
52
    }
53
54
    /**
55
     * {@inheritdoc}
56
     */
57
    public function authenticate(ServerRequestInterface $request): ?IdentityInterface
58
    {
59
        [$username, $password] = $this->getAuthCredentials($request);
60
61
        if ($this->auth) {
62
            if ($username !== null || $password !== null) {
63
                $identity = call_user_func($this->auth, $username, $password);
64
65
                return $identity;
66
            }
67
        } elseif ($username !== null) {
68
            $identity = $this->identityRepository->findIdentityByToken($username, get_class($this));
69
70
            return $identity;
71
        }
72
73
        return null;
74
    }
75
76
    /**
77
     * {@inheritdoc}
78
     */
79
    public function challenge(ResponseInterface $response): ResponseInterface
80
    {
81
        return $response->withHeader('WWW-Authenticate', "Basic realm=\"{$this->realm}\"");
82
    }
83
84
    private function getAuthCredentials(ServerRequestInterface $request)
85
    {
86
        $username = $_SERVER['PHP_AUTH_USER'] ?? null;
87
        $password = $_SERVER['PHP_AUTH_PW'] ?? null;
88
        if ($username !== null || $password !== null) {
89
            return [$username, $password];
90
        }
91
92
        /*
93
         * Apache with php-cgi does not pass HTTP Basic authentication to PHP by default.
94
         * To make it work, add the following line to to your .htaccess file:
95
         *
96
         * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
97
         */
98
        $headers = $request->getHeader('Authorization');
99
        $authToken = !empty($headers) ? \reset($headers) : $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
100
        if ($authToken !== null && strncasecmp($authToken, 'basic', 5) === 0) {
101
            $parts = array_map(static function ($value) {
102
                return strlen($value) === 0 ? null : $value;
103
            }, explode(':', base64_decode(mb_substr($authToken, 6)), 2));
104
105
            if (\count($parts) < 2) {
106
                return [$parts[0], null];
107
            }
108
109
            return $parts;
110
        }
111
112
        return [null, null];
113
    }
114
}
115