session = $session; $this->qr_code_message = $qr_code_message; } /** * @param Request $request * * @return string * * @throws AuthorizationException * @throws ChannelNotActiveException * @throws InvalidDateException * @throws Account_Exception * @throws API_Exception * @throws User_Not_Found_Exception */ public function configure( Request $request ) { if ( ! $request->is_post() ) { return $this->show_configuration_page(); } $trusted_devices_storage = $this->storage->get_trusted_devices_storage(); $user_storage = $this->storage->get_user_storage(); $integration_user = $this->get_integration_user(); $code = $request->post( 'code' ); $totp_secret = $request->post( 'totp_secret' ); try { if ( empty( $totp_secret ) ) { throw new ValidationException( array( 'totp_secret' => array( ValidationRules::REQUIRED ) ) ); } if ( empty( $code ) ) { throw new ValidationException( array( 'code' => array( ValidationRules::REQUIRED ) ) ); } $auth = $this->api_wrapper->request_auth_via_totp( $totp_secret ); $authentications = new AuthenticationCollection(); $authentications->add( $auth ); $result = $this->api_wrapper->check_code( $authentications, $code ); } catch ( ValidationException $e ) { $this->flash->add_message_now( 'error', $this->get_validation_error( $e ) ); return $this->show_configuration_page( $totp_secret, true ); } if ( $result->accepted() ) { $old_secret = $integration_user->getTotpSecret(); $this->api_wrapper->update_integration_user( $integration_user->setTotpSecret( $totp_secret ) ); $user_storage->enable_totp(); $user_storage->enable_2fa(); $this->flash->add_message( 'success', 'totp-configured' ); if ( $old_secret !== $totp_secret ) { $user_id = $user_storage->get_user_id(); $this->session->log_out_on_other_devices( $user_id ); $trusted_devices_storage->delete_trusted_devices( $user_id ); } return $this->redirect( Route::SUBMENU_CHANNEL, Route::ACTION_CONFIGURE_TOTP ); } $this->flash->add_message_now( 'error', 'token-invalid' ); return $this->show_configuration_page( $totp_secret, true ); } /** * @param Request $request * * @return Redirection_Response * * @throws User_Not_Found_Exception */ public function enable( Request $request ) { $user_storage = $this->storage->get_user_storage(); if ( $user_storage->is_totp_configured() ) { $user_storage->enable_totp(); $user_storage->enable_2fa(); $this->flash->add_message( 'success', 'totp-enabled' ); } else { $this->flash->add_message( 'error', 'cannot-enable-totp' ); } return $this->redirect( Route::SUBMENU_CHANNEL ); } /** * @param Request $request * * @return Redirection_Response */ public function disable( Request $request ) { $trusted_devices_storage = $this->storage->get_trusted_devices_storage(); $options_storage = $this->storage->get_options(); $user_storage = $this->storage->get_user_storage(); try { if ( $options_storage->has_twofas_role( $user_storage->get_roles() ) ) { $this->flash->add_message( 'error', '2fa-role' ); return $this->redirect( Route::SUBMENU_CHANNEL ); } $user_storage->disable_totp(); $user_storage->disable_2fa(); $trusted_devices_storage->delete_trusted_devices( $user_storage->get_user_id() ); $this->flash->add_message( 'success', 'totp-disabled' ); } catch ( User_Not_Found_Exception $e ) { $this->flash->add_message( 'error', 'default' ); } return $this->redirect( Route::SUBMENU_CHANNEL ); } /** * @param Request $request * * @return Redirection_Response */ public function remove_configuration( Request $request ) { try { $trusted_devices_storage = $this->storage->get_trusted_devices_storage(); $user_storage = $this->storage->get_user_storage(); if ( $this->legacy_mode_checker->totp_is_obligatory_and_legacy_mode_is_not_active() ) { $this->flash->add_message( 'error', '2fa-role-remove' ); return $this->redirect( Route::SUBMENU_CHANNEL ); } $user = $this->get_integration_user(); $this->api_wrapper->update_integration_user( $user->setTotpSecret( null )->setMobileSecret( null ) ); $user_storage->remove_totp(); if ( $this->legacy_mode_checker->is_2fa_enabled_in_legacy_mode() ) { $this->flash->add_message( 'success', 'configuration-removed' ); return $this->redirect( Route::SUBMENU_CHANNEL ); } $user_storage->disable_2fa(); $trusted_devices_storage->delete_trusted_devices( $user_storage->get_user_id() ); $this->flash->add_message( 'success', 'configuration-removed' ); } catch ( API_Exception $e ) { $this->flash->add_message( 'error', 'configuration-remove-error' ); } catch ( User_Not_Found_Exception $e ) { $this->flash->add_message( 'error', 'user-not-found' ); } return $this->redirect( Route::SUBMENU_CHANNEL ); } /** * @param Request $request * * @return JSON_Response * * @throws API_Exception */ public function reload( Request $request ) { try { $user_storage = $this->storage->get_user_storage(); if ( $user_storage->is_totp_configured() ) { return $this->json( array( 'error' => 'QR code cannot be reloaded if TOTP is configured.' ), 403 ); } $mobile_secret = $this->get_integration_user()->getMobileSecret(); $totp_secret = TotpSecretGenerator::generate(); $message = $this->get_qr_code_message( $totp_secret, $mobile_secret ); $response = array(); $response['qrCode'] = $this->get_qr_code( $message ); $response['qrCodeMessage'] = $message; $response['totpSecret'] = $totp_secret; } catch ( User_Not_Found_Exception $e ) { return $this->json( array( 'error' => 'User has not been found.' ), 404 ); } return $this->json( $response, 200 ); } /** * @param string|null $totp_secret * @param bool $validation_error * * @return View_Response * * @throws Account_Exception * @throws API_Exception */ private function show_configuration_page( $totp_secret = null, $validation_error = false ) { try { $client = $this->api_wrapper->get_client(); $user = $this->get_integration_user(); if ( empty( $totp_secret ) ) { $totp_secret = $this->get_totp_secret( $user ); } $mobile_secret = $user->getMobileSecret(); $qr_code_message = $this->get_qr_code_message( $totp_secret, $mobile_secret ); $qr_code = $this->get_qr_code( $qr_code_message ); $status_data = $this->get_user_status_data(); $data = array( 'has_client_card' => $client->hasCard(), 'qr_code_message' => $qr_code_message, 'qr_code' => $qr_code, 'totp_secret' => $totp_secret, 'offline_codes_count' => $user->getBackupCodesCount(), 'active_tab' => 'tokens', 'validation_error' => $validation_error, ); return $this->render( Views::CONFIGURE_TOTP, array_merge( $data, $status_data ) ); } catch ( User_Not_Found_Exception $e ) { return $this->error( Errors::USER_NOT_FOUND_ERROR ); } } /** * @return IntegrationUser|null * * @throws ValidationException * @throws AuthorizationException * @throws API_Exception * @throws User_Not_Found_Exception */ private function get_integration_user() { $user_id = $this->storage->get_user_storage()->get_user_id(); $user = $this->api_wrapper->get_integration_user_by_external_id( $user_id ); if ( is_null( $user ) ) { $user = $this->api_wrapper->create_integration_user( $user_id ); } $this->check_mobile_secret( $user ); return $user; } /** * @param IntegrationUser $user * * @return string */ private function get_totp_secret( IntegrationUser $user ) { $totp_secret = $user->getTotpSecret(); if ( empty( $totp_secret ) ) { $totp_secret = TotpSecretGenerator::generate(); } return $totp_secret; } /** * @param IntegrationUser $user * * @throws ValidationException * @throws AuthorizationException * @throws API_Exception */ private function check_mobile_secret( IntegrationUser $user ) { $mobile_secret = $user->getMobileSecret(); if ( empty( $mobile_secret ) ) { $user->setMobileSecret( MobileSecretGenerator::generate() ); $this->api_wrapper->update_integration_user( $user ); } } /** * @param string $message * * @return string */ private function get_qr_code( $message ) { $generator = QR_Code_Generator_Factory::create(); return $generator->generateBase64( $message ); } /** * @param string $totp_secret * @param string $mobile_secret * * @return string * * @throws User_Not_Found_Exception */ private function get_qr_code_message( $totp_secret, $mobile_secret ) { return $this->qr_code_message->create( $totp_secret, $mobile_secret ); } }