Completed
Push — master ( ab5aec...dc8798 )
by Raffael
01:44
created

Auth::getDefaultAdapter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
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
                case 'adapter':
96
                    foreach($value as $name => $adapter) {
97
                        $this->injectAdapter($name, $adapter);
98
                    }
99
                break;
100
                default:
101
                    throw new Exception('invalid option '.$option.' given');
102
            }
103
        }
104
105
        return $this;
106
    }
107
108
109
    /**
110
     * Has adapter
111
     *
112
     * @param  string $name
113
     * @return bool
114
     */
115
    public function hasAdapter(string $name): bool
116
    {
117
        return isset($this->adapter[$name]);
118
    }
119
120
121
    /**
122
     * Get default adapter
123
     *
124
     * @return array
125
     */
126
    public function getDefaultAdapter(): array
127
    {
128
        return [];
129
    }
130
131
132
    /**
133
     * Inject adapter
134
     *
135
     * @param  string $name
136
     * @param  AdapterInterface $adapter
137
     * @return AdapterInterface
138
     */
139
    public function injectAdapter(string $name, AdapterInterface $adapter) : AdapterInterface
140
    {
141
        if ($this->hasAdapter($name)) {
142
            throw new Exception('auth adapter '.$name.' is already registered');
143
        }
144
145
        $this->adapter[$name] = $adapter;
146
        return $adapter;
147
    }
148
149
150
    /**
151
     * Get adapter
152
     *
153
     * @param  string $name
154
     * @return AdapterInterface
155
     */
156
    public function getAdapter(string $name): AdapterInterface
157
    {
158
        if (!$this->hasAdapter($name)) {
159
            throw new Exception('auth adapter '.$name.' is not registered');
160
        }
161
162
        return $this->adapter[$name];
163
    }
164
165
166
    /**
167
     * Get adapters
168
     *
169
     * @param  array $adapters
170
     * @return array
171
     */
172
    public function getAdapters(array $adapters = []): array
173
    {
174
        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...
175
            return $this->adapter;
176
        } else {
177
            $list = [];
178
            foreach ($adapter as $name) {
179
                if (!$this->hasAdapter($name)) {
180
                    throw new Exception('auth adapter '.$name.' is not registered');
181
                }
182
                $list[$name] = $this->adapter[$name];
183
            }
184
185
            return $list;
186
        }
187
    }
188
189
190
    /**
191
     * Create identity
192
     *
193
     * @param  AdapterInterface $adapter
194
     * @return Identity
195
     */
196
    protected function createIdentity(AdapterInterface $adapter): Identity
197
    {
198
        $map = new $this->attribute_map_class($adapter->getAttributeMap(), $this->logger);
199
        $this->identity = new $this->identity_class($adapter, $map, $this->logger);
200
        return $this->identity;
201
    }
202
203
204
    /**
205
     * Authenticate
206
     *
207
     * @return  bool
208
     */
209
    public function requireOne(): bool
210
    {
211
        $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...
212
213
        foreach ($this->adapter as $name => $adapter) {
214
            try {
215
                if ($adapter->authenticate()) {
216
                    $this->createIdentity($adapter);
217
218
                    $this->logger->info("identity [{$this->identity->getIdentifier()}] authenticated over adapter [{$name}]", [
219
                        'category' => get_class($this)
220
                    ]);
221
                    $_SERVER['REMOTE_USER'] = $this->identity->getIdentifier();
222
223
                    return true;
224
                }
225
            } catch (\Exception $e) {
226
                $this->logger->error("failed authenticate user, unexcepted exception was thrown", [
227
                    'category' => get_class($this),
228
                    'exception'=> $e
229
                ]);
230
            }
231
232
            $this->logger->debug("auth adapter [{$name}] failed", [
233
                'category' => get_class($this)
234
            ]);
235
        }
236
237
        $this->logger->warning("all authentication adapter have failed", [
238
            'category' => get_class($this)
239
        ]);
240
241
        return false;
242
    }
243
244
245
    /**
246
     * Get identity
247
     *
248
     * @return Identity
249
     */
250
    public function getIdentity(): Identity
251
    {
252
        if (!$this->isAuthenticated()) {
253
            throw new Exception('no valid authentication yet');
254
        } else {
255
            return $this->identity;
256
        }
257
    }
258
259
260
    /**
261
     * Check if valid identity exists
262
     *
263
     * @return bool
264
     */
265
    public function isAuthenticated(): bool
266
    {
267
        return ($this->identity instanceof Identity);
268
    }
269
}
270