• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

codeigniter4 / shield / 6132899896

09 Sep 2023 08:02PM UTC coverage: 92.601% (+0.05%) from 92.549%
6132899896

push

github

web-flow
Merge pull request #811 from kenjis/reuse-workflows

chore: use workflows in codeigniter4/.github

2115 of 2284 relevant lines covered (92.6%)

49.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.41
/src/Entities/User.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace CodeIgniter\Shield\Entities;
6

7
use CodeIgniter\Database\Exceptions\DataException;
8
use CodeIgniter\I18n\Time;
9
use CodeIgniter\Shield\Authentication\Authenticators\Session;
10
use CodeIgniter\Shield\Authentication\Traits\HasAccessTokens;
11
use CodeIgniter\Shield\Authorization\Traits\Authorizable;
12
use CodeIgniter\Shield\Models\LoginModel;
13
use CodeIgniter\Shield\Models\UserIdentityModel;
14
use CodeIgniter\Shield\Traits\Activatable;
15
use CodeIgniter\Shield\Traits\Bannable;
16
use CodeIgniter\Shield\Traits\Resettable;
17

18
/**
19
 * @property string|null         $email
20
 * @property int|string|null     $id
21
 * @property UserIdentity[]|null $identities
22
 * @property Time|null           $last_active
23
 * @property string|null         $password
24
 * @property string|null         $password_hash
25
 * @property string|null         $username
26
 */
27
class User extends Entity
28
{
29
    use Authorizable;
30
    use HasAccessTokens;
31
    use Resettable;
32
    use Activatable;
33
    use Bannable;
34

35
    /**
36
     * @var UserIdentity[]|null
37
     */
38
    private ?array $identities = null;
39

40
    private ?string $email         = null;
41
    private ?string $password      = null;
42
    private ?string $password_hash = null;
43

44
    /**
45
     * @var string[]
46
     * @phpstan-var list<string>
47
     * @psalm-var list<string>
48
     */
49
    protected $dates = [
50
        'created_at',
51
        'updated_at',
52
        'deleted_at',
53
        'last_active',
54
    ];
55

56
    /**
57
     * @var array<string, string>
58
     */
59
    protected $casts = [
60
        'id'          => '?integer',
61
        'active'      => 'int_bool',
62
        'permissions' => 'array',
63
        'groups'      => 'array',
64
    ];
65

66
    /**
67
     * Returns the first identity of the given $type for this user.
68
     *
69
     * @param string $type See const ID_TYPE_* in Authenticator.
70
     *                     'email_2fa'|'email_activate'|'email_password'|'magic-link'|'access_token'
71
     */
72
    public function getIdentity(string $type): ?UserIdentity
73
    {
74
        $identities = $this->getIdentities($type);
336✔
75

76
        return count($identities) ? array_shift($identities) : null;
336✔
77
    }
78

79
    /**
80
     * ensures that all of the user's identities are loaded
81
     * into the instance for faster access later.
82
     */
83
    private function populateIdentities(): void
84
    {
85
        if ($this->identities === null) {
336✔
86
            /** @var UserIdentityModel $identityModel */
87
            $identityModel = model(UserIdentityModel::class);
336✔
88

89
            $this->identities = $identityModel->getIdentities($this);
336✔
90
        }
91
    }
92

93
    /**
94
     * Accessor method for this user's UserIdentity objects.
95
     * Will populate if they don't exist.
96
     *
97
     * @param string $type 'all' returns all identities.
98
     *
99
     * @return UserIdentity[]
100
     */
101
    public function getIdentities(string $type = 'all'): array
102
    {
103
        $this->populateIdentities();
336✔
104

105
        if ($type === 'all') {
336✔
106
            return $this->identities;
10✔
107
        }
108

109
        $identities = [];
336✔
110

111
        foreach ($this->identities as $identity) {
336✔
112
            if ($identity->type === $type) {
80✔
113
                $identities[] = $identity;
80✔
114
            }
115
        }
116

117
        return $identities;
336✔
118
    }
119

120
    public function setIdentities(array $identities): void
121
    {
122
        $this->identities = $identities;
6✔
123
    }
124

125
    /**
126
     * Creates a new identity for this user with an email/password
127
     * combination.
128
     *
129
     * @phpstan-param array{email: string, password: string} $credentials
130
     */
131
    public function createEmailIdentity(array $credentials): void
132
    {
133
        /** @var UserIdentityModel $identityModel */
134
        $identityModel = model(UserIdentityModel::class);
124✔
135

136
        $identityModel->createEmailIdentity($this, $credentials);
124✔
137

138
        // Ensure we will reload all identities
139
        $this->identities = null;
124✔
140
    }
141

142
    /**
143
     * Returns the user's Email/Password identity.
144
     */
145
    public function getEmailIdentity(): ?UserIdentity
146
    {
147
        return $this->getIdentity(Session::ID_TYPE_EMAIL_PASSWORD);
336✔
148
    }
149

150
    /**
151
     * If $email, $password, or $password_hash have been updated,
152
     * will update the user's email identity record with the
153
     * correct values.
154
     */
155
    public function saveEmailIdentity(): bool
156
    {
157
        if (empty($this->email) && empty($this->password) && empty($this->password_hash)) {
350✔
158
            return true;
332✔
159
        }
160

161
        $identity = $this->getEmailIdentity();
38✔
162
        if ($identity === null) {
38✔
163
            // Ensure we reload all identities
164
            $this->identities = null;
36✔
165

166
            $this->createEmailIdentity([
36✔
167
                'email'    => $this->email,
36✔
168
                'password' => '',
36✔
169
            ]);
36✔
170

171
            $identity = $this->getEmailIdentity();
36✔
172
        }
173

174
        if (! empty($this->email)) {
38✔
175
            $identity->secret = $this->email;
38✔
176
        }
177

178
        if (! empty($this->password)) {
38✔
179
            $identity->secret2 = service('passwords')->hash($this->password);
30✔
180
        }
181

182
        if (! empty($this->password_hash) && empty($this->password)) {
38✔
183
            $identity->secret2 = $this->password_hash;
14✔
184
        }
185

186
        /** @var UserIdentityModel $identityModel */
187
        $identityModel = model(UserIdentityModel::class);
38✔
188

189
        try {
190
            /** @throws DataException */
191
            $identityModel->save($identity);
38✔
192
        } catch (DataException $e) {
2✔
193
            // There may be no data to update.
194
            $messages = [
2✔
195
                lang('Database.emptyDataset', ['insert']),
2✔
196
                lang('Database.emptyDataset', ['update']),
2✔
197
            ];
2✔
198
            if (in_array($e->getMessage(), $messages, true)) {
2✔
199
                return true;
2✔
200
            }
201

202
            throw $e;
×
203
        }
204

205
        return true;
36✔
206
    }
207

208
    /**
209
     * Update the last used at date for an identity record.
210
     */
211
    public function touchIdentity(UserIdentity $identity): void
212
    {
213
        /** @var UserIdentityModel $identityModel */
214
        $identityModel = model(UserIdentityModel::class);
22✔
215

216
        $identityModel->touchIdentity($identity);
22✔
217
    }
218

219
    /**
220
     * Accessor method to grab the user's email address.
221
     * Will cache it in $this->email, since it has
222
     * to hit the database the first time to get it, most likely.
223
     */
224
    public function getEmail(): ?string
225
    {
226
        if ($this->email === null) {
368✔
227
            $this->email = $this->getEmailIdentity()->secret ?? null;
318✔
228
        }
229

230
        return $this->email;
368✔
231
    }
232

233
    public function setEmail(string $email): void
234
    {
235
        $this->email = $email;
386✔
236
    }
237

238
    public function getPassword(): ?string
239
    {
240
        return $this->password;
336✔
241
    }
242

243
    public function setPassword(string $password): User
244
    {
245
        $this->password = $password;
338✔
246

247
        return $this;
338✔
248
    }
249

250
    public function setPasswordHash(string $hash): User
251
    {
252
        $this->password_hash = $hash;
338✔
253

254
        return $this;
338✔
255
    }
256

257
    /**
258
     * Accessor method to grab the user's password hash.
259
     * Will cache it in $this->attributes, since it has
260
     * to hit the database the first time to get it, most likely.
261
     */
262
    public function getPasswordHash(): ?string
263
    {
264
        if ($this->password_hash === null) {
336✔
265
            $this->password_hash = $this->getEmailIdentity()->secret2 ?? null;
336✔
266
        }
267

268
        return $this->password_hash;
336✔
269
    }
270

271
    /**
272
     * Returns the previous login information for this user
273
     */
274
    public function previousLogin(): ?Login
275
    {
276
        /** @var LoginModel $logins */
277
        $logins = model(LoginModel::class);
2✔
278

279
        return $logins->previousLogin($this);
2✔
280
    }
281

282
    /**
283
     * Returns the last login information for this user as
284
     */
285
    public function lastLogin(): ?Login
286
    {
287
        /** @var LoginModel $logins */
288
        $logins = model(LoginModel::class);
2✔
289

290
        return $logins->lastLogin($this);
2✔
291
    }
292
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc