PassiveIdP::getByState()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 5
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\Module\adfs\IdP;
6
7
use Exception;
8
use SimpleSAML\Assert\Assert;
9
use SimpleSAML\Auth;
10
use SimpleSAML\Configuration;
11
use SimpleSAML\Error;
12
use SimpleSAML\IdP\IFrameLogoutHandler;
13
use SimpleSAML\IdP\LogoutHandlerInterface;
14
use SimpleSAML\IdP\TraditionalLogoutHandler;
15
use SimpleSAML\Metadata\MetaDataStorageHandler;
16
use SimpleSAML\Session;
17
use Symfony\Component\HttpFoundation\Response;
18
19
use function call_user_func;
20
use function substr;
21
use function time;
22
use function var_export;
23
24
/**
25
 * IdP class.
26
 *
27
 * This class implements the various functions used by IdP.
28
 *
29
 * @package simplesamlphp/simplesamlphp-module-adfs
30
 */
31
32
class PassiveIdP
33
{
34
    /**
35
     * A cache for resolving IdP id's.
36
     *
37
     * @var \SimpleSAML\Module\adfs\IdP\PassiveIdP[]
38
     */
39
    private static array $idpCache = [];
40
41
    /**
42
     * The identifier for this IdP.
43
     *
44
     * @var string
45
     */
46
    private string $id;
47
48
    /**
49
     * The configuration for this IdP.
50
     *
51
     * @var \SimpleSAML\Configuration
52
     */
53
    private Configuration $config;
54
55
    /**
56
     * The global configuration.
57
     *
58
     * @var \SimpleSAML\Configuration
59
     */
60
    private Configuration $globalConfig;
61
62
    /**
63
     * Our authsource.
64
     *
65
     * @var \SimpleSAML\Auth\Simple
66
     */
67
    private Auth\Simple $authSource;
68
69
70
    /**
71
     * Initialize an IdP.
72
     *
73
     * @param \SimpleSAML\Configuration $config The configuration
74
     * @param string $id The identifier of this IdP.
75
     *
76
     * @throws \SimpleSAML\Error\Exception If the IdP is disabled or no such auth source was found.
77
     */
78
    private function __construct(Configuration $config, string $id)
79
    {
80
        $this->id = $id;
81
82
        $this->globalConfig = $config;
83
        $metadata = MetaDataStorageHandler::getMetadataHandler($this->globalConfig);
0 ignored issues
show
Unused Code introduced by
The call to SimpleSAML\Metadata\Meta...r::getMetadataHandler() has too many arguments starting with $this->globalConfig. ( Ignorable by Annotation )

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

83
        /** @scrutinizer ignore-call */ 
84
        $metadata = MetaDataStorageHandler::getMetadataHandler($this->globalConfig);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
84
85
        if (substr($id, 0, 5) === 'adfs:') {
86
            if (!$this->globalConfig->getOptionalBoolean('enable.adfs-idp', false)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->globalConfig->get...nable.adfs-idp', false) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
87
                throw new Error\Exception('enable.adfs-idp disabled in config.php.');
88
            }
89
            $this->config = $metadata->getMetaDataConfig(substr($id, 5), 'adfs-idp-hosted');
90
        } else {
91
            throw new Exception("Protocol not implemented.");
92
        }
93
94
        $auth = $this->config->getString('passiveAuth');
95
        if (Auth\Source::getById($auth) !== null) {
96
            $this->authSource = new Auth\Simple($auth);
97
        } else {
98
            throw new Error\Exception('No such "' . $auth . '" auth source found.');
99
        }
100
    }
101
102
103
    /**
104
     * Retrieve the ID of this IdP.
105
     *
106
     * @return string The ID of this IdP.
107
     */
108
    public function getId(): string
109
    {
110
        return $this->id;
111
    }
112
113
114
    /**
115
     * Retrieve an IdP by ID.
116
     *
117
     * @param \SimpleSAML\Configuration $config The Configuration
118
     * @param string $id The identifier of the IdP.
119
     *
120
     * @return \SimpleSAML\Module\adfs\IdP\PassiveIdP The IdP.
121
     */
122
    public static function getById(Configuration $config, string $id): PassiveIdP
123
    {
124
        if (isset(self::$idpCache[$id])) {
125
            return self::$idpCache[$id];
126
        }
127
128
        $idp = new self($config, $id);
129
        self::$idpCache[$id] = $idp;
130
        return $idp;
131
    }
132
133
134
    /**
135
     * Retrieve the IdP "owning" the state.
136
     *
137
     * @param \SimpleSAML\Configuration $config The Configuration.
138
     * @param array<mixed> &$state The state array.
139
     *
140
     * @return \SimpleSAML\Module\adfs\IdP\PassiveIdP The IdP.
141
     */
142
    public static function getByState(Configuration $config, array &$state): PassiveIdP
143
    {
144
        Assert::notNull($state['core:IdP']);
145
146
        return self::getById($config, $state['core:IdP']);
147
    }
148
149
150
    /**
151
     * Retrieve the configuration for this IdP.
152
     *
153
     * @return \SimpleSAML\Configuration The configuration object.
154
     */
155
    public function getConfig(): Configuration
156
    {
157
        return $this->config;
158
    }
159
160
161
    /**
162
     * Is the current user authenticated?
163
     *
164
     * @return boolean True if the user is authenticated, false otherwise.
165
     */
166
    public function isAuthenticated(): bool
167
    {
168
        return $this->authSource->isAuthenticated();
169
    }
170
171
172
    /**
173
     * Called after authproc has run.
174
     *
175
     * @param array<mixed> $state The authentication request state array.
176
     */
177
    public static function postAuthProc(array $state): void
178
    {
179
        Assert::isCallable($state['Responder']);
180
181
        if (isset($state['core:SP'])) {
182
            $session = Session::getSessionFromRequest();
183
            $session->setData(
184
                'core:idp-ssotime',
185
                $state['core:IdP'] . ';' . $state['core:SP'],
186
                time(),
187
                Session::DATA_TIMEOUT_SESSION_END,
188
            );
189
        }
190
191
        call_user_func($state['Responder'], $state);
192
        Assert::true(false);
193
    }
194
195
196
    /**
197
     * The user is authenticated.
198
     *
199
     * @param array<mixed> $state The authentication request state array.
200
     *
201
     * @throws \SimpleSAML\Error\Exception If we are not authenticated.
202
     */
203
    public static function postAuth(array $state): Response
204
    {
205
        $idp = PassiveIdP::getByState(Configuration::getInstance(), $state);
206
207
        if (!$idp->isAuthenticated()) {
208
            throw new Error\Exception('Not authenticated.');
209
        }
210
211
        $state['Attributes'] = $idp->authSource->getAttributes();
212
213
        if (isset($state['SPMetadata'])) {
214
            $spMetadata = $state['SPMetadata'];
215
        } else {
216
            $spMetadata = [];
217
        }
218
219
        if (isset($state['core:SP'])) {
220
            $session = Session::getSessionFromRequest();
221
            $previousSSOTime = $session->getData('core:idp-ssotime', $state['core:IdP'] . ';' . $state['core:SP']);
222
            if ($previousSSOTime !== null) {
223
                $state['PreviousSSOTimestamp'] = $previousSSOTime;
224
            }
225
        }
226
227
        $idpMetadata = $idp->getConfig()->toArray();
228
229
        $pc = new Auth\ProcessingChain($idpMetadata, $spMetadata, 'idp');
230
231
        $state['ReturnCall'] = ['\SimpleSAML\Module\adfs\IdP\PassiveIdP', 'postAuthProc'];
232
        $state['Destination'] = $spMetadata;
233
        $state['Source'] = $idpMetadata;
234
235
        $pc->processState($state);
236
237
        return self::postAuthProc($state);
0 ignored issues
show
Bug introduced by
Are you sure the usage of self::postAuthProc($state) targeting SimpleSAML\Module\adfs\I...siveIdP::postAuthProc() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug Best Practice introduced by
The expression return self::postAuthProc($state) returns the type void which is incompatible with the type-hinted return Symfony\Component\HttpFoundation\Response.
Loading history...
238
    }
239
240
241
    /**
242
     * Authenticate the user.
243
     *
244
     * This function authenticates the user.
245
     *
246
     * @param array<mixed> &$state The authentication request state.
247
     */
248
    private function authenticate(array &$state): Response
249
    {
250
        return $this->authSource->login($state);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->authSource->login($state) targeting SimpleSAML\Auth\Simple::login() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug Best Practice introduced by
The expression return $this->authSource->login($state) returns the type void which is incompatible with the type-hinted return Symfony\Component\HttpFoundation\Response.
Loading history...
251
    }
252
253
254
    /**
255
     * Process authentication requests.
256
     *
257
     * @param array<mixed> &$state The authentication request state.
258
     */
259
    public function handleAuthenticationRequest(array &$state): Response
260
    {
261
        Assert::notNull($state['Responder']);
262
263
        $state['core:IdP'] = $this->id;
264
265
        if (isset($state['SPMetadata']['entityid'])) {
266
            $spEntityId = $state['SPMetadata']['entityid'];
267
        } elseif (isset($state['SPMetadata']['entityID'])) {
268
            $spEntityId = $state['SPMetadata']['entityID'];
269
        } else {
270
            $spEntityId = null;
271
        }
272
273
        $state['core:SP'] = $spEntityId;
274
        $state['IdPMetadata'] = $this->getConfig()->toArray();
275
        $state['ReturnCallback'] = ['\SimpleSAML\Module\adfs\IdP\PassiveIdP', 'postAuth'];
276
277
        try {
278
            return $this->authenticate($state);
279
        } catch (Error\Exception $e) {
280
            Auth\State::throwException($state, $e);
281
        } catch (Exception $e) {
282
            $e = new Error\UnserializableException($e);
283
            Auth\State::throwException($state, $e);
284
        }
285
286
        throw new Exception('Should never happen.');
287
    }
288
289
290
    /**
291
     * Find the logout handler of this IdP.
292
     *
293
     * @return \SimpleSAML\IdP\LogoutHandlerInterface The logout handler class.
294
     *
295
     * @throws \Exception If we cannot find a logout handler.
296
     */
297
    public function getLogoutHandler(): LogoutHandlerInterface
298
    {
299
        // find the logout handler
300
        $logouttype = $this->getConfig()->getOptionalString('logouttype', 'traditional');
301
        switch ($logouttype) {
302
            case 'traditional':
303
                $handler = TraditionalLogoutHandler::class;
304
                break;
305
            case 'iframe':
306
                $handler = IFrameLogoutHandler::class;
307
                break;
308
            default:
309
                throw new Error\Exception('Unknown logout handler: ' . var_export($logouttype, true));
310
        }
311
312
        /** @var \SimpleSAML\IdP\LogoutHandlerInterface */
313
        return new $handler($this);
314
    }
315
316
317
    /**
318
     * Finish the logout operation.
319
     *
320
     * This function will never return.
321
     *
322
     * @param array<mixed> &$state The logout request state.
323
     */
324
    public function finishLogout(array &$state): Response
325
    {
326
        Assert::notNull($state['Responder']);
327
        return call_user_func($state['Responder'], $state);
328
    }
329
}
330