Completed
Push — master ( f74ca7...ea85ac )
by Damien
10:39
created

LoginController::actionIndex()   B

Complexity

Conditions 6
Paths 48

Size

Total Lines 81

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
dl 0
loc 81
c 0
b 0
f 0
ccs 0
cts 60
cp 0
rs 7.7923
cc 6
nc 48
nop 0
crap 42

How to fix   Long Method   

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: 1/10/18
6
 * Time: 11:52 AM
7
 */
8
9
namespace flipbox\saml\sp\controllers;
10
11
use Craft;
12
use flipbox\saml\core\controllers\messages\AbstractController;
13
use flipbox\saml\core\exceptions\InvalidMetadata;
14
use flipbox\saml\core\helpers\MessageHelper;
15
use flipbox\saml\core\services\bindings\Factory;
16
use flipbox\saml\core\validators\Response as ResponseValidator;
17
use flipbox\saml\sp\events\RelayState;
18
use flipbox\saml\sp\records\ProviderRecord;
19
use flipbox\saml\sp\Saml;
20
use flipbox\saml\sp\traits\SamlPluginEnsured;
21
use SAML2\AuthnRequest;
22
use SAML2\Response as SamlResponse;
23
use yii\base\Event;
24
use yii\web\HttpException;
25
26
class LoginController extends AbstractController
27
{
28
    use SamlPluginEnsured;
29
30
    /**
31
     * Happens before the RelayState is used to redirect the user to where they were
32
     * initially trying to go.
33
     */
34
    const EVENT_BEFORE_RELAYSTATE_REDIRECT = 'eventBeforeRelayStateRedirect';
35
    /**
36
     * Happens after the RelayState is created and before the AuthNRequest
37
     * is sent off to the IdP. Use this event if you want to modify the
38
     * RelyState before it's sent to the IdP.
39
     */
40
    const EVENT_AFTER_RELAYSTATE_CREATION = 'eventBeforeRelayStateCreation';
41
42
    protected $allowAnonymous = [
43
        'actionIndex',
44
        'actionRequest',
45
    ];
46
47
    public $enableCsrfValidation = false;
48
49
    /**
50
     * @param \yii\base\Action $action
51
     * @return bool
52
     */
53
    public function beforeAction($action)
54
    {
55
        if ($action->actionMethod === 'actionIndex' || $action->actionMethod === 'actionRequest') {
56
            return true;
57
        }
58
59
        return parent::beforeAction($action);
60
    }
61
62
    /**
63
     * @return \yii\web\Response
64
     * @throws HttpException
65
     * @throws \Exception
66
     * @throws \Throwable
67
     * @throws \craft\errors\ElementNotFoundException
68
     * @throws \flipbox\saml\core\exceptions\InvalidMessage
69
     * @throws \yii\base\Exception
70
     * @throws \yii\base\UserException
71
     */
72
    public function actionIndex()
73
    {
74
75
        /** @var SamlResponse $response */
76
        $response = Factory::receive();
77
78
        if (! $identityProvider = Saml::getInstance()->getProvider()->findByEntityId(
79
            MessageHelper::getIssuer($response->getIssuer())
80
        )->one()) {
81
            $this->throwIdpNotFoundWithResponse($response);
82
        }
83
84
        if (! $serviceProvider = Saml::getInstance()->getProvider()->findOwn()) {
85
            $this->throwSpNotFound();
86
        }
87
88
        $validator = new ResponseValidator(
89
            $identityProvider,
90
            $serviceProvider
91
        );
92
93
        $validator->validate($response);
94
        $settings = Saml::getInstance()->getSettings();
95
        // Transform to User START!
96
        Saml::getInstance()->getLogin()->transformToUser(
97
            $user = Saml::getInstance()->getUser()->getByResponse($response, $serviceProvider, $settings),
98
            $response,
99
            $identityProvider,
100
            $serviceProvider,
101
            $settings
102
        );
103
        // Transform to User END!
104
105
        // User Group Start
106
        Saml::getInstance()->getUserGroups()->sync(
107
            $user,
108
            $response,
109
            $serviceProvider,
110
            $settings
111
        );
112
        // User Group End
113
114
        // Identity START
115
        $identity = Saml::getInstance()->getProviderIdentity()->getByUserAndResponse(
116
            $user,
117
            $response,
118
            $serviceProvider,
119
            $identityProvider
120
        );
121
122
        // LOGIN
123
        Saml::getInstance()->getLogin()->byIdentity($identity);
124
        // Identity END
125
126
        //get relay state but don't error!
127
        $relayState = $response->getRelayState() ?: \Craft::$app->request->getParam('RelayState');
128
        try {
129
            $redirect = $relayState;
130
            if (Saml::getInstance()->getSettings()->encodeRelayState) {
131
                $redirect = base64_decode($relayState);
132
            }
133
134
            Saml::info('RelayState: ' . $redirect);
135
        } catch (\Exception $e) {
136
            $redirect = \Craft::$app->getUser()->getReturnUrl();
137
        }
138
139
        Event::trigger(
140
            self::class,
141
            self::EVENT_BEFORE_RELAYSTATE_REDIRECT,
142
            $event = new RelayState([
143
                'idp' => $identityProvider,
144
                'sp' => $serviceProvider,
145
                'relayState' => $relayState,
146
                'redirect' => $redirect,
147
            ])
148
        );
149
150
        Craft::$app->user->removeReturnUrl();
151
        return $this->redirect($event->redirect);
152
    }
153
154
    /**
155
     * @param SamlResponse $response
156
     * @throws HttpException
157
     */
158
    protected function throwIdpNotFoundWithResponse(SamlResponse $response)
159
    {
160
        throw new HttpException(
161
            400,
162
            sprintf(
163
                'Identity Provider is not found. Possibly a configuration problem. Issuer/EntityId: %s',
164
                MessageHelper::getIssuer($response->getIssuer()) ?: 'IS NULL'
165
            )
166
        );
167
    }
168
169
    /**
170
     * @param SamlResponse $response
0 ignored issues
show
Bug introduced by
There is no parameter named $response. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
171
     * @throws HttpException
172
     */
173
    protected function throwIdpNotFoundWithUid($uid)
174
    {
175
        throw new HttpException(
176
            400,
177
            sprintf(
178
                'Identity Provider is not found with UID: %s',
179
                $uid
180
            )
181
        );
182
    }
183
184
    /**
185
     * @throws HttpException
186
     * @throws \craft\errors\SiteNotFoundException
187
     */
188
    protected function throwSpNotFound()
189
    {
190
        throw new HttpException(
191
            400,
192
            sprintf(
193
                'Service Provider is not found. Possibly a configuration problem. My Provider/Current EntityId: %s',
194
                Saml::getInstance()->getSettings()->getEntityId() ?: 'IS NULL'
195
            )
196
        );
197
    }
198
199
200
    /**
201
     * @param null $uid
202
     * @throws HttpException
203
     * @throws InvalidMetadata
204
     * @throws \craft\errors\SiteNotFoundException
205
     * @throws \yii\base\ExitException
206
     * @throws \yii\base\InvalidConfigException
207
     */
208
    public function actionRequest($uid = null)
209
    {
210
        //build uid condition
211
        $uidCondition = [];
212
        if ($uid) {
213
            $uidCondition = [
214
                'uid' => $uid,
215
            ];
216
        }
217
218
        /**
219
         * @var ProviderRecord $idp
220
         */
221
        if (! $idp = Saml::getInstance()->getProvider()->findByIdp(
222
            $uidCondition
223
        )->one()
224
        ) {
225
            $this->throwIdpNotFoundWithUid($uid);
226
        }
227
228
        if (! $sp = Saml::getInstance()->getProvider()->findOwn()) {
229
            $this->throwSpNotFound();
230
        }
231
232
        /**
233
         * @var $authnRequest AuthnRequest
234
         */
235
        $authnRequest = Saml::getInstance()->getAuthnRequest()->create(
236
            $sp,
237
            $idp
238
        );
239
240
        /**
241
         * Extra layer of security, save the id and check it on the return.
242
         */
243
        Saml::getInstance()->getSession()->setRequestId(
244
            $authnRequest->getID()
245
        );
246
247
248
        // Grab the return URL, or use a param sent as a default
249
        // TODO - seems like if there's a parameter sent, that should be used as an override.
250
        $relayState = Craft::$app->getUser()->getReturnUrl(
251
        // Is RelayState set on this request?
252
        // You can override the param within settings
253
            Craft::$app->request->getParam(
254
                Saml::getInstance()->getSettings()->relayStateOverrideParam,
255
                null
256
            )
257
        );
258
        if (Saml::getInstance()->getSettings()->encodeRelayState) {
259
            $relayState = base64_encode($relayState);
260
        }
261
262
        Event::trigger(
263
            self::class,
264
            self::EVENT_AFTER_RELAYSTATE_CREATION,
265
            $event = new RelayState([
266
                'idp' => $idp,
267
                'sp' => $sp,
268
                'relayState' => $relayState,
269
            ])
270
        );
271
272
        $authnRequest->setRelayState(
273
            $event->relayState
274
        );
275
276
        Factory::send(
277
            $authnRequest,
278
            $idp
279
        );
280
281
        Craft::$app->end();
282
    }
283
}
284