Completed
Push — master ( 72ecff...582962 )
by Damien
11:32
created

AbstractMetadataController::processSaveAction()   F

Complexity

Conditions 17
Paths 1153

Size

Total Lines 112

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 112
rs 0.8399
c 0
b 0
f 0
cc 17
nc 1153
nop 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
 * Created by PhpStorm.
4
 * User: dsmrt
5
 * Date: 2/10/18
6
 * Time: 10:47 PM
7
 */
8
9
namespace flipbox\saml\core\controllers;
10
11
use Craft;
12
use flipbox\keychain\records\KeyChainRecord;
13
use flipbox\saml\core\controllers\cp\view\AbstractController;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, flipbox\saml\core\controllers\AbstractController.

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 flipbox\saml\core\controllers\cp\view\metadata\AbstractEditController;
15
use flipbox\saml\core\controllers\cp\view\metadata\VariablesTrait;
16
use flipbox\saml\core\exceptions\InvalidMetadata;
17
use flipbox\saml\core\helpers\SerializeHelper;
18
use flipbox\saml\core\models\GroupOptions;
19
use flipbox\saml\core\models\MetadataOptions;
20
use flipbox\saml\core\models\SettingsInterface;
21
use flipbox\saml\core\records\AbstractProvider;
22
use flipbox\saml\core\records\ProviderInterface;
23
use yii\web\NotFoundHttpException;
24
25
abstract class AbstractMetadataController extends AbstractController implements \flipbox\saml\core\EnsureSAMLPlugin
26
{
27
28
    use VariablesTrait;
29
30
    /**
31
     * @return string
32
     * @throws InvalidMetadata
33
     * @throws \yii\web\ForbiddenHttpException
34
     */
35
    public function actionIndex()
36
    {
37
38
        $this->requireAdmin(false);
39
40
        /** @var AbstractProvider $provider */
41
        $provider = $this->getPlugin()->getProvider()->findByEntityId(
42
            $this->getPlugin()->getSettings()->getEntityId()
43
        )->one();
44
45
        if (! $provider) {
46
            throw new InvalidMetadata('Metadata for this server is missing. Please configure this plugin.');
47
        }
48
49
        SerializeHelper::xmlContentType();
50
        return $provider->toXmlString();
51
    }
52
53
    /**
54
     * @return \yii\web\Response
55
     * @throws \Exception
56
     * @throws \yii\web\BadRequestHttpException
57
     */
58
    public function actionAutoCreate()
59
    {
60
        $this->requireAdmin(false);
61
        $this->requirePostRequest();
62
63
        $record = $this->processSaveAction();
64
65
        $entityDescriptor = $this->getPlugin()->getMetadata()->create(
66
            $this->getPlugin()->getSettings(),
0 ignored issues
show
Bug introduced by
It seems like $this->getPlugin()->getSettings() can be null; however, create() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
67
            $record->keychain
0 ignored issues
show
Bug introduced by
Accessing keychain on the interface flipbox\saml\core\records\ProviderInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
68
        );
69
70
        $provider = $this->getPlugin()->getProvider()->create(
71
            $entityDescriptor,
72
            $record->keychain
0 ignored issues
show
Bug introduced by
Accessing keychain on the interface flipbox\saml\core\records\ProviderInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
73
        );
74
75
        $record->entityId = $provider->getEntityId();
0 ignored issues
show
Bug introduced by
Accessing entityId on the interface flipbox\saml\core\records\ProviderInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
76
        $record->metadata = $provider->metadata;
0 ignored issues
show
Bug introduced by
Accessing metadata on the interface flipbox\saml\core\records\ProviderInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
77
        $record->setMetadataModel($provider->getMetadataModel());
78
79
80
        if (! $this->getPlugin()->getProvider()->save($record)) {
81
            return $this->renderTemplate(
82
                $this->getTemplateIndex() . AbstractEditController::TEMPLATE_INDEX . DIRECTORY_SEPARATOR . 'edit',
83
                array_merge(
84
                    [
85
                        'provider' => $record,
86
                        'keychain' => $record->keychain ?: new KeyChainRecord(),
0 ignored issues
show
Bug introduced by
Accessing keychain on the interface flipbox\saml\core\records\ProviderInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
87
                    ],
88
                    $this->prepVariables($record)
89
                )
90
            );
91
        }
92
93
        return $this->redirectToPostedUrl();
94
    }
95
96
    /**
97
     * @return \yii\web\Response
98
     * @throws \Exception
99
     * @throws \yii\web\BadRequestHttpException
100
     */
101
    public function actionSave()
102
    {
103
104
        $this->requireAdmin(false);
105
        $this->requirePostRequest();
106
107
        /** @var AbstractProvider $record */
108
        $record = $this->processSaveAction();
109
110
        if ($record->hasErrors() || ! $this->getPlugin()->getProvider()->save($record)) {
111
            return $this->renderTemplate(
112
                $this->getTemplateIndex() . AbstractEditController::TEMPLATE_INDEX . DIRECTORY_SEPARATOR . 'edit',
113
                array_merge(
114
                    [
115
                        'provider' => $record,
116
                        'keychain' => $record->keychain ?: new KeyChainRecord(),
117
                    ],
118
                    $this->prepVariables($record)
119
                )
120
            );
121
        }
122
123
        Craft::$app->getSession()->setNotice(Craft::t($this->getPlugin()->getHandle(), 'Provider saved.'));
124
125
        return $this->redirectToPostedUrl();
126
    }
127
128
    /**
129
     * Actions
130
     */
131
132
    /**
133
     * @return \yii\web\Response
134
     * @throws \yii\web\BadRequestHttpException
135
     * @throws \yii\web\ForbiddenHttpException
136
     * @throws \Exception
137
     */
138
    public function actionChangeStatus()
139
    {
140
141
        $this->requireAdmin(false);
142
        $this->requirePostRequest();
143
144
        $providerId = Craft::$app->request->getRequiredBodyParam('identifier');
145
146
        /** @var string $recordClass */
147
        $recordClass = $this->getPlugin()->getProviderRecordClass();
148
149
        /** @var AbstractProvider $record */
150
        $record = $recordClass::find()->where([
151
            'id' => $providerId,
152
        ])->one();
153
154
        $record->enabled = ! $record->enabled;
155
156
        if (! $this->getPlugin()->getProvider()->save($record)) {
157
            return $this->renderTemplate(
158
                $this->getTemplateIndex() . AbstractEditController::TEMPLATE_INDEX . DIRECTORY_SEPARATOR . 'edit',
159
                array_merge(
160
                    [
161
                        'provider' => $record,
162
                        'keychain' => $record->keychain ?: new KeyChainRecord(),
163
                    ],
164
                    $this->prepVariables($record)
165
                )
166
            );
167
        }
168
169
        return $this->redirectToPostedUrl();
170
    }
171
172
    /**
173
     * @return \yii\web\Response
174
     * @throws \yii\web\BadRequestHttpException
175
     * @throws \yii\web\ForbiddenHttpException
176
     */
177
    public function actionDelete()
178
    {
179
        $this->requireAdmin(false);
180
        $this->requirePostRequest();
181
182
        $providerId = Craft::$app->request->getRequiredBodyParam('identifier');
183
184
        /** @var string $recordClass */
185
        $recordClass = $this->getPlugin()->getProviderRecordClass();
186
187
        /** @var ProviderInterface $record */
188
        $record = $recordClass::find()->where([
189
            'id' => $providerId,
190
        ])->one();
191
192
        if (! $this->getPlugin()->getProvider()->delete($record)) {
193
            return $this->renderTemplate(
194
                $this->getTemplateIndex() . AbstractEditController::TEMPLATE_INDEX . DIRECTORY_SEPARATOR . 'edit',
195
                array_merge(
196
                    [
197
                        'provider' => $record,
198
                        'keychain' => $record->keychain ?: new KeyChainRecord(),
0 ignored issues
show
Bug introduced by
Accessing keychain on the interface flipbox\saml\core\records\ProviderInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
199
                    ],
200
                    $this->prepVariables($record)
201
                )
202
            );
203
        }
204
205
        return $this->redirectToPostedUrl();
206
    }
207
208
    /**
209
     * @return ProviderInterface
210
     * @throws \Exception
211
     */
212
    protected function processSaveAction()
213
    {
214
215
        $providerId = Craft::$app->request->getParam('identifier');
216
        $keyId = Craft::$app->request->getParam('keychain');
217
        $providerType = Craft::$app->request->getParam('providerType');
218
        $metadata = Craft::$app->request->getParam('metadata-text');
219
        $metadataUrl = Craft::$app->request->getParam('metadata-url-text');
220
        $metadataUrlInterval = Craft::$app->request->getParam('metadata-url-interval-text');
221
        $mapping = Craft::$app->request->getParam('mapping', []);
222
        $label = Craft::$app->request->getRequiredParam('label');
223
        $nameIdOverride = Craft::$app->request->getParam('nameIdOverride');
224
225
226
        $plugin = $this->getPlugin();
227
228
        $recordClass = $this->getPlugin()->getProviderRecordClass();
229
        /** @var AbstractProvider $record */
230
        if ($providerId) {
231
            $record = $recordClass::find()->where([
232
                'id' => $providerId,
233
            ])->one();
234
235
            if (! $record) {
236
                throw new \Exception("Provider with ID: {$providerId} not found.");
237
            }
238
        } else {
239
            $record = new $recordClass();
240
241
            //enabled is default
242
            $record->enabled = true;
243
        }
244
245
246
        // Metadata
247
        if (! $metadata && $metadataUrl) {
248
            $metadataModel = $this->getPlugin()->getMetadata()->fetchByUrl($metadataUrl);
249
            $record->metadata = $metadataModel->toXML()->ownerDocument->saveXML();
250
            $record->setMetadataModel($metadataModel);
251
        } else {
252
            $record->metadata = $metadata;
253
        }
254
255
        // Mapping
256
        if (is_array($mapping)) {
257
            $record->setMapping(
258
                $mapping
259
            );
260
        }
261
262
        $record->providerType = $providerType;
263
        $record->nameIdOverride = $nameIdOverride;
264
265
        // IDP Plugin on SP Provider ONLY
266
        if ($this->getPlugin()->getMyType() === SettingsInterface::IDP
267
            &&
268
            $providerType === SettingsInterface::SP
269
        ) {
270
            // Encryption settings
271
            $record->encryptAssertions = Craft::$app->request->getParam('encryptAssertions') ?: 0;
272
            $record->encryptionMethod = Craft::$app->request->getParam('encryptionMethod');
273
            $record->setGroupOptions(
274
                $groupOptions = new GroupOptions([
275
                    'options' => Craft::$app->request->getParam('groupOptions', []) ?: [],
276
                ])
277
            );
278
        }
279
280
        $record->setMetadataOptions(
281
            new MetadataOptions([
282
                'url' => $metadataUrl,
283
                'expiryInterval' => $metadataUrlInterval,
284
            ])
285
        );
286
287
        // Group properties
288
        $record->syncGroups = Craft::$app->request->getParam('syncGroups') ?: 0;
289
290
        $record->groupsAttributeName =
291
            Craft::$app->request->getParam('groupsAttributeName') ?:
292
                AbstractProvider::DEFAULT_GROUPS_ATTRIBUTE_NAME;
293
294
        /**
295
         * check for label and add error if it's empty
296
         */
297
        if ($label) {
298
            $record->label = $label;
299
        } else {
300
            $record->addError('label', Craft::t($plugin->getHandle(), "Label is required."));
301
        }
302
303
304
        if ($keyId) {
305
            /** @var KeyChainRecord $keychain */
306
            if ($keychain = KeyChainRecord::find()->where([
307
                'id' => $keyId,
308
            ])->one()) {
309
                $record->setKeychain(
310
                    $keychain
311
                );
312
            }
313
        }
314
315
        /**
316
         * Metadata should exist for the remote provider
317
         */
318
        if ($plugin->getRemoteType() === $providerType && ! $record->metadata) {
319
            $record->addError('metadata-text', Craft::t($plugin->getHandle(), "Metadata cannot be empty."));
320
        }
321
322
        return $record;
323
    }
324
325
    /**
326
     * @param $keyId
327
     * @return static
328
     * @throws NotFoundHttpException
329
     * @throws \yii\web\ForbiddenHttpException
330
     * @throws \yii\web\HttpException
331
     */
332
    public function actionDownloadCertificate($keyId)
333
    {
334
        $this->requireAdmin(false);
335
336
        /** @var KeyChainRecord $keychain */
337
        if (! $keychain = KeyChainRecord::find()->where([
338
            'id' => $keyId,
339
        ])->one()) {
340
            throw new NotFoundHttpException('Key not found');
341
        }
342
343
        return Craft::$app->response->sendContentAsFile($keychain->getDecryptedCertificate(), 'certificate.crt');
344
    }
345
}
346