import Foundation import Clairvoyant import Vapor final class Authenticator: MetricAccessManager { private var writers: Set init(writers: [String]) { self.writers = Set(writers) } func hasAuthorization(for key: String) -> Bool { // Note: This is not a constant-time compare, so there may be an opportunity // for timing attack here. Sets perform hashed lookups, so this may be less of an issue, // and we're not doing anything critical in this application. // Worst case, an unauthorized person with a lot of free time and energy to hack this system // is able to change contents of the database, which are backed up in any case. writers.contains(key) } func metricListAccess(isAllowedForToken accessToken: AccessToken) throws { guard let key = String(data: accessToken, encoding: .utf8) else { return } guard hasAuthorization(for: key) else { throw MetricError.accessDenied } } func metricAccess(to metric: MetricId, isAllowedForToken accessToken: AccessToken) throws { try metricListAccess(isAllowedForToken: accessToken) } func authorize(_ request: Request) throws { guard let key = request.headers.first(name: "key") else { throw Abort(.badRequest) // 400 } guard hasAuthorization(for: key) else { throw Abort(.forbidden) // 403 } } }