Completed
Push — master ( fe1a09...0c1faf )
by Raffael
01:39
created

Auth::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 2
1
<?php
2
declare(strict_types = 1);
3
4
/**
5
 * Micro
6
 *
7
 * @author    Raffael Sahli <[email protected]>
8
 * @copyright Copyright (c) 2017 gyselroth GmbH (https://gyselroth.com)
9
 * @license   MIT https://opensource.org/licenses/MIT
10
 */
11
12
namespace Micro;
13
14
use \Micro\Auth\Exception;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Micro\Exception.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
15
use \Micro\Auth\Adapter\AdapterInterface;
16
use \Micro\Auth\Identity;
17
use \Psr\Log\LoggerInterface as Logger;
18
use \Micro\Auth\AttributeMap;
19
use \Micro\Container\AdapterAwareInterface;
20
21
class Auth implements AdapterAwareInterface
22
{
23
    /**
24
     * Adapter
25
     *
26
     * @var array
27
     */
28
    protected $adapter = [];
29
30
31
    /**
32
     * Identity
33
     *
34
     * @var Identity
35
     */
36
    protected $identity;
37
38
39
    /**
40
     * Logger
41
     *
42
     * @var Logger
43
     */
44
    protected $logger;
45
46
47
    /**
48
     * Identity class
49
     *
50
     * @var string
51
     */
52
    protected $identity_class = Identity::class;
53
54
55
    /**
56
     * Attribute map class
57
     *
58
     * @var string
59
     */
60
    protected $attribute_map_class = AttributeMap::class;
61
62
63
    /**
64
     * Initialize
65
     *
66
     * @param   Logger $logger
67
     * @param   Iterable $config
68
     * @return  void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
69
     */
70
    public function __construct(Logger $logger, ? Iterable $config = null)
71
    {
72
        $this->logger = $logger;
73
        $this->setOptions($config);
74
    }
75
76
77
    /**
78
     * Set options
79
     *
80
     * @param  Iterable $config
81
     * @return Auth
82
     */
83
    public function setOptions(? Iterable $config = null) : Auth
84
    {
85
        if ($config === null) {
86
            return $this;
87
        }
88
89
        foreach ($config as $option => $value) {
90
            switch ($option) {
91
                case 'identity_class':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
92
                case 'attribute_map_class':
93
                    $this->{$option} = (string)$value;
94
                break;
95
96
                case 'adapter':
97
                    foreach ($value as $name => $adapter) {
98
                        $this->injectAdapter($name, $adapter);
99
                    }
100
                break;
101
            }
102
        }
103
104
        return $this;
105
    }
106
107
108
    /**
109
     * Has adapter
110
     *
111
     * @param  string $name
112
     * @return bool
113
     */
114
    public function hasAdapter(string $name): bool
115
    {
116
        return isset($this->adapter[$name]);
117
    }
118
119
120
    /**
121
     * Inject adapter
122
     *
123
     * @param  string $name
124
     * @param  AdapterInterface $adapter
125
     * @return AdapterInterface
126
     */
127
    public function injectAdapter(string $name, AdapterInterface $adapter) : AdapterInterface
128
    {
129
        if ($this->hasAdapter($name)) {
130
            throw new Exception('auth adapter '.$name.' is already registered');
131
        }
132
133
        $this->adapter[$name] = $adapter;
134
        return $adapter;
135
    }
136
137
138
    /**
139
     * Get adapter
140
     *
141
     * @param  string $name
142
     * @return AdapterInterface
143
     */
144
    public function getAdapter(string $name): AdapterInterface
145
    {
146
        if (!$this->hasAdapter($name)) {
147
            throw new Exception('auth adapter '.$name.' is not registered');
148
        }
149
150
        return $this->adapter[$name];
151
    }
152
153
154
    /**
155
     * Get adapters
156
     *
157
     * @param  array $adapters
158
     * @return array
159
     */
160
    public function getAdapters(array $adapters = []): array
161
    {
162
        if (empty($adapter)) {
0 ignored issues
show
Bug introduced by
The variable $adapter does not exist. Did you mean $adapters?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
163
            return $this->adapter;
164
        } else {
165
            $list = [];
166
            foreach ($adapter as $name) {
167
                if (!$this->hasAdapter($name)) {
168
                    throw new Exception('auth adapter '.$name.' is not registered');
169
                }
170
                $list[$name] = $this->adapter[$name];
171
            }
172
173
            return $list;
174
        }
175
    }
176
177
178
    /**
179
     * Create identity
180
     *
181
     * @param  AdapterInterface $adapter
182
     * @return Identity
183
     */
184
    protected function createIdentity(AdapterInterface $adapter): Identity
185
    {
186
        $map = new $this->attribute_map_class($adapter->getAttributeMap(), $this->logger);
187
        $this->identity = new $this->identity_class($adapter, $map, $this->logger);
188
        return $this->identity;
189
    }
190
191
192
    /**
193
     * Authenticate
194
     *
195
     * @return  bool
196
     */
197
    public function requireOne(): bool
198
    {
199
        $result = false;
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
200
201
        foreach ($this->adapter as $name => $adapter) {
202
            try {
203
                if ($adapter->authenticate()) {
204
                    $this->createIdentity($adapter);
205
206
                    $this->logger->info("identity [{$this->identity->getIdentifier()}] authenticated over adapter [{$name}]", [
207
                        'category' => get_class($this)
208
                    ]);
209
                    $_SERVER['REMOTE_USER'] = $this->identity->getIdentifier();
210
211
                    return true;
212
                }
213
            } catch (\Exception $e) {
214
                $this->logger->error("failed authenticate user, unexcepted exception was thrown", [
215
                    'category' => get_class($this),
216
                    'exception'=> $e
217
                ]);
218
            }
219
220
            $this->logger->debug("auth adapter [{$name}] failed", [
221
                'category' => get_class($this)
222
            ]);
223
        }
224
225
        $this->logger->warning("all authentication adapter have failed", [
226
            'category' => get_class($this)
227
        ]);
228
229
        return false;
230
    }
231
232
233
    /**
234
     * Get identity
235
     *
236
     * @return Identity
237
     */
238
    public function getIdentity(): Identity
239
    {
240
        if (!$this->isAuthenticated()) {
241
            throw new Exception('no valid authentication yet');
242
        } else {
243
            return $this->identity;
244
        }
245
    }
246
247
248
    /**
249
     * Check if valid identity exists
250
     *
251
     * @return bool
252
     */
253
    public function isAuthenticated(): bool
254
    {
255
        return ($this->identity instanceof Identity);
256
    }
257
}
258