tao-test/app/taoClientDiagnostic/controller/CompatibilityChecker.php

393 lines
13 KiB
PHP
Raw Normal View History

2022-08-29 20:14:13 +02:00
<?php
/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2015-2019 (original work) Open Assessment Technologies SA;
*
*/
namespace oat\taoClientDiagnostic\controller;
use oat\tao\model\mvc\DefaultUrlService;
use oat\taoClientDiagnostic\exception\StorageException;
use oat\taoClientDiagnostic\model\authorization\Authorization;
use oat\taoClientDiagnostic\model\CompatibilityChecker as CompatibilityCheckerModel;
use oat\taoClientDiagnostic\model\diagnostic\DiagnosticServiceInterface;
use oat\taoClientDiagnostic\model\schoolName\SchoolNameService;
use oat\taoClientDiagnostic\model\storage\Storage;
use Sinergi\BrowserDetector\Browser;
use Sinergi\BrowserDetector\Os;
/**
* Class CompatibilityChecker
* @package oat\taoClientDiagnostic\controller
*/
class CompatibilityChecker extends \tao_actions_CommonModule
{
const COOKIE_ID = 'compatibility_checker_id';
/**
* If logged in, display index view with config data
* If not, forward to login
*/
public function index()
{
$authorizationService = $this->getServiceLocator()->get(Authorization::SERVICE_ID);
if ($authorizationService->isAuthorized()) {
$config = $this->loadConfig();
if (!empty($config['diagnostic']['pageTitle'])) {
$this->setData('title', $config['diagnostic']['pageTitle']);
}
$this->setData('client_config_url', $this->getClientConfigUrl());
$this->setData('content-config', $config);
$this->setData('logout', $this->getServiceLocator()->get(DefaultUrlService::SERVICE_ID)->getLogoutUrl());
$this->setData('content-controller', 'taoClientDiagnostic/controller/CompatibilityChecker/diagnostics');
$this->setData('content-template', 'CompatibilityChecker' . DIRECTORY_SEPARATOR . 'diagnostics.tpl');
$this->setView('index.tpl');
} else {
$this->redirect($authorizationService->getAuthorizationUrl(_url('index')));
}
}
/**
* Render browser detection view
*/
public function whichBrowser()
{
$result = [
'browser' => $this->getBrowserDetector()->getName(),
'browserVersion' => $this->getBrowserDetector()->getVersion(),
'os' => $this->getOsDetector()->getName(),
'osVersion' => $this->getOsDetector()->getVersion(),
'isMobile' => $this->getOsDetector()->isMobile(),
];
$this->returnJson($result);
}
/**
* Check if requester is compatible (os+browser)
* Register compatibility
* return json message
*/
public function check()
{
try {
$data = $this->getData(true);
$id = $this->getId();
$checker = $this->getServiceLocator()->get(CompatibilityCheckerModel::SERVICE_ID);
$isCompatible = (int)$checker->isCompatibleConfig();
$data['compatible'] = $isCompatible;
try {
$storageService = $this->getServiceLocator()->get(Storage::SERVICE_ID);
$storageService->store($id, $data);
} catch (StorageException $e) {
$this->logInfo($e->getMessage());
}
$compatibilityMessage = [
//Not compatible
CompatibilityCheckerModel::COMPATIBILITY_NONE => [
'success' => true,
'type' => 'error',
'message' => __('Your system requires a compatibility update, please contact your system administrator.')
],
//Compatible
CompatibilityCheckerModel::COMPATIBILITY_COMPATIBLE => [
'success' => true,
'type' => 'success',
'message' => __('This browser is compatible but its support may end soon.')
],
//Not tested
CompatibilityCheckerModel::COMPATIBILITY_NOT_TESTED => [
'success' => true,
'type' => 'warning',
'message' => __('This browser is not tested.')
],
//Fully supported
CompatibilityCheckerModel::COMPATIBILITY_SUPPORTED => [
'success' => true,
'type' => 'success',
'message' => __('This browser is supported.')
],
//Not supported
CompatibilityCheckerModel::COMPATIBILITY_NOT_SUPPORTED => [
'success' => true,
'type' => 'warning',
'message' => __('This browser is not supported.')
],
];
$this->returnJson($compatibilityMessage[$isCompatible]);
} catch (\common_exception_MissingParameter $e) {
$this->returnJson(array('success' => false, 'type' => 'error', 'message' => $e->getUserMessage()));
}
}
/**
* Action is used to check upload speed from client side.
*/
public function upload()
{
try {
if ($this->getRequestMethod() !== \Request::HTTP_POST) {
throw new \common_exception_NotImplemented('Only post method is accepted.');
}
if (!isset($_POST['upload']) || !is_string($_POST['upload'])) {
throw new \common_exception_InconsistentData("'upload' POST variable is missed.");
}
$size = mb_strlen($_POST['upload'], '8bit');
$result = ['success' => true, 'size' => $size];
} catch (\common_exception_NotImplemented $e) {
$result = ['success' => false, 'error' => $e->getMessage()];
$this->logWarning($e->getMessage());
} catch (\common_exception_InconsistentData $e) {
$result = ['success' => false, 'error' => $e->getMessage()];
$this->logWarning($e->getMessage());
} catch (\Exception $e) {
$result = ['success' => false, 'type' => 'error', 'message' => 'Please contact administrator'];
$this->logWarning($e->getMessage());
}
$this->returnJson($result);
}
/**
* Register data from the front end
*/
public function storeData()
{
$data = $this->getData();
$id = $this->getId();
try {
$storageService = $this->getServiceLocator()->get(Storage::SERVICE_ID);
$storageService->store($id, $data);
$this->returnJson(array('success' => true, 'type' => 'success'));
} catch (StorageException $e) {
$this->logInfo($e->getMessage());
$this->returnJson(array('success' => false, 'type' => 'error'));
}
}
/**
* Retrieve a school name
*/
public function schoolName()
{
// simple counter measure to slow down brute force attack
sleep(1);
$data = $this->getParameters();
$required = ['school_number', 'school_pin'];
$response = [];
$success = true;
foreach($required as $fieldName) {
if (!isset($data[$fieldName])) {
$success = false;
$response['errorCode'] = 400;
$response['errorMessage'] = __('Missing field %s', $fieldName);
break;
}
}
if ($success) {
try {
$schoolNameProvider = $this->getServiceLocator()->get(SchoolNameService::SERVICE_ID);
$response['data'] = $schoolNameProvider->getSchoolName($data['school_number'], $data['school_pin']);
} catch(\Exception $e) {
$success = false;
$response['errorCode'] = 404;
$response['errorMessage'] = __('Cannot retrieve the school name. Please verify your input');
}
}
$response['success'] = $success;
$this->returnJson($response);
}
/**
* Fetch POST data
* Get login by cookie
* Get Ip
* If check parameters is true, check mandatory parameters
*
* @param bool $check
* @return array
* @throws \common_exception_MissingParameter
*/
protected function getData($check = false)
{
$data = $this->getParameters();
if ($this->hasRequestParameter('type')) {
$type = $this->getRequestParameter('type');
unset($data['type']);
if ($type !== 'custom_input') {
foreach ($data as $key => $value) {
$data[$type . '_' . $key] = $value;
unset($data[$key]);
}
}
}
$data = $this->mapData($data);
if ($this->hasRequestParameter('school_name')) {
$data[Storage::DIAGNOSTIC_SCHOOL_NAME] = \tao_helpers_Display::sanitizeXssHtml(trim($this->getRequestParameter('school_name')));
}
if ($this->hasRequestParameter('school_id')) {
$data[Storage::DIAGNOSTIC_SCHOOL_ID] = \tao_helpers_Display::sanitizeXssHtml(trim($this->getRequestParameter('school_id')));
}
if ($this->hasRequestParameter('workstation')) {
$data[Storage::DIAGNOSTIC_WORKSTATION] = \tao_helpers_Display::sanitizeXssHtml(trim($this->getRequestParameter('workstation')));
}
if ($this->hasRequestParameter('school_number')) {
$data[Storage::DIAGNOSTIC_SCHOOL_NUMBER] = \tao_helpers_Display::sanitizeXssHtml(trim($this->getRequestParameter('school_number')));
}
if ($check) {
if (!$this->hasRequestParameter('os')) {
throw new \common_exception_MissingParameter('os');
}
if (!$this->hasRequestParameter('osVersion')) {
throw new \common_exception_MissingParameter('osVersion');
}
if (!$this->hasRequestParameter('browser')) {
throw new \common_exception_MissingParameter('browser');
}
if (!$this->hasRequestParameter('browserVersion')) {
throw new \common_exception_MissingParameter('browserVersion');
}
$data['osVersion'] = preg_replace('/[^\w\.]/', '', $data['osVersion']);
$data['browserVersion'] = preg_replace('/[^\w\.]/', '', $data['browserVersion']);
}
if (isset($_COOKIE['login'])) {
$data['login'] = $_COOKIE['login'];
} else {
$data['login'] = 'Anonymous';
}
$user = \common_session_SessionManager::getSession()->getUser();
if ($user && $user->getIdentifier()) {
$data['user_id'] = $user->getIdentifier();
}
$data['version'] = $this->getServiceLocator()->get(\common_ext_ExtensionsManager::SERVICE_ID)->getExtensionById('taoClientDiagnostic')->getVersion();
$data['ip'] = (!empty($_SERVER['HTTP_X_REAL_IP'])) ? $_SERVER['HTTP_X_REAL_IP'] : ((!empty($_SERVER['REMOTE_ADDR'])) ? $_SERVER['REMOTE_ADDR'] : 'unknown');
return $data;
}
/**
* Get current http parameters
*
* @return array
*/
protected function getParameters()
{
return $this->getRequestParameters();
}
/**
* Get cookie id OR create it if doesnt exist
* @return string
*/
protected function getId()
{
if (! isset($_COOKIE[self::COOKIE_ID])) {
$id = uniqid();
setcookie(self::COOKIE_ID, $id);
} else {
$id = $_COOKIE[self::COOKIE_ID];
}
return $id;
}
/**
* Delete cookie id
*/
public function deleteId()
{
setcookie(self::COOKIE_ID, null);
$this->returnJson(array('success' => true, 'type' => 'success'));
}
/**
* Get config parameters for compatibility check
*
* @return mixed
*/
protected function loadConfig()
{
/** @var DiagnosticServiceInterface $service */
$service = $this->getServiceLocator()->get(DiagnosticServiceInterface::SERVICE_ID);
$config = $service->getDiagnosticJsConfig();
$config['controller'] = 'CompatibilityChecker';
return $config;
}
/**
* Map custom input data from the 'customInput' configuration.
*
* @param array $data
* @return array
*/
protected function mapData(array $data) {
$config = $this->loadConfig();
foreach ($data as $k => $d) {
if (!empty($config['diagnostic']['customInput'][$k])) {
$data[$config['diagnostic']['customInput'][$k]] = $d;
unset($data[$k]);
}
}
return $data;
}
/**
* Get the browser detector
*
* @return Browser
*/
protected function getBrowserDetector()
{
return new Browser();
}
/**
* Get the operating system detector
*
* @return Os
*/
protected function getOsDetector()
{
return new Os();
}
}