Passed
Pull Request — master (#24)
by Sergei
02:13
created

CurrentUser::getId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\User\CurrentUser;
6
7
use Psr\EventDispatcher\EventDispatcherInterface;
8
use Yiisoft\Access\AccessCheckerInterface;
9
use Yiisoft\Auth\IdentityInterface;
10
use Yiisoft\Auth\IdentityRepositoryInterface;
11
use Yiisoft\User\CurrentUser\Storage\CurrentIdentityStorageInterface;
12
use Yiisoft\User\CurrentUser\Event\AfterLogout;
13
use Yiisoft\User\CurrentUser\Event\AfterLogin;
14
use Yiisoft\User\CurrentUser\Event\BeforeLogout;
15
use Yiisoft\User\CurrentUser\Event\BeforeLogin;
16
use Yiisoft\User\GuestIdentity;
17
18
/**
19
 * Maintains current identity and allows logging in and out using it.
20
 */
21
final class CurrentUser
22
{
23
    private CurrentIdentityStorageInterface $currentIdentityStorage;
24
    private IdentityRepositoryInterface $identityRepository;
25
    private EventDispatcherInterface $eventDispatcher;
26
    private ?AccessCheckerInterface $accessChecker = null;
27
28
    private ?IdentityInterface $identity = null;
29
    private ?IdentityInterface $temporaryIdentity = null;
30
31 25
    public function __construct(
32
        CurrentIdentityStorageInterface $currentIdentityStorage,
33
        IdentityRepositoryInterface $identityRepository,
34
        EventDispatcherInterface $eventDispatcher
35
    ) {
36 25
        $this->currentIdentityStorage = $currentIdentityStorage;
37 25
        $this->identityRepository = $identityRepository;
38 25
        $this->eventDispatcher = $eventDispatcher;
39 25
    }
40
41 1
    public function setAccessChecker(AccessCheckerInterface $accessChecker): void
42
    {
43 1
        $this->accessChecker = $accessChecker;
44 1
    }
45
46
    /**
47
     * Returns the identity object associated with the currently logged-in user.
48
     */
49 20
    public function getIdentity(): IdentityInterface
50
    {
51 20
        $identity = $this->temporaryIdentity ?? $this->identity;
52
53 20
        if ($identity === null) {
54 14
            $identity = $this->determineIdentity();
55 14
            $this->identity = $identity;
56
        }
57
58 20
        return $identity;
59
    }
60
61 14
    private function determineIdentity(): IdentityInterface
62
    {
63 14
        $identity = null;
64
65 14
        $id = $this->currentIdentityStorage->get();
66 14
        if ($id !== null) {
67 2
            $identity = $this->identityRepository->findIdentity($id);
68
        }
69
70 14
        return $identity ?? new GuestIdentity();
71
    }
72
73
    /**
74
     * Returns a value that uniquely represents the user.
75
     *
76
     * @see CurrentUser::getIdentity()
77
     *
78
     * @return string|null The unique identifier for the user. If `null`, it means the user is a guest.     *
79
     */
80 1
    public function getId(): ?string
81
    {
82 1
        return $this->getIdentity()->getId();
83
    }
84
85
    /**
86
     * Returns a value indicating whether the user is a guest (not authenticated).
87
     *
88
     * @see getIdentity()
89
     *
90
     * @return bool Whether the current user is a guest.
91
     */
92 15
    public function isGuest(): bool
93
    {
94 15
        return $this->getIdentity() instanceof GuestIdentity;
95
    }
96
97
    /**
98
     * Checks if the user can perform the operation as specified by the given permission.
99
     *
100
     * Note that you must provide access checker via {@see CurrentUser::setAccessChecker()} in order to use this
101
     * method. Otherwise it will always return `false`.
102
     *
103
     * @param string $permissionName The name of the permission (e.g. "edit post") that needs access check.
104
     * @param array $params Name-value pairs that would be passed to the rules associated with the roles and
105
     * permissions assigned to the user.
106
     *
107
     * @return bool Whether the user can perform the operation as specified by the given permission.
108
     */
109 2
    public function can(string $permissionName, array $params = []): bool
110
    {
111 2
        if ($this->accessChecker === null) {
112 1
            return false;
113
        }
114
115 1
        return $this->accessChecker->userHasPermission($this->getId(), $permissionName, $params);
116
    }
117
118
    /**
119
     * Logs in a user.
120
     *
121
     * @param IdentityInterface $identity The user identity (which should already be authenticated).
122
     *
123
     * @return bool Whether the user is logged in.
124
     */
125 8
    public function login(IdentityInterface $identity): bool
126
    {
127 8
        if ($this->beforeLogin($identity)) {
128 8
            $this->switchIdentity($identity);
129 8
            $this->afterLogin($identity);
130
        }
131 8
        return !$this->isGuest();
132
    }
133
134
    /**
135
     * This method is called before logging in a user.
136
     * The default implementation will trigger the {@see BeforeLogin} event.
137
     *
138
     * @param IdentityInterface $identity The user identity information.
139
     *
140
     * @return bool Whether the user should continue to be logged in.
141
     */
142 8
    private function beforeLogin(IdentityInterface $identity): bool
143
    {
144 8
        $event = new BeforeLogin($identity);
145 8
        $this->eventDispatcher->dispatch($event);
146 8
        return $event->isValid();
147
    }
148
149
    /**
150
     * This method is called after the user is successfully logged in.
151
     *
152
     * @param IdentityInterface $identity The user identity information.
153
     */
154 8
    private function afterLogin(IdentityInterface $identity): void
155
    {
156 8
        $this->eventDispatcher->dispatch(new AfterLogin($identity));
157 8
    }
158
159
    /**
160
     * Logs out the current user.
161
     *
162
     * @return bool Whether the user is logged out.
163
     */
164 3
    public function logout(): bool
165
    {
166 3
        if ($this->isGuest()) {
167 1
            return false;
168
        }
169
170 2
        $identity = $this->getIdentity();
171 2
        if ($this->beforeLogout($identity)) {
172 2
            $this->switchIdentity(new GuestIdentity());
173 2
            $this->afterLogout($identity);
174
        }
175
176 2
        return $this->isGuest();
177
    }
178
179
    /**
180
     * This method is invoked when calling {@see CurrentUser::logout()} to log out a user.
181
     *
182
     * @param IdentityInterface $identity The user identity information.
183
     *
184
     * @return bool Whether the user should continue to be logged out.
185
     */
186 2
    private function beforeLogout(IdentityInterface $identity): bool
187
    {
188 2
        $event = new BeforeLogout($identity);
189 2
        $this->eventDispatcher->dispatch($event);
190 2
        return $event->isValid();
191
    }
192
193
    /**
194
     * This method is invoked right after a user is logged out via {@see CurrentUser::logout()}.
195
     *
196
     * @param IdentityInterface $identity The user identity information.
197
     */
198 2
    private function afterLogout(IdentityInterface $identity): void
199
    {
200 2
        $this->eventDispatcher->dispatch(new AfterLogout($identity));
201 2
    }
202
203 2
    public function setTemporaryIdentity(IdentityInterface $identity): void
204
    {
205 2
        $this->temporaryIdentity = $identity;
206 2
    }
207
208 1
    public function clearTemporaryIdentity(): void
209
    {
210 1
        $this->temporaryIdentity = null;
211 1
    }
212
213
    /**
214
     * Switches to a new identity for the current user.
215
     *
216
     * This method is called by {@see CurrentUser::login()} and {@see CurrentUser::logout()}
217
     * when the current user needs to be associated with the corresponding identity information.
218
     *
219
     * @param IdentityInterface $identity The identity information to be associated with the current user.
220
     * In order to indicate that the user is guest, use {@see GuestIdentity}.
221
     */
222 8
    private function switchIdentity(IdentityInterface $identity): void
223
    {
224 8
        $this->identity = $identity;
225
226 8
        $id = $identity->getId();
227 8
        if ($id === null) {
228 2
            $this->currentIdentityStorage->clear();
229
        } else {
230 8
            $this->currentIdentityStorage->set($id);
231
        }
232 8
    }
233
}
234