- Entities (Сутності)
- Загальна інформація
- Mixins (Миксины)
- mStorage - Implements CRUID operations with entities (ORM)
- audit - Аудит рівня запису
- rls - Row Level Security (Безпека рівня записів)
- aclRls - Access Control List Row Level Security
- als - Attribute Level Security
- dataHistory - Історичність записів
- unity
- tree — деревоподібні структури з побудовою шляху до даних
- fts — повнотекстове індексування даних сутності
- softLock — песимістичні блокування (Pessimistic locks)
- clobTruncate — обрізає великі текстові поля
Entities (Сутності) #
Загальна інформація #
Сутність (Entity) - базова одиниця доступу до даних платформи UnityBase. Сутність визначає як спосіб зберігання даних, так і поведінку (методи). Метадані сутності можуть бути використані клієнтськими додатками для побудови користувацького інтерфейсу.
У клієнт-серверній архітектурі найближча аналогія сутності - view у базі даних із тригерами на вставку/оновлення/видалення записів. В OOP - клас, що реалізує ORM.
З точки зору розробника сутність - це:
- файл у форматі JSON з розширенням
.meta(метафайл), що описує сутність та її атрибути - опціонально, файл із таким самим іменем, але розширенням
.js, що описує додатково поведінку (методи) сутності мовою JavaScript
Використовуючи метафайл, UnityBase може:
- згенерувати скрипт на створення таблиці в цільовій БД для зберігання даних сутності (якщо таблиця вже є - скрипт на модифікацію)
- згенерувати інструкції SQL для вибірки/вставки/видалення/додавання даних
- згенерувати базовий користувацький інтерфейс довідника (гріда) і форми редагування запису
- згенерувати документацію у форматі HTML
- відобразити сутність у графічному вигляді на ER diagram
Приклад метафайлу film_film.meta:
{
"caption" : "Кінофільм",
"description" : "Кінифільми",
"documentation": "Зберігає всі зареєстровані кінофільми",
"descriptionAttribute" : "title",
"attributes" : {
"IMDB": {
"dataType": "String",
"size": 12,
"caption": "IMDB",
"description" : "Ідентифікатор IMDB",
"documentation": "Internet Movie Database (IMDb) ідентифікатор може бути використаний для отримання додаткової інформації про фільм через API www.omdbapi.com"
"allowNull" : false,
"isUnique": true
},
"title": {
"dataType" : "String",
"size" : 100,
"caption" : "Назва",
"description" : "Назва кінофільму",
"allowNull" : false
}
},
"mixins" : {
"mStorage" : {
"simpleAudit": true,
"safeDelete": true
}
}
}
У цьому метафайлі ми описали сутність кінофільм із кодом film_film, у якої є два атрибути: «Ідентифікатор IMDB» і “Назва”.
Повна специфікація метафайлу описується наступною JSON schema:
Mixins (Миксины) #
Самі по собі сутності не мають поведінки, вона описується методами сутності. UnityBase містить низку готових методів, що розв'язують найпоширеніші завдання. Реалізація методів міститься в Міксинах. Розробник може додати свої методи до сутності або перекрити методи, додані міксинами.
У прикладі вище для сутності film_film ми додали міксин mStorage. Власне він додав для нашої сутності
методи для CRUID операцій.
mStorage - Implements CRUID operations with entities (ORM) #
Добавляет методы:
- addNew
- select
- insert
- update
- delete
Implements "Optimistic locking" and "Soft deletion" - check out mStorage mixin guide for additional reading.
audit - Аудит рівня запису #
Забезпечує запис усіх низькорівневих операцій insert/update/delete у сутність аудиту ubs_audit.
Для операцій update записуються як значення ДО оновлення, так і нові. За замовчуванням увімкнено для всіх сутностей системи.
rls - Row Level Security (Безпека рівня записів) #
RLS is a security feature which allows developers to give access to a sub-set of data in their entity to others.
Traditional permission systems don't distinguish between individual rows in a table, so access is all-or-nothing.
Grant to select method on an entity will allow a user to access all rows of that entity.
RLS supplements these with an additional layer of access controls on a per-row basis, so requirements such as "a manager can only view sensitive employee information for those employees who report to them" can be specified and enforced by the ORM.
Check out Row Level Security Mixin for additional reading.
aclRls - Access Control List Row Level Security #
Access Control List Row Level Security is a commonly used implementation of Row Level Security conception, so we implement it as a mixin and add a client-side features for edit ACL.
Unlike acl mixin, what allows set "static" rule for selecting rows, aclRls mixin allows end user or program
to specify grants for each row.
Consider we have doc_document entity and wants to allow supervisor sets specific grants for every document.
And we need to give grant either to user, or to role or to group or to organization unit.
For this we add aclRls mixin into mixins section of doc_document metadata and specify onEntities to be
uba_dubject: for users, groups and roles, since this entity is a unity for all administration subjectsorg_unit: for organization units
doc_document.meta example:
{
"caption": "Documents",
"attributes": [
// document attributes ...
],
"mixins": {
"mStorage": {
},
"aclRls": {
"onEntities": [
"org_unit",
"uba_subject"
]
}
}
}
After execution of ubcli migrate command doc_document_acl table will be added into database.
Entity doc_document_acl will be automatically generated by (inside @unitybase/ub model metadata hook).
Here is a result:
After this for each doc_document.select call aclRls mixin will add
and exists (select 1 from doc_document_acl where instanceID = doc_couemtn.ID and valueID in :admSubjIDs)
condition.
The admSubjIDs parameter value is returned by function, specified in the aclRls.subjectIDsFn mixin parameter.
If the mixin parameter is not provided, the default RLS.getDefaultAclRlsSubjects implementation will be used.
The default
RLS.getDefaultAclRlsSubjectsis applicable for cases, when onEntities contains uba_subject or/and org_unit only. In other cases, it is required to specifysubjectIDsFnand develop a custom implementation.
Filtering condition can be changed from Exist to In by specifying aclRls.selectionRule: "In",
but this is not recommended, because some DBMS generate more efficient query plan with Exists method.
It is possible to disable adding an aclRls filter by specifying function name in aclRls.skipIfFn.
If function returns true - filtering condition is not added.
By default, skipIfFn is set to RLS.isSuperUserOrInAdminGroup, which skip aclRls in case current user is admin or root
or Admin group member.
Parameters skipIfFn, subjectIDsFn and selectionRule can be specified for every entity or defined globally in the
application ubConfig.json:
{
"application": {
"domain": {
//...
},
"mixinsDefaults": {
"aclRls": {
"skipIfFn": "RLS.isSuperUserOrInAdminGroup",
"subjectIDsFn": "RLS.getDefaultAclRlsSubjects",
"selectionRule": "Exists"
}
}
}
}
For individual call aclRls can be disabled by adding misc({__skipAclRls: true}) into repository
UB.Repository('doc_document').attrs(['ID'])
.misc({skipAclRls: true}).limit(1).selectSingle()
!
skipAclRlsworks ONLY for server repository
For other aclRls options see description in entity schema
Before UB@5.22.10 instead of
skipIfFnandsubjectIDsFnexprMethodmethod is used. This method is OBSOLETE
"sameAs" ALC configuration #
For master-detail relations (for example we have a detail doc_doumentitems belong to doc_document) master entity ACL
usually should be used. In this case detail entity aclRlc can be configured as such
doc_doumentitems.meta example:
{
"caption": "Document items",
"attributes": [
{
"name": "docID",
"dataType": "Entity",
"associatedEntity": "doc_document",
"cascadeDelete": true,
"caption": "Document",
"allowNull": false,
"defaultView": false
},
// other attributes ...
],
"mixins": {
"mStorage": {
},
"aclRls": {
"sameAs": "doc_document",
"entityConnectAttr": "docID"
},
}
}
For entities with aclRls configured using sameAs the same ACL RLS storage is used (doc_document_acl in example above).
ACL editing UI is disabled for such entities.
als - Attribute Level Security #
Методи міксину:
getallroles- Отримати список усіх ролей, включно з динамічними ролями сутностіgetallstates- Отримати список усіх станів, включно з динамічними станами сутностіbeforeselect- Підготовка службових даних для укладача запитів, включно з поточним станом і поточними ролями запису сутностіafterselect- Формування ALS-інформації для клієнтаbeforeupdate- Підготовка службових даних для укладача запитів, включно з поточним станом і поточними ролями запису сутності
Властивості міксину:
-
stateAttrName- ім'я атрибута, який містить у собі інформацію про поточний стан запису сутності.
Якщо значення цієї властивості задано, то міксин виконає запит до БД, щоб отримати його, інакше міксин викличе у сутності методgetRecordCurrState. МетодgetRecordCurrStateпід час свого виконання повинен записати значення поточного стану у властивість_currRecordState(рядковий тип).Ця властивість використовується в методах
beforeselectіbeforeupdate.Приклад реалізації
getRecordCurrState:/** * Сформувати і повернути поточний стан для запису сутності * @param {ubMethodParams} runparams * @return {Boolean} */ me.getRecordCurrState = function(runparams) { var /** @type {TubDataStore} */ iDoc, rp = runparams.mParams; iDoc = UB.Repository('doc_incdoc').attrs(['mi_wfState']) .where('[ID]', '=', rp.ID) .select(); if (!iDoc.eof) { rp._currRecordState = iDoc.get('mi_wfState'); }else{ rp._currRecordState = ''; } return true; }; -
stateEnumGroup- назва групи нумерованого списку із сутностіubm_enum.
Якщо значення цієї властивості задано, то міксин спочатку виконає запит до сутностіubm_enumі з отриманих даних заповнить два масиви станів: масив значень і масив описів.
Якщо значення не задано, то цей крок пропускається.
Після цього міксин викличе у поточної сутності методaddAllDynStates, від якої також чекає масив значень і масив описів.
Ці два масиви під час виконанняaddAllDynStatesмають бути записані у властивості_dynStateValuesі_dynStateNamesвідповідно.Ця властивість використовується в методі
getallstates.Приклад реалізації
addAllDynStates:/** * Сформувати і повернути масив усіх динамічних станів сутності * @param {ubMethodParams} runparams * @return {Boolean} */ me.addAllDynStates= function(runparams) { var rp = runparams.mParams, arrValues = [], arrNames = []; arrValues.push('new'); arrNames.push('Новий запис'); arrValues.push('closed'); arrNames.push('Закритий запис'); rp._dynStateValues = arrValues; rp._dynStateNames = arrNames; return true; };Ще один приклад реалізації
addAllDynStates:/** * Сформувати і повернути масив усіх динамічних ролей сутності * @param {ubMethodParams} runparams * @return {Boolean} */ me.addAllDynRoles = function(runparams) { var rp = runparams.mParams, arrValues = [], arrNames = []; arrValues.push('recordOwner'); arrNames.push('Создатель записи'); arrValues.push('worldOwner'); arrNames.push('Создатель мира'); rp._dynRoleValues = arrValues; rp._dynRoleNames = arrNames; return true; }; -
optimistic— булеве значення, яке дозволяє визначити, чи є адміністрування оптимістичним (true) чи песимістичним (false).
Значення цієї властивості використовується лише в одному випадку — коли на сутності немає ЖОДНОГО адміністрування ALS. У такому випадку при оптимістичному адмініструванні всі атрибути матимуть УСІ права, при песимістичному адмініструванні атрибути НЕ МАЮТЬ прав
Умови для адміністрування атрибутів за допомогою міксину ALS #
Для адміністрування атрибутів сутності за допомогою міксину ALS необхідно дотримання таких умов:
- Міксин має бути вказаний у метафайлі сутності:
... "mStorage": { "simpleAudit": true, "safeDelete": true }, "als": { "stateAttrName": "mi_wfState", "stateEnumGroup": "", "optimistic": true }, ... - У
Post-параметрахвід клієнта на верхньому рівні має бути параметр з назвоюID
(Це пов’язано з тим, що на даний момент міксин адмініструє поки що лише ОДИН запис сутності) - У
Post-параметрахвід клієнта на верхньому рівні має бути параметрalsNeed: true
Алгоритм роботи міксина ALS #
Міксин ALS працює наступним чином:
-
Якщо клієнт викликав метод
select, то вPost-параметрахвідбувається пошукIDтаalsNeed: true.
Якщо клієнт викликав методupdate, то вPost-параметрахвідбувається пошукIDтаexecParams.
Якщо всі необхідні параметри знайдені, то перехід до п.2. Інакше міксин не виконує жодних дій. -
Якщо у міксина задано властивість
stateAttrName, то відбувається отримання цього значення. -
Відбувається отримання ролі поточного запису сутності (з ідентифікатором
ID).
(Це ролі сесії користувача, під якою виконується міксин, а також динамічні ролі сутності з методуgetRecordCurrDynRoles)Приклад реалізації методу
getRecordCurrDynRoles:/** * Сформировать и вернуть массив динамических ролей для записи сущности * @param {ubMethodParams} runparams * @return {Boolean} */ me.getRecordCurrDynRoles = function(runparams) { var /** @type {TubDataStore} */ iDoc, arrValues = [], rp = runparams.mParams; iDoc = UB.Repository('doc_incdoc').attrs(['mi_owner']) .where('[ID]', '=', rp.ID) .select(); if (!iDoc.eof) { if (iDoc.get('mi_owner') === Session.userID){ arrValues.push('recordOwner'); } } arrValues.push('worldOwner'); rp._currDynRoleValues = arrValues; return true; }; -
Міксин формує службову інформацію для автора запитів, а під час побудови запиту для кожного атрибута визначає права для поточного стану та поточних ролей.
-
Якщо клієнт викликав метод
select, то міксин у методіafterselectсформує для клієнта таку структуру:... "fieldList": ["ID", "outNumber", "outDate"], "resultAls": { "ID": "SUM", "outNumber": "UM", "outDate": "S" }, ... -
Якщо клієнт викликав метод
updateі не має прав на зміну даних, то міксин не дозволить йому змінити атрибут.Текст помилки буде таким: ALS: Update deny. Cannot update attribute "outDate" in entity "myWork" for state "NEW".
Символьні значення прав:
- Select — право користувача переглядати значення атрибута
- Символ: S
- Значення: 0b001
- Update - право користувача змінювати значення атрибута
- Символ: U
- Значення: 0b010
- Mandatory - чи є значення атрибута обов'язковим
- Символ: M
- Значення: 0b100
Значення можна комбінувати, отримуючи різні бітові маски доступу:
1 - можна вибирати
2 - можна оновлювати
4 - обов'язкове
3 - можна вибирати та оновлювати
7 - вибирати, оновлювати та обов'язкове
5 - обов'язкове та вибирати
і т.д.
dataHistory - Історичність записів #
unity #
tree — деревоподібні структури з побудовою шляху до даних #
fts — повнотекстове індексування даних сутності #
softLock — песимістичні блокування (Pessimistic locks) #
See detailed pessimistic locking guide
clobTruncate — обрізає великі текстові поля #
Актуально для Oracle, де робота з CLOB надзвичайно повільна
