hiqdev /
yii2-mfa
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * Multi-factor authentication for Yii2 projects |
||
| 4 | * |
||
| 5 | * @link https://github.com/hiqdev/yii2-mfa |
||
| 6 | * @package yii2-mfa |
||
| 7 | * @license BSD-3-Clause |
||
| 8 | * @copyright Copyright (c) 2016-2018, HiQDev (http://hiqdev.com/) |
||
| 9 | */ |
||
| 10 | |||
| 11 | namespace hiqdev\yii2\mfa\controllers; |
||
| 12 | |||
| 13 | use hiqdev\yii2\mfa\base\ApiMfaIdentityInterface; |
||
| 14 | use hiqdev\yii2\mfa\base\MfaIdentityInterface; |
||
| 15 | use hiqdev\yii2\mfa\behaviors\OauthLoginBehavior; |
||
| 16 | use hiqdev\yii2\mfa\forms\InputForm; |
||
| 17 | use hiqdev\yii2\mfa\validator\BackUrlValidatorInterface; |
||
| 18 | use Yii; |
||
| 19 | use yii\filters\AccessControl; |
||
| 20 | use yii\filters\ContentNegotiator; |
||
| 21 | use yii\filters\VerbFilter; |
||
| 22 | use yii\web\Response; |
||
| 23 | |||
| 24 | /** |
||
| 25 | * TOTP controller. |
||
| 26 | * Time-based One Time Password. |
||
| 27 | */ |
||
| 28 | class TotpController extends \yii\web\Controller |
||
| 29 | { |
||
| 30 | private const TOTP_BACK_URL = 'totp-back-url'; |
||
| 31 | |||
| 32 | public $enableCsrfValidation = false; |
||
| 33 | |||
| 34 | public function behaviors() |
||
| 35 | { |
||
| 36 | return array_merge(parent::behaviors(), [ |
||
| 37 | 'filterApi' => [ |
||
| 38 | 'class' => OauthLoginBehavior::class, |
||
| 39 | 'only' => ['api-temporary-secret', 'api-disable', 'api-enable'], |
||
| 40 | ], |
||
| 41 | 'access' => [ |
||
| 42 | 'class' => AccessControl::class, |
||
| 43 | 'denyCallback' => [$this, 'denyCallback'], |
||
| 44 | 'rules' => [ |
||
| 45 | // ? - guest |
||
| 46 | [ |
||
| 47 | 'actions' => ['check'], |
||
| 48 | 'roles' => ['?'], |
||
| 49 | 'allow' => true, |
||
| 50 | ], |
||
| 51 | // @ - authenticated |
||
| 52 | [ |
||
| 53 | 'actions' => ['enable', 'disable', 'toggle', 'api-temporary-secret', 'api-disable', 'api-enable', 'back'], |
||
| 54 | 'roles' => ['@'], |
||
| 55 | 'allow' => true, |
||
| 56 | ], |
||
| 57 | ], |
||
| 58 | ], |
||
| 59 | 'verbFilter' => [ |
||
| 60 | 'class' => VerbFilter::class, |
||
| 61 | 'actions' => [ |
||
| 62 | 'api-temporary-secret' => ['POST'], |
||
| 63 | 'api-enable' => ['POST'], |
||
| 64 | 'api-disable' => ['POST'], |
||
| 65 | ], |
||
| 66 | ], |
||
| 67 | 'contentNegotiator' => [ |
||
| 68 | 'class' => ContentNegotiator::class, |
||
| 69 | 'only' => ['api-temporary-secret', 'api-disable', 'api-enable'], |
||
| 70 | 'formats' => [ |
||
| 71 | 'application/json' => Response::FORMAT_JSON, |
||
| 72 | ], |
||
| 73 | ], |
||
| 74 | ]); |
||
| 75 | } |
||
| 76 | |||
| 77 | public function denyCallback() |
||
| 78 | { |
||
| 79 | return $this->goHome(); |
||
| 80 | } |
||
| 81 | |||
| 82 | public function actionEnable($back = null) |
||
| 83 | { |
||
| 84 | /** @var MfaIdentityInterface $user */ |
||
| 85 | $user = Yii::$app->user->identity; |
||
| 86 | if ($user->getTotpSecret()) { |
||
| 87 | Yii::$app->session->setFlash('error', Yii::t('mfa', 'Two-factor authentication is already enabled. Disable first.')); |
||
| 88 | |||
| 89 | return empty($back) ? $this->goHome() : $this->deferredRedirect($back); |
||
| 90 | } |
||
| 91 | |||
| 92 | $model = new InputForm(); |
||
| 93 | $secret = $this->module->getTotp()->getSecret(); |
||
| 94 | |||
| 95 | if ($model->load(Yii::$app->request->post()) && $model->validate()) { |
||
| 96 | if ($this->module->getTotp()->verifyCode($secret, $model->code)) { |
||
| 97 | $user->setTotpSecret($secret); |
||
| 98 | $this->module->getTotp()->setIsVerified(true); |
||
| 99 | if ($user->save() && Yii::$app->user->login($user)) { |
||
| 100 | Yii::$app->session->setFlash('success', Yii::t('mfa', 'Two-factor authentication successfully enabled.')); |
||
| 101 | |||
| 102 | return empty($back) ? $this->goBack() : $this->deferredRedirect($back); |
||
| 103 | } else { |
||
| 104 | Yii::$app->session->setFlash('error', Yii::t('mfa', 'Sorry, we have failed to enable two-factor authentication.')); |
||
| 105 | |||
| 106 | return empty($back) ? $this->goHome() : $this->deferredRedirect($back); |
||
| 107 | } |
||
| 108 | } else { |
||
| 109 | $model->addError('code', Yii::t('mfa', 'Wrong verification code. Please verify your secret and try again.')); |
||
| 110 | } |
||
| 111 | } |
||
| 112 | |||
| 113 | $qrcode = $this->module->getTotp()->getQRCodeImageAsDataUri($user->getUsername(), $secret); |
||
| 114 | |||
| 115 | return $this->render('enable', compact('model', 'secret', 'qrcode')); |
||
| 116 | } |
||
| 117 | |||
| 118 | public function actionDisable($back = null) |
||
| 119 | { |
||
| 120 | /** @var MfaIdentityInterface $user */ |
||
| 121 | $user = Yii::$app->user->identity; |
||
| 122 | $model = new InputForm(); |
||
| 123 | $secret = $user->getTotpSecret(); |
||
| 124 | |||
| 125 | if ($model->load(Yii::$app->request->post()) && $model->validate()) { |
||
| 126 | if ($this->module->getTotp()->verifyCode($secret, $model->code)) { |
||
| 127 | $this->module->getTotp()->removeSecret(); |
||
| 128 | $user->setTotpSecret(''); |
||
| 129 | if ($user->save()) { |
||
| 130 | Yii::$app->session->setFlash('success', Yii::t('mfa', 'Two-factor authentication successfully disabled.')); |
||
| 131 | } |
||
| 132 | |||
| 133 | return empty($back) ? $this->goBack() : $this->deferredRedirect($back); |
||
| 134 | } else { |
||
| 135 | $model->addError('code', Yii::t('mfa', 'Wrong verification code. Please verify your secret and try again.')); |
||
| 136 | } |
||
| 137 | } |
||
| 138 | return $this->render('disable', compact('model')); |
||
| 139 | } |
||
| 140 | |||
| 141 | public function actionBack() |
||
| 142 | { |
||
| 143 | $url = Yii::$app->getSession()->get(self::TOTP_BACK_URL); |
||
| 144 | if (empty($url)) { |
||
| 145 | return $this->goBack(); |
||
| 146 | } |
||
| 147 | Yii::$app->getSession()->remove(self::TOTP_BACK_URL); |
||
| 148 | |||
| 149 | return $this->redirect($url); |
||
| 150 | } |
||
| 151 | |||
| 152 | public function deferredRedirect($url = null) |
||
| 153 | { |
||
| 154 | if (!empty($url) && Yii::createObject(BackUrlValidatorInterface::class)->validate($url)) { |
||
| 155 | Yii::$app->getSession()->set(self::TOTP_BACK_URL, $url); |
||
| 156 | } |
||
| 157 | return $this->render('redirect'); |
||
| 158 | } |
||
| 159 | |||
| 160 | public function actionToggle($back = null) |
||
| 161 | { |
||
| 162 | /** @var MfaIdentityInterface $user */ |
||
| 163 | $user = Yii::$app->user->identity; |
||
| 164 | |||
| 165 | return empty($user->getTotpSecret()) ? $this->actionEnable($back) : $this->actionDisable($back); |
||
| 166 | } |
||
| 167 | |||
| 168 | public function actionCheck() |
||
| 169 | { |
||
| 170 | /** @var MfaIdentityInterface $user */ |
||
| 171 | $user = $this->module->getHalfUser(); |
||
| 172 | $model = new InputForm(); |
||
| 173 | $secret = $user->getTotpSecret(); |
||
| 174 | |||
| 175 | if ($model->load(Yii::$app->request->post()) && $model->validate()) { |
||
| 176 | if ($this->module->getTotp()->verifyCode($secret, $model->code)) { |
||
| 177 | $this->module->getTotp()->setIsVerified(true); |
||
| 178 | Yii::$app->user->login($user); |
||
| 179 | |||
| 180 | return $this->goBack(); |
||
| 181 | } else { |
||
| 182 | $model->addError('code', Yii::t('mfa', 'Wrong verification code. Please verify your secret and try again.')); |
||
| 183 | } |
||
| 184 | } |
||
| 185 | |||
| 186 | return $this->render('check', [ |
||
| 187 | 'model' => $model, |
||
| 188 | 'issuer' => $this->module->getTotp()->issuer, |
||
| 189 | 'username' => $user->getUsername(), |
||
| 190 | ]); |
||
| 191 | } |
||
| 192 | |||
| 193 | /** |
||
| 194 | * @inheritDoc |
||
| 195 | */ |
||
| 196 | public function goBack($defaultUrl = null) |
||
| 197 | { |
||
| 198 | $redirectUrl = Yii::$app->params['totpRedirectBackAction.url']; |
||
| 199 | if (!empty($redirectUrl)) { |
||
| 200 | return $this->redirect($redirectUrl); |
||
| 201 | } |
||
| 202 | |||
| 203 | return parent::goBack($defaultUrl); |
||
| 204 | } |
||
| 205 | |||
| 206 | public function actionApiEnable() |
||
| 207 | { |
||
| 208 | /** @var ApiMfaIdentityInterface $identity */ |
||
| 209 | $identity = \Yii::$app->user->identity; |
||
| 210 | $secret = $identity->getTotpSecret(); |
||
| 211 | if (!empty($secret)) { |
||
| 212 | return ['_error' => 'mfa already enabled' . $secret]; |
||
| 213 | } |
||
| 214 | |||
| 215 | if (!$this->module->getTotp()->verifyCode($identity->getTemporarySecret(), $this->request->post('code', ''))) { |
||
| 216 | return ['_error' => 'invalid totp code']; |
||
| 217 | } |
||
| 218 | |||
| 219 | $identity->setTotpSecret($identity->getTemporarySecret()); |
||
| 220 | $identity->setTemporarySecret(null); |
||
| 221 | $identity->save(); |
||
| 222 | |||
| 223 | return ['id' => $identity->getId()]; |
||
|
0 ignored issues
–
show
|
|||
| 224 | } |
||
| 225 | |||
| 226 | public function actionApiDisable() |
||
| 227 | { |
||
| 228 | /** @var ApiMfaIdentityInterface $identity */ |
||
| 229 | $identity = \Yii::$app->user->identity; |
||
| 230 | $secret = $identity->getTotpSecret(); |
||
| 231 | if (empty($secret)) { |
||
| 232 | return ['_error' => 'mfa disabled, enable first']; |
||
| 233 | } |
||
| 234 | |||
| 235 | if (!$this->module->getTotp()->verifyCode($secret, $this->request->post('code', ''))) { |
||
| 236 | return ['_error' => 'invalid totp code']; |
||
| 237 | } |
||
| 238 | |||
| 239 | $identity->setTotpSecret(''); |
||
| 240 | $identity->save(); |
||
| 241 | |||
| 242 | return ['id' => $identity->getId()]; |
||
|
0 ignored issues
–
show
The method
getId() does not seem to exist on object<hiqdev\yii2\mfa\b...piMfaIdentityInterface>.
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...
|
|||
| 243 | } |
||
| 244 | |||
| 245 | public function actionApiTemporarySecret() |
||
| 246 | { |
||
| 247 | /** @var ApiMfaIdentityInterface $identity */ |
||
| 248 | $identity = \Yii::$app->user->identity; |
||
| 249 | $secret = $this->module->getTotp()->getSecret(); |
||
| 250 | $identity->setTemporarySecret($secret); |
||
| 251 | $identity->save(); |
||
| 252 | |||
| 253 | return ['secret' => $secret]; |
||
| 254 | } |
||
| 255 | } |
||
| 256 |
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.