Passed
Push — master ( d309df...7ec489 )
by Petr
02:37
created

AFile   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 256
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 143
c 0
b 0
f 0
dl 0
loc 256
ccs 123
cts 123
cp 1
rs 9.1199
wmc 41

9 Methods

Rating   Name   Duplication   Size   Complexity  
A getDataOnly() 0 18 4
A readAccounts() 0 11 2
A deleteAccount() 0 31 5
B createAccount() 0 45 9
A updatePassword() 0 27 5
A getUserClass() 0 12 1
A __construct() 0 6 1
B updateAccount() 0 29 8
A authenticate() 0 23 6

How to fix   Complexity   

Complex Class

Complex classes like AFile often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AFile, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace kalanis\kw_auth\Sources\Files;
4
5
6
use kalanis\kw_auth\AuthException;
7
use kalanis\kw_auth\Data\FileUser;
8
use kalanis\kw_auth\Interfaces;
9
use kalanis\kw_auth\Sources\TAuthLock;
10
use kalanis\kw_locks\Interfaces\ILock;
11
use kalanis\kw_locks\LockException;
12
13
14
/**
15
 * Class AFile
16
 * @package kalanis\kw_auth\Sources\Files
17
 * Authenticate via single file
18
 */
19
abstract class AFile implements Interfaces\IAuth, Interfaces\IAccessAccounts
20
{
21
    use TAuthLock;
22
    use TLines;
23
    use TStore;
24
25
    const PW_ID = 0;
26
    const PW_NAME = 1;
27
    const PW_PASS = 2;
28
    const PW_GROUP = 3;
29
    const PW_CLASS = 4;
30
    const PW_DISPLAY = 5;
31
    const PW_DIR = 6;
32
    const PW_FEED = 7;
33
34
    /** @var Interfaces\IMode */
35
    protected $mode = null;
36
    /** @var string */
37
    protected $path = '';
38
39
    /**
40
     * @param Interfaces\IMode $mode hashing mode
41
     * @param ILock $lock file lock
42
     * @param string $path use full path with file name
43
     * @param Interfaces\IKATranslations|null $lang
44
     */
45 17
    public function __construct(Interfaces\IMode $mode, ILock $lock, string $path, ?Interfaces\IKATranslations $lang = null)
46
    {
47 17
        $this->setLang($lang);
48 17
        $this->mode = $mode;
49 17
        $this->path = $path;
50 17
        $this->initAuthLock($lock);
51
    }
52
53 7
    public function authenticate(string $userName, array $params = []): ?Interfaces\IUser
54
    {
55 7
        if (empty($params['password'])) {
56 2
            throw new AuthException($this->getLang()->kauPassMustBeSet());
57
        }
58 5
        $name = $this->stripChars($userName);
59 5
        $pass = strval($params['password']);
60
61 5
        $this->checkLock();
62
        try {
63 5
            $passLines = $this->openFile($this->path);
64 1
        } catch (AuthException $ex) {
65
            // silence the problems on storage
66 1
            return null;
67
        }
68 4
        foreach ($passLines as &$line) {
69 4
            if ($line[static::PW_NAME] == $name) {
70 4
                if ($this->mode->check($pass, strval($line[static::PW_PASS]))) {
71 4
                    return $this->getUserClass($line);
72
                }
73
            }
74
        }
75 4
        return null;
76
    }
77
78 7
    public function getDataOnly(string $userName): ?Interfaces\IUser
79
    {
80 7
        $name = $this->stripChars($userName);
81
82
        // load from password
83 7
        $this->checkLock();
84
        try {
85 7
            $passwordLines = $this->openFile($this->path);
86 2
        } catch (AuthException $ex) {
87
            // silence the problems on storage
88 2
            return null;
89
        }
90 5
        foreach ($passwordLines as &$line) {
91 5
            if ($line[static::PW_NAME] == $name) {
92 5
                return $this->getUserClass($line);
93
            }
94
        }
95 4
        return null;
96
    }
97
98
    /**
99
     * @param array<int, string|int|float> $line
100
     * @return Interfaces\IUser
101
     */
102 9
    protected function getUserClass(array &$line): Interfaces\IUser
103
    {
104 9
        $user = new FileUser();
105 9
        $user->setData(
106 9
            intval($line[static::PW_ID]),
107 9
            strval($line[static::PW_NAME]),
108 9
            intval($line[static::PW_GROUP]),
109 9
            intval($line[static::PW_CLASS]),
110 9
            strval($line[static::PW_DISPLAY]),
111 9
            strval($line[static::PW_DIR])
112
        );
113 9
        return $user;
114
    }
115
116 5
    public function createAccount(Interfaces\IUser $user, string $password): void
117
    {
118 5
        $userName = $this->stripChars($user->getAuthName());
119 5
        $directory = $this->stripChars($user->getDir());
120 5
        $displayName = $this->stripChars($user->getDisplayName());
121
122
        // not everything necessary is set
123 5
        if (empty($userName) || empty($directory) || empty($password)) {
124 2
            throw new AuthException($this->getLang()->kauPassMissParam());
125
        }
126 3
        $this->checkLock();
127
128 3
        $uid = Interfaces\IUser::LOWEST_USER_ID;
129 3
        $this->getLock()->create();
130
131
        // read password
132
        try {
133 3
            $passLines = $this->openFile($this->path);
134 1
        } catch (AuthException $ex) {
135
            // silence the problems on storage
136 1
            $passLines = [];
137
        }
138 3
        foreach ($passLines as &$line) {
139 2
            $uid = max($uid, intval($line[static::PW_ID]));
140
        }
141 3
        $uid++;
142
143
        $newUserPass = [
144
            static::PW_ID => $uid,
145
            static::PW_NAME => $userName,
146 3
            static::PW_PASS => $this->mode->hash($password),
147 3
            static::PW_GROUP => empty($user->getGroup()) ? $uid : $user->getClass() ,
148 3
            static::PW_CLASS => empty($user->getClass()) ? Interfaces\IAccessClasses::CLASS_USER : $user->getClass() ,
149 3
            static::PW_DISPLAY => empty($displayName) ? $userName : $displayName,
150
            static::PW_DIR => $directory,
151
            static::PW_FEED => '',
152
        ];
153 3
        ksort($newUserPass);
154 3
        $passLines[] = $newUserPass;
155
156
        // now save it
157
        try {
158 3
            $this->saveFile($this->path, $passLines);
159 3
        } finally {
160 3
            $this->getLock()->delete();
161
        }
162
    }
163
164
    /**
165
     * @throws AuthException
166
     * @throws LockException
167
     * @return Interfaces\IUser[]
168
     */
169 2
    public function readAccounts(): array
170
    {
171 2
        $this->checkLock();
172
173 2
        $passLines = $this->openFile($this->path);
174 2
        $result = [];
175 2
        foreach ($passLines as &$line) {
176 2
            $result[] = $this->getUserClass($line);
177
        }
178
179 2
        return $result;
180
    }
181
182 3
    public function updateAccount(Interfaces\IUser $user): void
183
    {
184 3
        $userName = $this->stripChars($user->getAuthName());
185 3
        $directory = $this->stripChars($user->getDir());
186 3
        $displayName = $this->stripChars($user->getDisplayName());
187
188 3
        $this->checkLock();
189
190 3
        $this->getLock()->create();
191
        try {
192 3
            $passwordLines = $this->openFile($this->path);
193 1
        } catch (AuthException $ex) {
194 1
            $this->getLock()->delete();
195 1
            throw $ex;
196
        }
197 2
        foreach ($passwordLines as &$line) {
198 2
            if ($line[static::PW_NAME] == $userName) {
199
                // REFILL
200 2
                $line[static::PW_GROUP] = !empty($user->getGroup()) ? $user->getGroup() : $line[static::PW_GROUP] ;
201 2
                $line[static::PW_CLASS] = !empty($user->getClass()) ? $user->getClass() : $line[static::PW_CLASS] ;
202 2
                $line[static::PW_DISPLAY] = !empty($displayName) ? $displayName : $line[static::PW_DISPLAY] ;
203 2
                $line[static::PW_DIR] = !empty($directory) ? $directory : $line[static::PW_DIR] ;
204
            }
205
        }
206
207
        try {
208 2
            $this->saveFile($this->path, $passwordLines);
209 2
        } finally {
210 2
            $this->getLock()->delete();
211
        }
212
    }
213
214 3
    public function updatePassword(string $userName, string $passWord): void
215
    {
216 3
        $name = $this->stripChars($userName);
217
        // load from shadow
218 3
        $this->checkLock();
219
220 3
        $changed = false;
221 3
        $this->getLock()->create();
222
223
        try {
224 3
            $lines = $this->openFile($this->path);
225 1
        } catch (AuthException $ex) {
226 1
            $this->getLock()->delete();
227 1
            throw $ex;
228
        }
229 2
        foreach ($lines as &$line) {
230 2
            if ($line[static::PW_NAME] == $name) {
231 2
                $changed = true;
232 2
                $line[static::PW_PASS] = $this->mode->hash($passWord);
233
            }
234
        }
235
        try {
236 2
            if ($changed) {
237 2
                $this->saveFile($this->path, $lines);
238
            }
239 2
        } finally {
240 2
            $this->getLock()->delete();
241
        }
242
    }
243
244 3
    public function deleteAccount(string $userName): void
245
    {
246 3
        $name = $this->stripChars($userName);
247 3
        $this->checkLock();
248
249 3
        $changed = false;
250 3
        $this->getLock()->create();
251
252
        // update password
253
        try {
254 3
            $passLines = $this->openFile($this->path);
255 1
        } catch (AuthException $ex) {
256
            // removal on non-existent file is not possible and not necessary
257 1
            $this->getLock()->delete();
258 1
            return;
259
        }
260
261 2
        foreach ($passLines as $index => &$line) {
262 2
            if ($line[static::PW_NAME] == $name) {
263 2
                unset($passLines[$index]);
264 2
                $changed = true;
265
            }
266
        }
267
268
        // now save it all
269
        try {
270 2
            if ($changed) {
271 2
                $this->saveFile($this->path, $passLines);
272
            }
273 2
        } finally {
274 2
            $this->getLock()->delete();
275
        }
276
    }
277
}
278