Passed
Push — master ( 5d2f4a...4ba7a2 )
by Petr
07:39
created

AccountsSingleFile::getDataOnly()   A

Complexity

Conditions 5
Paths 10

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 13
c 1
b 0
f 0
nc 10
nop 1
dl 0
loc 22
ccs 12
cts 12
cp 1
crap 5
rs 9.5222
1
<?php
2
3
namespace kalanis\kw_auth_sources\Sources\Files;
4
5
6
use kalanis\kw_accounts\AccountsException;
7
use kalanis\kw_accounts\Data\FileUser;
8
use kalanis\kw_accounts\Interfaces as acc_interfaces;
9
use kalanis\kw_auth_sources\AuthSourcesException;
10
use kalanis\kw_auth_sources\Interfaces;
11
use kalanis\kw_auth_sources\Traits;
12
use kalanis\kw_locks\Interfaces\ILock;
13
use kalanis\kw_locks\LockException;
14
15
16
/**
17
 * Class AccountsSingleFile
18
 * @package kalanis\kw_auth_sources\Sources\Files
19
 * Authenticate via single file
20
 */
21
class AccountsSingleFile implements acc_interfaces\IAuth, acc_interfaces\IProcessAccounts
22
{
23
    use Traits\TAuthLock;
24
    use Traits\TLines;
25
    use Traits\TStatusTransform;
26
27
    const PW_ID = 0;
28
    const PW_NAME = 1;
29
    const PW_PASS = 2;
30
    const PW_GROUP = 3;
31
    const PW_CLASS = 4;
32
    const PW_STATUS = 5;
33
    const PW_DISPLAY = 6;
34
    const PW_DIR = 7;
35
    const PW_EXTRA = 8;
36
    const PW_FEED = 9;
37
38
    /** @var Storages\AStorage */
39
    protected $storage = null;
40
    /** @var Interfaces\IHashes */
41
    protected $mode = null;
42
    /** @var Interfaces\IStatus */
43
    protected $status = null;
44
    /** @var Interfaces\IExtraParser */
45
    protected $extraParser = null;
46
    /** @var string[] */
47
    protected $path = [];
48
49
    /**
50
     * @param Storages\AStorage $storage where is it stored and how to access there
51
     * @param Interfaces\IHashes $mode hashing mode
52
     * @param Interfaces\IStatus $status which status is necessary to use that feature
53
     * @param Interfaces\IExtraParser $parser parsing extra arguments from and to string
54
     * @param ILock $lock file lock
55
     * @param string[] $path use full path with file name
56
     * @param Interfaces\IKAusTranslations|null $lang
57
     */
58 27
    public function __construct(
59
        Storages\AStorage $storage,
60
        Interfaces\IHashes $mode,
61
        Interfaces\IStatus $status,
62
        Interfaces\IExtraParser $parser,
63
        ILock $lock,
64
        array $path,
65
        ?Interfaces\IKAusTranslations $lang = null
66
    )
67
    {
68 27
        $this->setAusLang($lang);
69 27
        $this->initAuthLock($lock);
70 27
        $this->storage = $storage;
71 27
        $this->mode = $mode;
72 27
        $this->status = $status;
73 27
        $this->path = $path;
74 27
        $this->extraParser = $parser;
75 27
    }
76
77 5
    public function authenticate(string $userName, array $params = []): ?acc_interfaces\IUser
78
    {
79 5
        if (empty($params['password'])) {
80 1
            throw new AccountsException($this->getAusLang()->kauPassMustBeSet());
81
        }
82 4
        $name = $this->stripChars($userName);
83 4
        $pass = strval($params['password']);
84
85
        try {
86 4
            $this->checkLock();
87
            try {
88 3
                $passLines = $this->openPassword();
89 1
            } catch (AuthSourcesException $ex) {
90
                // silence the problems on storage
91 1
                return null;
92
            }
93 2
            foreach ($passLines as &$line) {
94 2
                if ($line[static::PW_NAME] == $name) {
95
                    if (
96 2
                        $this->mode->checkHash($pass, strval($line[static::PW_PASS]))
97 2
                        && $this->status->allowLogin($this->transformFromStringToInt(strval($line[static::PW_STATUS])))
98
                    ) {
99 2
                        return $this->getUserClass($line);
100
                    }
101
                }
102
            }
103 2
            return null;
104
105 1
        } catch (AuthSourcesException | LockException $ex) {
106 1
            throw new AccountsException($ex->getMessage(), $ex->getCode(), $ex);
107
        }
108
    }
109
110 6
    public function getDataOnly(string $userName): ?acc_interfaces\IUser
111
    {
112 6
        $name = $this->stripChars($userName);
113
114
        // load from password
115
        try {
116 6
            $this->checkLock();
117
            try {
118 5
                $passwordLines = $this->openPassword();
119 2
            } catch (AuthSourcesException $ex) {
120
                // silence the problems on storage
121 2
                return null;
122
            }
123 3
            foreach ($passwordLines as &$line) {
124 3
                if ($line[static::PW_NAME] == $name) {
125 3
                    return $this->getUserClass($line);
126
                }
127
            }
128 2
            return null;
129
130 1
        } catch (AuthSourcesException | LockException $ex) {
131 1
            throw new AccountsException($ex->getMessage(), $ex->getCode(), $ex);
132
        }
133
    }
134
135
    /**
136
     * @param array<int, string|int|float> $line
137
     * @throws AuthSourcesException
138
     * @return acc_interfaces\IUser
139
     */
140 5
    protected function getUserClass(array &$line): acc_interfaces\IUser
141
    {
142 5
        $user = new FileUser();
143 5
        $user->setUserData(
144 5
            strval($line[static::PW_ID]),
145 5
            strval($line[static::PW_NAME]),
146 5
            strval($line[static::PW_GROUP]),
147 5
            intval($line[static::PW_CLASS]),
148 5
            $this->transformFromStringToInt(strval($line[static::PW_STATUS])),
149 5
            strval($line[static::PW_DISPLAY]),
150 5
            strval($line[static::PW_DIR]),
151 5
            $this->extraParser->expand(strval($line[static::PW_EXTRA]))
152
        );
153 5
        return $user;
154
    }
155
156 4
    public function createAccount(acc_interfaces\IUser $user, string $password): bool
157
    {
158 4
        $userName = $this->stripChars($user->getAuthName());
159 4
        $directory = $this->stripChars($user->getDir());
160 4
        $displayName = $this->stripChars($user->getDisplayName());
161
162
        // not everything necessary is set
163 4
        if (empty($userName) || empty($directory) || empty($password)) {
164 1
            throw new AccountsException($this->getAusLang()->kauPassMissParam());
165
        }
166
167
        try {
168 3
            $this->checkLock();
169
170 2
            $uid = acc_interfaces\IUser::LOWEST_USER_ID;
171 2
            $this->getLock()->create();
172
173
            // read password
174
            try {
175 2
                $passLines = $this->openPassword();
176 1
            } catch (AuthSourcesException $ex) {
177
                // silence the problems on storage
178 1
                $passLines = [];
179
            }
180 2
            foreach ($passLines as &$line) {
181 1
                $uid = max($uid, intval($line[static::PW_ID]));
182
            }
183 2
            $uid++;
184
185
            $newUserPass = [
186 2
                static::PW_ID => strval($uid),
187 2
                static::PW_NAME => $userName,
188 2
                static::PW_PASS => $this->mode->createHash($password),
189 2
                static::PW_GROUP => empty($user->getGroup()) ? $uid : $user->getGroup() ,
190 2
                static::PW_CLASS => empty($user->getClass()) ? acc_interfaces\IProcessClasses::CLASS_USER : strval($user->getClass()) ,
191 2
                static::PW_STATUS => $this->transformFromIntToString($user->getStatus()),
192 2
                static::PW_DISPLAY => empty($displayName) ? $userName : $displayName,
193 2
                static::PW_DIR => $directory,
194 2
                static::PW_EXTRA => $this->extraParser->compact($user->getExtra()),
195 2
                static::PW_FEED => '',
196
            ];
197 2
            ksort($newUserPass);
198 2
            $passLines[] = $newUserPass;
199
200
            // now save it
201
            try {
202 2
                $result = $this->savePassword($passLines);
203 2
            } finally {
204 2
                $this->getLock()->delete();
205
            }
206 2
            return $result;
207
208 1
        } catch (AuthSourcesException | LockException $ex) {
209 1
            throw new AccountsException($ex->getMessage(), $ex->getCode(), $ex);
210
        }
211
    }
212
213 2
    public function readAccounts(): array
214
    {
215
        try {
216 2
            $this->checkLock();
217
218 1
            $passLines = $this->openPassword();
219 1
            $result = [];
220 1
            foreach ($passLines as &$line) {
221 1
                $result[] = $this->getUserClass($line);
222
            }
223
224 1
            return $result;
225
226 1
        } catch (AuthSourcesException | LockException $ex) {
227 1
            throw new AccountsException($ex->getMessage(), $ex->getCode(), $ex);
228
        }
229
    }
230
231 3
    public function updateAccount(acc_interfaces\IUser $user): bool
232
    {
233 3
        $userName = $this->stripChars($user->getAuthName());
234 3
        $directory = $this->stripChars($user->getDir());
235 3
        $displayName = $this->stripChars($user->getDisplayName());
236
237
        try {
238 3
            $this->checkLock();
239
240 2
            $this->getLock()->create();
241
            try {
242 2
                $passwordLines = $this->openPassword();
243 1
            } finally {
244 2
                $this->getLock()->delete();
245
            }
246 1
            foreach ($passwordLines as &$line) {
247 1
                if ($line[static::PW_NAME] == $userName) {
248
                    // REFILL
249 1
                    $line[static::PW_GROUP] = !empty($user->getGroup()) ? $user->getGroup() : $line[static::PW_GROUP] ;
250 1
                    $line[static::PW_CLASS] = !empty($user->getClass()) ? strval($user->getClass()) : $line[static::PW_CLASS] ;
251 1
                    $line[static::PW_STATUS] = $this->transformFromIntToString($user->getStatus());
252 1
                    $line[static::PW_DISPLAY] = !empty($displayName) ? $displayName : $line[static::PW_DISPLAY] ;
253 1
                    $line[static::PW_DIR] = !empty($directory) ? $directory : $line[static::PW_DIR] ;
254 1
                    $line[static::PW_EXTRA] = !empty($user->getExtra()) ? $this->extraParser->compact($user->getExtra()) : $line[static::PW_EXTRA] ;
255
                }
256
            }
257
258
            try {
259 1
                $result = $this->savePassword($passwordLines);
260 1
            } finally {
261 1
                $this->getLock()->delete();
262
            }
263 1
            return $result;
264
265 2
        } catch (AuthSourcesException | LockException $ex) {
266 2
            throw new AccountsException($ex->getMessage(), $ex->getCode(), $ex);
267
        }
268
    }
269
270 3
    public function updatePassword(string $userName, string $passWord): bool
271
    {
272 3
        $name = $this->stripChars($userName);
273
        // load from shadow
274
        try {
275 3
            $this->checkLock();
276
277 2
            $changed = false;
278 2
            $this->getLock()->create();
279
280
            try {
281 2
                $lines = $this->openPassword();
282 1
            } finally {
283 2
                $this->getLock()->delete();
284
            }
285 1
            foreach ($lines as &$line) {
286 1
                if ($line[static::PW_NAME] == $name) {
287 1
                    $changed = true;
288 1
                    $line[static::PW_PASS] = $this->mode->createHash($passWord);
289
                }
290
            }
291
            try {
292 1
                if ($changed) {
293 1
                    $this->savePassword($lines);
294
                }
295 1
            } finally {
296 1
                $this->getLock()->delete();
297
            }
298 1
            return true;
299
300 2
        } catch (AuthSourcesException | LockException $ex) {
301 2
            throw new AccountsException($ex->getMessage(), $ex->getCode(), $ex);
302
        }
303
    }
304
305 3
    public function deleteAccount(string $userName): bool
306
    {
307 3
        $name = $this->stripChars($userName);
308
309
        try {
310 3
            $this->checkLock();
311
312 2
            $changed = false;
313 2
            $this->getLock()->create();
314
315
            // update password
316
            try {
317 2
                $passLines = $this->openPassword();
318 1
            } catch (AuthSourcesException $ex) {
319
                // removal on non-existent file is not possible and not necessary
320 1
                $this->getLock()->delete();
321 1
                return true;
322
            }
323
324 1
            foreach ($passLines as $index => &$line) {
325 1
                if ($line[static::PW_NAME] == $name) {
326 1
                    unset($passLines[$index]);
327 1
                    $changed = true;
328
                }
329
            }
330
331
            // now save it all
332
            try {
333 1
                if ($changed) {
334 1
                    $this->savePassword($passLines);
335
                }
336 1
            } finally {
337 1
                $this->getLock()->delete();
338
            }
339 1
            return true;
340
341 1
        } catch (AuthSourcesException | LockException $ex) {
342 1
            throw new AccountsException($ex->getMessage(), $ex->getCode(), $ex);
343
        }
344
    }
345
346
    /**
347
     * @throws AuthSourcesException
348
     * @return array<int, array<int, string|int>>
349
     */
350 9
    protected function openPassword(): array
351
    {
352 9
        return $this->storage->read($this->path);
353
    }
354
355
    /**
356
     * @param array<int, array<int, string|int>> $lines
357
     * @throws AuthSourcesException
358
     * @return bool
359
     */
360 2
    protected function savePassword(array $lines): bool
361
    {
362 2
        return $this->storage->write($this->path, $lines);
363
    }
364
365
    /**
366
     * @return string
367
     * @codeCoverageIgnore translation
368
     */
369
    protected function noDirectoryDelimiterSet(): string
370
    {
371
        return $this->getAusLang()->kauNoDelimiterSet();
372
    }
373
}
374