Как прочитать параметр в промежуточном программном обеспечении Vapor, не потребляя его - PullRequest
2 голосов
/ 10 марта 2019

Я новичок в среде 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?

1 Ответ

3 голосов
/ 10 марта 2019

Привет, похоже, вы могли бы получить Campaign из запроса, не опуская его вот так

guard let parameter = req.parameters.values.first else {
    throw Abort(.forbidden)
}
try Campaign.resolveParameter(parameter.value, on: req)

Так что ваш окончательный код может выглядеть как

final class CampaignMiddleware: Middleware {
  func respond(to request: Request, chainingTo next: Responder) throws -> Future<Response> {
    let user = try request.requireAuthenticated(User.self)
    guard let parameter = request.parameters.values.first else {
        throw Abort(.forbidden)
    }
    return try Campaign.resolveParameter(parameter.value, on: request).flatMap { 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)
    }
  }
}
...