From de9ea2b2c2934562172008aabfdbca10ab9aedba Mon Sep 17 00:00:00 2001 From: Marcin Czachurski Date: Sun, 10 Nov 2024 15:08:54 +0100 Subject: [PATCH] Add security headers to all responses --- .../Application+Configure.swift | 8 +++-- .../Cache/CacheControlMiddleware.swift | 17 ---------- .../Controllers/AccountController.swift | 13 ++++++++ .../ActivityPubActorController.swift | 3 ++ .../ActivityPubActorsController.swift | 7 +++++ .../ActivityPubSharedController.swift | 1 + .../Controllers/AttachmentsController.swift | 7 +++++ .../AuthenticationClientsController.swift | 5 +++ .../Controllers/AvatarsController.swift | 2 ++ .../Controllers/BookmarksController.swift | 1 + .../Controllers/CategoriesController.swift | 1 + .../Controllers/CountriesController.swift | 2 +- .../Controllers/ErrorItemsController.swift | 3 ++ .../Controllers/FavouritesController.swift | 1 + .../FollowRequestsController.swift | 3 ++ .../Controllers/HeadersController.swift | 2 ++ .../Controllers/HealthController.swift | 1 + .../Controllers/IdentityController.swift | 3 ++ .../InstanceBlockedDomainsController.swift | 4 +++ .../Controllers/InstanceController.swift | 2 +- .../Controllers/InvitationsController.swift | 3 ++ .../Controllers/LicensesController.swift | 1 + .../Controllers/LocationsController.swift | 2 ++ .../Controllers/NodeInfoController.swift | 2 +- .../Controllers/NotificationsController.swift | 3 ++ .../Controllers/ProfileController.swift | 1 + .../PushSubscriptionsController.swift | 4 +++ .../Controllers/RegisterController.swift | 3 ++ .../Controllers/RelationshipsController.swift | 1 + .../Controllers/ReportsController.swift | 4 +++ .../Controllers/RolesController.swift | 3 ++ .../Controllers/RulesController.swift | 4 +++ .../Controllers/SearchController.swift | 1 + .../Controllers/SettingsController.swift | 4 ++- .../Controllers/StatusesController.swift | 17 ++++++++++ .../Controllers/TimelinesController.swift | 6 ++++ .../Controllers/TrendingController.swift | 3 ++ .../Controllers/UserAliasesController.swift | 3 ++ .../Controllers/UsersController.swift | 20 ++++++++++++ .../Controllers/WellKnownController.swift | 6 ++-- .../ResponseHeaders/CacheControl.swift | 12 +++++++ .../CacheControlMiddleware.swift | 31 +++++++++++++++++++ .../CommonHeadersMiddleware.swift | 22 +++++++++++++ 43 files changed, 216 insertions(+), 26 deletions(-) delete mode 100644 Sources/VernissageServer/Cache/CacheControlMiddleware.swift create mode 100644 Sources/VernissageServer/ResponseHeaders/CacheControl.swift create mode 100644 Sources/VernissageServer/ResponseHeaders/CacheControlMiddleware.swift create mode 100644 Sources/VernissageServer/ResponseHeaders/CommonHeadersMiddleware.swift diff --git a/Sources/VernissageServer/Application+Configure.swift b/Sources/VernissageServer/Application+Configure.swift index daf6d95b..cdb18b22 100644 --- a/Sources/VernissageServer/Application+Configure.swift +++ b/Sources/VernissageServer/Application+Configure.swift @@ -154,11 +154,15 @@ extension Application { ) let corsMiddleware = CORSMiddleware(configuration: corsConfiguration) self.middleware.use(corsMiddleware, at: .beginning) - - // Catches errors and converts to HTTP response. + + // Custom response header middleware. let errorMiddleware = CustomErrorMiddleware() self.middleware.use(errorMiddleware) + // Atatch common headers to HTTP response. + let commonHeadersMiddleware = CommonHeadersMiddleware() + self.middleware.use(commonHeadersMiddleware) + // Configure public files middleware. let publicFolderPath = self.directory.publicDirectory let fileMiddleware = FileMiddleware( diff --git a/Sources/VernissageServer/Cache/CacheControlMiddleware.swift b/Sources/VernissageServer/Cache/CacheControlMiddleware.swift deleted file mode 100644 index 616a9cdf..00000000 --- a/Sources/VernissageServer/Cache/CacheControlMiddleware.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// https://mczachurski.dev -// Copyright © 2024 Marcin Czachurski and the repository contributors. -// Licensed under the Apache License 2.0. -// - -import Vapor - -/// Middleware which adds to response headers `Cache-Control` header set to one hour. -struct CacheControlMiddleware: AsyncMiddleware { - func respond(to request: Request, chainingTo next: AsyncResponder) async throws -> Response { - let response = try await next.respond(to: request) - response.headers.cacheControl = .init(isPublic: true, maxAge: 3600) - - return response - } -} diff --git a/Sources/VernissageServer/Controllers/AccountController.swift b/Sources/VernissageServer/Controllers/AccountController.swift index 91ad8d43..ec683c28 100644 --- a/Sources/VernissageServer/Controllers/AccountController.swift +++ b/Sources/VernissageServer/Controllers/AccountController.swift @@ -19,14 +19,17 @@ extension AccountController: RouteCollection { accountGroup .grouped(LoginHandlerMiddleware()) + .grouped(CacheControlMiddleware(.noStore)) .post("login", use: login) accountGroup .grouped(EventHandlerMiddleware(.accountLogout)) + .grouped(CacheControlMiddleware(.noStore)) .post("logout", use: logout) accountGroup .grouped(EventHandlerMiddleware(.accountConfirm)) + .grouped(CacheControlMiddleware(.noStore)) .grouped("email") .post("confirm", use: confirm) @@ -35,6 +38,7 @@ extension AccountController: RouteCollection { .grouped(UserPayload.guardMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.accountConfirm)) + .grouped(CacheControlMiddleware(.noStore)) .grouped("email") .post("resend", use: resend) @@ -43,6 +47,7 @@ extension AccountController: RouteCollection { .grouped(UserPayload.guardMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.accountChangeEmail)) + .grouped(CacheControlMiddleware(.noStore)) .put("email", use: changeEmail) accountGroup @@ -50,20 +55,24 @@ extension AccountController: RouteCollection { .grouped(UserPayload.guardMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.accountChangePassword, storeRequest: false)) + .grouped(CacheControlMiddleware(.noStore)) .put("password", use: changePassword) accountGroup .grouped(EventHandlerMiddleware(.accountForgotToken)) + .grouped(CacheControlMiddleware(.noStore)) .grouped("forgot") .post("token", use: forgotPasswordToken) accountGroup .grouped(EventHandlerMiddleware(.accountForgotConfirm, storeRequest: false)) + .grouped(CacheControlMiddleware(.noStore)) .grouped("forgot") .post("confirm", use: forgotPasswordConfirm) accountGroup .grouped(EventHandlerMiddleware(.accountRefresh)) + .grouped(CacheControlMiddleware(.noStore)) .post("refresh-token", use: refresh) accountGroup @@ -71,12 +80,14 @@ extension AccountController: RouteCollection { .grouped(UserPayload.guardMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.accountRevoke)) + .grouped(CacheControlMiddleware(.noStore)) .delete("refresh-token", ":username", use: revoke) accountGroup .grouped(UserAuthenticator()) .grouped(UserPayload.guardMiddleware()) .grouped(EventHandlerMiddleware(.accountGetTwoFactorToken)) + .grouped(CacheControlMiddleware(.noStore)) .get("get-2fa-token", use: getTwoFactorToken) accountGroup @@ -84,6 +95,7 @@ extension AccountController: RouteCollection { .grouped(UserPayload.guardMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.accountEnableTwoFactorAuthentication)) + .grouped(CacheControlMiddleware(.noStore)) .post("enable-2fa", use: enableTwoFactorAuthentication) accountGroup @@ -91,6 +103,7 @@ extension AccountController: RouteCollection { .grouped(UserPayload.guardMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.accountDisableTwoFactorAuthentication)) + .grouped(CacheControlMiddleware(.noStore)) .post("disable-2fa", use: disableTwoFactorAuthentication) } } diff --git a/Sources/VernissageServer/Controllers/ActivityPubActorController.swift b/Sources/VernissageServer/Controllers/ActivityPubActorController.swift index 369d581c..2b645663 100644 --- a/Sources/VernissageServer/Controllers/ActivityPubActorController.swift +++ b/Sources/VernissageServer/Controllers/ActivityPubActorController.swift @@ -19,14 +19,17 @@ extension ActivityPubActorController: RouteCollection { actorGroup .grouped(EventHandlerMiddleware(.actorRead)) + .grouped(CacheControlMiddleware(.public())) .get(use: read) actorGroup .grouped(EventHandlerMiddleware(.activityPubInbox)) + .grouped(CacheControlMiddleware(.noStore)) .post("inbox", use: inbox) actorGroup .grouped(EventHandlerMiddleware(.activityPubOutbox)) + .grouped(CacheControlMiddleware(.noStore)) .post("outbox", use: outbox) } } diff --git a/Sources/VernissageServer/Controllers/ActivityPubActorsController.swift b/Sources/VernissageServer/Controllers/ActivityPubActorsController.swift index 6f22b035..7e6399f4 100644 --- a/Sources/VernissageServer/Controllers/ActivityPubActorsController.swift +++ b/Sources/VernissageServer/Controllers/ActivityPubActorsController.swift @@ -18,32 +18,39 @@ extension ActivityPubActorsController: RouteCollection { activityPubGroup .grouped(EventHandlerMiddleware(.activityPubRead)) + .grouped(CacheControlMiddleware(.noStore)) .get(":name", use: read) activityPubGroup .grouped(EventHandlerMiddleware(.activityPubInbox)) + .grouped(CacheControlMiddleware(.noStore)) .post(":name", "inbox", use: inbox) activityPubGroup .grouped(EventHandlerMiddleware(.activityPubOutbox)) + .grouped(CacheControlMiddleware(.noStore)) .post(":name", "outbox", use: outbox) activityPubGroup .grouped(EventHandlerMiddleware(.activityPubFollowing)) + .grouped(CacheControlMiddleware(.noStore)) .get(":name", "following", use: following) activityPubGroup .grouped(EventHandlerMiddleware(.activityPubFollowers)) + .grouped(CacheControlMiddleware(.noStore)) .get(":name", "followers", use: followers) // Support for: https://example.com/@johndoe/statuses/:id activityPubGroup .grouped(EventHandlerMiddleware(.activityPubStatus)) + .grouped(CacheControlMiddleware(.noStore)) .get(":name", "statuses", ":id", use: status) // Support for: https://example.com/statuses/:id statusesGroup .grouped(EventHandlerMiddleware(.activityPubStatus)) + .grouped(CacheControlMiddleware(.noStore)) .get(":id", use: status) } } diff --git a/Sources/VernissageServer/Controllers/ActivityPubSharedController.swift b/Sources/VernissageServer/Controllers/ActivityPubSharedController.swift index 01018302..b2b15b5c 100644 --- a/Sources/VernissageServer/Controllers/ActivityPubSharedController.swift +++ b/Sources/VernissageServer/Controllers/ActivityPubSharedController.swift @@ -17,6 +17,7 @@ extension ActivityPubSharedController: RouteCollection { activityPubSharedGroup .grouped(EventHandlerMiddleware(.activityPubSharedInbox)) + .grouped(CacheControlMiddleware(.noStore)) .post("inbox", use: inbox) } } diff --git a/Sources/VernissageServer/Controllers/AttachmentsController.swift b/Sources/VernissageServer/Controllers/AttachmentsController.swift index 5e21eadf..9fc3088f 100644 --- a/Sources/VernissageServer/Controllers/AttachmentsController.swift +++ b/Sources/VernissageServer/Controllers/AttachmentsController.swift @@ -23,34 +23,41 @@ extension AttachmentsController: RouteCollection { photosGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.attachmentsCreate)) + .grouped(CacheControlMiddleware(.noStore)) .on(.POST, AttachmentsController.uri, body: .collect(maxSize: "20mb"), use: upload) photosGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.attachmentsHdrCreate)) + .grouped(CacheControlMiddleware(.noStore)) .on(.POST, AttachmentsController.uri, ":id", "hdr", body: .collect(maxSize: "20mb"), use: uploadHdr) photosGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.attachmentsHdrDelete)) + .grouped(CacheControlMiddleware(.noStore)) .on(.DELETE, AttachmentsController.uri, ":id", "hdr", use: deleteHdr) photosGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.attachmentsUpdate)) + .grouped(CacheControlMiddleware(.noStore)) .put(AttachmentsController.uri, ":id", use: update) photosGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.attachmentsDelete)) + .grouped(CacheControlMiddleware(.noStore)) .delete(AttachmentsController.uri, ":id", use: delete) photosGroup .grouped(EventHandlerMiddleware(.attachmentsDescribe)) + .grouped(CacheControlMiddleware(.noStore)) .get(AttachmentsController.uri, ":id", "describe", use: describe) photosGroup .grouped(EventHandlerMiddleware(.attachmentsHashtags)) + .grouped(CacheControlMiddleware(.noStore)) .get(AttachmentsController.uri, ":id", "hashtags", use: hashtags) } } diff --git a/Sources/VernissageServer/Controllers/AuthenticationClientsController.swift b/Sources/VernissageServer/Controllers/AuthenticationClientsController.swift index 24fc9d47..4248f797 100644 --- a/Sources/VernissageServer/Controllers/AuthenticationClientsController.swift +++ b/Sources/VernissageServer/Controllers/AuthenticationClientsController.swift @@ -23,14 +23,17 @@ extension AuthenticationClientsController: RouteCollection { .grouped(UserPayload.guardIsAdministratorMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.authClientsCreate)) + .grouped(CacheControlMiddleware(.noStore)) .post(use: create) authClientsGroup .grouped(EventHandlerMiddleware(.authClientsList)) + .grouped(CacheControlMiddleware(.noStore)) .get(use: list) authClientsGroup .grouped(EventHandlerMiddleware(.authClientsRead)) + .grouped(CacheControlMiddleware(.noStore)) .get(":id", use: read) authClientsGroup @@ -39,6 +42,7 @@ extension AuthenticationClientsController: RouteCollection { .grouped(UserPayload.guardIsAdministratorMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.authClientsUpdate)) + .grouped(CacheControlMiddleware(.noStore)) .put(":id", use: update) authClientsGroup @@ -47,6 +51,7 @@ extension AuthenticationClientsController: RouteCollection { .grouped(UserPayload.guardIsAdministratorMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.authClientsDelete)) + .grouped(CacheControlMiddleware(.noStore)) .delete(":id", use: delete) } } diff --git a/Sources/VernissageServer/Controllers/AvatarsController.swift b/Sources/VernissageServer/Controllers/AvatarsController.swift index d6fb334c..8d0e7e0e 100644 --- a/Sources/VernissageServer/Controllers/AvatarsController.swift +++ b/Sources/VernissageServer/Controllers/AvatarsController.swift @@ -23,11 +23,13 @@ extension AvatarsController: RouteCollection { usersGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.avatarUpdate)) + .grouped(CacheControlMiddleware(.noStore)) .on(.POST, ":name", body: .collect(maxSize: "2mb"), use: update) usersGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.avatarDelete)) + .grouped(CacheControlMiddleware(.noStore)) .delete(":name", use: delete) } } diff --git a/Sources/VernissageServer/Controllers/BookmarksController.swift b/Sources/VernissageServer/Controllers/BookmarksController.swift index 79204159..f24294f1 100644 --- a/Sources/VernissageServer/Controllers/BookmarksController.swift +++ b/Sources/VernissageServer/Controllers/BookmarksController.swift @@ -23,6 +23,7 @@ extension BookmarksController: RouteCollection { timelinesGroup .grouped(EventHandlerMiddleware(.bookmarksList)) + .grouped(CacheControlMiddleware(.noStore)) .get(use: list) } } diff --git a/Sources/VernissageServer/Controllers/CategoriesController.swift b/Sources/VernissageServer/Controllers/CategoriesController.swift index d5756d42..2dd6526a 100644 --- a/Sources/VernissageServer/Controllers/CategoriesController.swift +++ b/Sources/VernissageServer/Controllers/CategoriesController.swift @@ -22,6 +22,7 @@ extension CategoriesController: RouteCollection { locationsGroup .grouped(EventHandlerMiddleware(.categoriesList)) + .grouped(CacheControlMiddleware(.public())) .get(use: list) } } diff --git a/Sources/VernissageServer/Controllers/CountriesController.swift b/Sources/VernissageServer/Controllers/CountriesController.swift index 27a01af5..704d2ce4 100644 --- a/Sources/VernissageServer/Controllers/CountriesController.swift +++ b/Sources/VernissageServer/Controllers/CountriesController.swift @@ -23,7 +23,7 @@ extension CountriesController: RouteCollection { locationsGroup .grouped(EventHandlerMiddleware(.countriesList)) - .grouped(CacheControlMiddleware()) + .grouped(CacheControlMiddleware(.public())) .get(use: list) } } diff --git a/Sources/VernissageServer/Controllers/ErrorItemsController.swift b/Sources/VernissageServer/Controllers/ErrorItemsController.swift index 373efbf2..ad5ecd92 100644 --- a/Sources/VernissageServer/Controllers/ErrorItemsController.swift +++ b/Sources/VernissageServer/Controllers/ErrorItemsController.swift @@ -24,17 +24,20 @@ extension ErrorItemsController: RouteCollection { errorItemsGroup .grouped(UserPayload.guardIsModeratorMiddleware()) .grouped(EventHandlerMiddleware(.errorList)) + .grouped(CacheControlMiddleware(.noStore)) .get(use: list) errorItemsGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.errorCreate)) + .grouped(CacheControlMiddleware(.noStore)) .post(use: create) errorItemsGroup .grouped(UserPayload.guardIsModeratorMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.errorDelete)) + .grouped(CacheControlMiddleware(.noStore)) .delete(":id", use: delete) } } diff --git a/Sources/VernissageServer/Controllers/FavouritesController.swift b/Sources/VernissageServer/Controllers/FavouritesController.swift index fbf74789..80fbc285 100644 --- a/Sources/VernissageServer/Controllers/FavouritesController.swift +++ b/Sources/VernissageServer/Controllers/FavouritesController.swift @@ -23,6 +23,7 @@ extension FavouritesController: RouteCollection { timelinesGroup .grouped(EventHandlerMiddleware(.favouritesList)) + .grouped(CacheControlMiddleware(.noStore)) .get(use: list) } } diff --git a/Sources/VernissageServer/Controllers/FollowRequestsController.swift b/Sources/VernissageServer/Controllers/FollowRequestsController.swift index 0a1f98de..995a4bbe 100644 --- a/Sources/VernissageServer/Controllers/FollowRequestsController.swift +++ b/Sources/VernissageServer/Controllers/FollowRequestsController.swift @@ -23,16 +23,19 @@ extension FollowRequestsController: RouteCollection { relationshipsGroup .grouped(EventHandlerMiddleware(.followRequestList)) + .grouped(CacheControlMiddleware(.noStore)) .get(use: list) relationshipsGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.followRequestApprove)) + .grouped(CacheControlMiddleware(.noStore)) .post(":id", "approve", use: approve) relationshipsGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.followRequestReject)) + .grouped(CacheControlMiddleware(.noStore)) .post(":id", "reject", use: reject) } } diff --git a/Sources/VernissageServer/Controllers/HeadersController.swift b/Sources/VernissageServer/Controllers/HeadersController.swift index de0052d4..d36f7e85 100644 --- a/Sources/VernissageServer/Controllers/HeadersController.swift +++ b/Sources/VernissageServer/Controllers/HeadersController.swift @@ -23,11 +23,13 @@ extension HeadersController: RouteCollection { usersGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.headerUpdate)) + .grouped(CacheControlMiddleware(.noStore)) .on(.POST, ":name", body: .collect(maxSize: "2mb"), use: update) usersGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.headerDelete)) + .grouped(CacheControlMiddleware(.noStore)) .delete(":name", use: delete) } } diff --git a/Sources/VernissageServer/Controllers/HealthController.swift b/Sources/VernissageServer/Controllers/HealthController.swift index 0ea6543d..5a4029ef 100644 --- a/Sources/VernissageServer/Controllers/HealthController.swift +++ b/Sources/VernissageServer/Controllers/HealthController.swift @@ -23,6 +23,7 @@ extension HealthController: RouteCollection { locationsGroup .grouped(EventHandlerMiddleware(.healthRead)) + .grouped(CacheControlMiddleware(.noStore)) .get(use: read) } } diff --git a/Sources/VernissageServer/Controllers/IdentityController.swift b/Sources/VernissageServer/Controllers/IdentityController.swift index 09e1445c..1eb989d9 100644 --- a/Sources/VernissageServer/Controllers/IdentityController.swift +++ b/Sources/VernissageServer/Controllers/IdentityController.swift @@ -20,12 +20,15 @@ extension IdentityController: RouteCollection { .grouped(IdentityController.uri) identityGroup + .grouped(CacheControlMiddleware(.noStore)) .get("authenticate", ":uri", use: authenticate) identityGroup + .grouped(CacheControlMiddleware(.noStore)) .get("callback", ":uri", use: callback) identityGroup + .grouped(CacheControlMiddleware(.noStore)) .post("login", use: login) } } diff --git a/Sources/VernissageServer/Controllers/InstanceBlockedDomainsController.swift b/Sources/VernissageServer/Controllers/InstanceBlockedDomainsController.swift index 1e70b4f1..0ffcf2be 100644 --- a/Sources/VernissageServer/Controllers/InstanceBlockedDomainsController.swift +++ b/Sources/VernissageServer/Controllers/InstanceBlockedDomainsController.swift @@ -23,21 +23,25 @@ extension InstanceBlockedDomainsController: RouteCollection { domainsGroup .grouped(EventHandlerMiddleware(.instanceBlockedDomainsList)) + .grouped(CacheControlMiddleware(.noStore)) .get(use: list) domainsGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.instanceBlockedDomainsCreate)) + .grouped(CacheControlMiddleware(.noStore)) .post(use: create) domainsGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.instanceBlockedDomainsUpdate)) + .grouped(CacheControlMiddleware(.noStore)) .put(":id", use: update) domainsGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.instanceBlockedDomainsDelete)) + .grouped(CacheControlMiddleware(.noStore)) .delete(":id", use: delete) } } diff --git a/Sources/VernissageServer/Controllers/InstanceController.swift b/Sources/VernissageServer/Controllers/InstanceController.swift index 1d01f856..63e0ceba 100644 --- a/Sources/VernissageServer/Controllers/InstanceController.swift +++ b/Sources/VernissageServer/Controllers/InstanceController.swift @@ -21,7 +21,7 @@ extension InstanceController: RouteCollection { instanceGroup .grouped(EventHandlerMiddleware(.instance)) - .grouped(CacheControlMiddleware()) + .grouped(CacheControlMiddleware(.public())) .get(use: instance) } } diff --git a/Sources/VernissageServer/Controllers/InvitationsController.swift b/Sources/VernissageServer/Controllers/InvitationsController.swift index 10ed5d27..0b887486 100644 --- a/Sources/VernissageServer/Controllers/InvitationsController.swift +++ b/Sources/VernissageServer/Controllers/InvitationsController.swift @@ -22,16 +22,19 @@ extension InvitationsController: RouteCollection { invitationsGroup .grouped(EventHandlerMiddleware(.invitationList)) + .grouped(CacheControlMiddleware(.noStore)) .get(use: list) invitationsGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.invitationGenerate)) + .grouped(CacheControlMiddleware(.noStore)) .post("generate", use: generate) invitationsGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.invitationDelete)) + .grouped(CacheControlMiddleware(.noStore)) .delete(":id", use: delete) } } diff --git a/Sources/VernissageServer/Controllers/LicensesController.swift b/Sources/VernissageServer/Controllers/LicensesController.swift index d765840a..a15bdfe8 100644 --- a/Sources/VernissageServer/Controllers/LicensesController.swift +++ b/Sources/VernissageServer/Controllers/LicensesController.swift @@ -23,6 +23,7 @@ extension LicensesController: RouteCollection { locationsGroup .grouped(EventHandlerMiddleware(.licensesList)) + .grouped(CacheControlMiddleware(.public())) .get(use: list) } } diff --git a/Sources/VernissageServer/Controllers/LocationsController.swift b/Sources/VernissageServer/Controllers/LocationsController.swift index c726fca1..c969a2e0 100644 --- a/Sources/VernissageServer/Controllers/LocationsController.swift +++ b/Sources/VernissageServer/Controllers/LocationsController.swift @@ -23,10 +23,12 @@ extension LocationsController: RouteCollection { locationsGroup .grouped(EventHandlerMiddleware(.locationsList)) + .grouped(CacheControlMiddleware(.noStore)) .get(use: search) locationsGroup .grouped(EventHandlerMiddleware(.locationsRead)) + .grouped(CacheControlMiddleware(.private())) .get(":id", use: read) } } diff --git a/Sources/VernissageServer/Controllers/NodeInfoController.swift b/Sources/VernissageServer/Controllers/NodeInfoController.swift index 90761c61..acf22786 100644 --- a/Sources/VernissageServer/Controllers/NodeInfoController.swift +++ b/Sources/VernissageServer/Controllers/NodeInfoController.swift @@ -20,7 +20,7 @@ extension NodeInfoController: RouteCollection { webfingerGroup .grouped(EventHandlerMiddleware(.webfinger)) - .grouped(CacheControlMiddleware()) + .grouped(CacheControlMiddleware(.public())) .get("2.0", use: nodeinfo2) } } diff --git a/Sources/VernissageServer/Controllers/NotificationsController.swift b/Sources/VernissageServer/Controllers/NotificationsController.swift index 9b7b5ac1..57358097 100644 --- a/Sources/VernissageServer/Controllers/NotificationsController.swift +++ b/Sources/VernissageServer/Controllers/NotificationsController.swift @@ -23,15 +23,18 @@ extension NotificationsController: RouteCollection { notificationsGroup .grouped(EventHandlerMiddleware(.notificationsList)) + .grouped(CacheControlMiddleware(.noStore)) .get(use: list) notificationsGroup .grouped(EventHandlerMiddleware(.notificationsCount)) + .grouped(CacheControlMiddleware(.noStore)) .get("count", use: count) notificationsGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.notificationsCount)) + .grouped(CacheControlMiddleware(.noStore)) .post("marker", ":id", use: marker) } } diff --git a/Sources/VernissageServer/Controllers/ProfileController.swift b/Sources/VernissageServer/Controllers/ProfileController.swift index d526bda3..35cb7e21 100644 --- a/Sources/VernissageServer/Controllers/ProfileController.swift +++ b/Sources/VernissageServer/Controllers/ProfileController.swift @@ -15,6 +15,7 @@ extension ProfileController: RouteCollection { routes .grouped(UserAuthenticator()) .grouped(EventHandlerMiddleware(.usersRead)) + .grouped(CacheControlMiddleware(.noStore)) .get(":name", use: read) } } diff --git a/Sources/VernissageServer/Controllers/PushSubscriptionsController.swift b/Sources/VernissageServer/Controllers/PushSubscriptionsController.swift index 9e075115..5d7b9f3a 100644 --- a/Sources/VernissageServer/Controllers/PushSubscriptionsController.swift +++ b/Sources/VernissageServer/Controllers/PushSubscriptionsController.swift @@ -22,21 +22,25 @@ extension PushSubscriptionsController: RouteCollection { domainsGroup .grouped(EventHandlerMiddleware(.pushSubscriptionsList)) + .grouped(CacheControlMiddleware(.noStore)) .get(use: list) domainsGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.pushSubscriptionsCreate)) + .grouped(CacheControlMiddleware(.noStore)) .post(use: create) domainsGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.pushSubscriptionsUpdate)) + .grouped(CacheControlMiddleware(.noStore)) .put(":id", use: update) domainsGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.pushSubscriptionsDelete)) + .grouped(CacheControlMiddleware(.noStore)) .delete(":id", use: delete) } } diff --git a/Sources/VernissageServer/Controllers/RegisterController.swift b/Sources/VernissageServer/Controllers/RegisterController.swift index 483e0112..5ed07d88 100644 --- a/Sources/VernissageServer/Controllers/RegisterController.swift +++ b/Sources/VernissageServer/Controllers/RegisterController.swift @@ -20,14 +20,17 @@ extension RegisterController: RouteCollection { registerGroup .grouped(EventHandlerMiddleware(.registerNewUser, storeRequest: false)) + .grouped(CacheControlMiddleware(.noStore)) .post(use: newUser) registerGroup .grouped(EventHandlerMiddleware(.registerUserName)) + .grouped(CacheControlMiddleware(.noStore)) .get("username", ":name", use: isUserNameTaken) registerGroup .grouped(EventHandlerMiddleware(.registerEmail)) + .grouped(CacheControlMiddleware(.noStore)) .get("email", ":email", use: isEmailConnected) } } diff --git a/Sources/VernissageServer/Controllers/RelationshipsController.swift b/Sources/VernissageServer/Controllers/RelationshipsController.swift index 08433b87..5e506d99 100644 --- a/Sources/VernissageServer/Controllers/RelationshipsController.swift +++ b/Sources/VernissageServer/Controllers/RelationshipsController.swift @@ -23,6 +23,7 @@ extension RelationshipsController: RouteCollection { relationshipsGroup .grouped(EventHandlerMiddleware(.relationships)) + .grouped(CacheControlMiddleware(.noStore)) .get(use: relationships) } } diff --git a/Sources/VernissageServer/Controllers/ReportsController.swift b/Sources/VernissageServer/Controllers/ReportsController.swift index dc06348b..51f3a92e 100644 --- a/Sources/VernissageServer/Controllers/ReportsController.swift +++ b/Sources/VernissageServer/Controllers/ReportsController.swift @@ -24,23 +24,27 @@ extension ReportsController: RouteCollection { reportsGroup .grouped(UserPayload.guardIsModeratorMiddleware()) .grouped(EventHandlerMiddleware(.reportsList)) + .grouped(CacheControlMiddleware(.noStore)) .get(use: list) reportsGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.reportsCreate)) + .grouped(CacheControlMiddleware(.noStore)) .post(use: create) reportsGroup .grouped(UserPayload.guardIsModeratorMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.reportsClose)) + .grouped(CacheControlMiddleware(.noStore)) .post(":id", "close", use: close) reportsGroup .grouped(UserPayload.guardIsModeratorMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.reportsRestore)) + .grouped(CacheControlMiddleware(.noStore)) .post(":id", "restore", use: restore) } } diff --git a/Sources/VernissageServer/Controllers/RolesController.swift b/Sources/VernissageServer/Controllers/RolesController.swift index 2b8c92d0..3a6ff20e 100644 --- a/Sources/VernissageServer/Controllers/RolesController.swift +++ b/Sources/VernissageServer/Controllers/RolesController.swift @@ -22,15 +22,18 @@ extension RolesController: RouteCollection { rolesGroup .grouped(EventHandlerMiddleware(.rolesList)) + .grouped(CacheControlMiddleware(.noStore)) .get(use: list) rolesGroup .grouped(EventHandlerMiddleware(.rolesRead)) + .grouped(CacheControlMiddleware(.noStore)) .get(":id", use: read) rolesGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.rolesUpdate)) + .grouped(CacheControlMiddleware(.noStore)) .put(":id", use: update) } } diff --git a/Sources/VernissageServer/Controllers/RulesController.swift b/Sources/VernissageServer/Controllers/RulesController.swift index ff6440f3..d6d39d4e 100644 --- a/Sources/VernissageServer/Controllers/RulesController.swift +++ b/Sources/VernissageServer/Controllers/RulesController.swift @@ -23,21 +23,25 @@ extension RulesController: RouteCollection { rulesGroup .grouped(EventHandlerMiddleware(.rulesList)) + .grouped(CacheControlMiddleware(.public())) .get(use: list) rulesGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.rulesCreate)) + .grouped(CacheControlMiddleware(.noStore)) .post(use: create) rulesGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.rulesUpdate)) + .grouped(CacheControlMiddleware(.noStore)) .put(":id", use: update) rulesGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.rulesDelete)) + .grouped(CacheControlMiddleware(.noStore)) .delete(":id", use: delete) } } diff --git a/Sources/VernissageServer/Controllers/SearchController.swift b/Sources/VernissageServer/Controllers/SearchController.swift index 05a4d509..de069a05 100644 --- a/Sources/VernissageServer/Controllers/SearchController.swift +++ b/Sources/VernissageServer/Controllers/SearchController.swift @@ -22,6 +22,7 @@ extension SearchController: RouteCollection { searchGroup .grouped(EventHandlerMiddleware(.search)) + .grouped(CacheControlMiddleware(.noStore)) .get(use: search) } } diff --git a/Sources/VernissageServer/Controllers/SettingsController.swift b/Sources/VernissageServer/Controllers/SettingsController.swift index 547be595..0d666100 100644 --- a/Sources/VernissageServer/Controllers/SettingsController.swift +++ b/Sources/VernissageServer/Controllers/SettingsController.swift @@ -23,11 +23,12 @@ extension SettingsController: RouteCollection { .grouped(UserPayload.guardMiddleware()) .grouped(UserPayload.guardIsAdministratorMiddleware()) .grouped(EventHandlerMiddleware(.settingsList)) + .grouped(CacheControlMiddleware(.noStore)) .get(use: settings) rolesGroup .grouped(EventHandlerMiddleware(.settingsList)) - .grouped(CacheControlMiddleware()) + .grouped(CacheControlMiddleware(.public())) .get("public", use: publicSettings) rolesGroup @@ -36,6 +37,7 @@ extension SettingsController: RouteCollection { .grouped(UserPayload.guardIsAdministratorMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.settingsUpdate)) + .grouped(CacheControlMiddleware(.noStore)) .put(use: update) } } diff --git a/Sources/VernissageServer/Controllers/StatusesController.swift b/Sources/VernissageServer/Controllers/StatusesController.swift index 475a05b9..163c25cf 100644 --- a/Sources/VernissageServer/Controllers/StatusesController.swift +++ b/Sources/VernissageServer/Controllers/StatusesController.swift @@ -24,20 +24,24 @@ extension StatusesController: RouteCollection { .grouped(UserPayload.guardMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.statusesCreate)) + .grouped(CacheControlMiddleware(.noStore)) .post(use: create) statusesGroup .grouped(EventHandlerMiddleware(.statusesList)) + .grouped(CacheControlMiddleware(.noStore)) .get(use: list) statusesGroup .grouped(EventHandlerMiddleware(.statusesRead)) + .grouped(CacheControlMiddleware(.noStore)) .get(":id", use: read) statusesGroup .grouped(UserPayload.guardMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.statusesDelete)) + .grouped(CacheControlMiddleware(.noStore)) .delete(":id", use: delete) statusesGroup @@ -45,6 +49,7 @@ extension StatusesController: RouteCollection { .grouped(UserPayload.guardIsModeratorMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.statusesUnlist)) + .grouped(CacheControlMiddleware(.noStore)) .post(":id", "unlist", use: unlist) statusesGroup @@ -52,56 +57,66 @@ extension StatusesController: RouteCollection { .grouped(UserPayload.guardIsModeratorMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.statusesApplyContentWarning)) + .grouped(CacheControlMiddleware(.noStore)) .post(":id", "apply-content-warning", use: applyContentWarning) statusesGroup .grouped(EventHandlerMiddleware(.statusesContext)) + .grouped(CacheControlMiddleware(.noStore)) .get(":id", "context", use: context) statusesGroup .grouped(UserPayload.guardMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.statusesReblog)) + .grouped(CacheControlMiddleware(.noStore)) .post(":id", "reblog", use: reblog) statusesGroup .grouped(UserPayload.guardMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.statusesUnreblog)) + .grouped(CacheControlMiddleware(.noStore)) .post(":id", "unreblog", use: unreblog) statusesGroup .grouped(UserPayload.guardMiddleware()) .grouped(EventHandlerMiddleware(.statusesReblogged)) + .grouped(CacheControlMiddleware(.noStore)) .get(":id", "reblogged", use: reblogged) statusesGroup .grouped(UserPayload.guardMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.statusesFavourite)) + .grouped(CacheControlMiddleware(.noStore)) .post(":id", "favourite", use: favourite) statusesGroup .grouped(UserPayload.guardMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.statusesUnfavourite)) + .grouped(CacheControlMiddleware(.noStore)) .post(":id", "unfavourite", use: unfavourite) statusesGroup .grouped(UserPayload.guardMiddleware()) .grouped(EventHandlerMiddleware(.statusesFavourited)) + .grouped(CacheControlMiddleware(.noStore)) .get(":id", "favourited", use: favourited) statusesGroup .grouped(UserPayload.guardMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.statusesBookmark)) + .grouped(CacheControlMiddleware(.noStore)) .post(":id", "bookmark", use: bookmark) statusesGroup .grouped(UserPayload.guardMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.statusesUnbookmark)) + .grouped(CacheControlMiddleware(.noStore)) .post(":id", "unbookmark", use: unbookmark) statusesGroup @@ -109,6 +124,7 @@ extension StatusesController: RouteCollection { .grouped(UserPayload.guardIsModeratorMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.statusesFeature)) + .grouped(CacheControlMiddleware(.noStore)) .post(":id", "feature", use: feature) statusesGroup @@ -116,6 +132,7 @@ extension StatusesController: RouteCollection { .grouped(UserPayload.guardIsModeratorMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.statusesUnfeature)) + .grouped(CacheControlMiddleware(.noStore)) .post(":id", "unfeature", use: unfeature) } } diff --git a/Sources/VernissageServer/Controllers/TimelinesController.swift b/Sources/VernissageServer/Controllers/TimelinesController.swift index 50b7ba66..a343f35a 100644 --- a/Sources/VernissageServer/Controllers/TimelinesController.swift +++ b/Sources/VernissageServer/Controllers/TimelinesController.swift @@ -22,27 +22,33 @@ extension TimelinesController: RouteCollection { timelinesGroup .grouped(EventHandlerMiddleware(.timelinesPublic)) + .grouped(CacheControlMiddleware(.noStore)) .get("public", use: list) timelinesGroup .grouped(EventHandlerMiddleware(.timelinesCategories)) + .grouped(CacheControlMiddleware(.noStore)) .get("category", ":category", use: category) timelinesGroup .grouped(EventHandlerMiddleware(.timelinesHashtags)) + .grouped(CacheControlMiddleware(.noStore)) .get("hashtag", ":hashtag", use: hashtag) timelinesGroup .grouped(EventHandlerMiddleware(.timelinesFeaturedStatuses)) + .grouped(CacheControlMiddleware(.noStore)) .get("featured-statuses", use: featuredStatuses) timelinesGroup .grouped(EventHandlerMiddleware(.timelinesFeaturedUsers)) + .grouped(CacheControlMiddleware(.noStore)) .get("featured-users", use: featuredUsers) timelinesGroup .grouped(UserPayload.guardMiddleware()) .grouped(EventHandlerMiddleware(.timelinesPublic)) + .grouped(CacheControlMiddleware(.noStore)) .get("home", use: home) } } diff --git a/Sources/VernissageServer/Controllers/TrendingController.swift b/Sources/VernissageServer/Controllers/TrendingController.swift index 0107f4fe..2c6e1506 100644 --- a/Sources/VernissageServer/Controllers/TrendingController.swift +++ b/Sources/VernissageServer/Controllers/TrendingController.swift @@ -22,14 +22,17 @@ extension TrendingController: RouteCollection { timelinesGroup .grouped(EventHandlerMiddleware(.trendingStatuses)) + .grouped(CacheControlMiddleware(.noStore)) .get("statuses", use: statuses) timelinesGroup .grouped(EventHandlerMiddleware(.trendingUsers)) + .grouped(CacheControlMiddleware(.noStore)) .get("users", use: users) timelinesGroup .grouped(EventHandlerMiddleware(.trendingHashtags)) + .grouped(CacheControlMiddleware(.noStore)) .get("hashtags", use: hashtags) } } diff --git a/Sources/VernissageServer/Controllers/UserAliasesController.swift b/Sources/VernissageServer/Controllers/UserAliasesController.swift index e21c7f45..6da1b857 100644 --- a/Sources/VernissageServer/Controllers/UserAliasesController.swift +++ b/Sources/VernissageServer/Controllers/UserAliasesController.swift @@ -23,16 +23,19 @@ extension UserAliasesController: RouteCollection { userAliasesGroup .grouped(EventHandlerMiddleware(.userAliasesList)) + .grouped(CacheControlMiddleware(.noStore)) .get(use: list) userAliasesGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.userAliasesCreate)) + .grouped(CacheControlMiddleware(.noStore)) .post(use: create) userAliasesGroup .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.userAliasesDelete)) + .grouped(CacheControlMiddleware(.noStore)) .delete(":id", use: delete) } } diff --git a/Sources/VernissageServer/Controllers/UsersController.swift b/Sources/VernissageServer/Controllers/UsersController.swift index 0d5ca439..351bc761 100644 --- a/Sources/VernissageServer/Controllers/UsersController.swift +++ b/Sources/VernissageServer/Controllers/UsersController.swift @@ -24,54 +24,64 @@ extension UsersController: RouteCollection { .grouped(UserPayload.guardMiddleware()) .grouped(UserPayload.guardIsModeratorMiddleware()) .grouped(EventHandlerMiddleware(.usersList)) + .grouped(CacheControlMiddleware(.noStore)) .get(use: list) usersGroup .grouped(EventHandlerMiddleware(.usersRead)) + .grouped(CacheControlMiddleware(.noStore)) .get(":name", use: read) usersGroup .grouped(UserPayload.guardMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.usersUpdate)) + .grouped(CacheControlMiddleware(.noStore)) .put(":name", use: update) usersGroup .grouped(UserPayload.guardMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.usersDelete)) + .grouped(CacheControlMiddleware(.noStore)) .delete(":name", use: delete) usersGroup .grouped(UserPayload.guardMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.usersFollow)) + .grouped(CacheControlMiddleware(.noStore)) .post(":name", "follow", use: follow) usersGroup .grouped(UserPayload.guardMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.usersUnfollow)) + .grouped(CacheControlMiddleware(.noStore)) .post(":name", "unfollow", use: unfollow) usersGroup .grouped(EventHandlerMiddleware(.usersFollowers)) + .grouped(CacheControlMiddleware(.noStore)) .get(":name", "followers", use: followers) usersGroup .grouped(EventHandlerMiddleware(.usersFollowing)) + .grouped(CacheControlMiddleware(.noStore)) .get(":name", "following", use: following) usersGroup .grouped(UserPayload.guardMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.usersMute)) + .grouped(CacheControlMiddleware(.noStore)) .post(":name", "mute", use: mute) usersGroup .grouped(UserPayload.guardMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.usersUnmute)) + .grouped(CacheControlMiddleware(.noStore)) .post(":name", "unmute", use: unmute) usersGroup @@ -79,6 +89,7 @@ extension UsersController: RouteCollection { .grouped(UserPayload.guardIsModeratorMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.usersEnable)) + .grouped(CacheControlMiddleware(.noStore)) .post(":name", "enable", use: enable) usersGroup @@ -86,6 +97,7 @@ extension UsersController: RouteCollection { .grouped(UserPayload.guardIsModeratorMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.usersDisable)) + .grouped(CacheControlMiddleware(.noStore)) .post(":name", "disable", use: disable) usersGroup @@ -93,6 +105,7 @@ extension UsersController: RouteCollection { .grouped(UserPayload.guardIsAdministratorMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.userRolesConnect)) + .grouped(CacheControlMiddleware(.noStore)) .post(":name", "connect", ":role", use: connect) usersGroup @@ -100,6 +113,7 @@ extension UsersController: RouteCollection { .grouped(UserPayload.guardIsAdministratorMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.userRolesDisconnect)) + .grouped(CacheControlMiddleware(.noStore)) .post(":name", "disconnect", ":role", use: disconnect) usersGroup @@ -107,6 +121,7 @@ extension UsersController: RouteCollection { .grouped(UserPayload.guardIsModeratorMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.userApprove)) + .grouped(CacheControlMiddleware(.noStore)) .post(":name", "approve", use: approve) usersGroup @@ -114,6 +129,7 @@ extension UsersController: RouteCollection { .grouped(UserPayload.guardIsModeratorMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.userApprove)) + .grouped(CacheControlMiddleware(.noStore)) .post(":name", "reject", use: reject) usersGroup @@ -121,6 +137,7 @@ extension UsersController: RouteCollection { .grouped(UserPayload.guardIsModeratorMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.userFeature)) + .grouped(CacheControlMiddleware(.noStore)) .post(":name", "feature", use: feature) usersGroup @@ -128,6 +145,7 @@ extension UsersController: RouteCollection { .grouped(UserPayload.guardIsModeratorMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.userUnfeature)) + .grouped(CacheControlMiddleware(.noStore)) .post(":name", "unfeature", use: unfeature) usersGroup @@ -135,10 +153,12 @@ extension UsersController: RouteCollection { .grouped(UserPayload.guardIsModeratorMiddleware()) .grouped(XsrfTokenValidatorMiddleware()) .grouped(EventHandlerMiddleware(.userApprove)) + .grouped(CacheControlMiddleware(.noStore)) .post(":name", "refresh", use: refresh) usersGroup .grouped(EventHandlerMiddleware(.usersStatuses)) + .grouped(CacheControlMiddleware(.noStore)) .get(":name", "statuses", use: statuses) } } diff --git a/Sources/VernissageServer/Controllers/WellKnownController.swift b/Sources/VernissageServer/Controllers/WellKnownController.swift index cc87ba71..278c583b 100644 --- a/Sources/VernissageServer/Controllers/WellKnownController.swift +++ b/Sources/VernissageServer/Controllers/WellKnownController.swift @@ -17,17 +17,17 @@ extension WellKnownController: RouteCollection { wellKnownGroup .grouped(EventHandlerMiddleware(.webfinger)) - .grouped(CacheControlMiddleware()) + .grouped(CacheControlMiddleware(.public())) .get("webfinger", use: webfinger) wellKnownGroup .grouped(EventHandlerMiddleware(.nodeinfo)) - .grouped(CacheControlMiddleware()) + .grouped(CacheControlMiddleware(.public())) .get("nodeinfo", use: nodeinfo) wellKnownGroup .grouped(EventHandlerMiddleware(.hostMeta)) - .grouped(CacheControlMiddleware()) + .grouped(CacheControlMiddleware(.public())) .get("host-meta", use: hostMeta) } } diff --git a/Sources/VernissageServer/ResponseHeaders/CacheControl.swift b/Sources/VernissageServer/ResponseHeaders/CacheControl.swift new file mode 100644 index 00000000..1ea5711d --- /dev/null +++ b/Sources/VernissageServer/ResponseHeaders/CacheControl.swift @@ -0,0 +1,12 @@ +// +// https://mczachurski.dev +// Copyright © 2024 Marcin Czachurski and the repository contributors. +// Licensed under the Apache License 2.0. +// + +@_documentation(visibility: private) +public enum CacheControl: Sendable { + case `public`(maxAge: Int = 3600) + case `private`(maxAge: Int = 3600) + case noStore +} diff --git a/Sources/VernissageServer/ResponseHeaders/CacheControlMiddleware.swift b/Sources/VernissageServer/ResponseHeaders/CacheControlMiddleware.swift new file mode 100644 index 00000000..86a22d41 --- /dev/null +++ b/Sources/VernissageServer/ResponseHeaders/CacheControlMiddleware.swift @@ -0,0 +1,31 @@ +// +// https://mczachurski.dev +// Copyright © 2024 Marcin Czachurski and the repository contributors. +// Licensed under the Apache License 2.0. +// + +import Vapor + +/// Middleware which adds to response headers `Cache-Control` header set to one hour. +struct CacheControlMiddleware: AsyncMiddleware { + private let cacheControl: CacheControl + + init(_ cacheControl: CacheControl) { + self.cacheControl = cacheControl + } + + func respond(to request: Request, chainingTo next: AsyncResponder) async throws -> Response { + let response = try await next.respond(to: request) + + switch self.cacheControl { + case .public(let maxAge): + response.headers.cacheControl = .init(isPublic: true, maxAge: maxAge) + case .private(let maxAge): + response.headers.cacheControl = .init(isPrivate: true, maxAge: maxAge) + case .noStore: + response.headers.cacheControl = .init(noStore: true, isPrivate: true, maxAge: 0) + } + + return response + } +} diff --git a/Sources/VernissageServer/ResponseHeaders/CommonHeadersMiddleware.swift b/Sources/VernissageServer/ResponseHeaders/CommonHeadersMiddleware.swift new file mode 100644 index 00000000..6335c9e7 --- /dev/null +++ b/Sources/VernissageServer/ResponseHeaders/CommonHeadersMiddleware.swift @@ -0,0 +1,22 @@ +// +// https://mczachurski.dev +// Copyright © 2024 Marcin Czachurski and the repository contributors. +// Licensed under the Apache License 2.0. +// + +import Vapor + +/// Middleware which adds to response common headers such as `Referrer-Policy`, `Content-Security-Policy` etc. +struct CommonHeadersMiddleware: AsyncMiddleware { + + func respond(to request: Request, chainingTo next: AsyncResponder) async throws -> Response { + let response = try await next.respond(to: request) + + response.headers.replaceOrAdd(name: "Referrer-Policy", value: "same-origin") + response.headers.replaceOrAdd(name: "Content-Security-Policy", value: "default-src 'none'; frame-ancestors 'none'; form-action 'none'") + response.headers.replaceOrAdd(name: "X-Content-Type-Options", value: "nosniff") + response.headers.replaceOrAdd(name: "Strict-Transport-Security", value: "max-age=31557600") + + return response + } +}