Я согласен с идеей добавления ссылок типа HATEOAS
к данным, возвращаемым из моих маршрутов WebAPI
, которые предоставят клиенту подсказки о том, какие действия доступны для любого данного фрагмента данных.
Итак, у меня может быть что-то вроде:
clients: {
data: [
{data: {id: 1, name: "Client One", age: 1},
_links: {
self: {href: ".../api/clients/1}, method: "get"}
edit: {href: ".../api/clients/1}, method: "put"}
delete: {href: ".../api/clients/1}, method: "delete"}
}
}
],
_links: {
self: {href: ".../api/clients", method: "get"},
add: {href: ".../api/clients", method: "post"}
}
}
Я видел несколько постов, в которых говорится о том, как включить ссылки, чтобы эта часть не была проблемой.
Проблема
Часть идеи, стоящей за HATEOAS
, заключается в том, чтобы сделать ваши данные самоопределяемыми / самопутешествующими. Итак, я хотел бы, чтобы мои _links
включали только те, которые доступны пользователю на основании его / ее разрешений. IOW, если пользователю было разрешено Edit
клиенту, но не Delete
клиенту, тогда _links
должен включать редактирование, но не удаление.
Проблема в том, что при генерации ссылокЯ бы на самом деле должен был оценить Authorization Policy
Действие / Маршрут, чтобы определить, должна ли быть включена ссылка.
Итак, что-то вроде этого (это предназначено для обсуждения, а не на самом делеcode):
[Route("api/v1/[controller]")]
[APIController]
[Authorize(Policy="HR")]
public class UsersController : ControllerBase {
[HttpGet]
public async Task<IActionResult> GetClients() {
var results = new List<dynamic>();
var list = context.Clients.Select(u => new BasicClient(u.Id, u.Name))
.ToList();
foreach(var client in list){
//If user has access to GetClient Action
if(??) {
client._links.Self.Href = Url.Action("GetClient", new {id = client.Id});
}
//If user has access to DeleteClient Action
if(??) {
client._links.Delete.Href = Url.Action("DeleteClient", new {id = client.Id});
}
...
}
[HttpGet("{id}", Name="GetClient")]
[Authorize(Policy="ViewClient")]
public async Task<IActionResult> GetClient(int id){ ... }
[HttpDelete("{id}", Name="DeleteClient")]
[Authorize(Policy="DeleteClient")]
public async Task<IActionResult> DeleteClient(int id) { ... }
Пользователю потребуются разрешения, необходимые для выполнения политик HR
и ViewClients
, чтобы получить ссылку self
, и дополнительно политику DeleteClients
для полученияdelete
ссылка.
Возможно ли это? Я думаю об этом неправильно? Есть ли лучший способ?
Разъяснение
Хотя можно использовать AuthorizationService
для проверки конкретной политики, я пытаюсь избежатьиспользование жестко закодированных политик. Политики уже определены для Controller
и Actions
, к которым я пытаюсь получить доступ. Я хочу знать, есть ли способ проверить пользователя по Action
(и позволить инфраструктуре разобраться, какую политику необходимо применить).
Таким же образом, как Url.Action("GetClient",...)
принимаетимя Action
и строит правильную ссылку на основе определенных маршрутов, я хочу что-то вроде AuthService.AuthorizeAction("GetClient", User)
, которое выяснит, какие разрешения необходимы, и если у пользователя есть эти разрешения.
Надеялся, что.NetCore имеет что-то встроенное, что делает это. В противном случае, я полагаю, я мог бы использовать отражение и IAuthorizationService
, чтобы сделать это.