Merge pull request #3377 from chamilo/whispeak
Update API requests for Whispeak pluginpull/3378/head
commit
4399ccd5d4
@ -0,0 +1,345 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
namespace Chamilo\PluginBundle\WhispeakAuth\Controller; |
||||
|
||||
use Chamilo\PluginBundle\Entity\WhispeakAuth\LogEvent; |
||||
use Chamilo\PluginBundle\WhispeakAuth\Request\ApiRequest; |
||||
use Chamilo\UserBundle\Entity\User; |
||||
use ChamiloSession; |
||||
use Display; |
||||
use Login; |
||||
use WhispeakAuthPlugin; |
||||
|
||||
/** |
||||
* Class AuthenticationController. |
||||
* |
||||
* @package Chamilo\PluginBundle\WhispeakAuth\Controller |
||||
*/ |
||||
class AuthenticationController extends BaseController |
||||
{ |
||||
/** |
||||
* @throws \Exception |
||||
*/ |
||||
public function index() |
||||
{ |
||||
if (!$this->plugin->toolIsEnabled()) { |
||||
throw new \Exception(get_lang('NotAllowed')); |
||||
} |
||||
|
||||
/** @var array $lpQuestionInfo */ |
||||
$lpQuestionInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_QUIZ_QUESTION, []); |
||||
|
||||
|
||||
if (ChamiloSession::read(WhispeakAuthPlugin::SESSION_AUTH_PASSWORD, false)) { |
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_AUTH_PASSWORD); |
||||
|
||||
if (empty($lpQuestionInfo)) { |
||||
$message = $this->plugin->get_lang('MaxAttemptsReached') |
||||
.'<br><strong>'.$this->plugin->get_lang('LoginWithUsernameAndPassword').'</strong>'; |
||||
|
||||
Display::addFlash( |
||||
Display::return_message($message, 'warning') |
||||
); |
||||
} |
||||
|
||||
header('Location: '.api_get_path(WEB_PLUGIN_PATH).'whispeakauth/authentify_password.php'); |
||||
exit; |
||||
} |
||||
|
||||
/** @var array $lpItemInfo */ |
||||
$lpItemInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_LP_ITEM, []); |
||||
/** @var \learnpath $oLp */ |
||||
$oLp = ChamiloSession::read('oLP', null); |
||||
/** @var \Exercise $objExercise */ |
||||
$objExercise = ChamiloSession::read('objExercise', null); |
||||
|
||||
$isAuthOnLp = !empty($lpItemInfo) && !empty($oLp); |
||||
$isAuthOnQuiz = !empty($lpQuestionInfo) && !empty($objExercise); |
||||
$showFullPage = !$isAuthOnLp && !$isAuthOnQuiz; |
||||
|
||||
$user = api_get_user_entity( |
||||
ChamiloSession::read(WhispeakAuthPlugin::SESSION_2FA_USER, 0) ?: api_get_user_id() |
||||
); |
||||
|
||||
$showForm = !$user; |
||||
|
||||
if ($user) { |
||||
if (!WhispeakAuthPlugin::getAuthUidValue($user)) { |
||||
$message = Display::return_message($this->plugin->get_lang('SpeechAuthNotEnrolled'), 'warning'); |
||||
|
||||
if (!empty($lpQuestionInfo) && empty($lpItemInfo)) { |
||||
echo $message; |
||||
} else { |
||||
Display::addFlash($message); |
||||
} |
||||
|
||||
header('Location: '.api_get_path(WEB_PLUGIN_PATH).'whispeakauth/authentify_password.php'); |
||||
|
||||
exit; |
||||
} |
||||
} |
||||
|
||||
if (!empty($lpQuestionInfo) && empty($lpItemInfo)) { |
||||
echo api_get_js('rtc/RecordRTC.js'); |
||||
echo api_get_js_simple(api_get_path(WEB_PLUGIN_PATH).'whispeakauth/assets/js/RecordAudio.js'); |
||||
} |
||||
|
||||
$request = new ApiRequest(); |
||||
$response = $request->createAuthenticationSessionToken($user); |
||||
|
||||
if (empty($response['text'])) { |
||||
$varNumber = mt_rand(1, 6); |
||||
$response['text'] = $this->plugin->get_lang("AuthentifySampleText$varNumber"); |
||||
} |
||||
|
||||
ChamiloSession::write(WhispeakAuthPlugin::SESSION_SENTENCE_TEXT, $response['token']); |
||||
|
||||
if (!empty($lpQuestionInfo) && empty($lpItemInfo)) { |
||||
$template = new \Template('', $showFullPage, $showFullPage, false, true, false); |
||||
$template->assign('show_form', $showForm); |
||||
$template->assign('sample_text', $response['text']); |
||||
|
||||
echo $template->fetch('whispeakauth/view/authentify_recorder.html.twig'); |
||||
exit; |
||||
} |
||||
|
||||
$this->displayPage( |
||||
[ |
||||
'show_form' => $showForm, |
||||
'sample_text' => $response['text'], |
||||
] |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* @throws \Exception |
||||
*/ |
||||
public function ajax() |
||||
{ |
||||
$userId = api_get_user_id(); |
||||
$user2fa = ChamiloSession::read(WhispeakAuthPlugin::SESSION_2FA_USER, 0); |
||||
|
||||
if (!empty($user2fa) || !empty($userId)) { |
||||
$isAllowed = !empty($_FILES['audio']); |
||||
} else { |
||||
$isAllowed = !empty($_POST['username']) && !empty($_FILES['audio']); |
||||
} |
||||
|
||||
if (!$isAllowed || !$this->plugin->toolIsEnabled()) { |
||||
throw new \Exception(get_lang('NotAllowed')); |
||||
} |
||||
|
||||
if (!empty($user2fa)) { |
||||
$user = api_get_user_entity($user2fa); |
||||
} elseif (!empty($userId)) { |
||||
$user = api_get_user_entity($userId); |
||||
} else { |
||||
/** @var User|null $user */ |
||||
$user = \UserManager::getRepository()->findOneBy(['username' => $_POST['username']]); |
||||
} |
||||
|
||||
if (!$user) { |
||||
throw new \Exception(get_lang('NotFound')); |
||||
} |
||||
|
||||
$audioFilePath = $this->uploadAudioFile($user); |
||||
|
||||
$failedLogins = ChamiloSession::read(WhispeakAuthPlugin::SESSION_FAILED_LOGINS, 0); |
||||
$maxAttempts = $this->plugin->getMaxAttempts(); |
||||
|
||||
if ($maxAttempts && $failedLogins >= $maxAttempts) { |
||||
throw new \Exception($this->plugin->get_lang('MaxAttemptsReached')); |
||||
} |
||||
|
||||
$wsid = WhispeakAuthPlugin::getAuthUidValue($user->getId()); |
||||
|
||||
if (empty($wsid)) { |
||||
throw new \Exception($this->plugin->get_lang('SpeechAuthNotEnrolled')); |
||||
} |
||||
|
||||
$token = \ChamiloSession::read(\WhispeakAuthPlugin::SESSION_SENTENCE_TEXT); |
||||
|
||||
$request = new ApiRequest(); |
||||
$success = $request->performAuthentication($token, $wsid->getValue(), $audioFilePath); |
||||
|
||||
\ChamiloSession::erase(\WhispeakAuthPlugin::SESSION_SENTENCE_TEXT); |
||||
|
||||
/** @var array $lpItemInfo */ |
||||
$lpItemInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_LP_ITEM, []); |
||||
/** @var array $quizQuestionInfo */ |
||||
$quizQuestionInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_QUIZ_QUESTION, []); |
||||
|
||||
$message = $this->plugin->get_lang('AuthentifySuccess'); |
||||
|
||||
if (!$success) { |
||||
if (!empty($lpItemInfo)) { |
||||
$this->plugin->addAttemptInLearningPath( |
||||
LogEvent::STATUS_FAILED, |
||||
$user->getId(), |
||||
$lpItemInfo['lp_item'], |
||||
$lpItemInfo['lp'] |
||||
); |
||||
} |
||||
|
||||
if (!empty($quizQuestionInfo)) { |
||||
$this->plugin->addAttemptInQuiz( |
||||
LogEvent::STATUS_FAILED, |
||||
$user->getId(), |
||||
$quizQuestionInfo['question'], |
||||
$quizQuestionInfo['quiz'] |
||||
); |
||||
} |
||||
|
||||
if (empty($lpItemInfo) && empty($quizQuestionInfo)) { |
||||
$this->plugin->addAuthenticationAttempt(LogEvent::STATUS_FAILED, $user->getId()); |
||||
} |
||||
|
||||
$message = $this->plugin->get_lang('AuthentifyFailed'); |
||||
|
||||
ChamiloSession::write(WhispeakAuthPlugin::SESSION_FAILED_LOGINS, ++$failedLogins); |
||||
|
||||
if ($maxAttempts && $failedLogins >= $maxAttempts) { |
||||
$message .= PHP_EOL |
||||
.'<span data-reach-attempts="true">'.$this->plugin->get_lang('MaxAttemptsReached').'</span>' |
||||
.PHP_EOL |
||||
.'<br><strong>' |
||||
.$this->plugin->get_lang('LoginWithUsernameAndPassword') |
||||
.'</strong>'; |
||||
|
||||
if (!empty($user2fa)) { |
||||
Display::addFlash( |
||||
Display::return_message($message, 'warning', false) |
||||
); |
||||
} |
||||
} else { |
||||
$message .= PHP_EOL.$this->plugin->get_lang('TryAgain'); |
||||
|
||||
if ('true' === api_get_setting('allow_lostpassword')) { |
||||
$message .= '<br>' |
||||
.Display::url( |
||||
get_lang('LostPassword'), |
||||
api_get_path(WEB_CODE_PATH).'auth/lostPassword.php', |
||||
['target' => $lpItemInfo ? '_top' : '_self'] |
||||
); |
||||
} |
||||
} |
||||
} |
||||
|
||||
echo Display::return_message( |
||||
$message, |
||||
$success ? 'success' : 'warning', |
||||
false |
||||
); |
||||
|
||||
if (!$success && $maxAttempts && $failedLogins >= $maxAttempts) { |
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_FAILED_LOGINS); |
||||
|
||||
if (!empty($lpItemInfo)) { |
||||
echo '<script>window.location.href = "' |
||||
.api_get_path(WEB_PLUGIN_PATH) |
||||
.'whispeakauth/authentify_password.php";</script>'; |
||||
|
||||
exit; |
||||
} |
||||
|
||||
if (!empty($quizQuestionInfo)) { |
||||
$url = api_get_path(WEB_CODE_PATH).'exercise/exercise_submit.php?'.$quizQuestionInfo['url_params']; |
||||
|
||||
ChamiloSession::write(WhispeakAuthPlugin::SESSION_AUTH_PASSWORD, true); |
||||
|
||||
echo "<script>window.location.href = '".$url."';</script>"; |
||||
|
||||
exit; |
||||
} |
||||
|
||||
echo '<script>window.location.href = "'.api_get_path(WEB_PATH).'";</script>'; |
||||
|
||||
exit; |
||||
} |
||||
|
||||
if ($success) { |
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_SENTENCE_TEXT); |
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_FAILED_LOGINS); |
||||
|
||||
if (!empty($lpItemInfo)) { |
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_LP_ITEM); |
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_2FA_USER); |
||||
|
||||
$this->plugin->addAttemptInLearningPath( |
||||
LogEvent::STATUS_SUCCESS, |
||||
$user->getId(), |
||||
$lpItemInfo['lp_item'], |
||||
$lpItemInfo['lp'] |
||||
); |
||||
|
||||
echo '<script>window.location.href = "'.$lpItemInfo['src'].'";</script>'; |
||||
|
||||
exit; |
||||
} |
||||
|
||||
if (!empty($quizQuestionInfo)) { |
||||
$quizQuestionInfo['passed'] = true; |
||||
$url = api_get_path(WEB_CODE_PATH).'exercise/exercise_submit.php?'.$quizQuestionInfo['url_params']; |
||||
|
||||
ChamiloSession::write(WhispeakAuthPlugin::SESSION_QUIZ_QUESTION, $quizQuestionInfo); |
||||
|
||||
$this->plugin->addAttemptInQuiz( |
||||
LogEvent::STATUS_SUCCESS, |
||||
$user->getId(), |
||||
$quizQuestionInfo['question'], |
||||
$quizQuestionInfo['quiz'] |
||||
); |
||||
|
||||
echo '<script>window.location.href = "'.$url.'";</script>'; |
||||
|
||||
exit; |
||||
} |
||||
|
||||
if (empty($lpItemInfo) && empty($quizQuestionInfo)) { |
||||
$this->plugin->addAuthenticationAttempt(LogEvent::STATUS_SUCCESS, $user->getId()); |
||||
} |
||||
|
||||
$loggedUser = [ |
||||
'user_id' => $user->getId(), |
||||
'status' => $user->getStatus(), |
||||
'uidReset' => true, |
||||
]; |
||||
|
||||
if (empty($user2fa)) { |
||||
ChamiloSession::write(WhispeakAuthPlugin::SESSION_2FA_USER, $user->getId()); |
||||
} |
||||
|
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_FAILED_LOGINS); |
||||
ChamiloSession::write('_user', $loggedUser); |
||||
Login::init_user($user->getId(), true); |
||||
|
||||
echo '<script>window.location.href = "'.api_get_path(WEB_PATH).'";</script>'; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @inheritDoc |
||||
*/ |
||||
protected function displayPage(array $variables) |
||||
{ |
||||
global $htmlHeadXtra; |
||||
|
||||
$htmlHeadXtra[] = api_get_js('rtc/RecordRTC.js'); |
||||
$htmlHeadXtra[] = api_get_js_simple(api_get_path(WEB_PLUGIN_PATH).'whispeakauth/assets/js/RecordAudio.js'); |
||||
|
||||
$pageTitle = $this->plugin->get_title(); |
||||
|
||||
$template = new \Template($pageTitle); |
||||
|
||||
foreach ($variables as $key => $value) { |
||||
$template->assign($key, $value); |
||||
} |
||||
|
||||
$pageContent = $template->fetch('whispeakauth/view/authentify_recorder.html.twig'); |
||||
|
||||
$template->assign('header', $pageTitle); |
||||
$template->assign('content', $pageContent); |
||||
$template->display_one_col_template(); |
||||
} |
||||
} |
@ -0,0 +1,66 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
namespace Chamilo\PluginBundle\WhispeakAuth\Controller; |
||||
|
||||
use Chamilo\UserBundle\Entity\User; |
||||
use FFMpeg\FFMpeg; |
||||
use FFMpeg\Format\Audio\Wav; |
||||
|
||||
/** |
||||
* Class BaseController. |
||||
* |
||||
* @package Chamilo\PluginBundle\WhispeakAuth\Controller |
||||
*/ |
||||
abstract class BaseController |
||||
{ |
||||
/** |
||||
* @var \WhispeakAuthPlugin |
||||
*/ |
||||
protected $plugin; |
||||
|
||||
/** |
||||
* BaseController constructor. |
||||
*/ |
||||
public function __construct() |
||||
{ |
||||
$this->plugin = \WhispeakAuthPlugin::create(); |
||||
} |
||||
|
||||
/** |
||||
* @param array $variables |
||||
*/ |
||||
abstract protected function displayPage(array $variables); |
||||
|
||||
/** |
||||
* @param \Chamilo\UserBundle\Entity\User $user |
||||
* |
||||
* @throws \Exception |
||||
* @return string |
||||
*/ |
||||
protected function uploadAudioFile(User $user) |
||||
{ |
||||
$pluginName = $this->plugin->get_name(); |
||||
|
||||
$path = api_upload_file($pluginName, $_FILES['audio'], $user->getId()); |
||||
|
||||
if (false === $path) { |
||||
throw new \Exception(get_lang('UploadError')); |
||||
} |
||||
|
||||
$fullPath = api_get_path(SYS_UPLOAD_PATH).$pluginName.$path['path_to_save']; |
||||
$mimeType = mime_content_type($fullPath); |
||||
|
||||
if ('wav' !== substr($mimeType, -3)) { |
||||
$ffmpeg = FFMpeg::create(); |
||||
|
||||
$audioFile = $ffmpeg->open($fullPath); |
||||
|
||||
$fullPath = dirname($fullPath).'/audio.wav'; |
||||
|
||||
$audioFile->save(new Wav(), $fullPath); |
||||
} |
||||
|
||||
return $fullPath; |
||||
} |
||||
} |
@ -0,0 +1,98 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
namespace Chamilo\PluginBundle\WhispeakAuth\Controller; |
||||
|
||||
use Chamilo\PluginBundle\WhispeakAuth\Request\ApiRequest; |
||||
|
||||
/** |
||||
* Class EnrollmentController. |
||||
* |
||||
* @package Chamilo\PluginBundle\WhispeakAuth\Controller |
||||
*/ |
||||
class EnrollmentController extends BaseController |
||||
{ |
||||
/** |
||||
* @throws \Exception |
||||
*/ |
||||
public function index() |
||||
{ |
||||
if (!$this->plugin->toolIsEnabled()) { |
||||
throw new \Exception(get_lang('NotAllowed')); |
||||
} |
||||
|
||||
$user = api_get_user_entity(api_get_user_id()); |
||||
|
||||
$userIsEnrolled = \WhispeakAuthPlugin::checkUserIsEnrolled($user->getId()); |
||||
|
||||
if ($userIsEnrolled) { |
||||
throw new \Exception($this->plugin->get_lang('SpeechAuthAlreadyEnrolled')); |
||||
} |
||||
|
||||
$request = new ApiRequest(); |
||||
$response = $request->createEnrollmentSessionToken($user); |
||||
|
||||
\ChamiloSession::write(\WhispeakAuthPlugin::SESSION_SENTENCE_TEXT, $response['token']); |
||||
|
||||
$this->displayPage( |
||||
[ |
||||
'action' => 'enrollment', |
||||
'sample_text' => $response['text'], |
||||
] |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* @throws \Exception |
||||
*/ |
||||
public function ajax() |
||||
{ |
||||
if (!$this->plugin->toolIsEnabled() || empty($_FILES['audio'])) { |
||||
throw new \Exception(get_lang('NotAllowed')); |
||||
} |
||||
|
||||
$user = api_get_user_entity(api_get_user_id()); |
||||
|
||||
$audioFilePath = $this->uploadAudioFile($user); |
||||
|
||||
$token = \ChamiloSession::read(\WhispeakAuthPlugin::SESSION_SENTENCE_TEXT); |
||||
|
||||
if (empty($token)) { |
||||
throw new \Exception($this->plugin->get_lang('EnrollmentFailed')); |
||||
} |
||||
|
||||
$request = new ApiRequest(); |
||||
$response = $request->createEnrollment($token, $audioFilePath); |
||||
|
||||
\ChamiloSession::erase(\WhispeakAuthPlugin::SESSION_SENTENCE_TEXT); |
||||
|
||||
$this->plugin->saveEnrollment($user, $response['speaker']); |
||||
|
||||
echo \Display::return_message($this->plugin->get_lang('EnrollmentSuccess'), 'success'); |
||||
} |
||||
|
||||
/** |
||||
* @inheritDoc |
||||
*/ |
||||
protected function displayPage(array $variables) |
||||
{ |
||||
global $htmlHeadXtra; |
||||
|
||||
$htmlHeadXtra[] = api_get_js('rtc/RecordRTC.js'); |
||||
$htmlHeadXtra[] = api_get_js_simple(api_get_path(WEB_PLUGIN_PATH).'whispeakauth/assets/js/RecordAudio.js'); |
||||
|
||||
$pageTitle = $this->plugin->get_title(); |
||||
|
||||
$template = new \Template($pageTitle); |
||||
|
||||
foreach ($variables as $key => $value) { |
||||
$template->assign($key, $value); |
||||
} |
||||
|
||||
$pageContent = $template->fetch('whispeakauth/view/record_audio.html.twig'); |
||||
|
||||
$template->assign('header', $pageTitle); |
||||
$template->assign('content', $pageContent); |
||||
$template->display_one_col_template(); |
||||
} |
||||
} |
@ -0,0 +1,145 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
namespace Chamilo\PluginBundle\Entity\WhispeakAuth; |
||||
|
||||
use Chamilo\UserBundle\Entity\User; |
||||
use DateTime; |
||||
use Doctrine\ORM\Mapping as ORM; |
||||
|
||||
/** |
||||
* Class LogEvent. |
||||
* |
||||
* @package Chamilo\PluginBundle\Entity\WhispeakAuth |
||||
* |
||||
* @ORM\Table(name="whispeak_log_event") |
||||
* @ORM\Entity() |
||||
* @ORM\InheritanceType("SINGLE_TABLE") |
||||
* @ORM\DiscriminatorColumn(name="discr", type="string") |
||||
* @ORM\DiscriminatorMap({ |
||||
* "log_event" = "Chamilo\PluginBundle\Entity\WhispeakAuth\LogEvent", |
||||
* "log_event_lp" = "Chamilo\PluginBundle\Entity\WhispeakAuth\LogEventLp", |
||||
* "log_event_quiz" = "Chamilo\PluginBundle\Entity\WhispeakAuth\LogEventQuiz" |
||||
* }) |
||||
*/ |
||||
class LogEvent |
||||
{ |
||||
const STATUS_FAILED = 0; |
||||
const STATUS_SUCCESS = 1; |
||||
|
||||
/** |
||||
* @var int |
||||
* |
||||
* @ORM\Column(name="id", type="integer") |
||||
* @ORM\Id() |
||||
* @ORM\GeneratedValue() |
||||
*/ |
||||
private $id; |
||||
/** |
||||
* @var DateTime |
||||
* |
||||
* @ORM\Column(name="datetime", type="datetime") |
||||
*/ |
||||
private $datetime; |
||||
/** |
||||
* @var int |
||||
* |
||||
* @ORM\Column(name="action_status", type="smallint") |
||||
*/ |
||||
private $actionStatus; |
||||
/** |
||||
* @var User |
||||
* |
||||
* @ORM\ManyToOne(targetEntity="Chamilo\UserBundle\Entity\User") |
||||
* @ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false) |
||||
*/ |
||||
private $user; |
||||
|
||||
/** |
||||
* @return int |
||||
*/ |
||||
public function getId() |
||||
{ |
||||
return $this->id; |
||||
} |
||||
|
||||
/** |
||||
* @param int $id |
||||
* |
||||
* @return LogEvent |
||||
*/ |
||||
public function setId($id) |
||||
{ |
||||
$this->id = $id; |
||||
|
||||
return $this; |
||||
} |
||||
|
||||
/** |
||||
* @return DateTime |
||||
*/ |
||||
public function getDatetime() |
||||
{ |
||||
return $this->datetime; |
||||
} |
||||
|
||||
/** |
||||
* @param DateTime $datetime |
||||
* |
||||
* @return LogEvent |
||||
*/ |
||||
public function setDatetime($datetime) |
||||
{ |
||||
$this->datetime = $datetime; |
||||
|
||||
return $this; |
||||
} |
||||
|
||||
/** |
||||
* @return int |
||||
*/ |
||||
public function getActionStatus() |
||||
{ |
||||
return $this->actionStatus; |
||||
} |
||||
|
||||
/** |
||||
* @param int $actionStatus |
||||
* |
||||
* @return LogEvent |
||||
*/ |
||||
public function setActionStatus($actionStatus) |
||||
{ |
||||
$this->actionStatus = $actionStatus; |
||||
|
||||
return $this; |
||||
} |
||||
|
||||
/** |
||||
* @return User |
||||
*/ |
||||
public function getUser() |
||||
{ |
||||
return $this->user; |
||||
} |
||||
|
||||
/** |
||||
* @param User $user |
||||
* |
||||
* @return LogEvent |
||||
*/ |
||||
public function setUser($user) |
||||
{ |
||||
$this->user = $user; |
||||
|
||||
return $this; |
||||
} |
||||
|
||||
/** |
||||
* @return string |
||||
*/ |
||||
public function getTypeString() |
||||
{ |
||||
return '-'; |
||||
} |
||||
} |
@ -0,0 +1,84 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
namespace Chamilo\PluginBundle\Entity\WhispeakAuth; |
||||
|
||||
use Chamilo\CourseBundle\Entity\CLp; |
||||
use Chamilo\CourseBundle\Entity\CLpItem; |
||||
use Doctrine\ORM\Mapping as ORM; |
||||
|
||||
/** |
||||
* Class LogEventLp. |
||||
* |
||||
* @package Chamilo\PluginBundle\Entity\WhispeakAuth |
||||
* |
||||
* @ORM\Entity() |
||||
*/ |
||||
class LogEventLp extends LogEvent |
||||
{ |
||||
/** |
||||
* @var CLpItem |
||||
* |
||||
* @ORM\ManyToOne(targetEntity="Chamilo\CourseBundle\Entity\CLpItem") |
||||
* @ORM\JoinColumn(name="lp_item_id", referencedColumnName="iid") |
||||
*/ |
||||
private $lpItem; |
||||
/** |
||||
* @var CLp |
||||
* |
||||
* @ORM\ManyToOne(targetEntity="Chamilo\CourseBundle\Entity\CLp") |
||||
* @ORM\JoinColumn(name="lp_id", referencedColumnName="iid") |
||||
*/ |
||||
private $lp; |
||||
|
||||
/** |
||||
* @return CLpItem |
||||
*/ |
||||
public function getLpItem() |
||||
{ |
||||
return $this->lpItem; |
||||
} |
||||
|
||||
/** |
||||
* @param CLpItem $lpItem |
||||
* |
||||
* @return LogEventLp |
||||
*/ |
||||
public function setLpItem($lpItem) |
||||
{ |
||||
$this->lpItem = $lpItem; |
||||
|
||||
return $this; |
||||
} |
||||
|
||||
/** |
||||
* @return CLp |
||||
*/ |
||||
public function getLp() |
||||
{ |
||||
return $this->lp; |
||||
} |
||||
|
||||
/** |
||||
* @param CLp $lp |
||||
* |
||||
* @return LogEventLp |
||||
*/ |
||||
public function setLp($lp) |
||||
{ |
||||
$this->lp = $lp; |
||||
|
||||
return $this; |
||||
} |
||||
|
||||
/** |
||||
* @inheritDoc |
||||
*/ |
||||
public function getTypeString() |
||||
{ |
||||
$lpName = $this->lp->getName(); |
||||
$itemTitle = $this->getLpItem()->getTitle(); |
||||
|
||||
return "$lpName > $itemTitle"; |
||||
} |
||||
} |
@ -0,0 +1,84 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
namespace Chamilo\PluginBundle\Entity\WhispeakAuth; |
||||
|
||||
use Chamilo\CourseBundle\Entity\CQuiz; |
||||
use Chamilo\CourseBundle\Entity\CQuizQuestion; |
||||
use Doctrine\ORM\Mapping as ORM; |
||||
|
||||
/** |
||||
* Class LogEventQuiz. |
||||
* |
||||
* @package Chamilo\PluginBundle\Entity\WhispeakAuth |
||||
* |
||||
* @ORM\Entity() |
||||
*/ |
||||
class LogEventQuiz extends LogEvent |
||||
{ |
||||
/** |
||||
* @var CQuizQuestion |
||||
* |
||||
* @ORM\ManyToOne(targetEntity="Chamilo\CourseBundle\Entity\CQuizQuestion") |
||||
* @ORM\JoinColumn(name="question_id", referencedColumnName="iid") |
||||
*/ |
||||
private $question; |
||||
/** |
||||
* @var CQuiz |
||||
* |
||||
* @ORM\ManyToOne(targetEntity="Chamilo\CourseBundle\Entity\CQuiz") |
||||
* @ORM\JoinColumn(name="quiz_id", referencedColumnName="iid") |
||||
*/ |
||||
private $quiz; |
||||
|
||||
/** |
||||
* @return CQuizQuestion |
||||
*/ |
||||
public function getQuestion() |
||||
{ |
||||
return $this->question; |
||||
} |
||||
|
||||
/** |
||||
* @param CQuizQuestion $question |
||||
* |
||||
* @return LogEventQuiz |
||||
*/ |
||||
public function setQuestion($question) |
||||
{ |
||||
$this->question = $question; |
||||
|
||||
return $this; |
||||
} |
||||
|
||||
/** |
||||
* @return CQuiz |
||||
*/ |
||||
public function getQuiz() |
||||
{ |
||||
return $this->quiz; |
||||
} |
||||
|
||||
/** |
||||
* @param CQuiz $quiz |
||||
* |
||||
* @return LogEventQuiz |
||||
*/ |
||||
public function setQuiz($quiz) |
||||
{ |
||||
$this->quiz = $quiz; |
||||
|
||||
return $this; |
||||
} |
||||
|
||||
/** |
||||
* @inheritDoc |
||||
*/ |
||||
public function getTypeString() |
||||
{ |
||||
$quiz = strip_tags($this->getQuiz()->getTitle()); |
||||
$question = strip_tags($this->getQuestion()->getQuestion()); |
||||
|
||||
return "$quiz > $question"; |
||||
} |
||||
} |
@ -1,10 +1,20 @@ |
||||
Speech authentication with Whispeak |
||||
=================================== |
||||
|
||||
**Notice:** |
||||
|
||||
This plugin requires the user to grant permission to use the microphone connected on the web browser. Currently, |
||||
browsers are limiting this permission to be used only in a secure environment with HTTPS. |
||||
**If your portal does not work with HTTP, then Whispeak authentication may not work.** |
||||
|
||||
Instructions: |
||||
------------- |
||||
|
||||
> Make sure the directory `src/Chamilo\PluginBundle` is writable by the web server in order for the plugin is installed |
||||
> properly. This might imply a manual change on your server (outside of the Chamilo interface). |
||||
|
||||
1. Install plugin in Chamilo. |
||||
2. Set the plugin configuration with the token and API url. And enable the plugin. |
||||
2. Set the plugin configuration enabling the plugin and (optionally) set the max attempts. |
||||
3. Set the `login_bottom` region to the plugin. |
||||
4. Add `$_configuration['whispeak_auth_enabled'] = true;` to `configuration.php` file. |
||||
5. Optionally, you can add the `menu_administrator` region to se the user logged activities from Whispeak. |
||||
|
@ -0,0 +1,188 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
namespace Chamilo\PluginBundle\WhispeakAuth\Request; |
||||
|
||||
use Chamilo\UserBundle\Entity\User; |
||||
use GuzzleHttp\Client; |
||||
use GuzzleHttp\Exception\RequestException; |
||||
|
||||
/** |
||||
* Class ApiRequest. |
||||
* |
||||
* @package Chamilo\PluginBundle\WhispeakAuth\Request |
||||
*/ |
||||
class ApiRequest |
||||
{ |
||||
/** |
||||
* @var \WhispeakAuthPlugin |
||||
*/ |
||||
protected $plugin; |
||||
/** |
||||
* @var string |
||||
*/ |
||||
protected $apiKey; |
||||
|
||||
/** |
||||
* BaseController constructor. |
||||
*/ |
||||
public function __construct() |
||||
{ |
||||
$this->plugin = \WhispeakAuthPlugin::create(); |
||||
$this->apiKey = $this->plugin->get(\WhispeakAuthPlugin::SETTING_TOKEN); |
||||
} |
||||
|
||||
/** |
||||
* Create a session token to perform an enrollment. |
||||
* |
||||
* @param \Chamilo\UserBundle\Entity\User $user |
||||
* |
||||
* @throws \Exception |
||||
* |
||||
* @return array |
||||
*/ |
||||
public function createEnrollmentSessionToken(User $user) |
||||
{ |
||||
$apiKey = $this->plugin->get(\WhispeakAuthPlugin::SETTING_TOKEN); |
||||
$langIso = api_get_language_isocode($user->getLanguage()); |
||||
|
||||
return $this->sendRequest( |
||||
'get', |
||||
'enroll', |
||||
$apiKey, |
||||
['lang' => $langIso] |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* @param string $token |
||||
* @param string $audioFilePath |
||||
* |
||||
* @throws \Exception |
||||
* |
||||
* @return array |
||||
*/ |
||||
public function createEnrollment($token, $audioFilePath) |
||||
{ |
||||
return $this->sendRequest( |
||||
'post', |
||||
'enroll', |
||||
$token, |
||||
[], |
||||
[ |
||||
[ |
||||
'name' => 'file', |
||||
'contents' => fopen($audioFilePath, 'r'), |
||||
'filename' => basename($audioFilePath), |
||||
], |
||||
] |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* @param \Chamilo\UserBundle\Entity\User|null $user |
||||
* |
||||
* @throws \Exception |
||||
* |
||||
* @return array |
||||
*/ |
||||
public function createAuthenticationSessionToken(User $user = null) |
||||
{ |
||||
$apiKey = $this->plugin->get(\WhispeakAuthPlugin::SETTING_TOKEN); |
||||
|
||||
$langIso = api_get_language_isocode($user ? $user->getLanguage() : null); |
||||
|
||||
return $this->sendRequest( |
||||
'get', |
||||
'auth', |
||||
$apiKey, |
||||
['lang' => $langIso] |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* @param string $token |
||||
* @param string $speaker |
||||
* @param string $audioFilePath |
||||
* |
||||
* @throws \Exception |
||||
* |
||||
* @return bool |
||||
*/ |
||||
public function performAuthentication($token, $speaker, $audioFilePath) |
||||
{ |
||||
try { |
||||
$this->sendRequest( |
||||
'post', |
||||
'auth', |
||||
$token, |
||||
[], |
||||
[ |
||||
[ |
||||
'name' => 'speaker', |
||||
'contents' => $speaker, |
||||
], |
||||
[ |
||||
'name' => 'file', |
||||
'contents' => fopen($audioFilePath, 'r'), |
||||
'filename' => basename($audioFilePath), |
||||
], |
||||
] |
||||
); |
||||
|
||||
return true; |
||||
} catch (\Exception $e) { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @param string $method |
||||
* @param string $uri |
||||
* @param string $authBearer |
||||
* @param array $query |
||||
* @param array $multipart |
||||
* |
||||
* @throws \Exception |
||||
* |
||||
* @return array |
||||
*/ |
||||
private function sendRequest($method, $uri, $authBearer, array $query = [], array $multipart = []) |
||||
{ |
||||
$httpClient = new Client(['base_uri' => $this->plugin->getApiUrl()]); |
||||
|
||||
try { |
||||
$responseBody = $httpClient |
||||
->request( |
||||
$method, |
||||
$uri, |
||||
[ |
||||
'headers' => ['Authorization' => "Bearer $authBearer"], |
||||
'query' => $query, |
||||
'multipart' => $multipart, |
||||
] |
||||
) |
||||
->getBody() |
||||
->getContents(); |
||||
|
||||
return json_decode($responseBody, true); |
||||
} catch (RequestException $requestException) { |
||||
if (!$requestException->hasResponse()) { |
||||
throw new \Exception($requestException->getMessage()); |
||||
} |
||||
|
||||
$responseBody = $requestException->getResponse()->getBody()->getContents(); |
||||
$json = json_decode($responseBody, true); |
||||
|
||||
if (empty($json['message'])) { |
||||
throw new \Exception($requestException->getMessage()); |
||||
} |
||||
|
||||
$message = is_array($json['message']) ? implode(PHP_EOL, $json['message']) : $json['message']; |
||||
|
||||
throw new \Exception($message); |
||||
} catch (Exception $exception) { |
||||
throw new \Exception($exception->getMessage()); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,61 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
/** |
||||
* Class WhispeakConditionalLoginHook. |
||||
* |
||||
* Implements a Two-Factor Authentication with Whispeak. |
||||
*/ |
||||
class WhispeakConditionalLoginHook extends HookObserver implements HookConditionalLoginObserverInterface |
||||
{ |
||||
/** |
||||
* WhispeakConditionalLoginHook constructor. |
||||
*/ |
||||
protected function __construct() |
||||
{ |
||||
parent::__construct( |
||||
'plugin/whispeakauth/WhispeakAuthPlugin.php', |
||||
'whispeakauth' |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Return an associative array (callable, url) needed for Conditional Login. |
||||
* <code> |
||||
* [ |
||||
* 'conditional_function' => function (array $userInfo) {}, |
||||
* 'url' => '', |
||||
* ] |
||||
* </code> |
||||
* conditional_function returns false to redirect to the url and returns true to continue with the classical login. |
||||
* |
||||
* @param HookConditionalLoginEventInterface $hook |
||||
* |
||||
* @return array |
||||
*/ |
||||
public function hookConditionalLogin(HookConditionalLoginEventInterface $hook) |
||||
{ |
||||
return [ |
||||
'conditional_function' => function (array $userInfo) { |
||||
$isEnrolled = WhispeakAuthPlugin::checkUserIsEnrolled($userInfo['user_id']); |
||||
|
||||
if (!$isEnrolled) { |
||||
return true; |
||||
} |
||||
|
||||
$user2fa = (int) ChamiloSession::read(WhispeakAuthPlugin::SESSION_2FA_USER, 0); |
||||
|
||||
if ($user2fa === (int) $userInfo['user_id']) { |
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_2FA_USER); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
ChamiloSession::write(WhispeakAuthPlugin::SESSION_2FA_USER, $userInfo['user_id']); |
||||
|
||||
return false; |
||||
}, |
||||
'url' => api_get_path(WEB_PLUGIN_PATH).$this->getPluginName().'/authentify.php', |
||||
]; |
||||
} |
||||
} |
@ -0,0 +1,63 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
/** |
||||
* Class WhispeakMyStudentsLpTrackingHook. |
||||
*/ |
||||
class WhispeakMyStudentsLpTrackingHook extends HookObserver implements HookMyStudentsLpTrackingObserverInterface |
||||
{ |
||||
/** |
||||
* WhispeakMyStudentsLpTrackingHook constructor. |
||||
*/ |
||||
protected function __construct() |
||||
{ |
||||
parent::__construct( |
||||
'plugin/whispeakauth/WhispeakAuthPlugin.php', |
||||
'whispeakauth' |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* @param HookMyStudentsLpTrackingEventInterface $hook |
||||
* |
||||
* @return array |
||||
*/ |
||||
public function trackingHeader(HookMyStudentsLpTrackingEventInterface $hook) |
||||
{ |
||||
return [ |
||||
'value' => WhispeakAuthPlugin::create()->get_lang('plugin_title'), |
||||
'attrs' => ['class' => 'text-center'], |
||||
]; |
||||
} |
||||
|
||||
/** |
||||
* @param HookMyStudentsLpTrackingEventInterface $hook |
||||
* |
||||
* @throws \Doctrine\ORM\Query\QueryException |
||||
* |
||||
* @return array |
||||
*/ |
||||
public function trackingContent(HookMyStudentsLpTrackingEventInterface $hook) |
||||
{ |
||||
$data = $hook->getEventData(); |
||||
|
||||
$totalCount = WhispeakAuthPlugin::countAllAttemptsInLearningPath($data['lp_id'], $data['student_id']); |
||||
|
||||
if (0 === $totalCount) { |
||||
return [ |
||||
'value' => '-', |
||||
'attrs' => ['class' => 'text-center'], |
||||
]; |
||||
} |
||||
|
||||
$successCount = WhispeakAuthPlugin::countSuccessAttemptsInLearningPath($data['lp_id'], $data['student_id']); |
||||
|
||||
$attrs = ['class' => 'text-center ']; |
||||
$attrs['class'] .= $successCount <= $totalCount / 2 ? 'text-danger' : 'text-success'; |
||||
|
||||
return [ |
||||
'value' => Display::tag('strong', "$successCount / $totalCount"), |
||||
'attrs' => $attrs, |
||||
]; |
||||
} |
||||
} |
@ -0,0 +1,81 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
/** |
||||
* Class WhispeakMyStudentsQuizTrackingHook. |
||||
*/ |
||||
class WhispeakMyStudentsQuizTrackingHook extends HookObserver implements HookMyStudentsQuizTrackingObserverInterface |
||||
{ |
||||
/** |
||||
* WhispeakMyStudentsQuizTrackingHook constructor. |
||||
*/ |
||||
protected function __construct() |
||||
{ |
||||
parent::__construct( |
||||
'plugin/whispeakauth/WhispeakAuthPlugin.php', |
||||
'whispeakauth' |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Return an associative array this value and attributes. |
||||
* <code> |
||||
* [ |
||||
* 'value' => 'Users online', |
||||
* 'attrs' => ['class' => 'text-center'], |
||||
* ] |
||||
* </code>. |
||||
* |
||||
* @param HookMyStudentsQuizTrackingEventInterface $hook |
||||
* |
||||
* @return array |
||||
*/ |
||||
public function trackingHeader(HookMyStudentsQuizTrackingEventInterface $hook) |
||||
{ |
||||
return [ |
||||
'value' => WhispeakAuthPlugin::create()->get_lang('plugin_title'), |
||||
'attrs' => [ |
||||
'class' => 'text-center', |
||||
], |
||||
]; |
||||
} |
||||
|
||||
/** |
||||
* Return an associative array this value and attributes. |
||||
* <code> |
||||
* [ |
||||
* 'value' => '5 connected users ', |
||||
* 'attrs' => ['class' => 'text-center text-success'], |
||||
* ] |
||||
* </code>. |
||||
* |
||||
* @param HookMyStudentsQuizTrackingEventInterface $hook |
||||
* |
||||
* @throws \Doctrine\ORM\Query\QueryException |
||||
* |
||||
* @return array |
||||
*/ |
||||
public function trackingContent(HookMyStudentsQuizTrackingEventInterface $hook) |
||||
{ |
||||
$data = $hook->getEventData(); |
||||
|
||||
$totalCount = WhispeakAuthPlugin::countAllAttemptsInQuiz($data['quiz_id'], $data['student_id']); |
||||
|
||||
if (0 === $totalCount) { |
||||
return [ |
||||
'value' => '-', |
||||
'attrs' => ['class' => 'text-center'], |
||||
]; |
||||
} |
||||
|
||||
$successCount = WhispeakAuthPlugin::countSuccessAttemptsInQuiz($data['quiz_id'], $data['student_id']); |
||||
|
||||
$attrs = ['class' => 'text-center ']; |
||||
$attrs['class'] .= $successCount <= $totalCount / 2 ? 'text-danger' : 'text-success'; |
||||
|
||||
return [ |
||||
'value' => Display::tag('strong', "$successCount / $totalCount"), |
||||
'attrs' => $attrs, |
||||
]; |
||||
} |
||||
} |
@ -0,0 +1,128 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
use Chamilo\PluginBundle\Entity\WhispeakAuth\LogEvent; |
||||
use Chamilo\PluginBundle\Entity\WhispeakAuth\LogEventLp; |
||||
use Chamilo\PluginBundle\Entity\WhispeakAuth\LogEventQuiz; |
||||
|
||||
$cidReset = true; |
||||
|
||||
require_once __DIR__.'/../../main/inc/global.inc.php'; |
||||
|
||||
$plugin = WhispeakAuthPlugin::create(); |
||||
|
||||
api_protect_admin_script(true); |
||||
|
||||
$plugin->protectTool(); |
||||
|
||||
$form = new FormValidator('frm_filter', 'GET'); |
||||
$slctUsers = $form->addSelectAjax( |
||||
'users', |
||||
get_lang('Users'), |
||||
[], |
||||
[ |
||||
'url' => api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_like', |
||||
'id' => 'user_id', |
||||
'multiple' => true, |
||||
] |
||||
); |
||||
$form->addDatePicker('date', get_lang('Date')); |
||||
$form->addButtonSearch(get_lang('Search')); |
||||
|
||||
$results = []; |
||||
|
||||
if ($form->validate()) { |
||||
$formValues = $form->exportValues(); |
||||
$userIds = $formValues['users'] ?: []; |
||||
/** @var \DateTime $date */ |
||||
$starDate = api_get_utc_datetime($formValues['date'], true, true); |
||||
$endDate = clone $starDate; |
||||
$endDate->modify('next day'); |
||||
|
||||
$em = Database::getManager(); |
||||
$repo = $em->getRepository('ChamiloPluginBundle:WhispeakAuth\LogEvent'); |
||||
|
||||
foreach ($userIds as $userId) { |
||||
$qb = $em->createQueryBuilder(); |
||||
$results[$userId] = $qb |
||||
->select('event') |
||||
->from('ChamiloPluginBundle:WhispeakAuth\LogEvent', 'event') |
||||
->where( |
||||
$qb->expr()->gte('event.datetime', ':start_date') |
||||
) |
||||
->andWhere( |
||||
$qb->expr()->lt('event.datetime', ':end_date') |
||||
) |
||||
->andWhere( |
||||
$qb->expr()->eq('event.user', ':user') |
||||
) |
||||
->setParameters( |
||||
[ |
||||
'start_date' => $starDate->format('Y-m-d H:i:s'), |
||||
'end_date' => $endDate->format('Y-m-d H:i:s'), |
||||
'user' => $userId, |
||||
] |
||||
) |
||||
->getQuery() |
||||
->getResult(); |
||||
} |
||||
} |
||||
|
||||
$pageContent = ''; |
||||
|
||||
/** |
||||
* @var int $userId |
||||
* @var array|LogEvent[] $logEvents |
||||
*/ |
||||
foreach ($results as $userId => $logEvents) { |
||||
if (empty($logEvents)) { |
||||
continue; |
||||
} |
||||
|
||||
$user = $logEvents[0]->getUser(); |
||||
|
||||
$slctUsers->addOption($user->getCompleteNameWithUsername(), $user->getId()); |
||||
|
||||
$table = new HTML_Table(['class' => 'table table-hover']); |
||||
$table->setHeaderContents(0, 0, get_lang('DateTime')); |
||||
$table->setHeaderContents(0, 1, get_lang('Type')); |
||||
$table->setHeaderContents(0, 2, get_lang('Status')); |
||||
|
||||
foreach ($logEvents as $i => $logEvent) { |
||||
$row = $i + 1; |
||||
|
||||
$type = ''; |
||||
|
||||
switch (get_class($logEvent)) { |
||||
case LogEventQuiz::class: |
||||
$type = '<span class="label label-info">'.get_lang('Question').'</span>'.PHP_EOL; |
||||
break; |
||||
case LogEventLp::class: |
||||
$type = '<span class="label label-info">'.get_lang('LearningPath').'</span>'.PHP_EOL; |
||||
break; |
||||
} |
||||
|
||||
$table->setCellContents( |
||||
$row, |
||||
0, |
||||
[ |
||||
api_convert_and_format_date($logEvent->getDatetime(), DATE_TIME_FORMAT_SHORT), |
||||
$type.PHP_EOL.$logEvent->getTypeString(), |
||||
$logEvent->getActionStatus() === LogEvent::STATUS_SUCCESS ? get_lang('Success') : get_lang('Failed'), |
||||
] |
||||
); |
||||
} |
||||
|
||||
$table->updateColAttributes(0, ['class' => 'text-center']); |
||||
$table->updateColAttributes(2, ['class' => 'text-center']); |
||||
|
||||
$pageContent .= Display::page_header($user->getCompleteNameWithUsername()); |
||||
$pageContent .= $table->toHtml(); |
||||
} |
||||
|
||||
$template = new Template($plugin->get_title()); |
||||
$template->assign( |
||||
'content', |
||||
$form->returnForm().PHP_EOL.$pageContent |
||||
); |
||||
$template->display_one_col_template(); |
@ -0,0 +1,142 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
use Chamilo\PluginBundle\Entity\WhispeakAuth\LogEvent; |
||||
|
||||
$cidReset = true; |
||||
|
||||
require_once __DIR__.'/../../../main/inc/global.inc.php'; |
||||
|
||||
api_block_anonymous_users(false); |
||||
|
||||
$plugin = WhispeakAuthPlugin::create(); |
||||
|
||||
$plugin->protectTool(false); |
||||
|
||||
$tokenIsValid = Security::check_token(); |
||||
|
||||
if (!$tokenIsValid) { |
||||
WhispeakAuthPlugin::displayNotAllowedMessage(); |
||||
} |
||||
|
||||
$maxAttempts = $plugin->getMaxAttempts(); |
||||
$failedLogins = ChamiloSession::read(WhispeakAuthPlugin::SESSION_FAILED_LOGINS, 0); |
||||
|
||||
if ($maxAttempts && $failedLogins >= $maxAttempts) { |
||||
echo Display::return_message($plugin->get_lang('MaxAttemptsReached'), 'warning'); |
||||
|
||||
exit; |
||||
} |
||||
|
||||
$user = api_get_user_entity(api_get_user_id()); |
||||
$password = isset($_POST['password']) ? $_POST['password'] : null; |
||||
|
||||
if (empty($password) || empty($user)) { |
||||
WhispeakAuthPlugin::displayNotAllowedMessage(); |
||||
} |
||||
|
||||
if (!in_array($user->getAuthSource(), [PLATFORM_AUTH_SOURCE, CAS_AUTH_SOURCE])) { |
||||
WhispeakAuthPlugin::displayNotAllowedMessage(); |
||||
} |
||||
|
||||
/** @var array $lpItemInfo */ |
||||
$lpItemInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_LP_ITEM, []); |
||||
/** @var array $quizQuestionInfo */ |
||||
$quizQuestionInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_QUIZ_QUESTION, []); |
||||
|
||||
$isValidPassword = UserManager::isPasswordValid($user->getPassword(), $password, $user->getSalt()); |
||||
$isActive = $user->isActive(); |
||||
$isExpired = empty($user->getExpirationDate()) || $user->getExpirationDate() > api_get_utc_datetime(null, false, true); |
||||
|
||||
$userPass = true; |
||||
|
||||
if (!$isValidPassword || !$isActive || !$isExpired) { |
||||
if (!empty($lpItemInfo)) { |
||||
$plugin->addAttemptInLearningPath( |
||||
LogEvent::STATUS_FAILED, |
||||
$user->getId(), |
||||
$lpItemInfo['lp_item'], |
||||
$lpItemInfo['lp'] |
||||
); |
||||
} elseif (!empty($quizQuestionInfo)) { |
||||
$plugin->addAttemptInQuiz( |
||||
LogEvent::STATUS_FAILED, |
||||
$user->getId(), |
||||
$quizQuestionInfo['question'], |
||||
$quizQuestionInfo['quiz'] |
||||
); |
||||
} |
||||
|
||||
$userPass = false; |
||||
|
||||
$message = $plugin->get_lang('AuthentifyFailed'); |
||||
|
||||
if (!$isActive) { |
||||
$message .= PHP_EOL.get_lang('Account inactive'); |
||||
} |
||||
|
||||
if (!$isExpired) { |
||||
$message .= PHP_EOL.get_lang('AccountExpired'); |
||||
} |
||||
|
||||
ChamiloSession::write(WhispeakAuthPlugin::SESSION_FAILED_LOGINS, ++$failedLogins); |
||||
|
||||
if ($maxAttempts && $failedLogins >= $maxAttempts) { |
||||
$message .= PHP_EOL.'<span data-reach-attempts="true">'.$plugin->get_lang('MaxAttemptsReached').'</span>'; |
||||
} else { |
||||
$message .= PHP_EOL.$plugin->get_lang('TryAgain'); |
||||
} |
||||
|
||||
echo Display::return_message($message, 'error', false); |
||||
|
||||
if (!$maxAttempts || |
||||
($maxAttempts && $failedLogins >= $maxAttempts) |
||||
) { |
||||
$userPass = true; |
||||
} |
||||
} elseif ($isValidPassword) { |
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_FAILED_LOGINS); |
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_2FA_USER); |
||||
|
||||
if (!empty($lpItemInfo)) { |
||||
$plugin->addAttemptInLearningPath( |
||||
LogEvent::STATUS_SUCCESS, |
||||
$user->getId(), |
||||
$lpItemInfo['lp_item'], |
||||
$lpItemInfo['lp'] |
||||
); |
||||
} elseif (!empty($quizQuestionInfo)) { |
||||
$plugin->addAttemptInQuiz( |
||||
LogEvent::STATUS_SUCCESS, |
||||
$user->getId(), |
||||
$quizQuestionInfo['question'], |
||||
$quizQuestionInfo['quiz'] |
||||
); |
||||
} |
||||
|
||||
echo Display::return_message($plugin->get_lang('AuthentifySuccess'), 'success'); |
||||
} |
||||
|
||||
if ($userPass) { |
||||
$url = ''; |
||||
|
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_FAILED_LOGINS); |
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_2FA_USER); |
||||
|
||||
if ($lpItemInfo) { |
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_LP_ITEM); |
||||
|
||||
$url = $lpItemInfo['src']; |
||||
} elseif ($quizQuestionInfo) { |
||||
$quizQuestionInfo['passed'] = true; |
||||
$url = api_get_path(WEB_CODE_PATH).'exercise/exercise_submit.php?'.$quizQuestionInfo['url_params']; |
||||
|
||||
ChamiloSession::write(WhispeakAuthPlugin::SESSION_QUIZ_QUESTION, $quizQuestionInfo); |
||||
} |
||||
|
||||
if (!empty($url)) { |
||||
echo ' |
||||
<script>window.location.href = "'.$url.'";</script> |
||||
'; |
||||
} |
||||
} |
@ -1,26 +1,19 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
use Chamilo\PluginBundle\WhispeakAuth\Controller\AuthenticationController; |
||||
|
||||
$cidReset = true; |
||||
|
||||
require_once __DIR__.'/../../main/inc/global.inc.php'; |
||||
|
||||
$plugin = WhispeakAuthPlugin::create(); |
||||
|
||||
$plugin->protectTool(); |
||||
|
||||
$form = new FormValidator('enter_username', 'post', '#'); |
||||
$form->addText('username', get_lang('Username')); |
||||
|
||||
$htmlHeadXtra[] = api_get_js('rtc/RecordRTC.js'); |
||||
$htmlHeadXtra[] = api_get_js_simple(api_get_path(WEB_PLUGIN_PATH).'whispeakauth/assets/js/RecordAudio.js'); |
||||
|
||||
$template = new Template(); |
||||
$template->assign('form', $form->returnForm()); |
||||
$template->assign('sample_text', $plugin->getAuthentifySampleText()); |
||||
|
||||
$content = $template->fetch('whispeakauth/view/authentify_recorder.html.twig'); |
||||
$controller = new AuthenticationController(); |
||||
|
||||
$template->assign('header', $plugin->get_title()); |
||||
$template->assign('content', $content); |
||||
$template->display_one_col_template(); |
||||
try { |
||||
$controller->index(); |
||||
} catch (Exception $exception) { |
||||
api_not_allowed( |
||||
true, |
||||
Display::return_message($exception->getMessage(), 'warning') |
||||
); |
||||
} |
||||
|
@ -0,0 +1,79 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
$cidReset = true; |
||||
|
||||
require_once __DIR__.'/../../main/inc/global.inc.php'; |
||||
|
||||
$plugin = WhispeakAuthPlugin::create(); |
||||
|
||||
$plugin->protectTool(); |
||||
|
||||
$userId = ChamiloSession::read(WhispeakAuthPlugin::SESSION_2FA_USER, 0) ?: api_get_user_id(); |
||||
|
||||
/** @var array $lpItemInfo */ |
||||
$lpItemInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_LP_ITEM, []); |
||||
/** @var learnpath $oLp */ |
||||
$oLp = ChamiloSession::read('oLP', null); |
||||
/** @var array $lpQuestionInfo */ |
||||
$lpQuestionInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_QUIZ_QUESTION, []); |
||||
/** @var Exercise $objExercise */ |
||||
$objExercise = ChamiloSession::read('objExercise', null); |
||||
|
||||
$isAuthOnLp = !empty($lpItemInfo) && !empty($oLp); |
||||
$isAuthOnQuiz = !empty($lpQuestionInfo) && !empty($objExercise); |
||||
|
||||
$showFullPage = !$isAuthOnLp && !$isAuthOnQuiz; |
||||
|
||||
if (empty($userId)) { |
||||
api_not_allowed($showFullPage); |
||||
} |
||||
|
||||
if (!empty($lpQuestionInfo) && empty($lpItemInfo)) { |
||||
echo Display::return_message( |
||||
$plugin->get_lang('MaxAttemptsReached').'<br>' |
||||
.'<strong>'.$plugin->get_lang('LoginWithUsernameAndPassword').'</strong>', |
||||
'warning', |
||||
false |
||||
); |
||||
} |
||||
|
||||
$form = new FormValidator( |
||||
'form-login', |
||||
'POST', |
||||
api_get_path(WEB_PLUGIN_PATH).'whispeakauth/ajax/authentify_password.php', |
||||
null, |
||||
null, |
||||
FormValidator::LAYOUT_BOX_NO_LABEL |
||||
); |
||||
$form->addElement( |
||||
'password', |
||||
'password', |
||||
get_lang('Pass'), |
||||
['id' => 'password', 'icon' => 'lock fa-fw', 'placeholder' => get_lang('Pass')] |
||||
); |
||||
$form->addHidden('sec_token', ''); |
||||
$form->setConstants(['sec_token' => Security::get_token()]); |
||||
$form->addButton('submitAuth', get_lang('LoginEnter'), 'check', 'primary', 'default', 'btn-block'); |
||||
|
||||
$template = new Template( |
||||
!$showFullPage ? '' : $plugin->get_title(), |
||||
$showFullPage, |
||||
$showFullPage, |
||||
false, |
||||
true, |
||||
false |
||||
); |
||||
$template->assign('form', $form->returnForm()); |
||||
|
||||
$content = $template->fetch('whispeakauth/view/authentify_password.html.twig'); |
||||
|
||||
if (!empty($lpQuestionInfo) && empty($lpItemInfo)) { |
||||
echo $content; |
||||
|
||||
exit; |
||||
} |
||||
|
||||
$template->assign('header', $plugin->get_title()); |
||||
$template->assign('content', $content); |
||||
$template->display_one_col_template(); |
@ -1,28 +1,21 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
use Chamilo\PluginBundle\WhispeakAuth\Controller\EnrollmentController; |
||||
|
||||
$cidReset = true; |
||||
|
||||
require_once __DIR__.'/../../main/inc/global.inc.php'; |
||||
|
||||
api_block_anonymous_users(true); |
||||
|
||||
$userId = api_get_user_id(); |
||||
$plugin = WhispeakAuthPlugin::create(); |
||||
|
||||
$plugin->protectTool(); |
||||
|
||||
$sampleText = $plugin->get_lang('EnrollmentSampleText'); |
||||
|
||||
$htmlHeadXtra[] = api_get_js('rtc/RecordRTC.js'); |
||||
$htmlHeadXtra[] = api_get_js_simple(api_get_path(WEB_PLUGIN_PATH).'whispeakauth/assets/js/RecordAudio.js'); |
||||
|
||||
$template = new Template(); |
||||
$template->assign('is_authenticated', WhispeakAuthPlugin::checkUserIsEnrolled($userId)); |
||||
$template->assign('sample_text', $sampleText); |
||||
|
||||
$content = $template->fetch('whispeakauth/view/record_audio.html.twig'); |
||||
$controller = new EnrollmentController(); |
||||
|
||||
$template->assign('header', $plugin->get_title()); |
||||
$template->assign('content', $content); |
||||
$template->display_one_col_template(); |
||||
try { |
||||
$controller->index(); |
||||
} catch (Exception $exception) { |
||||
api_not_allowed( |
||||
true, |
||||
Display::return_message($exception->getMessage(), 'warning') |
||||
); |
||||
} |
||||
|
@ -0,0 +1,44 @@ |
||||
<div class="row"> |
||||
<div class="col-sm-6 col-sm-offset-3 col-md-4 col-md-offset-4"> |
||||
{{ form }} |
||||
|
||||
<br> |
||||
|
||||
<div id="frm-login-result"></div> |
||||
</div> |
||||
</div> |
||||
|
||||
<script> |
||||
$(function () { |
||||
$('#form-login').on('submit', function (e) { |
||||
e.preventDefault(); |
||||
|
||||
var formData = new FormData(this), |
||||
self = $(this); |
||||
self.children().prop('disabled', true); |
||||
self.find(':submit em.fa').get(0).className = 'fa fa-spinner fa-spin'; |
||||
|
||||
$ |
||||
.ajax({ |
||||
type: 'POST', |
||||
url: this.action, |
||||
data: formData, |
||||
processData: false, |
||||
contentType: false |
||||
}) |
||||
.then(function (response) { |
||||
$('#frm-login-result').html(response); |
||||
self.children().prop('disabled', false); |
||||
self.find(':submit em.fa').get(0).className = 'fa fa-check'; |
||||
|
||||
if ($('#frm-login-result > .alert.alert-success').length > 0 || |
||||
$('#frm-login-result > .alert.alert-danger [data-reach-attempts]').length > 0 |
||||
) { |
||||
self.find(':submit').prop('disabled', true); |
||||
} |
||||
}); |
||||
|
||||
this.reset(); |
||||
}); |
||||
}); |
||||
</script> |
@ -0,0 +1,45 @@ |
||||
<div class="text-center"> |
||||
<p>{{ 'YouNeedToIdentifyYourselfToAnswerThisQuestion'|get_plugin_lang('WhispeakAuthPlugin') }}</p> |
||||
|
||||
<button type="button" class="btn btn-info" id="whispeak-question-{{ question }}" data-loading-text="{{ 'PleaseWaitWhileLoading'|get_plugin_lang('WhispeakAuthPlugin')|escape('html') }}"> |
||||
{{ 'IdentifyMe'|get_plugin_lang('WhispeakAuthPlugin') }} |
||||
</button> |
||||
</div> |
||||
|
||||
<script> |
||||
$(function () { |
||||
function loadAuth() { |
||||
var $btnTrigger = $('#whispeak-question-{{ question }}'), |
||||
originalText = $btnTrigger.text(), |
||||
$modal = $('#global-modal'), |
||||
$modalTitle = $modal.find('.modal-title'), |
||||
$modalBody = $modal.find('.modal-body'), |
||||
$originalLoadingText = $btnTrigger.data('loading-text'); |
||||
|
||||
$btnTrigger.text($originalLoadingText).prop('disabled', true); |
||||
|
||||
$modalTitle.text($originalLoadingText); |
||||
$modalBody.html(''); |
||||
$modal.modal('show'); |
||||
|
||||
$ |
||||
.ajax({ |
||||
url: _p.web_plugin + 'whispeakauth/authentify.php' |
||||
}) |
||||
.then(function (response) { |
||||
$modalBody.html(response); |
||||
|
||||
$modalTitle.text('{{ 'plugin_title'|get_plugin_lang('WhispeakAuthPlugin') }}'); |
||||
$btnTrigger.text(originalText).prop('disabled', false); |
||||
}); |
||||
} |
||||
|
||||
$('#whispeak-question-{{ question }}').on('click', function (e) { |
||||
e.preventDefault(); |
||||
|
||||
loadAuth(); |
||||
}); |
||||
|
||||
loadAuth(); |
||||
}); |
||||
</script> |
Loading…
Reference in new issue