Passed
Push — master ( cca6a3...d8d955 )
by Tim
04:33
created

Adfs::usernamemixed()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 10
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 18
rs 9.9332
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\Module\adfs\Controller;
6
7
use Exception;
8
use SimpleSAML\{Configuration, IdP, Logger, Metadata, Module, Session, Utils};
9
use SimpleSAML\Error as SspError;
10
use SimpleSAML\Module\adfs\IdP\ADFS as ADFS_IDP;
11
use SimpleSAML\Module\adfs\IdP\MetadataBuilder;
12
use SimpleSAML\Module\adfs\IdP\PassiveIdP;
13
use SimpleSAML\Module\adfs\MetadataExchange;
14
use SimpleSAML\SOAP\XML\env_200305\Envelope;
15
use SimpleSAML\XML\DOMDocumentFactory;
16
use Symfony\Component\HttpFoundation\{Request, Response, StreamedResponse};
17
18
/**
19
 * Controller class for the adfs module.
20
 *
21
 * This class serves the adfs views available in the module.
22
 *
23
 * @package SimpleSAML\Module\adfs
24
 */
25
class Adfs
26
{
27
    /** @var \SimpleSAML\Configuration */
28
    protected Configuration $config;
29
30
    /** @var \SimpleSAML\Metadata\MetaDataStorageHandler */
31
    protected Metadata\MetaDataStorageHandler $metadata;
32
33
    /** @var \SimpleSAML\Session */
34
    protected Session $session;
35
36
    /** @var \SimpleSAML\Utils\Crypto */
37
    protected Utils\Crypto $cryptoUtils;
38
39
    /**
40
     * AdfsController constructor.
41
     *
42
     * @param \SimpleSAML\Configuration $config The configuration to use.
43
     * @param \SimpleSAML\Session $session The current user session.
44
     */
45
    public function __construct(Configuration $config, Session $session)
46
    {
47
        $this->config = $config;
48
        $this->metadata = Metadata\MetaDataStorageHandler::getMetadataHandler($config);
0 ignored issues
show
Unused Code introduced by
The call to SimpleSAML\Metadata\Meta...r::getMetadataHandler() has too many arguments starting with $config. ( Ignorable by Annotation )

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

48
        /** @scrutinizer ignore-call */ 
49
        $this->metadata = Metadata\MetaDataStorageHandler::getMetadataHandler($config);

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...
49
        $this->session = $session;
50
        $this->cryptoUtils = new Utils\Crypto();
51
    }
52
53
54
    /**
55
     * @param \Symfony\Component\HttpFoundation\Request $request
56
     * @return \Symfony\Component\HttpFoundation\Response|\SimpleSAML\XHTML\Template
57
     */
58
    public function metadata(Request $request): Response
59
    {
60
        if (!$this->config->getOptionalBoolean('enable.adfs-idp', false)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->config->getOption...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...
61
            throw new SspError\Error('NOACCESS');
62
        }
63
64
        // check if valid local session exists
65
        $authUtils = new Utils\Auth();
66
        if ($this->config->getOptionalBoolean('admin.protectmetadata', false) && !$authUtils->isAdmin()) {
67
            return new StreamedResponse([$authUtils, 'requireAdmin']);
68
        }
69
70
        try {
71
            if ($request->query->has('idpentityid')) {
72
                $idpentityid = $request->query->get('idpentityid');
73
            } else {
74
                $idpentityid = $this->metadata->getMetaDataCurrentEntityID('adfs-idp-hosted');
75
            }
76
            $idpmeta = $this->metadata->getMetaDataConfig($idpentityid, 'adfs-idp-hosted');
77
78
            $builder = new MetadataBuilder($this->config, $idpmeta);
79
80
            $document = $builder->buildDocument()->toXML();
81
            // Some products like DirX are known to break on pretty-printed XML
82
            $document->ownerDocument->formatOutput = false;
83
            $document->ownerDocument->encoding = 'UTF-8';
84
            $metaxml = $document->ownerDocument->saveXML();
0 ignored issues
show
Bug introduced by
The method saveXML() does not exist on null. ( Ignorable by Annotation )

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

84
            /** @scrutinizer ignore-call */ 
85
            $metaxml = $document->ownerDocument->saveXML();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
85
86
            $response = new Response();
87
            $response->setEtag(hash('sha256', $metaxml));
88
            $response->setPublic();
89
            if ($response->isNotModified($request)) {
90
                return $response;
91
            }
92
            $response->headers->set('Content-Type', 'application/samlmetadata+xml');
93
            $response->headers->set('Content-Disposition', 'attachment; filename="FederationMetadata.xml"');
94
            $response->setContent($metaxml);
95
96
            return $response;
97
        } catch (Exception $exception) {
98
            throw new SspError\Error('METADATA', $exception);
99
        }
100
    }
101
102
103
    /**
104
     * @param \Symfony\Component\HttpFoundation\Request $request
105
     * @return \Symfony\Component\HttpFoundation\Response
106
     */
107
    public function prp(Request $request): Response
108
    {
109
        Logger::info('ADFS - IdP.prp: Accessing ADFS IdP endpoint prp');
110
111
        $idpEntityId = $this->metadata->getMetaDataCurrentEntityID('adfs-idp-hosted');
112
        $idp = IdP::getById('adfs:' . $idpEntityId);
113
114
        if ($request->get('wa', null) !== null) {
115
            $wa = $request->get('wa');
116
            if ($wa === 'wsignout1.0') {
117
                return new StreamedResponse(
118
                    function () use ($idp) {
119
                        ADFS_IDP::receiveLogoutMessage($idp);
120
                    },
121
                );
122
            } elseif ($wa === 'wsignin1.0') {
123
                return ADFS_IDP::receiveAuthnRequest($request, $idp);
124
            }
125
            throw new SspError\BadRequest("Unsupported value for 'wa' specified in request.");
126
        } elseif ($request->get('assocId', null) !== null) {
127
            // logout response from ADFS SP
128
            // Association ID of the SP that sent the logout response
129
            $assocId = $request->get('assocId');
130
            // Data that was sent in the logout request to the SP. Can be null
131
            $relayState = $request->get('relayState');
132
            // null on success, or an instance of a \SimpleSAML\Error\Exception on failure.
133
            $logoutError = null;
134
135
            return new StreamedResponse(
136
                function () use ($idp, /** @scrutinizer ignore-type */ $assocId, $relayState, $logoutError) {
137
                    $idp->handleLogoutResponse($assocId, $relayState, $logoutError);
138
                },
139
            );
140
        }
141
        throw new SspError\BadRequest("Missing parameter 'wa' or 'assocId' in request.");
142
    }
143
144
145
    /**
146
     * @param \Symfony\Component\HttpFoundation\Request $request
147
     * @return \Symfony\Component\HttpFoundation\Response
148
     */
149
    public function mex(Request $request): Response
150
    {
151
        if (!$this->config->getOptionalBoolean('enable.adfs-idp', false)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->config->getOption...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...
152
            throw new SspError\Error('NOACCESS');
153
        }
154
155
        // check if valid local session exists
156
        $authUtils = new Utils\Auth();
157
        if ($this->config->getOptionalBoolean('admin.protectmetadata', false) && !$authUtils->isAdmin()) {
158
            return new StreamedResponse([$authUtils, 'requireAdmin']);
159
        }
160
161
        $mexBuilder = new MetadataExchange();
162
        $document = $mexBuilder->buildDocument()->toXML();
163
        // Some products like DirX are known to break on pretty-printed XML
164
        $document->ownerDocument->formatOutput = false;
165
        $document->ownerDocument->encoding = 'UTF-8';
166
167
        $document->setAttributeNS(
168
            'http://www.w3.org/2000/xmlns/',
169
            'xmlns:tns',
170
            'http://schemas.microsoft.com/ws/2008/06/identity/securitytokenservice',
171
        );
172
173
        $document->setAttributeNS(
174
            'http://www.w3.org/2000/xmlns/',
175
            'xmlns:soapenc',
176
            'http://schemas.xmlsoap.org/soap/encoding/',
177
        );
178
179
        $document->setAttributeNS(
180
            'http://www.w3.org/2000/xmlns/',
181
            'xmlns:msc',
182
            'http://schemas.microsoft.com/ws/2005/12/wsdl/contract',
183
        );
184
185
        $document->setAttributeNS(
186
            'http://www.w3.org/2000/xmlns/',
187
            'xmlns:wsam',
188
            'http://www.w3.org/2007/05/addressing/metadata',
189
        );
190
191
        $document->setAttributeNS(
192
            'http://www.w3.org/2000/xmlns/',
193
            'xmlns:wsap',
194
            'http://schemas.xmlsoap.org/ws/2004/08/addressing/policy',
195
        );
196
197
        $metaxml = $document->ownerDocument->saveXML();
198
199
        $response = new Response();
200
        $response->setEtag(hash('sha256', $metaxml));
201
        $response->setPublic();
202
        if ($response->isNotModified($request)) {
203
            return $response;
204
        }
205
        $response->headers->set('Content-Type', 'text/xml');
206
        $response->setContent($metaxml);
207
208
        return $response;
209
    }
210
211
212
    /**
213
     * @param \Symfony\Component\HttpFoundation\Request $request
214
     * @return \Symfony\Component\HttpFoundation\Response
215
     */
216
    public function usernamemixed(Request $request): Response
217
    {
218
        if (!$this->config->getOptionalBoolean('enable.adfs-idp', false)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->config->getOption...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...
219
            throw new SspError\Error('NOACCESS');
220
        }
221
222
        $soapMessage = $request->getContent();
223
        if ($soapMessage === false) {
0 ignored issues
show
introduced by
The condition $soapMessage === false is always false.
Loading history...
224
            throw new SspError\BadRequest('Missing SOAP-content.');
225
        }
226
227
        $domDocument = DOMDocumentFactory::fromString($soapMessage);
228
        $soapEnvelope = Envelope::fromXML($domDocument->documentElement);
229
230
        $idpEntityId = $this->metadata->getMetaDataCurrentEntityID('adfs-idp-hosted');
231
        $idp = PassiveIdP::getById($this->config, 'adfs:' . $idpEntityId);
232
233
        return ADFS_IDP::receivePassiveAuthnRequest($request, $soapEnvelope, $idp);
234
    }
235
}
236