Я новичок в среде Vapor и пытаюсь защитить несколько маршрутов.По сути, я хочу убедиться, что все маршруты в /campaigns/:id
доступны только в том случае, если пользователь действительно имеет доступ к этой конкретной кампании с этим идентификатором.Так что я не могу просто ввести любой идентификатор в URL и получить доступ к любой кампании.
Теперь, вместо добавления логики для этого к каждому отдельному маршруту (уже 6 пока), я решил использоватьпромежуточное программное обеспечение для этого.Это то, что я придумала до сих пор, с помощью некоторых дружелюбных людей из Vapor Discord:
final class CampaignMiddleware: Middleware {
func respond(to request: Request, chainingTo next: Responder) throws -> EventLoopFuture<Response> {
let user = try request.requireAuthenticated(User.self)
return try request.parameters.next(Campaign.self).flatMap(to: Response.self) { campaign in
guard try campaign.userID == user.requireID() else {
throw Abort(.forbidden, reason: "Campaign doesn't belong to you!")
}
return try next.respond(to: request)
}
}
}
struct CampaignController: RouteCollection {
func boot(router: Router) throws {
let route = router.grouped("campaigns")
let tokenAuthMiddleware = User.tokenAuthMiddleware()
let guardMiddleware = User.guardAuthMiddleware()
let tokenAuthGroup = route.grouped(tokenAuthMiddleware, guardMiddleware)
tokenAuthGroup.get(use: getAllHandler)
tokenAuthGroup.post(CampaignCreateData.self, use: createHandler)
// Everything under /campaigns/:id/*, CampaignMiddleware makes sure that the campaign actually belongs to you
let campaignRoute = tokenAuthGroup.grouped(Campaign.parameter)
let campaignMiddleware = CampaignMiddleware()
let protectedCampaignRoute = campaignRoute.grouped(campaignMiddleware)
protectedCampaignRoute.get(use: getOneHandler)
protectedCampaignRoute.delete(use: deleteHandler)
protectedCampaignRoute.put(use: updateHandler)
// Add /campaigns/:id/entries routes
let entryController = EntryController()
try protectedCampaignRoute.register(collection: entryController)
}
func getAllHandler(_ req: Request) throws -> Future<[Campaign]> {
let user = try req.requireAuthenticated(User.self)
return try user.campaigns.query(on: req).all()
}
func getOneHandler(_ req: Request) throws -> Future<Campaign> {
return try req.parameters.next(Campaign.self)
}
// ...deleted some other route handlers...
}
Проблема здесь в том, что промежуточное ПО «съедает» параметр кампании, выполняя request.parameters.next(Campaign.self)
.Таким образом, в getOneHandler
, где он также пытается получить доступ к req.parameters.next(Campaign.self)
, происходит сбой с ошибкой «Недостаточные параметры».Что имеет смысл, так как .next
фактически удаляет этот параметр из внутреннего массива параметров.
Теперь, как мне написать промежуточное программное обеспечение, которое использует параметр, не съедая его?Нужно ли мне использовать необработанные значения и запрашивать модель Campaign самостоятельно?Или можно как-то сбросить параметры после использования .next
?Или есть другой лучший способ справиться с авторизацией модели в Vapor 3?