Adfs::metadata()   F
last analyzed

Complexity

Conditions 20
Paths > 20000

Size

Total Lines 153
Code Lines 98

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 20
eloc 98
nc 111039
nop 1
dl 0
loc 153
rs 0
c 0
b 0
f 0

How to fix   Long Method    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
3
declare(strict_types=1);
4
5
namespace SimpleSAML\Module\adfs\Controller;
6
7
use Exception;
8
use SAML2\Constants;
9
use SimpleSAML\Assert\Assert;
10
use SimpleSAML\Configuration;
11
use SimpleSAML\Error as SspError;
12
use SimpleSAML\IdP;
13
use SimpleSAML\Locale\Translate;
14
use SimpleSAML\Logger;
15
use SimpleSAML\Module;
16
use SimpleSAML\Module\adfs\IdP\ADFS as ADFS_IDP;
17
use SimpleSAML\Metadata;
18
use SimpleSAML\Session;
19
use SimpleSAML\Utils;
20
use SimpleSAML\XHTML\Template;
21
use Symfony\Component\HttpFoundation\Request;
22
use Symfony\Component\HttpFoundation\Response;
23
use Symfony\Component\HttpFoundation\StreamedResponse;
24
25
/**
26
 * Controller class for the adfs module.
27
 *
28
 * This class serves the adfs views available in the module.
29
 *
30
 * @package SimpleSAML\Module\adfs
31
 */
32
class Adfs
33
{
34
    /** @var \SimpleSAML\Configuration */
35
    protected Configuration $config;
36
37
    /** @var \SimpleSAML\Metadata\MetaDataStorageHandler */
38
    protected Metadata\MetaDataStorageHandler $metadata;
39
40
    /** @var \SimpleSAML\Session */
41
    protected Session $session;
42
43
    /** @var \SimpleSAML\Utils\Crypto */
44
    protected Utils\Crypto $cryptoUtils;
45
46
    /**
47
     * AdfsController constructor.
48
     *
49
     * @param \SimpleSAML\Configuration $config The configuration to use.
50
     * @param \SimpleSAML\Session $session The current user session.
51
     */
52
    public function __construct(Configuration $config, Session $session)
53
    {
54
        $this->config = $config;
55
        $this->metadata = Metadata\MetaDataStorageHandler::getMetadataHandler();
56
        $this->session = $session;
57
        $this->cryptoUtils = new Utils\Crypto();
58
    }
59
60
61
    /**
62
     * @param \Symfony\Component\HttpFoundation\Request $request
63
     * @return \Symfony\Component\HttpFoundation\Response|\SimpleSAML\XHTML\Template
64
     */
65
    public function metadata(Request $request): Response
66
    {
67
        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...
68
            throw new SspError\Error('NOACCESS');
69
        }
70
71
        // check if valid local session exists
72
        if ($this->config->getOptionalBoolean('admin.protectmetadata', false)) {
73
            $authUtils = new Utils\Auth();
74
            $authUtils->requireAdmin();
75
        }
76
77
        try {
78
            if ($request->query->has('idpentityid')) {
79
                $idpentityid = $request->query->get('idpentityid');
80
            } else {
81
                $idpentityid = $this->metadata->getMetaDataCurrentEntityID('adfs-idp-hosted');
82
            }
83
            $idpmeta = $this->metadata->getMetaDataConfig($idpentityid, 'adfs-idp-hosted');
84
85
            $availableCerts = [];
86
            $keys = [];
87
            $certInfo = $this->cryptoUtils->loadPublicKey($idpmeta, false, 'new_');
88
89
            if ($certInfo !== null) {
90
                $availableCerts['new_idp.crt'] = $certInfo;
91
                $keys[] = [
92
                    'type'            => 'X509Certificate',
93
                    'signing'         => true,
94
                    'encryption'      => true,
95
                    'X509Certificate' => $certInfo['certData'],
96
                ];
97
                $hasNewCert = true;
98
            } else {
99
                $hasNewCert = false;
100
            }
101
102
            /** @var array $certInfo */
103
            $certInfo = $this->cryptoUtils->loadPublicKey($idpmeta, true);
104
            $availableCerts['idp.crt'] = $certInfo;
105
            $keys[] = [
106
                'type'            => 'X509Certificate',
107
                'signing'         => true,
108
                'encryption'      => ($hasNewCert ? false : true),
109
                'X509Certificate' => $certInfo['certData'],
110
            ];
111
112
            if ($idpmeta->hasValue('https.certificate')) {
113
                /** @var array $httpsCert */
114
                $httpsCert = $this->cryptoUtils->loadPublicKey($idpmeta, true, 'https.');
115
                Assert::keyExists($httpsCert, 'certData');
116
                $availableCerts['https.crt'] = $httpsCert;
117
                $keys[] = [
118
                    'type'            => 'X509Certificate',
119
                    'signing'         => true,
120
                    'encryption'      => false,
121
                    'X509Certificate' => $httpsCert['certData'],
122
                ];
123
            }
124
125
            $adfs_service_location = Module::getModuleURL('adfs') . '/idp/prp.php';
126
            $metaArray = [
127
                'metadata-set'        => 'adfs-idp-remote',
128
                'entityid'            => $idpentityid,
129
                'SingleSignOnService' => [
130
                    0 => [
131
                        'Binding'  => Constants::BINDING_HTTP_REDIRECT,
132
                        'Location' => $adfs_service_location
133
                    ]
134
                ],
135
                'SingleLogoutService' => [
136
                    0 => [
137
                        'Binding'  => Constants::BINDING_HTTP_REDIRECT,
138
                        'Location' => $adfs_service_location
139
                    ]
140
                ],
141
            ];
142
143
            if (count($keys) === 1) {
144
                $metaArray['certData'] = $keys[0]['X509Certificate'];
145
            } else {
146
                $metaArray['keys'] = $keys;
147
            }
148
149
            $metaArray['NameIDFormat'] = $idpmeta->getOptionalString(
150
                'NameIDFormat',
151
                Constants::NAMEID_TRANSIENT
152
            );
153
154
            if ($idpmeta->hasValue('OrganizationName')) {
155
                $metaArray['OrganizationName'] = $idpmeta->getLocalizedString('OrganizationName');
156
                $metaArray['OrganizationDisplayName'] = $idpmeta->getOptionalLocalizedString(
157
                    'OrganizationDisplayName',
158
                    $metaArray['OrganizationName']
159
                );
160
161
                if (!$idpmeta->hasValue('OrganizationURL')) {
162
                    throw new SspError\Exception('If OrganizationName is set, OrganizationURL must also be set.');
163
                }
164
                $metaArray['OrganizationURL'] = $idpmeta->getLocalizedString('OrganizationURL');
165
            }
166
167
            if ($idpmeta->hasValue('scope')) {
168
                $metaArray['scope'] = $idpmeta->getArray('scope');
169
            }
170
171
            if ($idpmeta->hasValue('EntityAttributes')) {
172
                $metaArray['EntityAttributes'] = $idpmeta->getArray('EntityAttributes');
173
            }
174
175
            if ($idpmeta->hasValue('UIInfo')) {
176
                $metaArray['UIInfo'] = $idpmeta->getArray('UIInfo');
177
            }
178
179
            if ($idpmeta->hasValue('DiscoHints')) {
180
                $metaArray['DiscoHints'] = $idpmeta->getArray('DiscoHints');
181
            }
182
183
            if ($idpmeta->hasValue('RegistrationInfo')) {
184
                $metaArray['RegistrationInfo'] = $idpmeta->getArray('RegistrationInfo');
185
            }
186
187
            $metaBuilder = new Metadata\SAMLBuilder($idpentityid);
188
            $metaBuilder->addSecurityTokenServiceType($metaArray);
189
            $metaBuilder->addOrganizationInfo($metaArray);
190
            $technicalContactEmail = $this->config->getOptionalString('technicalcontact_email', null);
191
            if ($technicalContactEmail !== null && $technicalContactEmail !== '[email protected]') {
192
                $metaBuilder->addContact(Utils\Config\Metadata::getContact([
193
                    'emailAddress' => $technicalContactEmail,
194
                    'givenName'    => $this->config->getOptionalString('technicalcontact_name', null),
195
                    'contactType'  => 'technical',
196
                ]));
197
            }
198
            $metaxml = $metaBuilder->getEntityDescriptorText();
199
200
            // sign the metadata if enabled
201
            $metaxml = Metadata\Signer::sign($metaxml, $idpmeta->toArray(), 'ADFS IdP');
202
203
            // make sure to export only the md:EntityDescriptor
204
            $i = strpos($metaxml, '<md:EntityDescriptor');
205
            $metaxml = substr($metaxml, $i ? $i : 0);
206
207
            // 22 = strlen('</md:EntityDescriptor>')
208
            $i = strrpos($metaxml, '</md:EntityDescriptor>');
209
            $metaxml = substr($metaxml, 0, $i ? $i + 22 : 0);
210
211
            $response = new Response();
212
            $response->headers->set('Content-Type', 'application/samlmetadata+xml');
213
            $response->setContent($metaxml);
214
215
            return $response;
216
        } catch (Exception $exception) {
217
            throw new SspError\Error('METADATA', $exception);
218
        }
219
    }
220
221
222
    /**
223
     * @param \Symfony\Component\HttpFoundation\Request $request
224
     * @return \Symfony\Component\HttpFoundation\Response
225
     */
226
    public function prp(Request $request): Response
227
    {
228
        Logger::info('ADFS - IdP.prp: Accessing ADFS IdP endpoint prp');
229
230
        $idpEntityId = $this->metadata->getMetaDataCurrentEntityID('adfs-idp-hosted');
231
        $idp = IdP::getById('adfs:' . $idpEntityId);
232
233
        if ($request->query->has('wa')) {
234
            $wa = $request->query->get('wa');
235
            if ($wa === 'wsignout1.0') {
236
                return new StreamedResponse(
237
                    function () use ($idp) {
238
                        ADFS_IDP::receiveLogoutMessage($idp);
239
                    }
240
                );
241
            } elseif ($wa === 'wsignin1.0') {
242
                return ADFS_IDP::receiveAuthnRequest($request, $idp);
243
            }
244
            throw new SspError\BadRequest("Unsupported value for 'wa' specified in request.");
245
        } elseif ($request->query->has('assocId')) {
246
            // logout response from ADFS SP
247
            // Association ID of the SP that sent the logout response
248
            $assocId = $request->query->get('assocId');
249
            // Data that was sent in the logout request to the SP. Can be null
250
            $relayState = $request->query->get('relayState');
251
            // null on success, or an instance of a \SimpleSAML\Error\Exception on failure.
252
            $logoutError = null;
253
254
            return new StreamedResponse(
255
                function () use ($idp, /** @scrutinizer ignore-type */ $assocId, $relayState, $logoutError) {
256
                    $idp->handleLogoutResponse($assocId, $relayState, $logoutError);
257
                }
258
            );
259
        }
260
        throw new SspError\BadRequest("Missing parameter 'wa' or 'assocId' in request.");
261
    }
262
}
263