Passed
Push — master ( f1fb72...be1df6 )
by Petr
08:10
created

AccountsSingleFile::noDirectoryDelimiterSet()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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