LoginContext::init()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 17
c 0
b 0
f 0
ccs 10
cts 10
cp 1
rs 9.4285
cc 3
eloc 7
nc 3
nop 0
crap 3
1
<?php
2
3
/**
4
 * AppserverIo\Psr\Security\Auth\Login\LoginContext
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Tim Wagner <[email protected]>
15
 * @copyright 2015 TechDivision GmbH <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/appserver-io-psr/security
18
 * @link      http://www.appserver.io
19
 */
20
21
namespace AppserverIo\Psr\Security\Auth\Login;
22
23
use AppserverIo\Lang\String;
24
use AppserverIo\Collections\HashMap;
25
use AppserverIo\Collections\ArrayList;
26
use AppserverIo\Lang\Reflection\ReflectionClass;
27
use AppserverIo\Psr\Security\Auth\Subject;
28
use AppserverIo\Psr\Security\Auth\Spi\LoginModuleInterface;
29
use AppserverIo\Psr\Security\Auth\Login\SecurityDomainConfigurationInterface;
30
use AppserverIo\Psr\Security\Auth\Callback\CallbackHandlerInterface;
31
32
/**
33
 * A generic LoginContext implementation.
34
 *
35
 * @author    Tim Wagner <[email protected]>
36
 * @copyright 2015 TechDivision GmbH <[email protected]>
37
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
38
 * @link      https://github.com/appserver-io-psr/security
39
 * @link      http://www.appserver.io
40
 */
41
class LoginContext implements LoginContextInterface
42
{
43
44
    /**
45
     * The subject to authenticate.
46
     *
47
     * @var \AppserverIo\Psr\Security\Auth\Subject
48
     */
49
    protected $subject;
50
51
    /**
52
     * The callback handler used by the login modules to communicate with the user.
53
     *
54
     * @var \AppserverIo\Psr\Security\Auth\Callback\CallbackHandlerInterface
55
     */
56
    protected $callbackHandler;
57
58
    /**
59
     * Configuration with the login modules to perform the authentication.
60
     *
61
     * @var \AppserverIo\Psr\Security\Auth\Login\SecurityDomainConfigurationInterface
62
     */
63
    protected $configuration;
64
65
    /**
66
     * The ArrayList with the module configuration.
67
     *
68
     * @var \AppserverIo\Collections\ArrayList
69
     */
70
    protected $moduleStack;
71
72
    /**
73
     * The HashMap to share the state between the login modules.
74
     *
75
     * @var \AppserverIo\Collections\HashMap
76
     */
77
    protected $sharedState;
78
79
    /**
80
     * TRUE if the login has been successful, else FALSE.
81
     *
82
     * @var boolean
83
     */
84
    protected $loginSucceeded = false;
85
86
    /**
87
     * Initialize the LoginContext with the passed objects.
88
     *
89
     * @param \AppserverIo\Psr\Security\Auth\Subject                                    $subject         The subject to authenticate
90
     * @param \AppserverIo\Psr\Security\Auth\Callback\CallbackHandlerInterface          $callbackHandler Used by the login modules to communicate with the user
91
     * @param \AppserverIo\Psr\Security\Auth\Login\SecurityDomainConfigurationInterface $configuration   The configuration with the login modules to perform the authentication
92
     */
93 5
    public function __construct(
94
        Subject $subject,
95
        CallbackHandlerInterface $callbackHandler,
96
        SecurityDomainConfigurationInterface $configuration
97
    ) {
98
99
        // set the passed objects
100 5
        $this->subject = $subject;
101 5
        $this->callbackHandler = $callbackHandler;
102 5
        $this->configuration = $configuration;
103
104
        // initialize the collections
105 5
        $this->sharedState = new HashMap();
106 5
        $this->moduleStack = new ArrayList();
107
108
        // initialize the LoginContext
109 5
        $this->init();
110 5
    }
111
112
    /**
113
     * Return's the login context configuration.
114
     *
115
     * @return \AppserverIo\Psr\Security\Auth\Login\SecurityDomainConfigurationInterface The configuration
116
     */
117 5
    protected function getConfiguration()
118
    {
119 5
        return $this->configuration;
120
    }
121
122
    /**
123
     * Add's the passed module info to the stack.
124
     *
125
     * @param \AppserverIo\Psr\Security\Auth\Login\ModuleInfo $moduleInfo The module info to add
126
     *
127
     * @return void
128
     */
129 1
    protected function addModuleInfo(ModuleInfo $moduleInfo)
130
    {
131 1
        $this->moduleStack->add($moduleInfo);
132 1
    }
133
134
    /**
135
     * Return's the stack with the module information.
136
     *
137
     * @return \AppserverIo\Collections\ArrayList The stack
138
     */
139 1
    protected function getModuleStack()
140
    {
141 1
        return $this->moduleStack;
142
    }
143
144
    /**
145
     * Return's the login context's shared state map.
146
     *
147
     * @return \AppserverIo\Collections\HashMap The map with the shared state data
148
     */
149 1
    protected function getSharedState()
150
    {
151 1
        return $this->sharedState;
152
    }
153
154
    /**
155
     * Return's the callback handler used by the login modules to communicate with the user.
156
     *
157
     * @return \AppserverIo\Psr\Security\Auth\Callback\CallbackHandlerInterface The callback handler
158
     */
159 1
    protected function getCallbackHandler()
160
    {
161 1
        return $this->callbackHandler;
162
    }
163
164
    /**
165
     * Initialize the LoginContext with the passed name.
166
     *
167
     * @return void
168
     */
169 5
    protected function init()
170
    {
171
        // load the authorization configuration for the apropriate security domain
172
        /** @var \AppserverIo\Psr\Security\Auth\Login\AuthConfigurationInterface $authConfiguration */
173 5
        if ($authConfiguration = $this->getConfiguration()->getAuthConfig()) {
174
            // prepare the login modules of the security domain
175
            /** @var \AppserverIo\Psr\Security\Auth\Login\LoginModuleConfigurationInterface $loginModule */
176 1
            foreach ($authConfiguration->getLoginModules() as $loginModule) {
177
                // load the login modules class name and initialization parameters
178 1
                $type = new String($loginModule->getType());
179 1
                $params = new HashMap($loginModule->getParamsAsArray());
180 1
                $controlFlag = new String($loginModule->getFlag());
181
                // add the module information to the stack
182 1
                $this->addModuleInfo(new ModuleInfo($type, $params, $controlFlag));
183 1
            }
184 1
        }
185 5
    }
186
187
    /**
188
     * Create's a new instance of the login module with the passed class name.
189
     *
190
     * @param \AppserverIo\Lang\String $className The login module class name
191
     *
192
     * @return \AppserverIo\Psr\Security\Auth\Spi\LoginModuleInterface The login module instance
193
     */
194 1
    protected function createLoginModuleInstance(String $className)
195
    {
196 1
        $reflectionClass = new ReflectionClass($className->stringValue());
0 ignored issues
show
Bug introduced by
It seems like $className is not always an object, but can also be of type string. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
197 1
        return $reflectionClass->newInstance();
198
    }
199
200
    /**
201
     * Perform the authentication.
202
     *
203
     * REQUIRED:   The login module is required to succeed for the authentication to be successful. If any required
204
     *             module fails, the authentication will fail. The remaining login modules in the stack will be called
205
     *             regardless of the outcome of the authentication.
206
     * REQUISITE:  The login module is required to succeed. If it succeeds, authentication continues down the login
207
     *             stack. If it fails, control immediately returns to the application.
208
     * SUFFICIENT: The login module is not required to succeed. If it does succeed, control immediately returns to the
209
     *             application. If it fails, authentication continues down the login stack.
210
     * OPTIONAL:   The login module is not required to succeed. Authentication still continues to proceed down the
211
     *             login stack regardless of whether the login module succeeds or fails.
212
     *
213
     * @return void
214
     * @throw \AppserverIo\Psr\Security\Auth\Login\LoginException Is thrown if the authentication fails
215
     * @see \AppserverIo\Psr\Security\Auth\Login\LoginContextInterface::login()
216
     */
217 5
    public function login()
218
    {
219
220
        // login has NOT succeeded yet
221 5
        $failure = false;
222
223
        // the array containing the initialized login modules
224 5
        $loginModules = array();
225
226
        // process the login modules and try to authenticate the user
227
        /** @var \AppserverIo\Psr\Security\Auth\Login\ModuleInfo $moduleInfo */
228 5
        foreach ($this->getModuleStack() as $index => $moduleInfo) {
229
            try {
230
                // initialize the login module and invoke the login() method
231
                /** @var \AppserverIo\Psr\Security\Auth\Spi\LoginModuleInterface $loginModules[$index] */
232 4
                $loginModules[$index] = $this->createLoginModuleInstance($moduleInfo->getType());
233 4
                $loginModules[$index]->initialize($this->getSubject(), $this->getCallbackHandler(), $this->getSharedState(), $moduleInfo->getParams());
234
235
                // query whether or not the login attempt failed
236 4
                if ($loginModules[$index]->login() === true) {
237
                    // commit the login attempt
238 2
                    $loginModules[$index]->commit();
239
                    // if the login module has the SUFFICIENT flag, we stop processing
240 2
                    if ($moduleInfo->hasControlFlag(new String(LoginModuleConfigurationInterface::SUFFICIENT))) {
241 1
                        break;
242
                    }
243
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
244 1
                } else {
245
                    // we need to be aware of the login module's control flag
246 2
                    if ($moduleInfo->hasControlFlag(new String(LoginModuleConfigurationInterface::REQUISITE))) {
247 1
                        throw new LoginException(sprintf('REQUISITE module %s failed', get_class($loginModules[$index])));
248 1
                    } elseif ($moduleInfo->hasControlFlag(new String(LoginModuleConfigurationInterface::REQUIRED))) {
249 1
                        $failure = true;
250 1
                    } else {
251
                        // do nothing, because we're OPTIONAL or SUFFICIENT
252
                    }
253
                }
254
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
255 3
            } catch (LoginException $le) {
256
                // abort the login process
257 1
                $loginModules[$index]->abort();
258
                // re-throw the exception
259 1
                throw $le;
260
            }
261 4
        }
262
263
        // query whether or not one of the required login modules failed
264 4
        if ($failure === true) {
265
            // abort the REQUIRED login modules
266 1
            foreach ($loginModules as $loginModule) {
267 1
                $loginModule->abort();
268 1
            }
269
            // throw an exception if one of the REQUIRED login modules failed
270 1
            throw new LoginException('Not all REQUIRED modules succeeded');
271
        }
272 3
    }
273
274
    /**
275
     * Logout the subject.
276
     *
277
     * @return void
278
     * @throw \AppserverIo\Psr\Security\Auth\Login\LoginException Is thrown if the logout fails
279
     * @see \AppserverIo\Psr\Security\Auth\Login\LoginContextInterface::logout()
280
     */
281 2
    public function logout()
282
    {
283
284
        // process the login modules and try to authenticate the user
285
        /** @var \AppserverIo\Psr\Security\Auth\Login\ModuleInfo $moduleInfo */
286 2
        foreach ($this->getModuleStack() as $moduleInfo) {
287
            try {
288
                // initialize the login module and invoke the logout() method
289
                /** @var \AppserverIo\Psr\Security\Auth\Spi\LoginModuleInterface $loginModule */
290 2
                $loginModule = $this->createLoginModuleInstance($moduleInfo->getType());
291 2
                $loginModule->initialize($this->getSubject(), $this->getCallbackHandler(), $this->getSharedState(), $moduleInfo->getParams());
292
293
                // query whether or not the login attempt failed
294 2
                $loginModule->logout();
295
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
296 2
            } catch (\Exception $e) {
297 1
                throw new LoginException($e->__toString());
298
            }
299 1
        }
300 1
    }
301
302
    /**
303
     * Return the authenticated subject.
304
     *
305
     * @return \AppserverIo\Psr\Security\Auth\Subject The authenticated Subject
306
     * @see \AppserverIo\Psr\Security\Auth\Login\LoginContextInterface::getSubject()
307
     */
308 1
    public function getSubject()
309
    {
310 1
        return $this->subject;
311
    }
312
}
313