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

nette / security / 26320370796

23 May 2026 01:54AM UTC coverage: 91.851% (+0.04%) from 91.812%
26320370796

push

github

dg
User: deprecated magic properties (BC break)

541 of 589 relevant lines covered (91.85%)

0.92 hits per line

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

94.57
/src/Security/User.php
1
<?php declare(strict_types=1);
1✔
2

3
/**
4
 * This file is part of the Nette Framework (https://nette.org)
5
 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6
 */
7

8
namespace Nette\Security;
9

10
use Nette;
11
use Nette\Utils\Arrays;
12
use function func_get_args;
13

14

15
/**
16
 * User authentication and authorization.
17
 *
18
 * @property-read bool $loggedIn
19
 * @property-read ?IIdentity $identity
20
 * @property-deprecated string|int|null $id
21
 * @property-deprecated list<string> $roles
22
 * @property-deprecated ?int $logoutReason
23
 * @property-deprecated Authenticator $authenticator
24
 * @property-deprecated Authorizator $authorizator
25
 */
26
class User
27
{
28
        use Nette\SmartObject;
29

30
        /** Log-out reason */
31
        public const
32
                LogoutManual = 1,
33
                LogoutInactivity = 2;
34

35
        /** @deprecated use User::LogoutManual */
36
        public const LOGOUT_MANUAL = self::LogoutManual;
37

38
        /** @deprecated use User::LogoutManual */
39
        public const MANUAL = self::LogoutManual;
40

41
        /** @deprecated use User::LogoutInactivity */
42
        public const LOGOUT_INACTIVITY = self::LogoutInactivity;
43

44
        /** @deprecated use User::LogoutInactivity */
45
        public const INACTIVITY = self::LogoutInactivity;
46

47
        /** role for an unauthenticated user, unless a guest identity provides its own roles */
48
        public string $guestRole = 'guest';
49

50
        /** default role for authenticated user without own identity */
51
        public string $authenticatedRole = 'authenticated';
52

53
        /** keep identity available (via getIdentity() and getId()) after logout or expiration; depends on the storage implementation */
54
        public bool $persistIdentity = true;
55

56
        /** @var array<callable(static): void>  Occurs when the user is successfully logged in */
57
        public array $onLoggedIn = [];
58

59
        /** @var array<callable(static): void>  Occurs when the user is logged out */
60
        public array $onLoggedOut = [];
61

62
        private ?IIdentity $identity = null;
63
        private ?bool $authenticated = null;
64
        private ?int $logoutReason = null;
65
        private ?IIdentity $guestIdentity = null;
66
        private bool $guestIdentityResolved = false;
67

68

69
        public function __construct(
1✔
70
                private UserStorage $storage,
71
                private ?Authenticator $authenticator = null,
72
                private ?Authorizator $authorizator = null,
73
        ) {
74
        }
1✔
75

76

77
        final public function getStorage(): UserStorage
78
        {
79
                return $this->storage;
×
80
        }
81

82

83
        /********************* Authentication ****************d*g**/
84

85

86
        /**
87
         * Authenticates the user. Accepts username and password, or an IIdentity directly.
88
         * @param  string|IIdentity  $username  username or identity
89
         * @throws AuthenticationException if authentication was not successful
90
         */
91
        public function login(
1✔
92
                string|IIdentity $username,
93
                #[\SensitiveParameter]
94
                ?string $password = null,
95
        ): void
96
        {
97
                $this->logout(clearIdentity: true);
1✔
98
                if ($username instanceof IIdentity) {
1✔
99
                        $this->identity = $username;
1✔
100
                } else {
101
                        $authenticator = $this->getAuthenticator();
1✔
102
                        $this->identity = $authenticator->authenticate(...func_get_args());
1✔
103
                }
104

105
                $id = $this->authenticator instanceof IdentityHandler
1✔
106
                        ? $this->authenticator->sleepIdentity($this->identity)
1✔
107
                        : $this->identity;
1✔
108

109
                $this->storage->saveAuthentication($id);
1✔
110
                $this->authenticated = true;
1✔
111
                $this->logoutReason = null;
1✔
112
                Arrays::invoke($this->onLoggedIn, $this);
1✔
113
        }
1✔
114

115

116
        /**
117
         * Logs out the user from the current session. The identity is kept available afterwards,
118
         * unless $clearIdentity is set or the $persistIdentity property is disabled.
119
         */
120
        final public function logout(bool $clearIdentity = false): void
1✔
121
        {
122
                $clearIdentity = $clearIdentity || !$this->persistIdentity;
1✔
123
                $logged = $this->isLoggedIn();
1✔
124
                $this->storage->clearAuthentication($clearIdentity);
1✔
125
                $this->authenticated = false;
1✔
126
                $this->logoutReason = self::LogoutManual;
1✔
127
                if ($logged) {
1✔
128
                        Arrays::invoke($this->onLoggedOut, $this);
1✔
129
                }
130

131
                $this->identity = $clearIdentity ? null : $this->identity;
1✔
132
        }
1✔
133

134

135
        /**
136
         * Checks whether the user is authenticated.
137
         */
138
        final public function isLoggedIn(): bool
139
        {
140
                $this->loadStoredData();
1✔
141
                return (bool) $this->authenticated;
1✔
142
        }
143

144

145
        /**
146
         * Returns the user identity. When not logged in, this is the retained identity (unless $persistIdentity
147
         * is disabled) or a guest identity if the authenticator provides one; null otherwise.
148
         */
149
        final public function getIdentity(): ?IIdentity
150
        {
151
                $this->loadStoredData();
1✔
152
                return $this->identity ?? $this->resolveGuestIdentity();
1✔
153
        }
154

155

156
        private function loadStoredData(): void
157
        {
158
                if ($this->authenticated !== null) {
1✔
159
                        return;
1✔
160
                }
161

162
                (function (bool $state, ?IIdentity $id, ?int $reason) use (&$identity) {
1✔
163
                        $identity = $id;
1✔
164
                        $this->authenticated = $state;
1✔
165
                        $this->logoutReason = $reason;
1✔
166
                })(...$this->storage->getState());
1✔
167

168
                $identity = $identity && $this->authenticator instanceof IdentityHandler
1✔
169
                        ? $this->authenticator->wakeupIdentity($identity)
1✔
170
                        : $identity;
1✔
171
                $this->authenticated = $this->authenticated && $identity !== null;
1✔
172
                $this->identity = !$this->authenticated && !$this->persistIdentity ? null : $identity;
1✔
173
        }
1✔
174

175

176
        /** Returns the guest identity provided by the IdentityHandler (if it implements getGuestIdentity()), or null. */
177
        private function resolveGuestIdentity(): ?IIdentity
178
        {
179
                if (!$this->guestIdentityResolved) {
1✔
180
                        $this->guestIdentityResolved = true;
1✔
181
                        $this->guestIdentity = $this->authenticator instanceof IdentityHandler && method_exists($this->authenticator, 'getGuestIdentity')
1✔
182
                                ? $this->authenticator->getGuestIdentity()
1✔
183
                                : null;
1✔
184
                }
185

186
                return $this->guestIdentity;
1✔
187
        }
188

189

190
        /**
191
         * Returns the ID of the identity returned by getIdentity(), so it may be the retained or guest
192
         * identity's ID even when not logged in; null if there is no identity.
193
         */
194
        public function getId(): string|int|null
195
        {
196
                $identity = $this->getIdentity();
1✔
197
                return $identity ? $identity->getId() : null;
1✔
198
        }
199

200

201
        /**
202
         * Discards the cached authentication state and identity, forcing a reload on next access.
203
         */
204
        final public function refreshStorage(): void
205
        {
206
                $this->identity = $this->authenticated = $this->logoutReason = null;
1✔
207
                $this->guestIdentity = null;
1✔
208
                $this->guestIdentityResolved = false;
1✔
209
        }
1✔
210

211

212
        /**
213
         * Sets authentication handler.
214
         */
215
        public function setAuthenticator(Authenticator $handler): static
1✔
216
        {
217
                $this->authenticator = $handler;
1✔
218
                $this->guestIdentityResolved = false;
1✔
219
                return $this;
1✔
220
        }
221

222

223
        /**
224
         * Returns authentication handler.
225
         */
226
        final public function getAuthenticator(): Authenticator
227
        {
228
                if (!$this->authenticator) {
1✔
229
                        throw new Nette\InvalidStateException('Authenticator has not been set.');
1✔
230
                }
231

232
                return $this->authenticator;
1✔
233
        }
234

235

236
        /**
237
         * Returns authentication handler, or null if none is set.
238
         */
239
        final public function getAuthenticatorIfExists(): ?Authenticator
240
        {
241
                return $this->authenticator;
×
242
        }
243

244

245
        /** @deprecated */
246
        final public function hasAuthenticator(): bool
247
        {
248
                return (bool) $this->authenticator;
×
249
        }
250

251

252
        /**
253
         * Enables log out after inactivity (like '20 minutes'). The identity is kept available afterwards,
254
         * unless $clearIdentity is set or the $persistIdentity property is disabled.
255
         */
256
        public function setExpiration(?string $expire, bool $clearIdentity = false): static
1✔
257
        {
258
                $this->storage->setExpiration($expire, $clearIdentity || !$this->persistIdentity);
1✔
259
                return $this;
1✔
260
        }
261

262

263
        /**
264
         * Returns the logout reason: LogoutManual or LogoutInactivity, or null if not applicable.
265
         */
266
        final public function getLogoutReason(): ?int
267
        {
268
                return $this->logoutReason;
1✔
269
        }
270

271

272
        /********************* Authorization ****************d*g**/
273

274

275
        /**
276
         * Returns effective roles derived from the login state, not from the (possibly retained) identity.
277
         * Logged in: the identity's roles, or authenticatedRole. Otherwise: the guest identity's roles, or guestRole.
278
         * @return list<string>
279
         */
280
        public function getRoles(): array
281
        {
282
                if (!$this->isLoggedIn()) {
1✔
283
                        return $this->resolveGuestIdentity()?->getRoles() ?? [$this->guestRole];
1✔
284
                }
285

286
                $identity = $this->getIdentity();
1✔
287
                return $identity?->getRoles() ?? [$this->authenticatedRole];
1✔
288
        }
289

290

291
        /**
292
         * Checks whether the user has the specified effective role.
293
         */
294
        final public function isInRole(string $role): bool
1✔
295
        {
296
                foreach ($this->getRoles() as $r) {
1✔
297
                        if ($role === ($r instanceof Role ? $r->getRoleId() : $r)) {
1✔
298
                                return true;
1✔
299
                        }
300
                }
301

302
                return false;
1✔
303
        }
304

305

306
        /**
307
         * Checks whether the user has access to the given resource and privilege.
308
         * Null means all resources or all privileges.
309
         */
310
        public function isAllowed(mixed $resource = Authorizator::All, mixed $privilege = Authorizator::All): bool
1✔
311
        {
312
                foreach ($this->getRoles() as $role) {
1✔
313
                        if ($this->getAuthorizator()->isAllowed($role, $resource, $privilege)) {
1✔
314
                                return true;
1✔
315
                        }
316
                }
317

318
                return false;
1✔
319
        }
320

321

322
        /**
323
         * Sets authorization handler.
324
         */
325
        public function setAuthorizator(Authorizator $handler): static
1✔
326
        {
327
                $this->authorizator = $handler;
1✔
328
                return $this;
1✔
329
        }
330

331

332
        /**
333
         * Returns current authorization handler.
334
         */
335
        final public function getAuthorizator(): Authorizator
336
        {
337
                if (!$this->authorizator) {
1✔
338
                        throw new Nette\InvalidStateException('Authorizator has not been set.');
1✔
339
                }
340

341
                return $this->authorizator;
1✔
342
        }
343

344

345
        /**
346
         * Returns authorization handler, or null if none is set.
347
         */
348
        final public function getAuthorizatorIfExists(): ?Authorizator
349
        {
350
                return $this->authorizator;
×
351
        }
352

353

354
        /** @deprecated */
355
        final public function hasAuthorizator(): bool
356
        {
357
                return (bool) $this->authorizator;
×
358
        }
359
}
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