Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
58 / 58
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
UserAuth
100.00% covered (success)
100.00%
58 / 58
100.00% covered (success)
100.00%
6 / 6
22
100.00% covered (success)
100.00%
1 / 1
 getVerifiedUseraccount
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 testPasswordMatching
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
3
 getUseraccountByAuthMethod
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
5
 testUseraccountExists
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 getBasicAuth
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
6
 getXAuthKey
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2
3namespace BO\Zmsapi\Helper;
4
5use BO\Slim\Render;
6use BO\Zmsdb\Useraccount;
7
8class UserAuth
9{
10     /**
11     * Get existing useraccount entity with verified password hash
12     *
13     * @return array $useraccount
14    */
15    public static function getVerifiedUseraccount($entity)
16    {
17        $useraccountQuery = new Useraccount();
18        $useraccount = $useraccountQuery->readEntity($entity->getId());
19        // TODO: Remove the password fields when password authentication is removed in the future
20        $originalPassword = $useraccount->password;
21        $useraccount = $useraccount->withVerifiedHash($entity->password);
22        // Only write if password was actually updated (rehashing needed) for system users
23        if ($useraccount->password !== $originalPassword) {
24            $useraccount = $useraccountQuery->writeUpdatedEntity($useraccount->getId(), $useraccount);
25        }
26        return $useraccount;
27    }
28
29    public static function testPasswordMatching($useraccount, $password)
30    {
31        // Do you have old, turbo-legacy, non-crypt hashes?
32        // TODO: Remove the password fields when password authentication is removed in the future
33        $result = (strpos($useraccount->password, '$') !== 0) ?
34            ($useraccount->password === md5($password)) :
35            password_verify($password, $useraccount->password);
36        if (! $result) {
37            $exception = new \BO\Zmsapi\Exception\Useraccount\InvalidCredentials();
38            $exception->data['password']['messages'] = [
39                'Der Nutzername und das Passwort passen nicht zusammen'
40            ];
41            throw $exception;
42        }
43        return true;
44    }
45
46
47    /**
48     * Get useraccount entity by http basic auth or XAuthKey
49     *
50     * @return array $useraccount
51    */
52    public static function getUseraccountByAuthMethod($request)
53    {
54        $useraccount = null;
55        $basicAuth = static::getBasicAuth($request);
56        $xAuthKey = static::getXAuthKey($request);
57        $useraccountQuery = new Useraccount();
58
59        // TODO: Remove the password fields when password authentication is removed in the future
60        if ($basicAuth && static::testUseraccountExists($basicAuth['username'])) {
61            $useraccount = $useraccountQuery->readEntity($basicAuth['username']);
62            $originalPassword = $useraccount->password;
63            $useraccount = $useraccount->withVerifiedHash($basicAuth['password']);
64            static::testPasswordMatching($useraccount, $basicAuth['password']);
65            // Only write if password was actually updated (rehashing needed) for system users
66            if ($useraccount->password !== $originalPassword) {
67                $useraccount = $useraccountQuery->writeUpdatedEntity($useraccount->getId(), $useraccount);
68            }
69        } elseif ($xAuthKey) {
70            $useraccount = $useraccountQuery->readEntityByAuthKey($xAuthKey);
71        }
72
73        return $useraccount;
74    }
75
76    /**
77     * Test if useraccount exists in db
78     *
79     * @return exception $exception
80    */
81    public static function testUseraccountExists($loginName, $password = false)
82    {
83        $query = new Useraccount();
84        if (! $query->readIsUserExisting($loginName, $password)) {
85            $exception = new \BO\Zmsapi\Exception\Useraccount\InvalidCredentials();
86            $exception->data['password']['messages'] = [
87                'Der Nutzername oder das Passwort wurden falsch eingegeben'
88            ];
89            throw $exception;
90        }
91        return true;
92    }
93
94    /**
95     * Get Basic Authorization header content.
96     *
97     * @return array $authorization
98     */
99    private static function getBasicAuth($request)
100    {
101        $header = $request->getHeaderLine('Authorization');
102        if (strpos($header, 'Basic') !== 0) {
103            return false;
104        }
105        $header = explode(':', base64_decode(substr($header, 6)), 2);
106        $authorization = [
107            'username' => $header[0],
108            'password' => isset($header[1]) ? $header[1] : null,
109        ];
110        $userInfo = explode(':', $request->getUri()->getUserInfo());
111        $userInfo = [
112            'username' => $userInfo[0],
113            'password' => isset($userInfo[1]) ? $userInfo[1] : null
114        ];
115        return (! $authorization || $authorization['password'] !== $userInfo['password']) ? false : $authorization;
116    }
117
118    /**
119     * Get XAuthKey from header
120     * Falls back to X-AuthKey cookie if header is not present
121     *
122     * @return array $useraccount
123    */
124    private static function getXAuthKey($request)
125    {
126        $xAuthKey = $request->getHeaderLine('X-AuthKey');
127        if (! $xAuthKey) {
128            $cookies = $request->getCookieParams();
129            $xAuthKey = (array_key_exists('X-AuthKey', $cookies)) ? $cookies['X-AuthKey'] : null;
130        }
131        return ($xAuthKey) ? $xAuthKey : false;
132    }
133}