Completed
Push — master ( a51466...8daf75 )
by Raffael
02:47
created

Auth::setOptions()   C

Complexity

Conditions 11
Paths 10

Size

Total Lines 38
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 38
rs 5.2653
c 0
b 0
f 0
cc 11
eloc 24
nc 10
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * Micro
6
 *
7
 * @copyright Copyright (c) 2017 gyselroth GmbH (https://gyselroth.com)
8
 * @license   MIT https://opensource.org/licenses/MIT
9
 */
10
11
namespace Micro;
12
13
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...
14
use \Micro\Auth\Adapter\AdapterInterface;
15
use \Micro\Auth\Identity;
16
use \Psr\Log\LoggerInterface as Logger;
17
use \Micro\Http\Response;
18
19
class Auth
20
{
21
    /**
22
     * Adapter
23
     *
24
     * @var array
25
     */
26
    protected $adapter = [];
27
    
28
29
    /**
30
     * Identity
31
     *
32
     * @var Identity
33
     */
34
    protected $identity;
35
36
37
    /**
38
     * Logger
39
     *
40
     * @var Logger
41
     */
42
    protected $logger;
43
44
45
    /**
46
     * Identity class
47
     *  
48
     * @var string
49
     */
50
    protected $identity_class = '\\Micro\\Auth\\Identity';
51
    
52
    
53
    /**
54
     * Attribute map class
55
     *  
56
     * @var string
57
     */
58
    protected $attribute_map_class = '\\Micro\\Auth\\AttributeMap';
59
60
61
    /**
62
     * Initialize
63
     *
64
     * @param   Iterable $config
65
     * @param   Logger $logger
66
     * @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...
67
     */
68
    public function __construct(?Iterable $config=null, Logger $logger)
69
    {
70
        $this->logger = $logger;
71
        $this->setOptions($config);
72
    }
73
74
75
    /**
76
     * Set options
77
     *
78
     * @param  Iterable $config
79
     * @return Log
80
     */
81
    public function setOptions(?Iterable $config=null): Auth
82
    {
83
        if($config === null) {
84
            return $this;
85
        }
86
87
        foreach($config as $option => $value) {
88
            switch($option) {
89
                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...
90
                case 'attribute_map_class': 
91
                    $this->{$option} = (string)$value;
92
                break;
93
94
                case 'adapter':
95
                    foreach($value as $name => $adapter) {
96
                        if(!isset($adapter['enabled']) || $adapter['enabled'] === '1') {
97
                            if(!isset($adapter['class'])) {
98
                                throw new Exception('class option is required');
99
                            }
100
                        
101
                            if(isset($adapter['config'])) {
102
                                $config = $adapter['config'];
103
                            } else {
104
                                $config = null;
105
                            }
106
                            $this->addAdapter($name, $adapter['class'], $config);
107
                        } else {
108
                            $this->logger->debug("skip disabled authentication adapter [".$name."]", [
109
                                'category' => get_class($this)
110
                            ]);
111
                        }
112
                    }
113
                break;
114
            }
115
        }    
116
    
117
        return $this;
118
    }
119
120
121
    /**
122
     * Has adapter
123
     *
124
     * @param  string $name
125
     * @return bool
126
     */
127
    public function hasAdapter(string $name): bool
128
    {
129
        return isset($this->adapter[$name]);
130
    }
131
132
133
    /**
134
     * Add adapter
135
     *
136
     * @param  string $name
137
     * @param  string $class
138
     * @param  Iterable $config
139
     * @return AdapterInterface
140
     */
141
    public function addAdapter(string $name, string $class, ?Iterable $config=null): AdapterInterface
142
    {
143
        if($this->hasAdapter($name)) {
144
            throw new Exception('auth adapter '.$name.' is already registered');
145
        }
146
            
147
        $adapter = new $class($config, $this->logger);
148
        if(!($adapter instanceof AdapterInterface)) {
149
            throw new Exception('auth adapter must include AdapterInterface interface');
150
        }
151
        $this->adapter[$name] = $adapter;
152
        return $adapter;
153
    }
154
155
156
    /**
157
     * Get adapter
158
     *      
159
     * @param  string $name
160
     * @return AdapterInterface
161
     */
162
    public function getAdapter(string $name): AdapterInterface
163
    {
164
        if(!$this->hasAdapter($name)) {
165
            throw new Exception('auth adapter '.$name.' is not registered');
166
        }
167
168
        return $this->adapter[$name];
169
    }
170
171
172
    /**
173
     * Get adapters
174
     *      
175
     * @param  array $adapters
176
     * @return array
177
     */
178
    public function getAdapters(array $adapters=[]): array
0 ignored issues
show
Unused Code introduced by
The parameter $adapters is not used and could be removed.

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

Loading history...
179
    {
180
        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...
181
            return $this->adapter;
182
        } else {
183
            $list = [];
184
            foreach($adapter as $name) {
185
                if(!$this->hasAdapter($name)) {
186
                    throw new Exception('auth adapter '.$name.' is not registered');
187
                }
188
                $list[$name] = $this->adapter[$name];
189
            }
190
191
            return $list;
192
        }
193
    }
194
195
196
    /**
197
     * Create identity
198
     *
199
     * @param  AdapterInterface $adapter
200
     * @return Identity
201
     */
202
    protected function createIdentity(AdapterInterface $adapter): Identity
203
    {
204
        $map = new $this->attribute_map_class($adapter->getAttributeMap(), $this->logger);
205
        $this->identity = new $this->identity_class($adapter, $map, $this->logger);
206
        return $this->identity;
207
    }
208
209
210
    /**
211
     * Authenticate
212
     *
213
     * @param   Iterable $adapters
0 ignored issues
show
Bug introduced by
There is no parameter named $adapters. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
214
     * @return  bool
215
     */
216
    public function requireOne(): bool
217
    {
218
        $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...
219
        
220
        foreach($this->adapter as $name => $adapter) {
221
            try {
222
                if ($adapter->authenticate()) {
223
                    $this->createIdentity($adapter);      
224
 
225
                    $this->logger->info("identity [{$this->identity->getIdentifier()}] authenticated over adapter [{$name}]", [
226
                        'category' => get_class($this)
227
                    ]);
228
                    $_SERVER['REMOTE_USER'] = $this->identity->getIdentifier();
229
                    
230
                    return true;
231
                }
232
            } catch (\Exception $e) {
233
                $this->logger->error("failed authenticate user, unexcepted exception was thrown", [
234
                    'category' => get_class($this),
235
                    'exception'=> $e
236
                ]);
237
            }
238
        
239
            $this->logger->debug("auth adapter [{$name}] failed", [
240
                'category' => get_class($this)
241
            ]);
242
        }
243
        
244
        $this->logger->warning("all authentication adapter have failed", [
245
           'category' => get_class($this)
246
        ]);
247
248
        return false;
249
    }
250
251
252
    /**
253
     * Get identity
254
     *
255
     * @return identity
256
     */
257
    public function getIdentity(): Identity
258
    {
259
        if (!$this->isAuthenticated()) {
260
            throw new Exception('no valid authentication yet');
261
        } else {
262
            return $this->identity;
263
        }
264
    }
265
266
267
    /**
268
     * Check if valid identity exists
269
     *
270
     * @return bool
271
     */
272
    public function isAuthenticated(): bool
273
    {
274
        return ($this->identity instanceof Identity);
275
    }
276
}
277