Skip to content
This repository was archived by the owner on Apr 20, 2024. It is now read-only.

Commit a9671f5

Browse files
Merge pull request #20 from madsodgaard/vapor4
Vapor 4 support + custom cache keys
2 parents 6892a52 + d201463 commit a9671f5

22 files changed

+446
-412
lines changed

.circleci/config.yml

Lines changed: 0 additions & 67 deletions
This file was deleted.

.codebeatignore

Lines changed: 0 additions & 2 deletions
This file was deleted.

.github/workflows/test.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: test
2+
on:
3+
pull_request:
4+
push:
5+
branches:
6+
- master
7+
jobs:
8+
linux:
9+
runs-on: ubuntu-latest
10+
container: swift:5.3-focal
11+
steps:
12+
- name: Check out code
13+
uses: actions/checkout@v2
14+
- name: Run tests with Thread Sanitizer
15+
run: swift test --enable-test-discovery --sanitize=thread
16+
macOS:
17+
runs-on: macos-latest
18+
steps:
19+
- name: Select latest available Xcode
20+
uses: maxim-lobanov/setup-xcode@v1
21+
with:
22+
xcode-version: latest
23+
- name: Check out code
24+
uses: actions/checkout@v2
25+
- name: Run tests with Thread Sanitizer
26+
run: swift test --enable-test-discovery --sanitize=thread

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ Config/secrets/
77
.DS_Store
88
.swift-version
99
CMakeLists.txt
10-
Package.resolved
10+
Package.resolved
11+
.swiftpm

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2017-2019 Nodes
3+
Copyright (c) 2017-2021 Nodes
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

Package.swift

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
1-
// swift-tools-version:4.2
1+
// swift-tools-version:5.3
22
import PackageDescription
33

44
let package = Package(
5-
name: "Gatekeeper",
5+
name: "gatekeeper",
6+
platforms: [
7+
.macOS(.v10_15),
8+
],
69
products: [
710
.library(
811
name: "Gatekeeper",
912
targets: ["Gatekeeper"]),
1013
],
1114
dependencies: [
12-
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"),
15+
.package(url: "https://github.com/vapor/vapor.git", from: "4.38.0"),
1316
],
1417
targets: [
1518
.target(
1619
name: "Gatekeeper",
1720
dependencies: [
18-
"Vapor"
21+
.product(name: "Vapor", package: "vapor")
1922
]),
2023
.testTarget(
2124
name: "GatekeeperTests",
22-
dependencies: ["Gatekeeper"]),
25+
dependencies: [
26+
"Gatekeeper",
27+
.product(name: "XCTVapor", package: "vapor")
28+
]),
2329
]
2430
)

README.md

Lines changed: 64 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
# Gatekeeper 👮
2-
[![Swift Version](https://img.shields.io/badge/Swift-4.2-brightgreen.svg)](http://swift.org)
3-
[![Vapor Version](https://img.shields.io/badge/Vapor-3-30B6FC.svg)](http://vapor.codes)
4-
[![Circle CI](https://circleci.com/gh/nodes-vapor/gatekeeper/tree/master.svg?style=shield)](https://circleci.com/gh/nodes-vapor/gatekeeper)
5-
[![codebeat badge](https://codebeat.co/badges/35c7b0bb-1662-44ae-b953-ab1d4aaf231f)](https://codebeat.co/projects/github.colasdn.workers.dev-nodes-vapor-gatekeeper-master)
6-
[![codecov](https://codecov.io/gh/nodes-vapor/gatekeeper/branch/master/graph/badge.svg)](https://codecov.io/gh/nodes-vapor/gatekeeper)
7-
[![Readme Score](http://readme-score-api.herokuapp.com/score.svg?url=https://github.com/nodes-vapor/gatekeeper)](http://clayallsopp.github.io/readme-score?url=https://github.com/nodes-vapor/gatekeeper)
2+
[![Swift Version](https://img.shields.io/badge/Swift-5.3-brightgreen.svg)](http://swift.org)
3+
[![Vapor Version](https://img.shields.io/badge/Vapor-4-30B6FC.svg)](http://vapor.codes)
84
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nodes-vapor/gatekeeper/master/LICENSE)
95

10-
Gatekeeper is a middleware that restricts the number of requests from clients, based on their IP address.
11-
It works by adding the clients IP address to the cache and count how many requests the clients can make during the Gatekeeper's defined lifespan and give back an HTTP 429(Too Many Requests) if the limit has been reached. The number of requests left will be reset when the defined timespan has been reached.
6+
Gatekeeper is a middleware that restricts the number of requests from clients, based on their IP address **(can be customized)**.
7+
It works by adding the clients identifier to the cache and count how many requests the clients can make during the Gatekeeper's defined lifespan and give back an HTTP 429(Too Many Requests) if the limit has been reached. The number of requests left will be reset when the defined timespan has been reached.
128

139
**Please take into consideration that multiple clients can be using the same IP address. eg. public wifi**
1410

@@ -18,15 +14,15 @@ It works by adding the clients IP address to the cache and count how many reques
1814
Update your `Package.swift` dependencies:
1915

2016
```swift
21-
.package(url: "https://github.com/nodes-vapor/gatekeeper.git", from: "3.0.0"),
17+
.package(url: "https://github.com/nodes-vapor/gatekeeper.git", from: "4.0.0"),
2218
```
2319

2420
as well as to your target (e.g. "App"):
2521

2622
```swift
2723
targets: [
2824
.target(name: "App", dependencies: [..., "Gatekeeper", ...]),
29-
// ...
25+
// ...
3026
]
3127
```
3228

@@ -40,15 +36,8 @@ import Gatekeeper
4036

4137
// [...]
4238

43-
// Register providers first
44-
try services.register(
45-
GatekeeperProvider(
46-
config: GatekeeperConfig(maxRequests: 10, per: .second),
47-
cacheFactory: { container -> KeyedCache in
48-
return try container.make()
49-
}
50-
)
51-
)
39+
app.caches.use(.memory)
40+
app.gatekeeper.config = .init(maxRequests: 10, per: .second)
5241
```
5342

5443
### Add to routes
@@ -58,7 +47,7 @@ You can add the `GatekeeperMiddleware` to specific routes or to all.
5847
**Specific routes**
5948
in routes.swift:
6049
```swift
61-
let protectedRoutes = router.grouped(GatekeeperMiddleware.self)
50+
let protectedRoutes = router.grouped(GatekeeperMiddleware())
6251
protectedRoutes.get("protected/hello") { req in
6352
return "Protected Hello, World!"
6453
}
@@ -68,15 +57,67 @@ protectedRoutes.get("protected/hello") { req in
6857
in configure.swift:
6958
```swift
7059
// Register middleware
71-
var middlewares = MiddlewareConfig() // Create _empty_ middleware config
72-
middlewares.use(GatekeeperMiddleware.self)
73-
services.register(middlewares)
60+
app.middlewares.use(GatekeeperMiddleware())
61+
```
62+
63+
#### Customizing config
64+
By default `GatekeeperMiddleware` uses `app.gatekeeper.config` as its configuration.
65+
However, you can pass a custom configuration to each `GatekeeperMiddleware` type via the initializer
66+
`GatekeeperMiddleware(config:)`. This allows you to set configuration on a per-route basis.
67+
68+
## Key Makers 🔑
69+
By default Gatekeeper uses the client's hostname (IP address) to identify them. This can cause issues where multiple clients are connected from the same network. Therefore, you can customize how Gatekeeper should identify the client by using the `GatekeeperKeyMaker` protocol.
70+
71+
`GatekeeperHostnameKeyMaker` is used by default.
72+
73+
You can configure which key maker Gatekeeper should use in `configure.swift`:
74+
```swift
75+
app.gatekeeper.keyMakers.use(.hostname) // default
76+
```
77+
78+
### Custom key maker
79+
This is an example of a key maker that uses the user's ID to identify them.
80+
```swift
81+
struct UserIDKeyMaker: GatekeeperKeyMaker {
82+
public func make(for req: Request) -> EventLoopFuture<String> {
83+
let userID = try req.auth.require(User.self).requireID()
84+
return req.eventLoop.future("gatekeeper_" + userID.uuidString)
85+
}
86+
}
87+
```
88+
89+
```swift
90+
extension Application.Gatekeeper.KeyMakers.Provider {
91+
public static var userID: Self {
92+
.init { app in
93+
app.gatekeeper.keyMakers.use { _ in UserIDKeyMaker() }
94+
}
95+
}
96+
}
97+
```
98+
**configure.swift:**
99+
```swift
100+
app.gatekeeper.keyMakers.use(.userID)
74101
```
75102

103+
## Cache 🗄
104+
Gatekeeper uses the same cache as configured by `app.caches.use()` from Vapor, by default.
105+
Therefore it is **important** to set up Vapor's cache if you're using this default behaviour. You can use an in-memory cache for Vapor like so:
106+
107+
**configure.swift**:
108+
```swift
109+
app.cache.use(.memory)
110+
```
111+
112+
### Custom cache
113+
You can override which cache to use by creating your own type that conforms to the `Cache` protocol from Vapor. Use `app.gatekeeper.caches.use()` to configure which cache to use.
114+
115+
76116
## Credits 🏆
77117

78118
This package is developed and maintained by the Vapor team at [Nodes](https://www.nodesagency.com).
79119
The package owner for this project is [Christian](https://github.com/cweinberger).
120+
Special thanks goes to [madsodgaard](https://github.com/madsodgaard) for his work on the Vapor 4 version!
80121

81122
## License 📄
82123

0 commit comments

Comments
 (0)