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

nette / security / 22835066665

09 Mar 2026 01:50AM UTC coverage: 92.466%. Remained the same
22835066665

push

github

dg
added CLAUDE.md

540 of 584 relevant lines covered (92.47%)

0.92 hits per line

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

92.77
/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-read string|int|null $id
21
 * @property-read string[] $roles
22
 * @property-read ?int $logoutReason
23
 * @property   IAuthenticator $authenticator
24
 * @property   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
        /** default role for unauthenticated user */
48
        public string $guestRole = 'guest';
49

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

53
        /** @var array<callable(self): void>  Occurs when the user is successfully logged in */
54
        public array $onLoggedIn = [];
55

56
        /** @var array<callable(self): void>  Occurs when the user is logged out */
57
        public array $onLoggedOut = [];
58

59
        private ?IIdentity $identity = null;
60
        private ?bool $authenticated = null;
61
        private ?int $logoutReason = null;
62

63

64
        public function __construct(
1✔
65
                private UserStorage $storage,
66
                private ?IAuthenticator $authenticator = null,
67
                private ?Authorizator $authorizator = null,
68
        ) {
69
        }
1✔
70

71

72
        final public function getStorage(): UserStorage
73
        {
74
                return $this->storage;
×
75
        }
76

77

78
        /********************* Authentication ****************d*g**/
79

80

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

102
                $id = $this->authenticator instanceof IdentityHandler
1✔
103
                        ? $this->authenticator->sleepIdentity($this->identity)
1✔
104
                        : $this->identity;
1✔
105

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

112

113
        /**
114
         * Logs out the user from the current session.
115
         */
116
        final public function logout(bool $clearIdentity = false): void
1✔
117
        {
118
                $logged = $this->isLoggedIn();
1✔
119
                $this->storage->clearAuthentication($clearIdentity);
1✔
120
                $this->authenticated = false;
1✔
121
                $this->logoutReason = self::LogoutManual;
1✔
122
                if ($logged) {
1✔
123
                        Arrays::invoke($this->onLoggedOut, $this);
1✔
124
                }
125

126
                $this->identity = $clearIdentity ? null : $this->identity;
1✔
127
        }
1✔
128

129

130
        /**
131
         * Checks whether the user is authenticated.
132
         */
133
        final public function isLoggedIn(): bool
134
        {
135
                if ($this->authenticated === null) {
1✔
136
                        $this->getStoredData();
1✔
137
                }
138

139
                return (bool) $this->authenticated;
1✔
140
        }
141

142

143
        /**
144
         * Returns the current user identity, or null if not authenticated.
145
         */
146
        final public function getIdentity(): ?IIdentity
147
        {
148
                if ($this->authenticated === null) {
1✔
149
                        $this->getStoredData();
1✔
150
                }
151

152
                return $this->identity;
1✔
153
        }
154

155

156
        private function getStoredData(): void
157
        {
158
                (function (bool $state, ?IIdentity $id, ?int $reason) use (&$identity) {
1✔
159
                        $identity = $id;
1✔
160
                        $this->authenticated = $state;
1✔
161
                        $this->logoutReason = $reason;
1✔
162
                })(...$this->storage->getState());
1✔
163

164
                $this->identity = $identity && $this->authenticator instanceof IdentityHandler
1✔
165
                        ? $this->authenticator->wakeupIdentity($identity)
1✔
166
                        : $identity;
1✔
167
                $this->authenticated = $this->authenticated && $this->identity !== null;
1✔
168
        }
1✔
169

170

171
        /**
172
         * Returns current user ID, if any.
173
         */
174
        public function getId(): string|int|null
175
        {
176
                $identity = $this->getIdentity();
1✔
177
                return $identity ? $identity->getId() : null;
1✔
178
        }
179

180

181
        /**
182
         * Discards cached authentication state, forcing a reload from storage on next access.
183
         */
184
        final public function refreshStorage(): void
185
        {
186
                $this->identity = $this->authenticated = $this->logoutReason = null;
1✔
187
        }
1✔
188

189

190
        /**
191
         * Sets authentication handler.
192
         */
193
        public function setAuthenticator(IAuthenticator $handler): static
1✔
194
        {
195
                $this->authenticator = $handler;
1✔
196
                return $this;
1✔
197
        }
198

199

200
        /**
201
         * Returns authentication handler.
202
         */
203
        final public function getAuthenticator(): IAuthenticator
204
        {
205
                if (!$this->authenticator) {
1✔
206
                        throw new Nette\InvalidStateException('Authenticator has not been set.');
1✔
207
                }
208

209
                return $this->authenticator;
1✔
210
        }
211

212

213
        /**
214
         * Returns authentication handler, or null if none is set.
215
         */
216
        final public function getAuthenticatorIfExists(): ?IAuthenticator
217
        {
218
                return $this->authenticator;
×
219
        }
220

221

222
        /** @deprecated */
223
        final public function hasAuthenticator(): bool
224
        {
225
                return (bool) $this->authenticator;
×
226
        }
227

228

229
        /**
230
         * Enables log out after inactivity (like '20 minutes').
231
         */
232
        public function setExpiration(?string $expire, bool $clearIdentity = false): static
1✔
233
        {
234
                $this->storage->setExpiration($expire, $clearIdentity);
1✔
235
                return $this;
1✔
236
        }
237

238

239
        /**
240
         * Returns the logout reason: LogoutManual or LogoutInactivity, or null if not applicable.
241
         */
242
        final public function getLogoutReason(): ?int
243
        {
244
                return $this->logoutReason;
1✔
245
        }
246

247

248
        /********************* Authorization ****************d*g**/
249

250

251
        /**
252
         * Returns effective roles of the user. Unauthenticated users get the guest role.
253
         * @return string[]
254
         */
255
        public function getRoles(): array
256
        {
257
                if (!$this->isLoggedIn()) {
1✔
258
                        return [$this->guestRole];
1✔
259
                }
260

261
                $identity = $this->getIdentity();
1✔
262
                return $identity?->getRoles() ?? [$this->authenticatedRole];
1✔
263
        }
264

265

266
        /**
267
         * Checks whether the user has the specified effective role.
268
         */
269
        final public function isInRole(string $role): bool
1✔
270
        {
271
                foreach ($this->getRoles() as $r) {
1✔
272
                        if ($role === ($r instanceof Role ? $r->getRoleId() : $r)) {
1✔
273
                                return true;
1✔
274
                        }
275
                }
276

277
                return false;
1✔
278
        }
279

280

281
        /**
282
         * Checks whether the user has access to the given resource and privilege.
283
         * Null means all resources or all privileges.
284
         */
285
        public function isAllowed(mixed $resource = Authorizator::All, mixed $privilege = Authorizator::All): bool
1✔
286
        {
287
                foreach ($this->getRoles() as $role) {
1✔
288
                        if ($this->getAuthorizator()->isAllowed($role, $resource, $privilege)) {
1✔
289
                                return true;
1✔
290
                        }
291
                }
292

293
                return false;
1✔
294
        }
295

296

297
        /**
298
         * Sets authorization handler.
299
         */
300
        public function setAuthorizator(Authorizator $handler): static
1✔
301
        {
302
                $this->authorizator = $handler;
1✔
303
                return $this;
1✔
304
        }
305

306

307
        /**
308
         * Returns current authorization handler.
309
         */
310
        final public function getAuthorizator(): Authorizator
311
        {
312
                if (!$this->authorizator) {
1✔
313
                        throw new Nette\InvalidStateException('Authorizator has not been set.');
1✔
314
                }
315

316
                return $this->authorizator;
1✔
317
        }
318

319

320
        /**
321
         * Returns authorization handler, or null if none is set.
322
         */
323
        final public function getAuthorizatorIfExists(): ?Authorizator
324
        {
325
                return $this->authorizator;
×
326
        }
327

328

329
        /** @deprecated */
330
        final public function hasAuthorizator(): bool
331
        {
332
                return (bool) $this->authorizator;
×
333
        }
334
}
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