Перейти до основного вмісту
Як працює SDK

Цю сторінку ще не перекладено — показано англійську версію.

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

Як оголошувати через атрибути в 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 оголошує цю межу заздалегідь, а не з’ясовує її по ходу. Якщо читання виходить за межу, відповідь міститиме стільки рядків, скільки дозволяє межа, плюс позначку, що результат обрізано. Мовчки вкороченої сторінки не буває.
  • Межу застарілості обчисленого дозволу оголошено, але відкликання на неї не чекає: платформа одразу скидає кеш.