This commit is contained in:
Pavel Dmitriev 2022-04-28 20:12:33 +03:00
parent f0d3e74b0c
commit a12a40c8b1
24 changed files with 963 additions and 39 deletions

39
CHANGELOG_EN.md Normal file
View File

@ -0,0 +1,39 @@
## V.0.9.0 RC
### Back
1. The organization of a transparent REST API through the fox\externalCallable interface - allows you to organize access to the necessary entities with minimal costs, but with all the necessary checks.
2. Support for third-party modules with the ability to install multiple instances of the same module (if supported by the module).
3. User authentication — both using built-in tools and using external OAuth sources (currently Gitlab, Gitea, Yandex, VK support is implemented).
4. User authorization — with the help of the built-in access control system based on roles, flexible verification of the user's access rights is performed. The lists of user roles are checked both by the back when making REST API calls, and by the front — for this, after authentication, the list of access rights of the current user is transmitted.
5. The ability to make requests without authorization, if necessary. For example, to implement Webhooks.
6. Embedded database migrations based on the description of the class structure.
7. A base class for performing standard database functions (write, read, search, delete) on the basis of which you can quickly create entities.
8. memcached support for both embedded and custom objects for quick access to frequently used data
9. Configuration storage for each module in the database (with cache) so it is in the environment of the container.
10. Built-in functionality for registering users by invitation with the ability to automatically add to groups both for granting access and for organizing into logical groups ("lists").
11. Built-in functionality of action confirmation codes for both built-in functions (password recovery, mail confirmation, etc.) and for user modules.
12. Support for S3-compatible storage management (creating bouquets, deleting data, etc.).
13. The ability to add any other object storage based on a typical interface.
14. Storing files (email attachments, servicedesk attachments, templates and report results, etc.) in S3-compatible object storage
15. Built-in support for OpenDocument ODS and ODT formats for automated document generation (reports, invoices, acts, etc.)
16. Built-in Fox Converter support for exporting documents in any office format.
17. Built-in support at the base class level of data exchange in JSON format with the ability to control the visibility of individual properties and create virtual properties.
18. Built-in eMail client that supports simultaneous work both for receiving and sending emails, including attachments from multiple accounts using IMAP and SMTP protocols (with and without authorization).
19. Storing metadata of modules (for example, the last synchronization, etc.) in the built-in storage with caching.
20. Support for receiving and generating data in Prometheus format
21. Built-in REST API Client for communication with third-party systems.
22. Built-in stringExportable && stringImportable interfaces for automatic conversion of objects to a string and back (for example, time to unixTime or ISO).
23. Built-in encryption module for critical data (for example, passwords) as well as the formation of hashes based on an individual master password.
24. A system for generating unique identifiers in the format 1000-0000-00 with a checksum for the formation of a register of documents, inventory and other objects. For example, to organize a global search (in future releases) and barcodes.
25. Built-in cron, which allows you to run periodic processes of modules in parallel with a limit on the maximum execution time and with the possibility of blocking the restart until the last process is completed separately for each task.
26. Support for multiple languages, for example, for sending notifications to e-mail or messengers. The list of languages may vary for Chimera Core and additional modules.
27. Logging of all user actions.
### Front
1. Built-in methods of REST interaction with both your own backup and other services.
2. Formation of a visual basic user interface in several languages
3. Basic system administration functions
4. Authorization, user registration. Session monitoring
5. Built-in Fox UI library for creating menus, dialogs, buttons, forms including forms for automatic password generation and autofill.
6. Access rights verification library to control the display of interface elements.
7. The ability to customize the interface by creating your own color themes and images.
8. Using FontAwesome 5 to form interfaces, it is also possible to add your own fonts with icons.

39
CHANGELOG_RU.md Normal file
View File

@ -0,0 +1,39 @@
## V.0.9.0 RC
### Бэк
1. Огранизация прозрачного REST API через интерфейс fox\externalCallable - позволяет с минимальными затратами но при этом со всеми необходимыми проверками организовать доступ к нужным сущностям.
2. Поддержка сторонних модулей с возможностью установки нескольких инстансов одного модуля (если поддерживается модулем).
3. Аутентификация пользователей — как с помощью встроенных средств так и с помощью внешних источников oAuth (на данный момент реализована поддержка Gitlab, Gitea, Yandex, VK).
4. Авторизация пользователей — с помощью встроенной системы контроля доступа, основанной на ролях производится гибкая проверка наличия у пользователя прав доступа. Списки ролей пользователя проверяются как бэком при выполнении вызовов REST API, так и фронтом — для этого после аутентификации передается список прав доступа текущего пользователя.
5. Возможность выполнения запросов без авторизации, если такое необходимо. Например для реализации Вебхуки.
6. Встроенные миграции БД на основе описания структуры класса.
7. Базовый класс для выполнения стандартных функции работы с БД (запись, чтение, поиск, удаление) на основе которого можно быстро создавать сущности.
8. Поддержка memcached как для встроенных объектов так и для пользовательских для быстрого доступа к часто используемым данным
9. Хранилище конфигурации для каждого модуля в БД (с кэшем) так и глобальной в окружении контейнера.
10. Встроенный функционал регистрации пользователей по приглашениям с возможность автоматического добавления в группы как для предоставления доступа так и для организации в логические группы («списки»).
11. Встроенный функционал кодов подтверждения действий как для встроенных функций (восстановление пароля, подтверждение почты итд) так и для пользовательских модулей.
12. Поддержка управления S3-совместимым хранилищем (создание букетов, удаление данных итд).
13. Возможность добавления любых других объектных хранилищ на основе типового интерфейса.
14. Хранение файлов (вложений электронной почты, вложений в servicedesk, шаблонов и результатов отчетов итд) в S3-совместимом объектном хранилище
15. Встроенная поддержка форматов OpenDocument ODS и ODT для автоматизированного формирования документов (отчеты, счета, акты итд)
16. Встроенная поддержка Fox Converter для экспорта документов в любом офисном формате.
17. Встроенная поддержка на уровне базового класса обмена данными в формате JSON с возможность контроля видимости отдельных свойств и создания виртуальных свойств.
18. Встроенный eMail клиент, поддерживающий одновременную работу как на получение так и на отправку писем в том чисте с вложениями с нескольких учетных записей по протоколам IMAP и SMTP (с авторизацией и без).
19. Хранение метаданных модулей (например, последняя синхронизация итд) во встроенном хранилище с кешированием.
20. Поддержка получения и формирования данных в формате Prometheus
21. Встроенный REST API Client для связи со сторонними системами.
22. Встроенные интерфейсы stringExportable && stringImportable для автоматической конвертации объектов в строку и обратно (например время в unixTime или ISO).
23. Встроенный модуль шифрования критиченых данных (например, паролей) а так же формирования хэшей на основе индивидуального мастер-пароля.
24. Система формирования уникальных идентификаторов в формате 1000-0000-00 с контрольной суммой для формирования реестра документов, инвентаризации и других объектов. Например для организации глобального поиска (в будущих релизах) и штрих-кодов.
25. Встроенный cron, позволяющий запускать периодические процессы модулей параллельно с ограничением по максимальному времени выполнения и с возможностью блокировки повторного запуска, пока не завершится прошлый процесс отдельно для каждой задачи.
26. Поддержка нескольких языков, например для отправки уведомлений на электронную почту или мессенджеры. Список языков может различаться для Chimera Core и дополнительных модулей.
27. Логирование всех действий пользователей.
### Фронт
1. Встроенные методы взаимодействия по REST как с собственным бэком, так и с другими службами.
2. Формирование визуального базового интерфейса пользователя на нескольких языках
3. Базовые функции администрирования системы
4. Авторизациия, регистрации пользователей. Контроль сессий
5. Встроенная библиотека Fox UI для формирования меню, диалогов, кнопок, форм включая формы автоматической генерации паролей и автозаполнения.
6. Библиотека проверки прав доступа для контроля за отображением элементов интерфейса.
7. Возможность кастомизации интерфейса в помощью создания собственных цветовых тем и изображений.
8. Использование FontAwesome 5 для формирования интерфейсов, так же возможно добавление собственных шрифтов с иконками.

View File

@ -1,9 +1,34 @@
# Chimera Fox Platform Mark2
Chimera Fox is a universal framework for quickly creating web applications. The back is written in PHP, and provides basic functions. Front on JS. Interaction via REST.
Полностью новая версия платформы Chimera Fox.
# How to run in docker
```
version: "2"
Предыдущие версии будут поддерживаться только в качестве обновлений безопасности.
networks:
interlink:
Обратная совместимость с предыдущими версиями отсутствует - это основная причина выхода новой платформы - совместимость со старыми версиями занимает слишком много ресурсов на поддержку, а фактически она уже не нужна.
services:
*Актуальные модули старой версии будут портированы на Mark2*
fox-web-mk2:
restart: always
image: mxfox/chimera-mk2-basic:lastest
container_name: fox-web-mk2
volumes:
- ./fox-log-web/logs:/var/log/apache2
- ./fox-log-cron/logs:/var/log/fox
networks:
- interlink
environment:
- "FOX_SQLSERVER=XXXXXX"
- "FOX_SQLUSER=XXXXX"
- "FOX_SQLPASSWD=XXXX"
- "FOX_SQLDB=XXXXX"
- "FOX_CACHEHOST=memcached"
- "FOX_TITLE=Mark2"
- "FOX_SITEPREFIX=https://mark2.fox.local"
- "FOX_MASTERSECRET=SuperSecretPassword"
- "FOX_INIT_PASSWORD=AnotherSuperSecretPassword"
```

View File

@ -11,6 +11,7 @@ use fox\oAuthProfile;
use fox\config;
use fox\userGroup;
use fox\authToken;
use fox\logEntry;
class register implements externalCallable {
@ -33,9 +34,6 @@ class register implements externalCallable {
$ic=null;
if (!empty($regCode)) {
$ic=userInvitation::getByCode($regCode);
if (!$ic || ($ic->expireStamp->stamp>time())) {
$ic=null;
}
} else {
$ic =userInvitation::getByEMail($eMail);
}
@ -88,11 +86,9 @@ class register implements externalCallable {
}
if ($ic) {
if ($ic && ($ic->expireStamp->stamp<=time())) {
foreach ($ic->joinGroupsId as $grid) {
$group = new userGroup($grid);
$group->join($u);
}
foreach ($ic->joinGroupsId as $grid) {
$group = new userGroup($grid);
$group->join($u);
}
if (!$ic->allowMultiUse) { $ic->delete(); }
}
@ -102,7 +98,7 @@ class register implements externalCallable {
} catch (\Exception $e) {
trigger_error($e->getMessage());
}
logEntry::add($request->instance, static::class, __FUNCTION__, null, "User ".$u->login." registered ", "INFO", $u, "user", $u->id);
$t = authToken::issue($u, "WEB");
return [
"token" => $t->token,
@ -148,6 +144,8 @@ class register implements externalCallable {
$t = authToken::issue($u, "WEB");
logEntry::add($request->instance, static::class, __FUNCTION__, null, "Password recovered for user ".$u->login, "INFO", $u, "user", $u->id);
return [
"token" => $t->token,
"expire" => $t->expireStamp->isNull() ? "Never" : $t->expireStamp

View File

@ -268,7 +268,12 @@ class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportabl
$stringRef = (is_bool($ref->{$key}) || ! (is_object($ref->{$key}) || is_array($ref->{$key})));
$stringVal = (is_bool($val) || ! (is_object($val) || is_array($val)));
$this->changelog .= "key: " . $key . " changed from " . ($stringRef ? (is_bool($ref->{$key}) ? ($ref->{$key} ? "true" : "false") : $ref->{$key}) : "<" . gettype($ref->{$key}) . ">") . " to " . ($stringVal ? (is_bool($val) ? ($val ? "true" : "false") : $val) : "<" . gettype($val) . ">") . ";\n ";
if (preg_match("/^_/",$key)) {
$this->changelog .= "key: " . $key . " changed \n";
} else {
$this->changelog .= "key: " . $key . " changed from " . ($stringRef ? (is_bool($ref->{$key}) ? ($ref->{$key} ? "true" : "false") : $ref->{$key}) : "<" . gettype($ref->{$key}) . ">") . " to " . ($stringVal ? (is_bool($val) ? ($val ? "true" : "false") : $val) : "<" . gettype($val) . ">") . ";\n ";
}
}
}
@ -284,17 +289,18 @@ class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportabl
if (property_exists($this, static::$sqlIdx) && ($this->{static::$sqlIdx} == null)) {
return false;
}
if (!$this->validateDelete()) {
throw new \Exception("ValidateDelete failed");
}
if (static::$allowDeleteFromDB) {
if ($this->validateDelete()) {
$this->checkSql();
$this->sql->quickExec("DELETE FROM `" . static::$sqlTable . "` where " . static::$sqlIdx . " = '" . $this->{static::$sqlIdx} . "'");
if (! (empty(static::$deletedFieldName))) {
$this->{static::$deletedFieldName} = true;
}
$this->{static::$sqlIdx} = null;
} else {
throw new \Exception("ValidateDelete failed");
$this->checkSql();
$this->sql->quickExec("DELETE FROM `" . static::$sqlTable . "` where " . static::$sqlIdx . " = '" . $this->{static::$sqlIdx} . "'");
if (! (empty(static::$deletedFieldName))) {
$this->{static::$deletedFieldName} = true;
}
$this->{static::$sqlIdx} = null;
} elseif (! (empty(static::$deletedFieldName))) {
$this->checkSql();
$this->sql->quickExec("UPDATE `" . static::$sqlTable . "` set `" . static::$deletedFieldName . "`='1' where " . $this::$sqlIdx . " = '" . $this->{static::$sqlIdx} . "'");
@ -583,4 +589,8 @@ class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportabl
}
return $rv;
}
protected static function log(string $instance, $method, string $message, ?user $user=null, ?string $refType=null, ?string $refId=null, string $msgCode=null, ?string $severity="INFO", $payload=null) {
return logEntry::add($instance, static::class, $method, $msgCode, $message, $severity, $user, $refType, $refId, $payload);
}
}

View File

@ -30,7 +30,7 @@ class ru {
Добрый день!<br/>
Для подтверждения адреса электронной почты укажите код <span style='color: #ff6f0f'>\${confCodePrint}</span> в форме подтверждения по адресу <a style='color: #ff6f0f; text-decoration: underline;' href='
\${sitePrefix}/auth/mailConfirm'>\${sitePrefix}/core/userEmailConfirm</a><br/>
\${sitePrefix}/core/userEmailConfirm'>\${sitePrefix}/core/userEmailConfirm</a><br/>
или перейдите по <a style='color: #ff6f0f; text-decoration: underline;' href='
\${sitePrefix}/core/userEmailConfirm?code=\${confCodePrint}'>ссылке</a><br/>
<br/>

61
core/fox/logEntry.php Normal file
View File

@ -0,0 +1,61 @@
<?php namespace fox;
class logEntry extends baseClass implements externalCallable {
protected $id;
protected time $entryStamp;
public $severity;
public $module;
public $class;
public $method;
public $userId;
public $msgCode;
public $message;
public $refType;
public $refId;
public $payload=[];
public static $sqlTable="tblLog";
protected static $sqlColumns = [
"severity"=>["type"=>"CHAR(10)","index"=>"INDEX"],
"module"=>["type"=>"VARCHAR(255)","index"=>"INDEX"],
"class"=>["type"=>"VARCHAR(255)"],
"method"=>["type"=>"VARCHAR(255)"],
"userId"=>["type"=>"INT","index"=>"INDEX"],
"msgCode"=>["type"=>"CHAR(16)"],
"message"=>["type"=>"TEXT"],
"refType"=>["type"=>"VARCHAR(255)","index"=>"INDEX"],
"refId"=>["type"=>"VARCHAR(255)","index"=>"INDEX"],
];
const sevDebug="DEBUG";
const sevInfo="INFO";
const sevWarning="WARN";
const sevAlert="ALERT";
protected function __xConstruct() {
$this->entryStamp=new time();
}
protected function validateSave() {
if ($this->entryStamp->isNull()) { $this->entryStamp=time::current(); }
return true;
}
public static function add(string $instance, string $class, string $method, ?string $msgCode=null, string $message, ?string $severity="INFO", ?user $user=null, ?string $refType=null, ?string $refId=null, $payload=null) {
$l = new static();
$l->module = $instance;
$l->class=$class;
$l->method=$method;
$l->userId = empty($user)?null:$user->id;
$l->msgCode=$msgCode;
$l->message=$message;
$l->refType=$refType;
$l->refId=$refId;
$l->payload=empty($payload)?[]:$payload;
$l->severity=$severity;
$l->save();
return $l;
}
}
?>

View File

@ -22,10 +22,12 @@ namespace fox;
* @property mixed $rxPassword
* @property mixed $rxLogin
* @property mixed $rxPassword
* @property mixed $rxURL
* @property mixed $txURL
*
**/
class mailAccount extends baseClass {
class mailAccount extends baseClass implements externalCallable {
protected $id;
public $address;
public $rxServer;
@ -45,6 +47,17 @@ class mailAccount extends baseClass {
public bool $default=false;
public bool $deleted=false;
const urlSchemas=[
"rx"=>[
"imap"=>["ssl"=>false,"port"=>143,"proto"=>"imap"],
"imaps"=>["ssl"=>true,"port"=>993,"proto"=>"imap"],
],
"tx"=>[
"smtp"=>["ssl"=>false,"port"=>587,"proto"=>"smtp"],
"smtps"=>["ssl"=>true,"port"=>465,"proto"=>"smtp"],
]
];
public static $deletedFieldName = "deleted";
public static $sqlTable = 'tblMailAccounts';
@ -76,21 +89,63 @@ class mailAccount extends baseClass {
public function __set($key, $val) {
switch ($key) {
case "txURL":
$this->parceURL($val, "tx");
break;
case "rxURL":
$this->parceURL($val, "rx");
break;
case "password": $this->__password = xcrypt::encrypt($val); break;
default: parent::__set($key, $val);
}
}
protected function parceURL($val, $dst) {
if (!array_key_exists($dst, static::urlSchemas)) {
foxException::throw("ERR","Invalid dst scheme",400,"UDSCH");
}
$schemaRef=static::urlSchemas[$dst];
$ref=parse_url($val);
if (!array_key_exists($ref["scheme"], $schemaRef)) {
foxException::throw("ERR","Invalid URL scheme",400,"UUSCH");
}
$this->{$dst."SSL"}=$schemaRef[$ref["scheme"]]["ssl"];
$this->{$dst."Port"}=empty($ref["port"])?$schemaRef[$ref["scheme"]]["port"]:$ref["port"];
$this->{$dst."Server"}=$ref["host"];
$this->{$dst."Proto"}=$schemaRef[$ref["scheme"]]["proto"];
}
public function connect() {
return new mailClient($this);
}
protected function validateSave() {
if (empty($this->login) || empty($this->__password) || empty($this->address)) { return false;}
if (empty($this->login) || empty($this->__password) || empty($this->address)) {
throw new foxException("Validation failed",406);
}
return true;
}
protected function validateDelete()
{
if ($this->default) {
foxException::throw("ERR","Default account deletion prohibited",406,"DDAX");
}
return true;
}
public function setDefault() {
if ($this->default) { return;}
if ($this->deleted) { throw new foxException("Unacceptable",406); }
$this->checkSql();
$this->sql->quickExec("UPDATE `".static::$sqlTable."` set `default`=0");
$this->default=true;
$this->save();
}
public static function getDefaultAccount(&$sql=null) {
$ref = new static();
@ -101,4 +156,72 @@ class mailAccount extends baseClass {
} else {return null;}
}
public static function API_GET_list(request $request) {
if (! $request->user->checkAccess("adminMailAccounts", "core")) {
throw new foxException("Forbidden", 403);
}
return static::search();
}
public static function API_DELETE(request $request) {
if (! $request->user->checkAccess("adminMailAccounts", "core")) {
throw new foxException("Forbidden", 403);
}
$m=new static(common::clearInput($request->function,"0-9"));
$m->delete();
static::log($request->instance,__FUNCTION__, "Mail account ".$m->address." deleted",$request->user,"mailAccount",$m->id);
}
public static function API_GET(request $request) {
if (! $request->user->checkAccess("adminMailAccounts", "core")) {
throw new foxException("Forbidden", 403);
}
$m=new static(common::clearInput($request->function,"0-9"));
return $m;
}
public static function APIX_GET_setDefault(request $request) {
if (! $request->user->checkAccess("adminMailAccounts", "core")) {
throw new foxException("Forbidden", 403);
}
$m=new static(common::clearInput($request->function,"0-9"));
$m->setDefault();
static::log($request->instance,__FUNCTION__, "Default mail account changed to ".$m->address,$request->user,"mailAccount",$m->id);
}
public static function API_PUT(request $request) {
if (! $request->user->checkAccess("adminMailAccounts", "core")) {
throw new foxException("Forbidden", 403);
}
$m=new static();
$m->address=common::clearInput($request->requestBody->address,"0-9A-Za-z._@-");
$m->rxURL=common::clearInput($request->requestBody->rxURL,"0-9A-Za-z._:/-");
$m->txURL=common::clearInput($request->requestBody->txURL,"0-9A-Za-z._:/-");
$m->login=common::clearInput($request->requestBody->login,"0-9A-Za-z._@-");
$m->password=$request->requestBody->password;
$m->rxFolder=common::clearInput($request->requestBody->rxFolder,"0-9A-Za-z._-");
$m->rxArchiveFolder=common::clearInput($request->requestBody->rxArchiveFolder,"0-9A-Za-z._-");$m->save();
static::log($request->instance,__FUNCTION__, "Mail account ".$m->address." created",$request->user,"mailAccount",$m->id,null,logEntry::sevInfo);
return $m;
}
public static function API_PATCH(request $request) {
if (! $request->user->checkAccess("adminMailAccounts", "core")) {
throw new foxException("Forbidden", 403);
}
$m=new static(common::clearInput($request->function,"0-9"));
$m->address=common::clearInput($request->requestBody->address,"0-9A-Za-z._@-");
$m->rxURL=common::clearInput($request->requestBody->rxURL,"0-9A-Za-z._:/-");
trigger_error(common::clearInput($request->requestBody->rxURL,"0-9A-Za-z._:/-"));
$m->txURL=common::clearInput($request->requestBody->txURL,"0-9A-Za-z._:/-");
$m->login=common::clearInput($request->requestBody->login,"0-9A-Za-z._@-");
if (!empty($request->requestBody->password)) { $m->password=$request->requestBody->password; }
$m->rxFolder=common::clearInput($request->requestBody->rxFolder,"0-9A-Za-z._-");
$m->rxArchiveFolder=common::clearInput($request->requestBody->rxArchiveFolder,"0-9A-Za-z._-");
$m->save();
static::log($request->instance,__FUNCTION__, "Mail account ".$m->address." updated",$request->user,"mailAccount",$m->id,null,logEntry::sevInfo,["changelog"=>"$m->changelog"]);
return $m;
}
}

View File

@ -57,6 +57,7 @@ class mailBlocklist extends baseClass implements externalCallable {
$bl=new static();
$bl->address=$eMail;
static::log($request->instance,__FUNCTION__, "Address ".$eMail." added to blockList",$request->user,"eMailAddress",$eMail,null,logEntry::sevInfo);
$bl->save();
}
@ -73,6 +74,7 @@ class mailBlocklist extends baseClass implements externalCallable {
if ($bl = static::getByAddress($eMail)) {
$bl->delete();
static::log($request->instance,__FUNCTION__, "Address ".$eMail." removed from blockList",$request->user,"eMailAddress",$eMail,null,logEntry::sevInfo);
foxRequestResult::throw("200", "Deleted");
} else {
foxException::throw("WARN", "Not found", 404,"ANF");

View File

@ -314,6 +314,7 @@ class moduleInfo extends baseClass implements externalCallable
if (empty($request->parameters[0])) {
$mod->delete();
static::log($request->instance,__FUNCTION__, "Module ".$mod->name." deleted",$request->user);
foxRequestResult::throw(200, "Deleted");
}
@ -324,12 +325,14 @@ class moduleInfo extends baseClass implements externalCallable
unset($mod->features[$idx]);
$mod->features=array_values($mod->features);
$mod->save();
static::log($request->instance,__FUNCTION__, "feature ".common::clearInput($request->requestBody->feature)." deleted from module ".$mod->name,$request->user);
foxRequestResult::throw(200, "Deleted");
}
break;
case "config":
config::del(common::clearInput($request->requestBody->key), $mod->name);
static::log($request->instance,__FUNCTION__, "config key ".common::clearInput($request->requestBody->key)." deleted from module ".$mod->name,$request->user);
foxRequestResult::throw(200, "Deleted");
break;
default:
@ -359,6 +362,7 @@ class moduleInfo extends baseClass implements externalCallable
$mod->features[]=common::clearInput($request->requestBody->feature);
$mod->features=array_values($mod->features);
$mod->save();
static::log($request->instance,__FUNCTION__, "feature ".common::clearInput($request->requestBody->feature)." set for module ".$mod->name,$request->user,"module",$user->id,null,logEntry::sevInfo);
foxRequestResult::throw(201, "Created");
}
foxRequestResult::throw(201, "Created");
@ -366,6 +370,7 @@ class moduleInfo extends baseClass implements externalCallable
case "config":
config::set(common::clearInput($request->requestBody->key), $request->requestBody->value, $mod->name);
static::log($request->instance,__FUNCTION__, "config key ".common::clearInput($request->requestBody->key)." set for module ".$mod->name,$request->user,"module",$user->id,null,logEntry::sevInfo);
foxRequestResult::throw(201, "Created");
break;
default:

View File

@ -37,6 +37,8 @@ class modules implements externalCallable
"adminModulesInstall"=>"Install modules",
"adminUsers"=>"Manage users",
"adminUserGroups"=>"Manage userGroups",
"adminMailAccounts"=>"Manage system mail accounts",
"adminAuthMethods"=>"Manage auth methods",
],
"configKeys"=> [
"converterURL"=>"FoxConverter URL prefix",
@ -73,10 +75,26 @@ class modules implements externalCallable
"ru" => "Группы",
"en" => "Groups"
],
"titleLangIdx" => "adminGroups",
"function" => "groups",
"pageKey" => "adminGrous"
]
],
[
"title" => [
"ru" => "Учетные записи почты",
"en" => "Mail accounts"
],
"function" => "mailAccounts",
"pageKey" => "mailAccounts",
],
[
"title" => [
"ru" => "Методы oAuth",
"en" => "oAuth methods"
],
"function" => "oauth",
"pageKey" => "oauth",
],
]
]
],
@ -208,7 +226,7 @@ class modules implements externalCallable
$mod->modPriority=$modPriority;
}
$mod->save();
static::log($request->instance,__FUNCTION__, "Module ".$mod->name." installed",$request->user,"module",$mod->id,null,logEntry::sevInfo);
foxRequestResult::throw(201, "Created", $mod);
}

View File

@ -14,7 +14,7 @@
class oAuthProfile extends baseClass implements externalCallable {
protected $id;
public $name;
protected $hash;
public $hash;
public $url;
public $clientId;
protected $__clientKey;
@ -45,6 +45,17 @@ class oAuthProfile extends baseClass implements externalCallable {
return true;
}
public function __set($key, $val) {
switch ($key) {
case "clientKey":
$this->__clientKey=$val;
break;
default:
parent::__set($key, $val);
break;
}
}
public function getClient($redirectUrl, $scope=null) : oAuthClient {
return new oAuthClient($this->url, $this->clientId, $this->__clientKey, $redirectUrl."/".$this->hash,$scope,$this->config);
}
@ -59,5 +70,81 @@ class oAuthProfile extends baseClass implements externalCallable {
throw new foxException("Invalid hash");
}
}
public static function API_GET_list(request $request) {
if (! $request->user->checkAccess("adminAuthMethods", "core")) {
throw new foxException("Forbidden", 403);
}
return static::search();
}
public static function APIX_GET_disable(request $request) {
if (! $request->user->checkAccess("adminAuthMethods", "core")) {
throw new foxException("Forbidden", 403);
}
$m=new static(common::clearInput($request->function,"0-9"));
$m->enabled=false;
$m->save();
static::log($request->instance,__FUNCTION__, "OAuth profile ".$m->name." disabled.",$request->user,"oAuthProfile",$m->id,null,logEntry::sevInfo);
}
public static function APIX_GET_enable(request $request) {
if (! $request->user->checkAccess("adminAuthMethods", "core")) {
throw new foxException("Forbidden", 403);
}
$m=new static(common::clearInput($request->function,"0-9"));
$m->enabled=true;
$m->save();
static::log($request->instance,__FUNCTION__, "OAuth profile ".$m->name." enabled.",$request->user,"oAuthProfile",$m->id,null,logEntry::sevInfo);
}
public static function API_GET(request $request) {
if (! $request->user->checkAccess("adminAuthMethods", "core")) {
throw new foxException("Forbidden", 403);
}
$m=new static(common::clearInput($request->function,"0-9"));
return $m;
}
public static function API_DELETE(request $request) {
if (! $request->user->checkAccess("adminAuthMethods", "core")) {
throw new foxException("Forbidden", 403);
}
$m=new static(common::clearInput($request->function,"0-9"));
$m->delete();
static::log($request->instance,__FUNCTION__, "OAuth profile ".$m->name." deleted.",$request->user,"oAuthProfile",$m->id,null,logEntry::sevInfo);
}
public static function API_PUT(request $request) {
if (! $request->user->checkAccess("adminAuthMethods", "core")) {
throw new foxException("Forbidden", 403);
}
$m=new static();
$m->name=common::clearInput($request->requestBody->name,"0-9A-Za-z._@-");
$m->url=common::clearInput($request->requestBody->url,"0-9A-Za-z._:/-");
$m->clientId=common::clearInput($request->requestBody->clientId,"0-9A-Za-z._:/-");
$m->clientKey=common::clearInput($request->requestBody->clientKey,"0-9A-Za-z._@-");
$m->config=$request->requestBody->config;
$m->hash=common::clearInput($request->requestBody->hash,"0-9A-Za-z._@-");
$m->save();
static::log($request->instance,__FUNCTION__, "OAuth profile ".$m->name." created.",$request->user,"oAuthProfile",$m->id,null,logEntry::sevInfo);
return $m;
}
public static function API_PATCH(request $request) {
if (! $request->user->checkAccess("adminAuthMethods", "core")) {
throw new foxException("Forbidden", 403);
}
$m=new static(common::clearInput($request->function,"0-9"));
$m->name=common::clearInput($request->requestBody->name,"0-9A-Za-z._@-");
$m->url=common::clearInput($request->requestBody->url,"0-9A-Za-z._:/-");
if (!empty($request->requestBody->clientId)) { $m->clientId=common::clearInput($request->requestBody->clientId,"0-9A-Za-z._:/-"); }
if (!empty($request->requestBody->clientKey)) { $m->clientKey=common::clearInput($request->requestBody->clientKey,"0-9A-Za-z._@-"); }
$m->config=$request->requestBody->config;
$m->hash=common::clearInput($request->requestBody->hash,"0-9A-Za-z._@-");
$m->save();
static::log($request->instance,__FUNCTION__, "OAuth profile ".$m->name." updated.",$request->user,"oAuthProfile",$m->id,null,logEntry::sevInfo,["changelog"=>$m->changelog]);
}
}
?>

View File

@ -325,14 +325,17 @@ class user extends baseClass implements externalCallable
}
$user = new static(common::clearInput($request->function,"0-9"));
$user->sendEMailConfirmation();
static::log($request->instance,__FUNCTION__, "mailConfirmation sent for user ".$user->login,$request->user,"user",$user->id,null,logEntry::sevInfo);
}
public static function API_GET_sendEMailConfirmation(request $request) {
$request->user->sendEMailConfirmation();
static::log($request->instance,__FUNCTION__, "mailConfirmation sent for user ".$user->login,$request->user,"user",$user->id,null,logEntry::sevInfo);
}
public static function API_POST_validateEMailCode(request $request) {
if ($request->user->validateEMailConfirmation(common::clearInput($request->requestBody->code,"0-9"))) {
static::log($request->instance,__FUNCTION__, "Mail address confirmed for user ".$request->user->login,$request->user,"user",$request->user->id,null,logEntry::sevInfo);
return;
} else {
foxException::throw("ERR", "Validation failed", 400);

View File

@ -182,7 +182,7 @@ class userGroup extends baseClass implements externalCallable
$group = new static(common::clearInput($request->requestBody->groupId,"0-9"));
$group->dropAccessRule(common::clearInput($request->requestBody->rule), common::clearInput($request->requestBody->module));
$group->save();
static::log($request->instance,__FUNCTION__, "Rule ".common::clearInput($request->requestBody->rule)."@".common::clearInput($request->requestBody->module)." removed from group ".$group->name,$request->user,"userGroup",$group->id,null,logEntry::sevInfo);
foxRequestResult::throw(200, "Deleted");
}
@ -194,6 +194,7 @@ class userGroup extends baseClass implements externalCallable
}
$group = new static(common::clearInput($request->requestBody->groupId,"0-9"));
$group->addAccessRule(common::clearInput($request->requestBody->rule), ($request->requestBody->forAll=="1")?"<all>":common::clearInput($request->requestBody->module));
static::log($request->instance,__FUNCTION__, "Rule ".common::clearInput($request->requestBody->rule)."@".common::clearInput($request->requestBody->module)." added for group ".$group->name,$request->user,"userGroup",$group->id,null,logEntry::sevInfo);
$group->save();
}
@ -233,6 +234,7 @@ class userGroup extends baseClass implements externalCallable
$group->name=$grName;
$group->isList=$request->requestBody->isList==1;
$group->save();
static::log($request->instance,__FUNCTION__, "UserGroup ".$group->name." created.",$request->user,"userGroup",$group->id,null,logEntry::sevInfo);
foxRequestResult::throw(201, "Created",$group);
break;
case "DELETE":
@ -242,6 +244,8 @@ class userGroup extends baseClass implements externalCallable
}
$group->delete();
static::log($request->instance,__FUNCTION__, "UserGroup ".$group->name." deleted.",$request->user,"userGroup",$group->id,null,logEntry::sevInfo);
throw new foxRequestResult("Deleted",200);
break;
default:

View File

@ -212,6 +212,7 @@ class userGroupMembership extends baseClass implements externalCallable
$userGroupMembership->groupId=$userGroup->id;
$userGroupMembership->save();
$user->flushACRCache();
static::log($request->instance,__FUNCTION__, "User ".$user->login ." join group ".$userGroup->name,$request->user,"user",$user->id,null,logEntry::sevInfo);
foxRequestResult::throw("201", "Created");
break;
@ -219,6 +220,8 @@ class userGroupMembership extends baseClass implements externalCallable
if ($userGroupMembership==null) {
foxException::throw("ERR", "Not found", 404, "UGN");
}
static::log($request->instance,__FUNCTION__, "User ".$userGroupMembership->user->login ." left group ".$userGroupMembership->group->name,$request->user,"user",$userGroupMembership->user->id,null,logEntry::sevInfo);
$userGroupMembership->delete();
$user->flushACRCache();
break;

View File

@ -112,7 +112,7 @@ class userInvitation extends baseClass implements externalCallable {
} catch (\Exception $e) {
trigger_error($e->getMessage());
}
static::log($request->instance,__FUNCTION__, "User invitation ".$inv->regCode." created.",$request->user,"userInvitation",$inv->id,null,logEntry::sevInfo);
return $inv;
}
@ -127,6 +127,7 @@ class userInvitation extends baseClass implements externalCallable {
}
$inv=new static(common::clearInput($request->function));
$inv->sendEmail();
static::log($request->instance,__FUNCTION__, "User invitation ".$inv->regCode." email resent.",$request->user,"userInvitation",$inv->id,null,logEntry::sevInfo);
}
public static function API_DELETE(request $request) {
@ -135,6 +136,7 @@ class userInvitation extends baseClass implements externalCallable {
}
$inv=new static(common::clearInput($request->function));
$inv->delete();
static::log($request->instance,__FUNCTION__, "User invitation ".$inv->regCode." deleted.",$request->user,"userInvitation",$inv->id,null,logEntry::sevInfo);
}
}

View File

@ -98,7 +98,7 @@ function btnGroupAdd_click() {
}
function btnGroupDel_click(ref) {
var gid=($(ref.currentTarget).attr("groupId"));
var gid=($(ref.target).closest("tr").attr("groupId"));
var buttons={};
buttons[langPack.core.iface.dialodDelButton]=function() {
$("#dialogInfo").dialog("close");
@ -115,5 +115,5 @@ function btnGroupDel_click(ref) {
buttons[langPack.core.iface.dialodCloseButton]=function() { $("#dialogInfo").dialog("close"); }
UI.showInfoDialog(langPack.core.iface.groups.delButtonTitle+" #"+gid+" "+$(ref.currentTarget).attr("xname"),langPack.core.iface.groups.delButtonTitle,buttons);
UI.showInfoDialog(langPack.core.iface.groups.delButtonTitle+" #"+gid+" "+$(ref.target).closest("tr").attr("xname"),langPack.core.iface.groups.delButtonTitle,buttons);
}

View File

@ -0,0 +1,210 @@
export function load() {
UI.breadcrumbsUpdate(langPack.core.breadcrumbs.modTitle+" / "+langPack.core.breadcrumbs.mailAccounts);
UI.createTabsPanel({
accounts:{"title":langPack.core.iface.mailAccounts.allTitle, preLoad: reloadList,buttons: UI.addButton({id: "btn_acctadd",icon: "fas fa-plus",title: langPack.core.iface.mailAccounts.addButtonTitle, onClick: btnAccAdd_click})},
})
}
function btnAccAdd_click(ref) {
var buttons={};
buttons[langPack.core.iface.add]=async function() {
let fdata=UI.collectForm("addgrp", true,false, false,true);
if (fdata.validateErrCount==0) {
API.exec({
errDict: langPack.core.iface.register.errors,
requestType: "PUT",
data: {
address: fdata.address.val,
rxURL: fdata.rxUrl.val,
txURL: fdata.txUrl.val,
login: fdata.login.val,
password: fdata.passwd.val,
rxFolder: fdata.rxInbox.val,
rxArchiveFolder: fdata.rxArchive.val,
},
method: "core/mailAccount",
onSuccess: function(json) {
UI.closeDialog('addgrp');
reloadList();
},
});
}
};
buttons[langPack.core.iface.dialodCloseButton]=function() { UI.closeDialog('addgrp');}
UI.createDialog(
UI.addFieldGroup([
UI.addField({title: langPack.core.iface.mailAccounts.address, type: "input", item: "acc_address", reqx:true}),
UI.addField({title: langPack.core.iface.mailAccounts.login, type: "input", item: "acc_login",reqx:false}),
UI.addField({title: langPack.core.iface.password, type: "passwordNew", item: "acc_passwd",reqx:false}),
UI.addField({title: langPack.core.iface.mailAccounts.rxURL, type: "input", item: "acc_rxUrl",reqx:true}),
UI.addField({title: langPack.core.iface.mailAccounts.txURL, type: "input", item: "acc_txUrl",reqx:true}),
UI.addField({title: langPack.core.iface.mailAccounts.rxFolder, type: "input", item: "reg_rxInbox",reqx: true, val: "INBOX" }),
UI.addField({title: langPack.core.iface.mailAccounts.archiveFolder, type: "input", item: "reg_rxArchive",reqx:true, val: "Archive"}),
]),
langPack.core.iface.add,
buttons,
515,1,'addgrp');
UI.openDialog('addgrp');
}
function reloadList() {
$("div.widget.accounts").empty();
API.exec("GET","core/mailAccount/list",{},function(json) {
let tx = $("<table>",{class: "datatable sel"});
$("<th>",{class: "idx", text: "#"}).appendTo(tx);
$("<th>",{class: "", text: langPack.core.iface.mailAccounts.address}).appendTo(tx);
$("<th>",{class: "", text: langPack.core.iface.mailAccounts.login}).appendTo(tx);
$("<th>",{class: "", text: langPack.core.iface.mailAccounts.module}).appendTo(tx);
$("<th>",{class: "", text: langPack.core.iface.mailAccounts.default}).appendTo(tx);
$("<th>",{class: "button", style: "text-align: center", append: $("<i>",{class: "fas fa-ellipsis-h"})}).appendTo(tx);
tx.appendTo("div.widget.accounts");
let i=0;
$.each(json.data, function(idx, acc) {
i++;
let row=$("<tr>",{}).bind('contextmenu', mliContextMenuOpen).addClass("contextMenu").attr("accId",acc.id).attr("accAddr",acc.address);
$("<td>",{class: "idx", text: i}).appendTo(row);
$("<td>",{class: "", text: acc.address}).appendTo(row);
$("<td>",{class: "", text: acc.login}).appendTo(row);
$("<td>",{class: "", text: acc.module}).appendTo(row);
$("<td>",{class: "", text: acc.default?langPack.core.iface.yes:langPack.core.iface.no}).appendTo(row);
$("<td>",{class: "button", style: "text-align: center", append: $("<i>",{class: "fas fa-ellipsis-h"})})
.click(mliContextMenuOpen)
.appendTo(row);
$("<tbody>",{append: row}).appendTo(tx);
});
})
}
function mliContextMenuOpen(el) {
UI.contextMenuOpen(el,[
{title: langPack.core.iface.mailAccounts.setDefault, onClick: function() {
accDefault_Click(el);
}},
{title: langPack.core.iface.edit, onClick: function() {
accEdit_Click(el);
}},
{title: langPack.core.iface.delete, onClick: function() {
accDelete_Click(el);
}},
],$(el.target).closest("tr").attr("accaddr"));
return false;
}
function accDelete_Click(ref) {
var acid=($(ref.target).closest("tr").attr("accid"));
var buttons={};
buttons[langPack.core.iface.dialodDelButton]=function() {
$("#dialogInfo").dialog("close");
API.exec({
requestType: "DELETE",
method: "core/mailAccount/"+acid,
onSuccess: function(json) {
reloadList();
},
errDict: langPack.core.iface.mailAccounts.errors,
});
};
buttons[langPack.core.iface.dialodCloseButton]=function() { $("#dialogInfo").dialog("close"); }
UI.showInfoDialog(langPack.core.iface.mailAccounts.delDialogText+" #"+acid+" "+$(ref.target).closest("tr").attr("accAddr"),langPack.core.iface.delete,buttons);
}
function accDefault_Click(ref) {
var acid=($(ref.target).closest("tr").attr("accid"));
var buttons={};
buttons[langPack.core.iface.set]=function() {
$("#dialogInfo").dialog("close");
API.exec({
requestType: "GET",
method: "core/mailAccount/"+acid+"/setDefault",
onSuccess: function(json) {
reloadList();
},
errDict: langPack.core.iface.mailAccounts.errors,
});
};
buttons[langPack.core.iface.dialodCloseButton]=function() { $("#dialogInfo").dialog("close"); }
UI.showInfoDialog(langPack.core.iface.mailAccounts.setDefaultDialogText+" #"+acid+" "+$(ref.target).closest("tr").attr("accAddr"),langPack.core.iface.set,buttons);
}
function accEdit_Click(ref) {
var buttons={};
var acid=($(ref.target).closest("tr").attr("accid"));
buttons[langPack.core.iface.add]=async function() {
let fdata=UI.collectForm("addgrp", true,false, false,true);
if (fdata.validateErrCount==0) {
API.exec({
errDict: langPack.core.iface.register.errors,
requestType: "PATCH",
data: {
address: fdata.address.val,
rxURL: fdata.rxUrl.val,
txURL: fdata.txUrl.val,
login: fdata.login.val,
password: fdata.passwd.val,
rxFolder: fdata.rxInbox.val,
rxArchiveFolder: fdata.rxArchive.val,
},
method: "core/mailAccount/"+acid,
onSuccess: function(json) {
UI.closeDialog('addgrp');
reloadList();
},
});
}
};
buttons[langPack.core.iface.dialodCloseButton]=function() { UI.closeDialog('addgrp');}
UI.createDialog(
UI.addFieldGroup([
UI.addField({title: langPack.core.iface.mailAccounts.address, type: "input", item: "acc_address", reqx:true}),
UI.addField({title: langPack.core.iface.mailAccounts.login, type: "input", item: "acc_login",reqx:false}),
UI.addField({title: langPack.core.iface.password, type: "passwordNew", item: "acc_passwd",reqx:false}),
UI.addField({title: langPack.core.iface.mailAccounts.rxURL, type: "input", item: "acc_rxUrl",reqx:true}),
UI.addField({title: langPack.core.iface.mailAccounts.txURL, type: "input", item: "acc_txUrl",reqx:true}),
UI.addField({title: langPack.core.iface.mailAccounts.rxFolder, type: "input", item: "reg_rxInbox",reqx: true, val: "INBOX" }),
UI.addField({title: langPack.core.iface.mailAccounts.archiveFolder, type: "input", item: "reg_rxArchive",reqx:true, val: "Archive"}),
]),
langPack.core.iface.add,
buttons,
515,1,'addgrp');
API.exec({
errDict: langPack.core.iface.register.errors,
requestType: "GET",
method: "core/mailAccount/"+acid,
onSuccess: function(json) {
$("#acc_address").val(json.data.address);
$("#acc_login").val(json.data.login);
$("#acc_rxUrl").val(json.data.rxProto+(json.data.rxSSL?"s":"")+"://"+json.data.rxServer+":"+json.data.rxPort);
$("#acc_txUrl").val(json.data.txProto+(json.data.txSSL?"s":"")+"://"+json.data.txServer+":"+json.data.txPort);
$("#reg_rxInbox").val(json.data.rxFolder);
$("#reg_rxArchive").val(json.data.rxArchiveFolder);
return false;
},
});
UI.openDialog('addgrp');
}

View File

@ -10,6 +10,8 @@ export var menuSelector={
"module":"adminModules",
"users":"adminUsers",
"group":"adminGrous",
mailAccounts:"mailAccounts",
oauth: "oauth",
};
export function load() {
@ -59,6 +61,16 @@ export function load() {
mod.load();
})
break;
case "mailAccounts":
import("./coreMailAccounts.js").then(function(mod) {
mod.load();
})
break;
case "oauth":
import("./coreOAuthProfiles.js").then(function(mod) {
mod.load();
})
break;
default:

View File

@ -0,0 +1,228 @@
export function load() {
UI.breadcrumbsUpdate(langPack.core.breadcrumbs.modTitle+" / "+langPack.core.breadcrumbs.oauth);
UI.createTabsPanel({
methods:{"title":langPack.core.iface.oauth.allTitle, preLoad: reloadList,buttons: UI.addButton({id: "btn_acctadd",icon: "fas fa-plus",title: langPack.core.iface.oauth.addButtonTitle, onClick: btnAdd_click})},
})
}
function reloadList(ref) {
$("div.widget.methods").empty();
API.exec("GET","core/oAuthProfile/list",{},function(json) {
let tx = $("<table>",{class: "datatable sel"});
$("<th>",{class: "idx", text: "#"}).appendTo(tx);
$("<th>",{class: "", text: langPack.core.iface.title}).appendTo(tx);
$("<th>",{class: "", text: langPack.core.iface.url}).appendTo(tx);
$("<th>",{class: "", text: langPack.core.iface.active}).appendTo(tx);
$("<th>",{class: "button", style: "text-align: center", append: $("<i>",{class: "fas fa-ellipsis-h"})}).appendTo(tx);
tx.appendTo("div.widget.methods");
let i=0;
$.each(json.data, function(idx, acc) {
i++;
let row=$("<tr>",{}).bind('contextmenu', mliContextMenuOpen).addClass("contextMenu").attr("accActive",acc.enabled?1:0).attr("accHash",acc.hash).attr("accId",acc.id).attr("accName",acc.name);
$("<td>",{class: "idx", text: i}).appendTo(row);
$("<td>",{class: "", text: acc.name}).appendTo(row);
$("<td>",{class: "", text: acc.url}).appendTo(row);
$("<td>",{class: "", text: acc.enabled?langPack.core.iface.yes:langPack.core.iface.no}).appendTo(row);
$("<td>",{class: "button", style: "text-align: center", append: $("<i>",{class: "fas fa-ellipsis-h"})})
.click(mliContextMenuOpen)
.appendTo(row);
$("<tbody>",{append: row}).appendTo(tx);
});
})
}
function mliContextMenuOpen(el) {
let menuItems=[];
let menuOnOff;
if ($(el.target).closest("tr").attr("accActive")==1) {
menuOnOff= {title: langPack.core.iface.disable, onClick: function() { profileDisable_Click(el) ; }};
} else {
menuOnOff= {title: langPack.core.iface.enable, onClick: function() { profileEnable_Click(el);}};
}
menuItems.push(
{title: "CopyURL", onClick: function() { UI.copySelText(getCallbackUrl($(el.target).closest("tr").attr("accHash")));}},
menuOnOff,
{title: langPack.core.iface.edit, onClick: function() { profileEdit_Click(el); }},
{title: langPack.core.iface.delete, onClick: function() { profileDelete_Click(el);}},
);
UI.contextMenuOpen(el,menuItems,$(el.target).closest("tr").attr("accName"));
return false;
}
function profileDisable_Click(ref) {
var acid=($(ref.target).closest("tr").attr("accId"));
var buttons={};
buttons[langPack.core.iface.disable]=function() {
$("#dialogInfo").dialog("close");
API.exec({
requestType: "GET",
method: "core/oAuthProfile/"+acid+"/disable",
onSuccess: function(json) {
reloadList();
},
errDict: langPack.core.iface.oauth.errors,
});
};
buttons[langPack.core.iface.dialodCloseButton]=function() { $("#dialogInfo").dialog("close"); }
UI.showInfoDialog(langPack.core.iface.disable+" #"+acid+" "+$(ref.target).closest("tr").attr("accName"),langPack.core.iface.disable,buttons);
}
function profileEnable_Click(ref) {
var acid=($(ref.target).closest("tr").attr("accid"));
var buttons={};
buttons[langPack.core.iface.enable]=function() {
$("#dialogInfo").dialog("close");
API.exec({
requestType: "GET",
method: "core/oAuthProfile/"+acid+"/enable",
onSuccess: function(json) {
reloadList();
},
errDict: langPack.core.iface.oauth.errors,
});
};
buttons[langPack.core.iface.dialodCloseButton]=function() { $("#dialogInfo").dialog("close"); }
UI.showInfoDialog(langPack.core.iface.enable+" #"+acid+" "+$(ref.target).closest("tr").attr("accName"),langPack.core.iface.enable,buttons);
}
function profileDelete_Click(ref) {
var acid=($(ref.target).closest("tr").attr("accid"));
var buttons={};
buttons[langPack.core.iface.delete]=function() {
$("#dialogInfo").dialog("close");
API.exec({
requestType: "DELETE",
method: "core/oAuthProfile/"+acid,
onSuccess: function(json) {
reloadList();
},
errDict: langPack.core.iface.oauth.errors,
});
};
buttons[langPack.core.iface.dialodCloseButton]=function() { $("#dialogInfo").dialog("close"); }
UI.showInfoDialog(langPack.core.iface.delete+" #"+acid+" "+$(ref.target).closest("tr").attr("accName"),langPack.core.iface.delete,buttons);
}
function btnAdd_click(ref) {
var buttons={};
var hash=UI.genPassword("0123456789abcdef", 64);
buttons[langPack.core.iface.add]=async function() {
let fdata=UI.collectForm("addgrp", true,false, false,true);
if (fdata.validateErrCount==0) {
API.exec({
errDict: langPack.core.iface.register.errors,
requestType: "PUT",
data: {
name: fdata.name.val,
url: fdata.url.val,
clientId: fdata.clientId.val,
clientKey: fdata.clientKey.val,
config: fdata.type.val,
hash: hash,
},
method: "core/oAuthProfile",
onSuccess: function(json) {
UI.closeDialog('addgrp');
reloadList();
},
});
}
};
buttons[langPack.core.iface.dialodCloseButton]=function() { UI.closeDialog('addgrp');}
UI.createDialog(
UI.addFieldGroup([
UI.addField({title: langPack.core.iface.title, type: "input", item: "acc_name", reqx:true}),
UI.addField({title: langPack.core.iface.url, type: "input", item: "acc_url",reqx:false}),
UI.addField({title: langPack.core.iface.template, type: "select", item: "acc_type",reqx:true, args: {"":"--"+langPack.core.iface.oauth.selectPreset+"--","vk":"VK","yandex":"Yandex", "gitea":"Gitea", "gitlab":"Gitlab"}}),
UI.addField({title: langPack.core.iface.oauth.callback, type: "multiline", args: {rows: 4}, item: "acc_callbackurl", disabled: true, val: "callback"}),
UI.addField({title: langPack.core.iface.oauth.clientId, type: "input", item: "acc_clientId",reqx:true}),
UI.addField({title: langPack.core.iface.oauth.clientKey, type: "input", item: "acc_clientKey",reqx:true}),
]),
langPack.core.iface.add,
buttons,
515,1,'addgrp');
$("#acc_callbackurl").val(getCallbackUrl(hash));
UI.openDialog('addgrp');
}
function getCallbackUrl(hash) {
return API.settings.get("sitePrefix")+"/auth/oauth/"+hash;
}
function profileEdit_Click(ref) {
var acid=($(ref.target).closest("tr").attr("accid"));
var buttons={};
buttons[langPack.core.iface.edit]=async function() {
let fdata=UI.collectForm("addgrp", true,false, false,true);
if (fdata.validateErrCount==0) {
API.exec({
errDict: langPack.core.iface.register.errors,
requestType: "PATCH",
data: {
name: fdata.name.val,
url: fdata.url.val,
clientId: fdata.clientId.val,
clientKey: fdata.clientKey.val,
config: fdata.type.val,
},
method: "core/oAuthProfile/"+acid,
onSuccess: function(json) {
UI.closeDialog('addgrp');
reloadList();
},
});
}
};
buttons[langPack.core.iface.dialodCloseButton]=function() { UI.closeDialog('addgrp');}
UI.createDialog(
UI.addFieldGroup([
UI.addField({title: langPack.core.iface.title, type: "input", item: "acc_name", reqx:true}),
UI.addField({title: langPack.core.iface.url, type: "input", item: "acc_url",reqx:false}),
UI.addField({title: langPack.core.iface.template, type: "select", item: "acc_type",reqx:true, args: {"":"--"+langPack.core.iface.oauth.selectPreset+"--","vk":"VK","yandex":"Yandex", "gitea":"Gitea", "gitlab":"Gitlab"}}),
UI.addField({title: langPack.core.iface.oauth.clientId, type: "input", item: "acc_clientId",reqx:true}),
UI.addField({title: langPack.core.iface.oauth.clientKey, type: "input", item: "acc_clientKey",reqx:false}),
]),
langPack.core.iface.add,
buttons,
385,1,'addgrp');
API.exec({
errDict: langPack.core.iface.register.errors,
requestType: "GET",
method: "core/oAuthProfile/"+acid,
onSuccess: function(json) {
$("#acc_name").val(json.data.name);
$("#acc_url").val(json.data.url);
$("#acc_type").val(json.data.config);
$("#acc_clientId").val(json.data.clientId);
return false;
},
});
UI.openDialog('addgrp');
}

View File

@ -8,6 +8,8 @@ export var langItem={
ok0: "Операция завершена успешно",
dataEmpty: "Информация недоступна, скорее здесь ничего нет.",
emptyInvCode: "-нет-",
enable: "Активировать",
disable: "Заблокировать",
dataLoading: "Информация загружается",
loginFailedDialogTitle: "Ошибка авторизации",
loginFailed401text: "Неправильные имя пользователя или пароль\nУточните данные и попробуйте еще раз",
@ -17,10 +19,12 @@ export var langItem={
active: "Активен",
version: "Версия",
title: "Наименование",
url: "URL",
desc: "Описание",
installed: "Установлен",
install: "Установить",
set: "Установить",
edit: "Изменить",
updated: "Обновлен",
invCode: "invCode",
dialodCloseButton: "Закрыть",
@ -132,6 +136,34 @@ export var langItem={
URNF: "Пользователь не найден либо восстановление пароля не предусмотрено спосбом авторизации",
IVCC: "Некорректный код подтверждения. Проверьте правильность кода и повторите попытку.",
}
},
mailAccounts: {
allTitle: "Учетные записи электронной почты",
addButtonTitle: "Добавить УЗ",
address: "Адрес",
login: "Логин",
module: "Модуль",
rxURL: "RX URL",
txURL: "TX URL",
rxFolder: "Входящие",
archiveFolder: "Архив",
default: "Основной",
setDefault: "Сделать основным",
delDialogText: "Удалить аккаунт",
setDefaultDialogText: "Установить основным аккаунт",
errors: {
DDAX: "Нельзя удалить основной аккаунт",
}
},
oauth: {
allTitle: "Методы oAuth",
addButtonTitle: "Добавить метод",
clientId: "ID Клиента",
clientKey: "Секретный ключ",
callback: "URL возврата",
selectPreset: "Выберите шаблон",
errors: {
}
}
},
@ -140,7 +172,9 @@ export var langItem={
modules: "Модули",
users: "Пользователи",
groups: "Группы",
myprofile: "Мой профиль"
myprofile: "Мой профиль",
mailAccounts: "Учетные записи ЭП",
oauth: "oAuth",
},
date: {

View File

@ -3,7 +3,8 @@ import * as UI from './ui.js';
import { langPack, lpCacheDrop } from './langpack.js';
export function load() {
$("body").empty();
$("body").empty().addClass("login");
$("html").addClass("login");
if (UI.parceURL().module=='auth' && UI.parceURL().function=='oauth') {
let hash = UI.parceURL().id;

View File

@ -295,7 +295,7 @@ export function addField(ref)//title, item, blockstyle, fieldstyle, type, args,
let password=genPassword();
$("#"+item_id).val(password);
$("#"+item_id).change();
if (typeof(ref.newPasswdGenCallback)) {
if (typeof(ref.newPasswdGenCallback)=="function") {
ref.newPasswdGenCallback(password);
}
@ -763,8 +763,8 @@ export function setTitle(title, desc)
}
export function initBody() {
$("body").empty();
$("html").removeClass("login");
$("body").empty().removeClass("login");
$("<div>",{id: "contextMenu", class: "contextMenu"}).appendTo("body").hide();
$("<div>", { class: "t_global"})
.appendTo("body")

View File

@ -1518,6 +1518,10 @@ div.t_main
width: 1681px;
}
div.mainfame {
width: 1676px;
}
div.t_global
{
width: 1881px;
@ -1848,6 +1852,10 @@ div.t_main
width: 1360px;
}
div#mainframe {
width: 1343px !important;
}
div.t_global
{
width: 1560px;
@ -1926,6 +1934,10 @@ div.t_main
margin-right: 0;
}
div#mainframe {
width: 1022px !important;
}
div.t_global
{
width: 1239px;
@ -2039,6 +2051,10 @@ div.t_main
}
div#mainframe {
width: 1022px !important;
}
div.t_global
{
width: 1035px;
@ -2135,6 +2151,10 @@ div.t_main
width: 710px;
}
div#mainframe {
width: 699px !important;
}
div.t_global
{
width: 710px;