Service   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 182
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 15
lcom 1
cbo 13
dl 0
loc 182
ccs 62
cts 62
cp 1
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A getPrincipal() 0 7 3
B login() 0 27 4
A publishLogin() 0 5 1
A resume() 0 17 2
A logout() 0 16 2
A publishLogout() 0 5 1
A publishResume() 0 9 1
1
<?php
2
declare(strict_types=1);
3
/**
4
 * Caridea
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
7
 * use this file except in compliance with the License. You may obtain a copy of
8
 * the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
 * License for the specific language governing permissions and limitations under
16
 * the License.
17
 *
18
 * @copyright 2015-2018 LibreWorks contributors
19
 * @license   Apache-2.0
20
 */
21
namespace Caridea\Auth;
22
23
use Caridea\Event\Publisher;
24
use Caridea\Session\Session;
25
use Caridea\Session\Map;
26
use Psr\Http\Message\ServerRequestInterface;
27
28
/**
29
 * Authentication service.
30
 *
31
 * @copyright 2015-2018 LibreWorks contributors
32
 * @license   Apache-2.0
33
 */
34
class Service implements \Caridea\Event\PublisherAware
35
{
36
    use \Psr\Log\LoggerAwareTrait;
37
    use \Caridea\Event\PublisherSetter;
38
39
    /**
40
     * @var Adapter The default auth adapter
41
     */
42
    protected $adapter;
43
    /**
44
     * @var \Caridea\Session\Session The session utility
45
     */
46
    protected $session;
47
    /**
48
     * @var \Caridea\Session\Map The session values
49
     */
50
    protected $values;
51
    /**
52
     * @var Principal The authenticated principal
53
     */
54
    protected $principal;
55
56
    /**
57
     * Creates a new authentication service.
58
     *
59
     * @param Session $session The session utility
60
     * @param Publisher $publisher An event publisher to broadcast authentication events
61
     * @param Adapter $adapter A default authentication adapter
62
     */
63 4
    public function __construct(Session $session, Publisher $publisher = null, Adapter $adapter = null)
64
    {
65 4
        $this->session = $session;
66 4
        $this->values = $session->getValues(__CLASS__);
67 4
        $this->publisher = $publisher ?? new \Caridea\Event\NullPublisher();
68 4
        $this->adapter = $adapter;
69 4
        $this->logger = new \Psr\Log\NullLogger();
70 4
    }
71
72
    /**
73
     * Gets the currently authenticated principal.
74
     *
75
     * If no one is authenticated, this will return an anonymous Principal. If
76
     * The session is not started but can be resumed, it will be resumed and the
77
     * principal will be loaded.
78
     *
79
     * @return Principal the authenticated principal
80
     */
81 2
    public function getPrincipal(): Principal
82
    {
83 2
        if ($this->principal === null && !$this->resume()) {
84 1
            $this->principal = Principal::getAnonymous();
85
        }
86 2
        return $this->principal;
87
    }
88
89
    /**
90
     * Authenticates a principal.
91
     *
92
     * @param ServerRequestInterface $request The Server Request message containing credentials
93
     * @param Adapter $adapter An optional adapter to use.
94
     *     Will use the default authentication adapter if none is specified.
95
     * @return bool Whether the session could be established
96
     * @throws \InvalidArgumentException If no adapter is provided and no default adapter is set
97
     * @throws Exception\UsernameNotFound if the provided username wasn't found
98
     * @throws Exception\UsernameAmbiguous if the provided username matches multiple accounts
99
     * @throws Exception\InvalidPassword if the provided password is invalid
100
     * @throws Exception\ConnectionFailed if the access to a remote data source failed
101
     *     (e.g. missing flat file, unreachable LDAP server, database login denied)
102
     */
103 4
    public function login(ServerRequestInterface $request, Adapter $adapter = null): bool
104
    {
105 4
        $started = $this->session->resume() || $this->session->start();
106 4
        if (!$started) {
107 1
            return false;
108
        }
109
110 3
        $login = $adapter ?? $this->adapter;
111 3
        if ($login === null) {
112 1
            throw new \InvalidArgumentException('You must specify an adapter for authentication');
113
        }
114 2
        $this->principal = $principal = $login->login($request);
115
116 2
        $this->session->clear();
117 2
        $this->session->regenerateId();
118
119 2
        $this->values->offsetSet('principal', $principal);
120 2
        $now = microtime(true);
121 2
        $this->values->offsetSet('firstActive', $now);
122 2
        $this->values->offsetSet('lastActive', $now);
123
124 2
        $this->logger->info(
125 2
            "Authentication login: {user}",
126 2
            ['user' => $principal]
127
        );
128 2
        return $this->publishLogin($principal);
129
    }
130
131
    /**
132
     * Publishes the login event.
133
     *
134
     * @param \Caridea\Auth\Principal $principal The authenticated principal
135
     * @return bool Always true
136
     */
137 2
    protected function publishLogin(Principal $principal): bool
138
    {
139 2
        $this->publisher->publish(new Event\Login($this, $principal));
140 2
        return true;
141
    }
142
143
    /**
144
     * Resumes an existing authenticated session.
145
     *
146
     * @return bool If an authentication session existed
147
     */
148 4
    public function resume(): bool
149
    {
150 4
        if ($this->values->offsetExists('principal')) {
151 3
            $this->principal = $this->values->get('principal');
152
153 3
            $this->logger->info(
154 3
                "Authentication resume: {user}",
155 3
                ['user' => $this->principal]
156
            );
157 3
            $this->publishResume($this->principal, $this->values);
158
159 3
            $this->values->offsetSet('lastActive', microtime(true));
160
161 3
            return true;
162
        }
163 1
        return false;
164
    }
165
166
    /**
167
     * Publishes the resume event.
168
     *
169
     * @param \Caridea\Auth\Principal $principal The authenticated principal
170
     * @param \Caridea\Session\Map $values The session values
171
     */
172 2
    protected function publishResume(Principal $principal, Map $values)
173
    {
174 2
        $this->publisher->publish(new Event\Resume(
175 2
            $this,
176 2
            $principal,
177 2
            $values->get('firstActive') ?? 0.0,
178 2
            $values->get('lastActive') ?? 0.0
179
        ));
180 2
    }
181
182
    /**
183
     * Logs out the currently authenticated principal.
184
     *
185
     * @return bool If a principal existed in the session to log out
186
     */
187 3
    public function logout(): bool
188
    {
189 3
        if ($this->values->offsetExists('principal')) {
190 2
            $principal = $this->getPrincipal();
191 2
            $this->principal = Principal::getAnonymous();
192
193 2
            $this->session->destroy();
194
195 2
            $this->logger->info(
196 2
                "Authentication logout: {user}",
197 2
                ['user' => $principal]
198
            );
199 2
            return $this->publishLogout($principal);
200
        }
201 1
        return false;
202
    }
203
204
    /**
205
     * Publishes the logout event.
206
     *
207
     * @param \Caridea\Auth\Principal $principal The authenticated principal
208
     * @return bool Always true
209
     */
210 2
    protected function publishLogout(Principal $principal): bool
211
    {
212 2
        $this->publisher->publish(new Event\Logout($this, $principal));
213 2
        return true;
214
    }
215
}
216