Passed
Push — main ( db6621...0d66ce )
by Thierry
02:23
created

UserFileReader::checkUser()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 2
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 4
rs 10
1
<?php
2
3
namespace Lagdo\DbAdmin\Config;
4
5
use Jaxon\Config\ConfigReader;
6
use Jaxon\Config\ConfigSetter;
7
8
use function array_map;
9
use function array_filter;
10
use function array_values;
11
use function env;
0 ignored issues
show
introduced by
The function env was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
12
use function in_array;
13
use function is_array;
14
use function is_file;
15
use function is_numeric;
16
use function is_string;
17
use function preg_match;
18
19
class UserFileReader
20
{
21
    /**
22
     * The constructor
23
     *
24
     * @param AuthInterface $auth
25
     * @param string $configFile
26
     */
27
    public function __construct(private AuthInterface $auth, private string $configFile)
28
    {}
29
30
    /**
31
     * @param string $value
32
     *
33
     * @return bool
34
     */
35
    private function callsEnvVar(string $value): bool
36
    {
37
        return preg_match('/^env\(.*\)$/', $value) !== false;
38
    }
39
40
    /**
41
     * @param array $server
42
     *
43
     * @return bool
44
     */
45
    private function checkPortNumber(array $server): bool
46
    {
47
        if (!isset($server['port']) || is_numeric($server['port'])) {
48
            return true;
49
        }
50
        if (!is_string($server['port'])) {
51
            return false;
52
        }
53
        return $this->callsEnvVar($server['port']);
54
    }
55
56
    /**
57
     * @param array $server
58
     *
59
     * @return bool
60
     */
61
    private function checkServer(array $server): bool
62
    {
63
        if (!isset($server['name']) || !isset($server['driver']) ||
64
            !is_string($server['name']) || !is_string($server['driver'])) {
65
            return false;
66
        }
67
        if ($server['driver'] === 'sqlite') {
68
            return isset($server['directory']) && is_string($server['directory']);
69
        }
70
        return isset($server['username']) && isset($server['password']) &&
71
            isset($server['host']) && is_string($server['username']) &&
72
            is_string($server['password']) && is_string($server['host']) &&
73
            $this->checkPortNumber($server);
74
    }
75
76
    /**
77
     * @param array $options
78
     *
79
     * @return bool
80
     */
81
    private function checkUser(array $options): bool
82
    {
83
        $user = $options['id']['user'] ?? null;
84
        return is_string($user) && $this->auth->user() === $user;
85
    }
86
87
    /**
88
     * @param array $options
89
     *
90
     * @return bool
91
     */
92
    private function checkUsers(array $options): bool
93
    {
94
        $users = $options['id']['users'] ?? null;
95
        return is_array($users) && in_array($this->auth->user(), $users);
96
    }
97
98
    /**
99
     * @param array $options
100
     *
101
     * @return bool
102
     */
103
    private function checkRole(array $options): bool
104
    {
105
        $role = $options['id']['role'] ?? null;
106
        return is_string($role) && $this->auth->role() === $role;
107
    }
108
109
    /**
110
     * @param array $options
111
     *
112
     * @return bool
113
     */
114
    private function checkRoles(array $options): bool
115
    {
116
        $roles = $options['id']['roles'] ?? null;
117
        return is_array($roles) && in_array($this->auth->role(), $roles);
118
    }
119
120
    /**
121
     * @param array $options
122
     *
123
     * @return bool
124
     */
125
    private function userMatches(array $options): bool
126
    {
127
        return $this->checkUser($options) || $this->checkUsers($options) ||
128
            $this->checkRole($options) || $this->checkRoles($options);
129
    }
130
131
    /**
132
     * @param string $value
133
     *
134
     * @return mixed
135
     */
136
    private function getOptionValue(string $value): mixed
137
    {
138
        // The regex here also captures the matching string.
139
        $match = preg_match('/^env\((.*)\)$/', $value, $matches);
140
        return $match === false || !isset($matches[1]) ? $value : env($matches[1]);
0 ignored issues
show
Bug introduced by
The function env was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

140
        return $match === false || !isset($matches[1]) ? $value : /** @scrutinizer ignore-call */ env($matches[1]);
Loading history...
141
    }
142
143
    /**
144
     * Replace options with values from the .env config.
145
     *
146
     * @param array $values
147
     *
148
     * @return array
149
     */
150
    private function getOptionValues(array $values): array
151
    {
152
        // Filter the servers list on valid entries
153
        $values['servers'] = array_filter($values['servers'] ?? [],
154
            fn(array $server) => $this->checkServer($server));
155
        // The values in the server options are the names of the
156
        // corresponding options in the .env file.
157
        $values['servers'] = array_map(function(array $server) {
158
            if ($server['driver'] === 'sqlite') {
159
                return $server;
160
            }
161
162
            $server['host'] = $this->getOptionValue($server['host']);
163
            $server['username'] = $this->getOptionValue($server['username']);
164
            $server['password'] = $this->getOptionValue($server['password']);
165
            if (isset($server['port']) && is_string($server['port'])) {
166
                $server['port'] = $this->getOptionValue($server['port']);
167
            }
168
            return $server;
169
        }, $values['servers'] ?? []);
170
171
        return $values;
172
    }
173
174
    /**
175
     * Get the options for the authenticated user.
176
     *
177
     * @param array $defaultOptions
178
     *
179
     * @return array
180
     */
181
    public function getOptions(array $defaultOptions): array
182
    {
183
        // If the config file doesn't exists, return an empty array.
184
        if (!is_file($this->configFile)) {
185
            return [];
186
        }
187
188
        // The key to use for the user options
189
        $userKey = 'user';
190
        // Remove the provider field.
191
        unset($defaultOptions['provider']);
192
193
        $setter = new ConfigSetter();
194
        $reader = new ConfigReader($setter);
195
        $userConfig = $setter->newConfig([$userKey => $defaultOptions]);
196
197
        $config = $reader->load($setter->newConfig(), $this->configFile);
198
        $commonOptions = $config->getOption('common', null);
199
        if (is_array($commonOptions)) {
200
            $userConfig = $setter->setOptions($userConfig, $commonOptions, $userKey);
201
        }
202
203
        $fallbackOptions = $config->getOption('fallback', null);
204
205
        $userList = $config->getOption('users', []);
206
        $userList = array_values(array_filter($userList,
207
            fn($options) => $this->userMatches($options)));
208
        $userOptions = $userList[0] ?? $fallbackOptions;
209
210
        if (!is_array($userOptions)) {
211
            return $this->getOptionValues($userConfig->getOption($userKey));
0 ignored issues
show
Bug introduced by
It seems like $userConfig->getOption($userKey) can also be of type null; however, parameter $values of Lagdo\DbAdmin\Config\Use...ader::getOptionValues() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

211
            return $this->getOptionValues(/** @scrutinizer ignore-type */ $userConfig->getOption($userKey));
Loading history...
212
        }
213
214
        unset($userOptions['id']); // Remove the id field.
215
        $userConfig = $setter->setOptions($userConfig, $userOptions, $userKey);
216
217
        return $this->getOptionValues($userConfig->getOption($userKey));
218
    }
219
}
220