Лучший способ проверки многоуровневых реляционных зависимостей

Предположим, что у вас есть сущности A, B, C и D.

  • D относится к C
  • C относится к B
  • B относится к A

Кроме того, пользователю разрешено работать только на D , если пользователю принадлежит A.

В определенном состоянии в приложении вы включаете ссылку на страницу, которая обращается к D. Таким образом, вы включаете идентификатор D в качестве параметра GET или POST.

Если пользователь нажимает на ссылку, приложение получает идентификатор D и начинает работать с D.

Простые приложения используют такие URL-адреса [modulo URL-rewriting]:

http://www.myServer.com/?action=1234&entity=D&ID=23 

Как проверить, разрешено ли пользователю работать на D?

A) Очевидным решением будет следующее: учитывая D, найдите C, затем найдите B и, в конце концов, найдите A. Если цепочка где-то сломается, доступ к D будет отклонен. К сожалению, для этого требуется – если тривиально реализовано – 4 доступа к базе данных, а не только для A.

B) Другим решением было бы сохранить идентификатор D в текущем сеансе в наборе доступных объектов, которые будут использоваться на следующей странице для визуализации.

C) В качестве альтернативы можно каким-либо образом зашифровать параметры GET и POST . В каждом запросе страницы первой операцией было бы дешифрование параметров запроса. Если операция дешифрования не удалась, доступ будет отклонен.

D) Или, при бесконечности, хеш всех ссылок на всех страницах , держите карту в сеансе, которая связывает хэши с URL-адресами и записывает только хэши на веб-страницы.

E) Наконец, вы можете сохранить ссылки на A, B и C в D, ссылки на A и B в C, ссылки на A в B. Таким образом, на каждом уровне можно было бы сразу найти корневую сущность.

Каково ваше решение в такой ситуации? И почему?

Хотя я включил тег PHP, я не хочу фокусировать этот вопрос на языке. Я был бы рад получить общие предложения. Или решения, которые уже реализованы, например, в слоях ORM .

UPDATE-1

Наконец, я выбрал D) .

Основной принцип:

Убедитесь, что идентификаторы некоторых подчиненных объектов всегда передаются безопасным / надежным способом. Таким образом, третье лицо не в состоянии изменить свои ценности.

Детали:

Этот вариант дает много преимуществ по дизайну:

Во-первых, идентификаторы или другие параметры связанных страниц никогда не попадают в браузер. Вместо

 http://www.myServer.com/?action=1234&entity=D&ID=23 

большинство страниц связаны таким образом

 http://www.myServer.com/?forwardHash=78sd7sdf98asd7ad5aa76asa4a465 

Все параметры следующей страницы, которые будут выполняться, полностью сохраняются внутри сеанса пользователя .

Поскольку все параметры страниц хранятся внутри сеанса пользователя, требуется гораздо меньше проверки . В частности, вышеупомянутые проверки реляционной зависимости больше не используются. Если что-то находится в сеансе пользователя, оно было перенесено с ранее проверенного шага диалога .

Более того, можно даже заставить пользователя только вызывать эту ссылку на текущей отображаемой странице . Каждый раз, когда они вызывают ссылку, приложение может аннулировать все другие ссылки на странице. Таким образом, пользователи не смогут открывать страницы в нескольких окнах и думать, что они видят два разных «состояния» приложения. Если они дважды вызовут ссылку, приложение может представить сообщение об ошибке.

Наконец, можно напрямую установить что-то, что я бы назвал диалоговыми окнами подзаголовка : вы запустили диалог, нажав URL текущей страницы в стеке продолжения в сеансе и открыв шаг диалога редактирования. Пользователь может либо упорядочить, либо умышленно отменить рабочий процесс диалога. Ссылка рабочего процесса отмены может автоматически отображаться как пользовательская опция, если стек продолжения не пуст.

Сохраняя продолжение в стеке в сеансе, он полностью изолируется от текущего шага диалога. Шаг диалога даже не знает о его вызывающем абоненте.

Обертывая функциональность внутри небольших вызовов менеджера, подпроцесс наконец вызывает FlowManager :: finishFlow (). Этот вызов выталкивает продолжение из стека и перенаправляет браузер на эту страницу – эффективно возвращается к точке, где начался рабочий процесс .

Поскольку мы используем стопку продолжений, можно даже запустить подчиненные потоки, которые подчиняются другим под-рабочим потокам .

    Очевидным решением будет следующее: учитывая D, найдите C, затем найдите B и, в конце концов, найдите A. Если цепочка где-то сломается, доступ к D будет отклонен. К сожалению, для этого требуется – если тривиально реализовано – 4 доступа к базе данных, а не только для A.

    Полагаю, это возможно. Отчасти это зависит от того, что означает «относится», но, предполагая относительно простую схему, я ожидаю, что вы сможете присоединиться ко всем четырем таблицам в одном выражении SQL. Если часть цепочки отсутствует, запрос не будет возвращать строки.

    Или я чего-то не хватает?

    Я не уверен, что понимаю, чего вы хотите достичь, но не можете ли вы использовать опцию A , чтобы проверить, разрешено ли пользователю работать с D только с одним доступом к базе данных ?:

     SELECT D.* FROM D JOIN C ON C.id = D.cid JOIN B ON B.id = C.bid JOIN A.id = B.aid WHERE A.ownedBy = @userID AND D.id = @idToBechecked