Issues (38)

src/Config/UserFileReader.php (3 issues)

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
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
     * @var string
23
     */
24
    private string $compareRegex = '/^env\(.*\)$/';
25
26
    /**
27
     * @var string
28
     */
29
    private string $captureRegex = '/^env\((.*)\)$/';
30
31
    /**
32
     * The constructor
33
     *
34
     * @param AuthInterface $auth
35
     * @param string $configFile
36
     */
37
    public function __construct(private AuthInterface $auth, private string $configFile)
38
    {}
39
40
    /**
41
     * @param string $value
42
     *
43
     * @return bool
44
     */
45
    private function callsEnvVar(string $value): bool
46
    {
47
        return preg_match($this->compareRegex, $value) !== false;
48
    }
49
50
    /**
51
     * @param array $server
52
     *
53
     * @return bool
54
     */
55
    private function checkPortNumber(array $server): bool
56
    {
57
        if (!isset($server['port']) || is_numeric($server['port'])) {
58
            return true;
59
        }
60
        if (!is_string($server['port'])) {
61
            return false;
62
        }
63
        return $this->callsEnvVar($server['port']);
64
    }
65
66
    /**
67
     * @param array $server
68
     *
69
     * @return bool
70
     */
71
    private function checkServer(array $server): bool
72
    {
73
        if (!isset($server['name']) || !isset($server['driver']) ||
74
            !is_string($server['name']) || !is_string($server['driver'])) {
75
            return false;
76
        }
77
        if ($server['driver'] === 'sqlite') {
78
            return isset($server['directory']) && is_string($server['directory']);
79
        }
80
        return isset($server['username']) && isset($server['password']) &&
81
            isset($server['host']) && is_string($server['username']) &&
82
            is_string($server['password']) && is_string($server['host']) &&
83
            $this->checkPortNumber($server);
84
    }
85
86
    /**
87
     * @param array $options
88
     *
89
     * @return bool
90
     */
91
    private function checkUser(array $options): bool
92
    {
93
        $user = $options['id']['user'] ?? null;
94
        return is_string($user) && $this->auth->user() === $user;
95
    }
96
97
    /**
98
     * @param array $options
99
     *
100
     * @return bool
101
     */
102
    private function checkUsers(array $options): bool
103
    {
104
        $users = $options['id']['users'] ?? null;
105
        return is_array($users) && in_array($this->auth->user(), $users);
106
    }
107
108
    /**
109
     * @param array $options
110
     *
111
     * @return bool
112
     */
113
    private function checkRole(array $options): bool
114
    {
115
        $role = $options['id']['role'] ?? null;
116
        return is_string($role) && $this->auth->role() === $role;
117
    }
118
119
    /**
120
     * @param array $options
121
     *
122
     * @return bool
123
     */
124
    private function checkRoles(array $options): bool
125
    {
126
        $roles = $options['id']['roles'] ?? null;
127
        return is_array($roles) && in_array($this->auth->role(), $roles);
128
    }
129
130
    /**
131
     * @param array $options
132
     *
133
     * @return bool
134
     */
135
    private function userMatches(array $options): bool
136
    {
137
        return $this->checkUser($options) || $this->checkUsers($options) ||
138
            $this->checkRole($options) || $this->checkRoles($options);
139
    }
140
141
    /**
142
     * @param string $value
143
     *
144
     * @return mixed
145
     */
146
    private function getOptionValue(string $value): mixed
147
    {
148
        // We need to capture the matching string.
149
        $match = preg_match($this->captureRegex, $value, $matches);
150
        return $match === false || !isset($matches[1]) ? $value : env($matches[1]);
0 ignored issues
show
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

150
        return $match === false || !isset($matches[1]) ? $value : /** @scrutinizer ignore-call */ env($matches[1]);
Loading history...
151
    }
152
153
    /**
154
     * @param array $server
155
     *
156
     * @return array
157
     */
158
    private function getSqliteOptions(array $server): array
159
    {
160
        $server['directory'] = $this->getOptionValue($server['directory']);
161
        return $server;
162
    }
163
164
    /**
165
     * @param array $server
166
     *
167
     * @return array
168
     */
169
    private function getServerOptions(array $server): array
170
    {
171
        $server['host'] = $this->getOptionValue($server['host']);
172
        $server['username'] = $this->getOptionValue($server['username']);
173
        $server['password'] = $this->getOptionValue($server['password']);
174
        if (isset($server['port']) && is_string($server['port'])) {
175
            $server['port'] = $this->getOptionValue($server['port']);
176
        }
177
        return $server;
178
    }
179
180
    /**
181
     * Replace options with values from the .env config.
182
     *
183
     * @param array $values
184
     *
185
     * @return array
186
     */
187
    private function getOptionValues(array $values): array
188
    {
189
        // Callback to filter the servers list on valid entries.
190
        $check = fn(array $server) => $this->checkServer($server);
191
        // Callback to get the server options final values.
192
        $convert = fn(array $server) => $server['driver'] === 'sqlite' ?
193
            $this->getSqliteOptions($server) : $this->getServerOptions($server);
194
        $values['servers'] = array_map($convert,
195
            array_filter($values['servers'] ?? [], $check));
196
197
        return $values;
198
    }
199
200
    /**
201
     * Get the options for the authenticated user.
202
     *
203
     * @param array $defaultOptions
204
     *
205
     * @return array
206
     */
207
    public function getOptions(array $defaultOptions): array
208
    {
209
        // If the config file doesn't exists, return an empty array.
210
        if (!is_file($this->configFile)) {
211
            return [];
212
        }
213
214
        // The key to use for the user options
215
        $userKey = 'user';
216
        // Remove the provider field.
217
        unset($defaultOptions['provider']);
218
219
        $setter = new ConfigSetter();
220
        $reader = new ConfigReader($setter);
221
        $userConfig = $setter->newConfig([$userKey => $defaultOptions]);
222
223
        $config = $reader->load($setter->newConfig(), $this->configFile);
224
        $commonOptions = $config->getOption('common', null);
225
        if (is_array($commonOptions)) {
226
            $userConfig = $setter->setOptions($userConfig, $commonOptions, $userKey);
227
        }
228
229
        $fallbackOptions = $config->getOption('fallback', null);
230
231
        $userList = $config->getOption('users', []);
232
        $userList = array_values(array_filter($userList,
233
            fn($options) => $this->userMatches($options)));
234
        $userOptions = $userList[0] ?? $fallbackOptions;
235
236
        if (!is_array($userOptions)) {
237
            return $this->getOptionValues($userConfig->getOption($userKey));
0 ignored issues
show
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

237
            return $this->getOptionValues(/** @scrutinizer ignore-type */ $userConfig->getOption($userKey));
Loading history...
238
        }
239
240
        unset($userOptions['id']); // Remove the id field.
241
        $userConfig = $setter->setOptions($userConfig, $userOptions, $userKey);
242
243
        return $this->getOptionValues($userConfig->getOption($userKey));
244
    }
245
}
246