Issues (23)

src/Auth.php (3 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Micro
7
 *
8
 * @copyright   Copryright (c) 2015-2018 gyselroth GmbH (https://gyselroth.com)
9
 * @license     MIT https://opensource.org/licenses/MIT
10
 */
11
12
namespace Micro\Auth;
13
14
use InvalidArgumentException;
15
use Micro\Auth\Adapter\AdapterInterface;
16
use Psr\Log\LoggerInterface;
17
18
class Auth
19
{
20
    /**
21
     * Adapter.
22
     *
23
     * @var array
24
     */
25
    protected $adapter = [];
26
27
    /**
28
     * Identity.
29
     *
30
     * @var IdentityInterface
31
     */
32
    protected $identity;
33
34
    /**
35
     * Logger.
36
     *
37
     * @var LoggerInterface
38
     */
39
    protected $logger;
40
41
    /**
42
     * Identity class.
43
     *
44
     * @var string
45
     */
46
    protected $identity_class = Identity::class;
47
48
    /**
49
     * Attribute map class.
50
     *
51
     * @var string
52
     */
53
    protected $attribute_map_class = AttributeMap::class;
54
55
    /**
56
     * Initialize.
57
     *
58
     * @param LoggerInterface $logger
59
     * @param iterable        $config
60
     */
61
    public function __construct(LoggerInterface $logger, ? Iterable $config = null)
62
    {
63
        $this->logger = $logger;
64
        $this->setOptions($config);
65
    }
66
67
    /**
68
     * Set options.
69
     *
70
     * @param iterable $config
71
     *
72
     * @return Auth
73
     */
74
    public function setOptions(? Iterable $config = null): self
75
    {
76
        if (null === $config) {
77
            return $this;
78
        }
79
80
        foreach ($config as $option => $value) {
81
            switch ($option) {
82
                case 'identity_class':
83
                case 'attribute_map_class':
84
                    $this->{$option} = (string) $value;
85
86
                break;
87
                default:
88
                    throw new InvalidArgumentException('invalid option '.$option.' given');
89
            }
90
        }
91
92
        return $this;
93
    }
94
95
    /**
96
     * Check if adapter is injected.
97
     *
98
     * @param string $name
99
     *
100
     * @return bool
101
     */
102
    public function hasAdapter(string $name): bool
103
    {
104
        return isset($this->adapter[$name]);
105
    }
106
107
    /**
108
     * Inject auth adapter.
109
     *
110
     * @param AdapterInterface $adapter
111
     * @param string           $name
112
     */
113
    public function injectAdapter(AdapterInterface $adapter, ?string $name = null): self
114
    {
115
        if (null === $name) {
116
            $name = get_class($adapter);
117
        }
118
119
        $this->logger->debug('inject auth adapter ['.$name.'] of type ['.get_class($adapter).']', [
120
            'category' => get_class($this),
121
        ]);
122
123
        if ($this->hasAdapter($name)) {
124
            throw new Exception\AdapterNotUnique('auth adapter '.$name.' is already registered');
125
        }
126
127
        $this->adapter[$name] = $adapter;
128
129
        return $this;
130
    }
131
132
    /**
133
     * Get adapter.
134
     *
135
     * @param string $name
136
     *
137
     * @return AdapterInterface
138
     */
139
    public function getAdapter(string $name): AdapterInterface
140
    {
141
        if (!$this->hasAdapter($name)) {
142
            throw new Exception\AdapterNotFound('auth adapter '.$name.' is not registered');
143
        }
144
145
        return $this->adapter[$name];
146
    }
147
148
    /**
149
     * Get adapters.
150
     *
151
     * @return AdapterInterface[]
152
     */
153
    public function getAdapters(array $adapters = []): array
0 ignored issues
show
The parameter $adapters is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

153
    public function getAdapters(/** @scrutinizer ignore-unused */ array $adapters = []): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
154
    {
155
        if (empty($adapter)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $adapter does not exist. Did you maybe mean $adapters?
Loading history...
156
            return $this->adapter;
157
        }
158
        $list = [];
159
        foreach ($adapter as $name) {
160
            if (!$this->hasAdapter($name)) {
161
                throw new Exception\AdapterNotFound('auth adapter '.$name.' is not registered');
162
            }
163
            $list[$name] = $this->adapter[$name];
164
        }
165
166
        return $list;
167
    }
168
169
    /**
170
     * Authenticate.
171
     *
172
     * @return bool
173
     */
174
    public function requireOne(): bool
175
    {
176
        $result = false;
0 ignored issues
show
The assignment to $result is dead and can be removed.
Loading history...
177
178
        foreach ($this->adapter as $name => $adapter) {
179
            $adapter->setup();
180
181
            try {
182
                if ($adapter->authenticate()) {
183
                    $this->createIdentity($adapter);
184
185
                    $this->logger->info("identity [{$this->identity->getIdentifier()}] authenticated over adapter [{$name}]", [
186
                        'category' => get_class($this),
187
                    ]);
188
                    $_SERVER['REMOTE_USER'] = $this->identity->getIdentifier();
189
190
                    return true;
191
                }
192
            } catch (\Exception $e) {
193
                $this->logger->error('failed authenticate user, unexcepted exception was thrown', [
194
                    'category' => get_class($this),
195
                    'exception' => $e,
196
                ]);
197
            }
198
199
            $this->logger->debug("auth adapter [{$name}] failed", [
200
                'category' => get_class($this),
201
            ]);
202
        }
203
204
        $this->logger->warning('all authentication adapter have failed', [
205
            'category' => get_class($this),
206
        ]);
207
208
        return false;
209
    }
210
211
    /**
212
     * Get identity.
213
     *
214
     * @return IdentityInterface
215
     */
216
    public function getIdentity(): IdentityInterface
217
    {
218
        if (!$this->isAuthenticated()) {
219
            throw new Exception\NotAuthenticated('no valid authentication yet');
220
        }
221
222
        return $this->identity;
223
    }
224
225
    /**
226
     * Check if valid identity exists.
227
     *
228
     * @return bool
229
     */
230
    public function isAuthenticated(): bool
231
    {
232
        return $this->identity instanceof Identity;
233
    }
234
235
    /**
236
     * Create identity.
237
     *
238
     * @param AdapterInterface $adapter
239
     *
240
     * @return IdentityInterface
241
     */
242
    public function createIdentity(AdapterInterface $adapter): IdentityInterface
243
    {
244
        $map = new $this->attribute_map_class($adapter->getAttributeMap(), $this->logger);
245
        $this->identity = new $this->identity_class($adapter, $map, $this->logger);
246
247
        return $this->identity;
248
    }
249
}
250