Довольно часто использую AI для небольших задач по написанию фрагментов shell-скриптов, и тут выдался случай проверить инструмент в более серьезном деле.


Задача


Нужно реализовать на Go логику контроля доступа для SQL-подобного языка.


Пример
    ROLE System;
    ROLE Admin;
    ROLE LocationUser;
    ROLE LocationManager;
    ROLE Application; -- Projector is executed with this role

    GRANT ALL ON ALL TABLES WITH TAG BackofficeTag TO LocationManager;
    GRANT INSERT,UPDATE ON ALL TABLES WITH TAG BackofficeTag TO LocationUser;
    GRANT SELECT ON TABLE Orders TO LocationUser;
    GRANT UPDATE (CloseDatetime, Client) ON TABLE Bill TO LocationUser;
    GRANT EXECUTE ON COMMAND Orders TO LocationUser;
    GRANT EXECUTE ON QUERY Query1 TO LocationUser;
    GRANT EXECUTE ON ALL QUERIES WITH TAG PosTag TO LocationUser;

Главное отклонение от SQL — это метки (tags), благодаря им появляется конструкция:


GRANT INSERT,UPDATE ON ALL TABLES WITH TAG BackofficeTag TO LocationUser;

Попытка номер раз


Подумал — дай набросаю два интерфейса: IACL и IACLBuilder, и пусть "железный человек" их реализует.


package acl

type IACL[QName comparable] interface {

    // Returs true if access is granted, false otherwise
    CheckTableAccess(op string, fields []string, table QName, roles []QName) bool

    // Returs true if access is granted, false otherwise
    CheckExecAccess(resource QName, roles []QName) bool
}

type IACLBuilder[QName comparable] interface {
    TagTable(tag QName, table QName) IACLBuilder[QName]

    // If empty op is used for a given table, all ops will be granted for this table and subsequent grants will be ignored
    // If empty fields is used for a given table, op will be granted for all fields subsequent grants will be ignored
    GrantOpOnTable(op string, fields []string, table QName, role QName) IACLBuilder[QName]

    // Same rules as for GrantOpOnTable, but grants are applied to all tables with given tag, see TagTable method
    GrantOpOnTableByTag(op string, fields []string, tag QName, role QName) IACLBuilder[QName]

    GrantExec(resource QName, role QName) IACLBuilder[QName]

    // Must be the last method to call
    Build() IACL[QName]
}

На этом этапе я решил, что контроль EXECUTE это другое и вынес его в отдельные методы.


Сначала я попросил Provide sketch implementation — с этим всё было хорошо, скомпилировалось.


Затем настал черёд запроса Sketch is ok, provide an implementation of IACL.CheckExecAccess and IACLBuilder.GrantExec, respect comments and keep in mind that I will ask you to implement everything. И тут сработало, что ж: Provide a testable example for IACL.CheckExecAccess and IACLBuilder.GrantExec implementation — и здесь исходники компилировались, а пример успешно проходил.


Ну, думаю — успех, Now provide an implementation of IACL.CheckTableAccess, IACLBuilder.TagTable, IACL.GrantOpOnTable and IACL.GrantOpOnTableByTag, respect comments. Тут начались проблемы на этапе компиляции, после ряда попыток "объяснить" я понял, что быстрее написать самому. Тем более что из задания "железный человек" не осознал, что GrantOpOnTableByTag() надо учитывать в CheckTableAccess(). Разъяснения здесь также не помогли.


Попытка номер два


Анализируя получившийся код и размышляя над путями его реализации, я понял, что логику можно и нужно разделить на два слоя. На одном будет базовая и быстрая проверка доступа к абстрактному ресурсу, а на другом более сложная логика типа "Сначала проверить доступ к таблице целиком, если доступа нет, проверить доступ к таблице по метке, если доступа нет, проверить доступ к конкретным полям". Ну т.е. то самое S из SOLID.


Интерфейс базового слоя упростился до уровня, с которым AI вполне справился:


package acl

type IACL[Role, Operation, Resource comparable] interface {
    // HasPermission checks if the specified combination was granted via IACLBuilder.Grant() call.
    HasPermission(role Role, o Operation, r Resource) bool
}

type IACLBuilder[Role, Operation, Resource comparable] interface {
    Grant(role Role, op Operation, rp Resource)
    Build() IACL[Role, Operation, Resource]
}

Выводы


  • S, S и ещё раз S

Комментарии (2)


  1. 3ycb
    18.11.2023 18:38

    Не ту картинку выложили на КПДВ. Надо было "Опять двойка"


    1. maxim_ge Автор
      18.11.2023 18:38
      -1

      Если идти к корням то возможны и другие варианты: