diff --git a/db/migrations/stored/R__procedures.sql b/db/migrations/stored/R__procedures.sql index 88fdbfa92830ac40905fe46b876b71a24413fef4..00b05fa0b4f4c48349ab472c6688d27f0da43d1c 100644 --- a/db/migrations/stored/R__procedures.sql +++ b/db/migrations/stored/R__procedures.sql @@ -1468,14 +1468,13 @@ CREATE PROCEDURE GetAuthTokens( IN pAccountID int(11)) BEGIN -- accountID, Created, Accessed, Mask - IF pAccountID = 0 THEN - SELECT auth_tokens.* - FROM auth_tokens; - ELSE - SELECT auth_tokens.* - FROM auth_tokens - WHERE auth_tokens.AccountID = pAccountID; - END IF; + SELECT * + FROM auth_tokens + INNER JOIN accounts ON accounts.ID = auth_tokens.AccountID + WHERE + auth_tokens.AccountID = pAccountID OR + pAccountID = 0 + ORDER BY auth_tokens.Created DESC; END// # ------------------------------------------------------------------------------------------- diff --git a/deploy/phpConfig/sidePanel/admin.json b/deploy/phpConfig/sidePanel/admin.json index 66fddee1b797ca6af8fb3ecc545a9fd55015fd61..56fcc6594560ac99ee851624632511ca22783c3d 100644 --- a/deploy/phpConfig/sidePanel/admin.json +++ b/deploy/phpConfig/sidePanel/admin.json @@ -30,6 +30,7 @@ "Items": [ { "Title": "Рстория авторизаций", "Anchor": "logs" }, { "Title": "Рстория выставления баллов", "Anchor": "#", "Disabled": "true" }, + { "Title": "Токены авторизации", "Anchor": "authTokens" }, { "Title": "Поддержка", "Anchor": "support" } ] } diff --git a/media/css/logs.css b/media/css/logs.css deleted file mode 100644 index 088e14f204ffea31ed8a3dfef30a2dba9d8d67c7..0000000000000000000000000000000000000000 --- a/media/css/logs.css +++ /dev/null @@ -1,3 +0,0 @@ -table.equal-width-cols td { - padding: 5px; -} diff --git a/media/js/office/tokens.js b/media/js/office/tokens.js new file mode 100644 index 0000000000000000000000000000000000000000..03092fa41ceb6158312abe8e34bbb25b83744186 --- /dev/null +++ b/media/js/office/tokens.js @@ -0,0 +1,54 @@ +$(() => { + $('#createToken').click(function () { + $(this).prop('disabled', true); + + $.getJSON(URLdir + 'handler/authTokens/create').success(result => { + Popup.success('Токен добавлен'); + location.reload(); + }).fail(jqXHR => { + let status = parseInt(jqXHR.status); + if (status == 403) { + Popup.error('Недостаточно прав'); + return; + } + try { + if (status != 400) throw null; + let message = JSON.parse(jqXHR.responseText).message; + if (!message) throw null; + Popup.error(message); + } catch (error) { + Popup.error('РќРµ удалось добавить токен'); + } + }).always(() => $(this).prop('disabled', false)); + }); + + $('.officeList').on('click', '.deleteToken', function (event) { + event.preventDefault(); + + $(this).prop('disabled', true); + + let $body = $(this).parents('tbody'); + let $row = $(this).parents('tr'); + let token = $row.children('.token').text(); + + $.getJSON(URLdir + 'handler/authTokens/delete/' + token).success(() => { + $row.remove(); + if (!$body.children().length) $body.append('<tr><td colspan="6" class="empty">Нет записей</td></tr>'); + Popup.success('Токен удален'); + }).fail(jqXHR => { + let status = parseInt(jqXHR.status); + if (status == 403) { + Popup.error('Недостаточно прав'); + return; + } + try { + if (status != 400) throw null; + let message = JSON.parse(jqXHR.responseText).message; + if (!message) throw null; + Popup.error(message); + } catch (error) { + Popup.error('РќРµ удалось удалить токен'); + } + }).always(() => $(this).prop('disabled', false)); + }); +}); \ No newline at end of file diff --git a/media/less/office/list.less b/media/less/office/list.less new file mode 100644 index 0000000000000000000000000000000000000000..3a2dfa878f5c1c9f431fd505a8b902170f6a57e9 --- /dev/null +++ b/media/less/office/list.less @@ -0,0 +1,41 @@ +@import (reference) "../common.less"; + +.officeList { + overflow-x: auto; + + table { + min-width: 100%; + border-collapse: collapse; + + td { + padding: 5px; + border: 1px solid #ccc; + vertical-align: middle; + } + + thead { + background-color: #f0f7fd; + + text-align: center; + text-overflow: ellipsis; + } + + tbody { + background-color: @ColorBaseWhite; + white-space: nowrap; + + tr { + &:hover, + &.focus { + background-color: @ColorBaseGrey; + } + } + + .empty { + background-color: @ColorBaseWhite; + + text-align: center; + } + } + } +} \ No newline at end of file diff --git a/media/less/office/tokens.less b/media/less/office/tokens.less new file mode 100644 index 0000000000000000000000000000000000000000..f9e899a0c751d33df06461d2365e0fa5c4275e3b --- /dev/null +++ b/media/less/office/tokens.less @@ -0,0 +1,17 @@ +.main_side_content { + .contentTitle { + float: left; + } + + .createToken { + float: right; + padding-left: 15px; + padding-right: 15px; + margin: 0; + margin-bottom: 10px; + } + + .officeList { + clear: both; + } +} \ No newline at end of file diff --git a/~dev_rating/application/classes/Controller/Handler/AuthTokens.php b/~dev_rating/application/classes/Controller/Handler/AuthTokens.php new file mode 100644 index 0000000000000000000000000000000000000000..1ae847eb715edb3dbbb32e9064cefc10f050d928 --- /dev/null +++ b/~dev_rating/application/classes/Controller/Handler/AuthTokens.php @@ -0,0 +1,33 @@ +<?php defined('SYSPATH') or die('No direct script access.'); + +/** Получение СЃРїРёСЃРєР° РіСЂСѓРїРї, РєСѓСЂСЃРѕРІ, предметов, Рё С‚.Рґ. */ +class Controller_Handler_AuthTokens extends Controller_Handler +{ + public function before() { + parent::before(); + + $this->user->checkAccess(User::RIGHTS_ADMIN); + } + + public function action_create() { + $token = Model_Account::createAuthToken($this->user->ID); + if (is_null($token)) { + HTTP_API_Exception::factory(500, 'Ошибка приложения'); + } + + $this->response->body(json_encode([ + success => true, + token => $token, + ])); + } + + public function action_delete() { + $token = $this->request->param('id'); + $errorCode = Model_Account::deleteAuthToken($token); + if ($errorCode != 0) { + HTTP_API_Exception::factory(500, 'Ошибка приложения'); + } + + $this->response->body(json_encode([ success => true ])); + } +} \ No newline at end of file diff --git a/~dev_rating/application/classes/Controller/Office/AuthTokens.php b/~dev_rating/application/classes/Controller/Office/AuthTokens.php new file mode 100644 index 0000000000000000000000000000000000000000..cd9c2725b9a05c015a171690729e025bd6c09e8c --- /dev/null +++ b/~dev_rating/application/classes/Controller/Office/AuthTokens.php @@ -0,0 +1,10 @@ +<?php defined('SYSPATH') or die('No direct script access.'); + +class Controller_Office_AuthTokens extends Controller_Environment_Office +{ + public function action_index() { + $this->twig->set([ + 'Tokens' => Model_Account::getAuthTokens(), + ])->set_filename(static::OFFICE . 'tokens'); + } +} \ No newline at end of file diff --git a/~dev_rating/application/classes/Model/Account.php b/~dev_rating/application/classes/Model/Account.php index 22bd45375cd08d1c67d555b38a7a25a330a26462..d6cb4436878753e3dac54553fc8b701d80a2c0ef 100644 --- a/~dev_rating/application/classes/Model/Account.php +++ b/~dev_rating/application/classes/Model/Account.php @@ -155,13 +155,6 @@ class Model_Account extends Model // auth tokens management // ===================================== - public static function deleteAuthToken($token) { - $sql = 'SELECT `deleteAuthToken`(:token) AS `res`'; - return DB::query(Database::SELECT, $sql) - ->param(':token', $token) - ->execute()->get('res'); - } - /** * @param int $accountID * @param int $mask bit mask with access rights @@ -176,12 +169,19 @@ class Model_Account extends Model ])->execute()->get('token'); } + public static function deleteAuthToken($token) { + $sql = 'SELECT `deleteAuthToken`(:token) AS `res`'; + return DB::query(Database::SELECT, $sql) + ->param(':token', $token) + ->execute()->get('res'); + } + /** * @param int $accountID - 0 to get auth tokens of all users * @return mixed */ public static function getAuthTokens($accountID = 0) { - $sql = 'CALL `GetAuthTokens`(:user)`'; + $sql = 'CALL `GetAuthTokens`(:user)'; return DB::query(Database::SELECT, $sql) ->param(':user', (int) $accountID) ->execute(); diff --git a/~dev_rating/application/views/office/logs.twig b/~dev_rating/application/views/office/logs.twig index c40aa5e032ed40881eb45d8ccea429ca88797652..a9cec07aea3c811d0c99bb54772ee9d75ad33155 100644 --- a/~dev_rating/application/views/office/logs.twig +++ b/~dev_rating/application/views/office/logs.twig @@ -3,27 +3,31 @@ {% block title %}Рстория авторизаций{% endblock %} -{% block office_content %} - - {{ HTML.style('static/css/logs.css')|raw }} +{% block office_media %} + {{ HTML.style('static/css/office/list.css')|raw }} +{% endblock %} +{% block office_content %} <h2 class="Margin10 Bottom">Рстория авторизаций</h2> - <table class="equal-width-cols" cellpadding="20"> - - <tr class="TableHead"> - <td>Дата Рё время</td> - <td>Р¤РРћ</td> - <td>AccoundID</td> - </tr> - - {% for Log in Logs %} - <tr> - <td>{{ Log.Date }}</td> - <td>{{ Log.LastName }} {{ Log.FirstName }} {{ Log.SecondName }}</td> - <td>{{ Log.AccountID }}</td> - </tr> - {% endfor %} - - </table> + <div class="officeList"> + <table> + <thead> + <tr> + <td>Дата авторизации</td> + <td>Р¤РРћ пользователя</td> + </tr> + </thead> + <tbody> + {% for Log in Logs %} + <tr> + <td>{{ Log.Date }}</td> + <td>{{ Log.LastName }} {{ Log.FirstName }} {{ Log.SecondName }}</td> + </tr> + {% else %} + <tr><td colspan="3" class="empty">Нет записей</td></tr> + {% endfor %} + </tbody> + </table> + </div> {% endblock %} diff --git a/~dev_rating/application/views/office/tokens.twig b/~dev_rating/application/views/office/tokens.twig new file mode 100644 index 0000000000000000000000000000000000000000..03c656274fa150ab35baf0b9f730dd5f0de6f1e8 --- /dev/null +++ b/~dev_rating/application/views/office/tokens.twig @@ -0,0 +1,46 @@ +{% extends "office/base" %} + + +{% block title %}Токены авторизации{% endblock %} + +{% block office_media %} + {{ HTML.script('static/js/office/tokens.js')|raw }} + + {{ HTML.style('static/css/office/list.css')|raw }} + {{ HTML.style('static/css/office/tokens.css')|raw }} +{% endblock %} + +{% block office_content %} + <h2 class="Margin10 Bottom contentTitle">Токены авторизации</h2> + + <button class="defaultForm GreenButton createToken" id="createToken">Создать токен</button> + + <div class="officeList"> + <table> + <thead> + <tr> + <td>Дата создания</td> + <td>Р¤РРћ пользователя</td> + <td>Токен</td> + <td>Дата авторизации</td> + <td>Маска</td> + <td>Действия</td> + </tr> + </thead> + <tbody> + {% for Token in Tokens %} + <tr> + <td>{{ Token.Created }}</td> + <td>{{ Token.LastName }} {{ Token.FirstName }} {{ Token.SecondName }}</td> + <td class='token'>{{ Token.Token }}</td> + <td>{{ Token.Accessed }}</td> + <td>{{ Token.Mask }}</td> + <td><a href="#" class="deleteToken">Удалить токен</a></td> + </tr> + {% else %} + <tr><td colspan="6" class="empty">Нет записей</td></tr> + {% endfor %} + </tbody> + </table> + </div> +{% endblock %}