From 4457e38d729c7b8c9a4639cd9d22533b258b9286 Mon Sep 17 00:00:00 2001 From: AntonBagliy <taccessviolation@gmail.com> Date: Tue, 31 Jul 2018 17:09:15 +0300 Subject: [PATCH] ADD: protect exported Excel files (close #71) --- .../V2_0_5_1__form_logs_constraints.sql | 6 ++++ db/migrations/stored/R__functions.sql | 20 ++++++++++- .../structure/V2_0_5_0__add_form_logs.sql | 14 ++++++++ deploy/phpConfig/security.php | 6 +++- deploy/phpConfig/sidePanel/dean.json | 1 - media/js/dean/dean.js | 8 +++-- media/js/discipline/rating.js | 1 + .../Controller/Handler/FileCreator.php | 34 ++++++++++++++++++- .../application/classes/Model/Logs.php | 11 ++++++ .../views/office/sheets/index.twig | 10 ++++++ 10 files changed, 104 insertions(+), 7 deletions(-) create mode 100644 db/migrations/constraints/V2_0_5_1__form_logs_constraints.sql create mode 100644 db/migrations/structure/V2_0_5_0__add_form_logs.sql diff --git a/db/migrations/constraints/V2_0_5_1__form_logs_constraints.sql b/db/migrations/constraints/V2_0_5_1__form_logs_constraints.sql new file mode 100644 index 000000000..114627db7 --- /dev/null +++ b/db/migrations/constraints/V2_0_5_1__form_logs_constraints.sql @@ -0,0 +1,6 @@ +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +SET time_zone = "+00:00"; + +ALTER TABLE `logs_discipline_forms` + ADD CONSTRAINT `logs_discipline_forms_ibfk_1` FOREIGN KEY (`AccountID`) REFERENCES `accounts` (`ID`), + ADD CONSTRAINT `logs_discipline_forms_ibfk_2` FOREIGN KEY (`DisciplineID`) REFERENCES `disciplines` (`ID`); diff --git a/db/migrations/stored/R__functions.sql b/db/migrations/stored/R__functions.sql index 917d0d7a7..9b644ceb7 100644 --- a/db/migrations/stored/R__functions.sql +++ b/db/migrations/stored/R__functions.sql @@ -2761,6 +2761,24 @@ NO SQL RETURN 0; END // +DROP FUNCTION IF EXISTS LogFormDownload// +CREATE FUNCTION LogFormDownload ( + pAccountID INT, + pDisciplineID INT, + pStage INT, + pLocked INT +) RETURNS int(11) # 0 - success, < 0 - failed +NO SQL +BEGIN + DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1; + + INSERT INTO logs_discipline_forms + (AccountID, DisciplineID, Stage, Locked, Date) + VALUES (pAccountID, pDisciplineID, pStage, pLocked, CURRENT_TIMESTAMP ); + + RETURN ROW_COUNT()-1; +END // + DELIMITER ; # ------------------------------------------------------------------------------------------- @@ -2794,4 +2812,4 @@ BEGIN RETURN vSemesterID; END// -DELIMITER ; \ No newline at end of file +DELIMITER ; diff --git a/db/migrations/structure/V2_0_5_0__add_form_logs.sql b/db/migrations/structure/V2_0_5_0__add_form_logs.sql new file mode 100644 index 000000000..20b32e2db --- /dev/null +++ b/db/migrations/structure/V2_0_5_0__add_form_logs.sql @@ -0,0 +1,14 @@ +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +SET time_zone = "+00:00"; + +CREATE TABLE IF NOT EXISTS `logs_discipline_forms` ( + `ID` int(11) NOT NULL AUTO_INCREMENT, + `AccountID` int(11) NOT NULL, + `DisciplineID` int(11) NOT NULL, + `Stage` int(11) NOT NULL, + `Locked` tinyint(1) NOT NULL, + `Date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`ID`), + KEY `AccountID` (`AccountID`), + KEY `DisciplineID` (`DisciplineID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/deploy/phpConfig/security.php b/deploy/phpConfig/security.php index b8a15018d..d07f27c94 100644 --- a/deploy/phpConfig/security.php +++ b/deploy/phpConfig/security.php @@ -24,6 +24,10 @@ return array( 'recoveryToken' => array( 'lifetime' => 2*3600, ) - ) + ), + 'excelProtection' => array( + 'usePassword' => 'true', + 'password' => 'Pefnesdy' + ) ); \ No newline at end of file diff --git a/deploy/phpConfig/sidePanel/dean.json b/deploy/phpConfig/sidePanel/dean.json index 345304260..75462af82 100644 --- a/deploy/phpConfig/sidePanel/dean.json +++ b/deploy/phpConfig/sidePanel/dean.json @@ -19,7 +19,6 @@ { "Title": "Отчеты", "Items": [ - { "Title": "Ведомости", "Anchor": "reports/sheets" }, { "Title": "Сводная ведомость", "Anchor": "reports/bill" } ] }, diff --git a/media/js/dean/dean.js b/media/js/dean/dean.js index c821a8bf1..a4db6d814 100644 --- a/media/js/dean/dean.js +++ b/media/js/dean/dean.js @@ -98,8 +98,9 @@ $(function () { $.fileDownload(g_URLdir + 'handler/FileCreator/GenerateFinalFormsForGroup', { httpMethod: "POST", data: { - "GroupID": parseInt($("#SelectGroup").val())//, - //"ExamType": $("input[name=DisciplineType]:checked").val() + "GroupID": parseInt($("#SelectGroup").val()), + //"ExamType": $("input[name=DisciplineType]:checked").val(), + "lock": $("input[name=Lock]").prop("checked") }, successCallback: function () { @@ -116,7 +117,8 @@ $(function () { httpMethod: "POST", data: { "disciplineID": parseInt($("#SelectDiscipline").val()), - "studyGroupID": parseInt($("#SelectGroup").val()) + "studyGroupID": parseInt($("#SelectGroup").val()), + "lock": $("input[name=Lock]").prop("checked") }, successCallback: function () { diff --git a/media/js/discipline/rating.js b/media/js/discipline/rating.js index 8ad3e84aa..b0e24d791 100644 --- a/media/js/discipline/rating.js +++ b/media/js/discipline/rating.js @@ -329,6 +329,7 @@ class Rating { 'disciplineID': self.settings.discipline.id, 'groupID': Base.parseid($(this)), 'stage': parseInt($(this).siblings('select').val()), + 'lock': true }, }); }); diff --git a/~dev_rating/application/classes/Controller/Handler/FileCreator.php b/~dev_rating/application/classes/Controller/Handler/FileCreator.php index 299edd454..13526d68a 100644 --- a/~dev_rating/application/classes/Controller/Handler/FileCreator.php +++ b/~dev_rating/application/classes/Controller/Handler/FileCreator.php @@ -190,7 +190,8 @@ class Controller_Handler_FileCreator extends Controller_Handler ->rule('groupID', 'not_empty') ->rule('groupID', 'digit') ->rule('stage', 'not_empty') - ->rule('stage', 'digit', [0, 1, 2, 3]); + ->rule('stage', 'digit', [0, 1, 2, 3]) + ->rule('lock', 'not_empty'); if (!$this->post->check()) throw HTTP_Exception::factory(417, "Некорректные параметры запроса!"); @@ -199,6 +200,7 @@ class Controller_Handler_FileCreator extends Controller_Handler $disciplineID = $this->post['disciplineID']; $groupID = $this->post['groupID']; $stage = $this->post['stage']; + $doLock = $this->post['lock']; // make form from template $info = Model_Rating::getFinalFormInfo($disciplineID, $groupID); @@ -213,6 +215,14 @@ class Controller_Handler_FileCreator extends Controller_Handler $subjName = str_replace(' ', '_', $subjName); $filename = $subjName . "_" . $grade . "_" . $group; $this->GetHeaders($filename); + + if($doLock === "true") { + $this->lockWorkbook($objPHPExcel); + } + + // Лог выгрузки ведомости, защищенной паролем + Model_Logs::logFormDownload($this->user->ID, $disciplineID, $stage, 1); + $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5'); $objWriter->save('php://output'); } @@ -591,6 +601,7 @@ class Controller_Handler_FileCreator extends Controller_Handler // parameters //$gradeID = $this->post['gradeID']; $groupID = $this->post['GroupID']; + $doLock = $this->post['lock']; // preparation $listDisciplines = Model_Group::with($groupID)->getDisciplines(); @@ -648,6 +659,9 @@ class Controller_Handler_FileCreator extends Controller_Handler $groupNum = $info['GroupNum']; } + if($doLock === "true") { + $this->lockWorkbook($objPHPExcel); + } # fixme: $stage param is missing $this->printDisciplineToExcelFile($objPHPExcel, $discipline->ID, $groupID, $info); $index++; @@ -776,4 +790,22 @@ class Controller_Handler_FileCreator extends Controller_Handler return $result; } + protected function lockWorkbook($workbook) { + $config = Kohana::$config->load('security.excelProtection'); + if ($config == null) throw HTTP_Exception::factory(400, 'Неправильная конфигурация security'); + $password = $config['password']; + $usePassword = $config['usePassword']; + if ($usePassword === 'true') { + + foreach ($workbook->getAllSheets() as $sheet) { + $sheet->getProtection()->setSheet(true); + $sheet->getProtection()->setSort(true); + $sheet->getProtection()->setSelectLockedCells(true); + $sheet->getProtection()->setInsertRows(true); + $sheet->getProtection()->setFormatCells(true); + $sheet->getProtection()->setPassword($password); + } + } + } + } diff --git a/~dev_rating/application/classes/Model/Logs.php b/~dev_rating/application/classes/Model/Logs.php index cec32ebc3..a9ec4d543 100644 --- a/~dev_rating/application/classes/Model/Logs.php +++ b/~dev_rating/application/classes/Model/Logs.php @@ -16,5 +16,16 @@ class Model_Logs extends Model return DB::query(Database::SELECT, $query)->execute()->as_array(); } + public static function logFormDownload($accountID, $disciplineID, $stage, $locked) { + $query = 'SELECT `LogFormDownload`(:account, :discipline, :stage, :locked)'; + + $result = DB::query(Database::UPDATE, $query) + ->param(':account', $accountID) + ->param(':discipline', $disciplineID) + ->param(':stage', $stage) + ->param(':locked', $locked) + ->execute(); + return $result; + } } diff --git a/~dev_rating/application/views/office/sheets/index.twig b/~dev_rating/application/views/office/sheets/index.twig index 577eb1c45..b91553d85 100644 --- a/~dev_rating/application/views/office/sheets/index.twig +++ b/~dev_rating/application/views/office/sheets/index.twig @@ -98,6 +98,16 @@ {% endfor %} </div> </div> + + <div class="LayerSection"> + <style>.CheckBoxDiv { text-align: left; clear: both;}</style> + <div class="CheckBoxDiv"> + <label> + <input id="Lock" name="Lock" type="checkbox" checked="true"> + Защищать таблицы в Excel + </label> + </div> + </div> </div> <div id="hidden_div" style="display: none">{ "facultyID" : {{ User.FacultyID }} }</div> -- GitLab