Source of file CMSProfileController.php
Size: 10,289 Bytes - Last Modified: 2021-12-24T06:35:30+00:00
/var/www/docs.ssmods.com/process/src/src/Controller/CMSProfileController.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 | <?php namespace _2fa; use _2fa\Authenticator; use SilverStripe\Forms\FieldList; use SilverStripe\Security\Member; use SilverStripe\Forms\FormAction; use SilverStripe\Core\Config\Config; use SilverStripe\Forms\LiteralField; use SilverStripe\Forms\CheckboxField; use SilverStripe\Core\Injector\Injector; use SilverStripe\Forms\ToggleCompositeField; use SilverStripe\Admin\CMSProfileController as SS_CMSProfileController; /** * Adds in handling of token creation and validation in the CMS 'My Profile' * section */ class CMSProfileController extends SS_CMSProfileController { private static $allowed_actions = [ 'regenerate_backup_tokens', 'regenerate_token', 'load_token_data', 'verify_and_activate', 'verify_and_deactivate', ]; /** * Adds in Two Factor Authentication tab. Tab state changes depending on * whether the member has TwoFactor enabled or not * * @param int $id * @param FieldList $fields * @return Form */ public function getEditForm($id = null, $fields = null) { $form = parent::getEditForm($id, $fields); $member = Member::currentUser(); if (!$member) { return $form; } $actions = $form->Actions(); $fields = $form->Fields(); // cleanup $fields->removeByName('BackupTokens'); // remove direct activation option $fields->removeByName('Has2FA'); if (!Injector::inst()->get(Authenticator::class)->is2FAenabled()) { return $form; } // activate/deactivate through button+popup instead $alterbutton = FormAction::create('open_deactivation_dialog') ->addExtraClass('twofactor_button twofactor_dialogbutton') ->setAttribute('data-infourl', $this->Link('load_token_data')) ->setUseButtonTag(true); if ($member->Has2FA) { $alterbutton ->setButtonContent( _t( "TWOFACTOR.ACTIVATE2FA", 'Two-Factor Authentication is: <strong>ACTIVE</strong> <br><small>click to deactivate</small>' ) ) ->setAttribute('data-icon', 'accept'); } else { $alterbutton ->setButtonContent( _t( "TWOFACTOR.ACTIVATE2FA", 'Two-Factor Authentication is: <strong>NOT ACTIVE</strong> <br><small>click to activate</small>' ) ) ->setAttribute('data-icon', 'accept_disabled'); } $fields->addFieldToTab('Root.TwoFactorAuthentication', $alterbutton); // token regeneration $actions->push( FormAction::create('regenerate_token') ->setTitle('Reset two-factor KEY') ->addExtraClass('ss-ui-action-destructive') ->addExtraClass('twofactor_regeneratetokens') ); // Backup tokens may always be shown $this->addBackupTokenInfo($fields, $actions); return $form; } /** * Undocumented function * * @param FieldList $fields * @return void */ private function addTokenInfo(FieldList &$fields) { $member = Member::currentUser(); if (!$member) { return; } if ($member->Has2FA) { // add token QR code $two_factor_fields[] = LiteralField::create( 'TokenSecurityWarning', _t( "TWOFACTOR.TOKENSECURITYWARNING", "<p><br>The button below reveals your security token for scanning.<br> <strong>Please reveal this only when no one else is observing your screen.</strong></p>" ) ); $two_factor_fields[] = ToggleCompositeField::create( 'SecurityToken', 'Security token', LiteralField::create( 'PrintableTOTPToken', $member->renderWith('TokenInfo') ) ); $fields->addFieldsToTab( 'Root.TwoFactorAuthentication', $two_factor_fields ); } } private function addBackupTokenInfo(FieldList &$fields, FieldList &$actions) { $member = Member::currentUser(); if (!$member) { return; } if ($member->Has2FA) { // backup-token info $backup_token_fields[] = LiteralField::create( 'BackupTokensSecurityWarning', _t( "TWOFACTOR.BACKUPTOKENSECURITYWARNING", "<p><br>The button below reveals your backup tokens. These can each be used only once.<br> <strong>Please reveal them only when no one else is observing your screen.</strong></p>" ) ); $backup_token_fields[] = ToggleCompositeField::create( 'BackupTokens', 'Backup tokens', [ LiteralField::create( 'DisplayBackupTokens', $member->renderWith('BackupTokenInfo') ) ] ); $fields->addFieldsToTab( 'Root.TwoFactorAuthentication', $backup_token_fields ); // backup-tokens interaction $actions->push( FormAction::create('regenerate_backup_tokens') ->setTitle('(Re)generate BACKUP tokens') ->addExtraClass('ss-ui-action-destructive') ->addExtraClass('twofactor_regeneratetokens') ); } } /** * This form action may get triggered to manually refresh secret/token when * running in 'fixed' token mode (regenerate_on_activation = false) * * @param $data Array * @param $form Form * @return SS_HTTPResponse */ public function regenerate_token($data, $form) { $member = Member::currentUser(); // set new secret/token on member $member->generateTOTPToken(); // if we're manually regenerating, user needs to re-activate & verify // 2FA after token change $member->Has2FA = false; $member->write(); $this->response ->addHeader( 'X-Status', 'Token/secret regenerated, please re-activate two-factor authentication' ) ->addHeader('X-Reload', true); return $this->getResponseNegotiator()->respond($this->request); } /** * Function to allow loading secret/QR code via Ajax * * @param $data * @param $form * @return \SS_HTTPResponse */ public function load_token_data($request) { $member = Member::currentUser(); if (!$member) { return; } // If we're in validated activation mode, this is the appropriate moment // to refresh the token if (!$member->Has2FA) { // set new secret/token on member $member->generateTOTPToken(); $member->write(); } return $member->customise(['CurrentController' => $this]) ->renderWith('TokenInfoDialog'); } /** * Function to allow verification & activation of two-factor-auth via Ajax * * @param $request * @return \SS_HTTPResponse */ public function verify_and_activate($request) { $member = Member::currentUser(); if (!$member) { return; } $TokenCorrect = $member->validateTOTP( (string) $request->postVar('VerificationInput') ); if ($TokenCorrect) { $member->Has2FA = true; $member->regenerateBackupTokens(); $member->write(); $this->response ->addHeader('X-Status', 'Two-Factor authentication activated successfully.') ->addHeader('X-Pjax', 'CurrentForm'); return $this->getResponseNegotiator()->respond($this->request); } // else: show feedback return $member ->customise( [ 'CurrentController' => $this, 'VerificationError' => true, ] ) ->renderWith('TokenInfoDialog'); } /** * Function to allow verification of password & dectivation of two-factor-auth via Ajax * * @param $request * @return \SS_HTTPResponse */ public function verify_and_deactivate($request) { $member = Member::currentUser(); if (!$member) { return; } $PasswordCorrect = $member->checkPassword( (string) $request->postVar('VerificationInput') ); if ($PasswordCorrect->isValid()) { $member->Has2FA = false; $member->write(); $this->response ->addHeader('X-Status', 'Two-Factor authentication deactivated successfully.') ->addHeader('X-Pjax', 'CurrentForm'); return $this->getResponseNegotiator()->respond($this->request); } // else: show feedback return $member ->customise( [ 'CurrentController' => $this, 'VerificationError' => true, ] ) ->renderWith('TokenInfoDialog'); } public function regenerate_backup_tokens($data, $form) { $member = Member::currentUser(); $member->regenerateBackupTokens(); $this->response ->addHeader('X-Status', 'Your old backup tokens are gone. Please record these new ones!') ->addHeader('X-Pjax', 'CurrentForm') ->addHeader('X-Reload', true); return $this->getResponseNegotiator()->respond($this->request); } } |