Цю сторінку ще не перекладено — показано англійську версію.
Access & Roles
Ролі складають із дозволів, а не зашивають у код. Атомарні дозволи складаються в ролі. Роль обмежує доступ до даних аж до рядка й стовпця і вирішує, які інтерфейси модулів збірка взагалі побачить. Це замінює поділ ключів на клієнтські й серверні: облікові дані вказують лише на обліковий запис, а його ролі сервер визначає при кожному запиті.
Коли це потрібно
- Потрібні облікові дані (credential), вужчі за «клієнт» чи «сервер». За ними сервер при кожному запиті визначає складені ролі (role).
- Доступ до даних має зупинятися на рівні рядків і стовпців: обмеження за регіоном, маскування персональних даних, підрядники лише з правом читання.
- Збірка має бачити лише ті інтерфейси, які відкриває її роль: у відвідувача кімнати викликів, щоб вигнати гравця чи закрити кімнату, просто немає.
- Кнопки в UI мають ставати неактивними саме тоді, коли дію заборонено:
CanIперевіряє ту саму політику, яку потім застосує сервер. - Не потрібно, якщо готові пресети (
player,room-owner,seller, …) уже відповідають вашим акторам. Усі модулі враховують їх за замовчуванням, а повний перелік є на сторінці Основні поняття.
Хто що робить
| Актор | Що робить на цій сторінці |
|---|---|
operator | оголошує ролі й політики, задає обмеження за рядками та стовпцями, видає ролі, випускає ключі |
match-organizer | організатори турніру: мають ключ зі складеною роллю, допускають учасників до матчу, але не можуть оформлювати повернення коштів |
будь-який актор | перед дією перевіряє CanI і бачить лише відкриті йому інтерфейси |
Коротко
entry-validator з обмеженнями за рядками й стовпцями, видайте цю роль, а тоді перевіряйте CanI перед дією[Role("entry-validator")]
public class EntryValidator
{
[Allow(Rooms.Membership.Administer)] public Permit GateEntries;
[Allow(Data.Records.Read, table: "player_profile", rows: "banned == false",
columns: "id, display_name")] public Permit SeeProfiles;
}
await PlayServ.Access.Grant(staffId, Roles.EntryValidator, Roles.MatchOrganizer);
var key = await PlayServ.Access.IssueKey(staffId); // the credential names no roles
// any actor, before attempting an operation:
if (await PlayServ.Access.CanI(Commerce.Orders.Administer)) Hud.ShowRefund();@Role('entry-validator')
export class EntryValidator {
@Allow(Rooms.membership.administer) gateEntries: Permit;
@Allow(Data.records.read, { table: 'player_profile', rows: 'banned == false',
columns: ['id', 'display_name'] }) seeProfiles: Permit;
}
await playserv.access.grant(staffId, Roles.entryValidator, Roles.matchOrganizer);
const key = await playserv.access.issueKey(staffId); // the credential names no roles
// any actor, before attempting an operation:
if (await playserv.access.canI(Commerce.orders.administer)) hud.showRefund();@role("entry-validator")
class EntryValidator:
gate_entries = allow(rooms.membership.administer)
see_profiles = allow(data.records.read, table="player_profile",
rows="banned == false", columns=["id", "display_name"])
await playserv.access.grant(staff_id, roles.ENTRY_VALIDATOR, roles.MATCH_ORGANIZER)
key = await playserv.access.issue_key(staff_id) # the credential names no roles
# any actor, before attempting an operation:
if await playserv.access.can_i(commerce.orders.administer):
hud.show_refund()Як оголошувати через атрибути в Go, поки не вирішено (O-1). Приклади з’являться, коли оберуть спосіб запису.
// declarations compile into the same pushed model — playserv push from the UE project or CI
USTRUCT(PSRole = "entry-validator")
struct FEntryValidator
{
GENERATED_BODY()
UPROPERTY(PSAllow = (Atom = "Rooms.Membership.Administer"))
FPSPermit GateEntries;
UPROPERTY(PSAllow = (Atom = "Data.Records.Read", Table = "player_profile",
Rows = "banned == false", Columns = "id, display_name"))
FPSPermit SeeProfiles;
};
// granting is an operator act; a build checks what its identity resolves to
const FPSActor Me = Client->Whoami(); // which interfaces this actor unlocks
Client->Access->CanI(TEXT("Commerce.Orders.Administer"),
TPSOnResult<bool>::CreateWeakLambda(this, [this](const TPSResult<bool>& Result)
{
if (!Result.HasValue()) { return; }
if (Result.Value()) { Hud->ShowRefund(); }
}));
// co_await support ships later: a built-in SDK coroutine wrapper that respects Unreal's GC and threading.// the same `[Role]` / `[Allow]` declaration as the server tab, on the Unity 2021.3 runtime
var me = playserv.Whoami(); // which interfaces this actor unlocks
if (await playserv.Access.CanI(Commerce.Orders.Administer)) hud.ShowRefund();Атомарний дозвіл (permission atom) — це пара: ресурс і одне з чотирьох дієслів — read, write, execute, administer. Набір дієслів фіксований. Якщо випадок у нього не вкладається, ділять ресурс, а не розширюють список. Тому перевірка чужого входу — це Rooms.Membership.Administer, а не окреме дієслово ValidateEntry. Діяти щодо чужої участі (membership) в кімнаті — це адміністрування, а зайти самому — це Rooms.Membership.Write на тому самому ресурсі.
Модель
ACL для даних — це роль × операція × предикат рядків × маска полів. Модель та сама, хоч де її задано: у коді, через API чи в сітці ролей в адмін-панелі.
З чого складається доступ.
| Термін | Що це |
|---|---|
атомарний дозвіл | одна пара ресурс × дієслово. Дієслів чотири: read (отримання, вибірка, підписка), write (створення, зміна, видалення і дії від власного імені — зайти, вийти), execute (виклик функції, застосування здібності) та administer (дії щодо інших: вигнати, закрити, примусово змінити стан) |
роль | іменований набір атомарних дозволів. Може містити іншу роль |
пресет ролі | постачається готовим, складений з атомарних дозволів і в уже задеплоєних споживачів далі працює без змін. Це відправна точка, а не обмеження: проєкт оголошує власні ролі з тих самих атомарних дозволів |
предикат рядків | які рядки — булів предикат над значеннями сесії |
маска полів | які поля — її оголошують окремо для кожної ролі й кожної операції (operation) |
Що відкривають облікові дані.
| Облікові дані | Що вони відкривають |
|---|---|
ключ гравця | його має збірка рушія. Гравець (player) за ним з’являється під час входу, а збірка бачить рядки cl у кожній таблиці операцій |
ключ хоста | він є у виділеного сервера або майстер-клієнта, і його ролі відкривають рядки mc |
завантажений код | виконується з роллю проєкту backend-service — саме її перевіряє Authoritative = true у таблиці лідерів (leaderboard) |
зареєстрований хук | нічого понад це не дає: функція зберігає роль, з якою її задеплоїли |
Легенда міток (fn / cl / mc / adm) живе на сторінці Основні поняття.
Що справджується для кожної перевірки.
| Завжди | Що це |
|---|---|
облікові дані | не містять списку ролей: вони вказують на обліковий запис (identity), а ролі сервер визначає при кожному запиті. Відкликання ролі одразу скидає закешований результат і не чекає, поки мине оголошена межа застарілості |
делегування | змінює область дії, але не можливості. Коли ви дієте від імені гравця, змінюється те, які рядки видно і кому приписують запис. Операцій, яких актор не мав, від цього не з’являється |
дієслово | відповідає на питання, який це вплив, а предикат — які це рядки. Якщо два випадки відрізняються лише тим, чий це рядок, різниця в предикаті. Якщо відрізняється сам вплив, це інша операція і, можливо, інше дієслово. Тому «вигнати» — це administer, а не write із широким предикатом |
власник | завжди бачить себе, хоч би що казав предикат |
видимість | — не про безпеку: оптимізація каналів і дозвіл — різні механізми, і жоден не замінює іншого |
вимкнений модуль | не має інтерфейсу. Увімкнено модуль чи ні — властивість збірки, тому для вимкненого модуля кодогенерація нічого не створює, а недоступний виклик дає помилку компіляції, а не відмову під час виконання |
інтерфейс модуля | залежить від актора, а не від сторони (див. Хто ухвалює рішення). Збірка room-visitor бачить вхід, вихід і читання, збірка room-owner — ще й вигнання гравців, закриття та налаштування кімнати, а whoami повідомляє, які інтерфейси відкриває поточний актор |
Хто видає роль. Видати чи відкликати роль гравця, а також оголосити в проєкті роль за замовчуванням для нового гравця можна операціями модуля Auth & Players. Цей модуль відповідає за облікові записи, а роль платформа визначає за тим записом, на який вказують облікові дані.
Помилки
- Якщо об’єкт приховує предикат, відповідь —
not found, а неforbidden: інакше сама відмова повідомила б тому, хто викликає, що об’єкт існує. - Якщо в того, хто викликає, немає потрібного права, відповідь —
forbidden, коли існування об’єкта не секрет. Відповідь називає, чого саме бракує, а не просто відмовляє. - Поля поза маскою у відповіді немає зовсім, а не приходить порожнім: інакше порожнє значення не можна було б відрізнити від прихованого.
- Роль, яка містить саму себе напряму або через ланцюжок, — помилка конф ігурації. Таке оголошення платформа відхиляє одразу, а не розбирає під час виконання.
- Делегування ніколи не розширює можливостей: виклик, який актор не міг би зробити від власного імені, платформа відхилить і тоді, коли він робить його від імені гравця.
Обмеження
Конкретні числа з’являться разом із розділом про обмеження платформи, а що відбувається на кожній межі, зафіксовано вже зараз.
- Розмір вибірки (selection) за предикатом рядків обмежено, і модель ACL оголошує цю межу заздалегідь, а не з’ясовує її по ходу. Якщо читання виходить за межу, відповідь міститиме стільки рядків, скільки дозволяє межа, плюс позначку, що результат обрізано. Мовчки вкороченої сторінки не буває.
- Межу застарілості обчисленого дозволу оголошено, але відкликання на неї не чекає: платформа одразу скидає кеш.