@universis/janitor
Version:
Universis api plugin for handling user authorization and rate limiting
385 lines (321 loc) • 12.5 kB
Markdown
# /janitor

Universis api plugin for rate limiting requests or slowing down service responses.
## Usage
npm i /janitor
## RateLimitService
`RateLimitService` is a configurable extension of [express-rate-limit](https://www.npmjs.com/package/express-rate-limit) for limiting service requests.
Register service under application services:
```json
{
"services": [
{
"serviceType": "@universis/janitor#RateLimitService"
}
]
}
```
and start configuring rate limited endpoints under `settings/universis/rateLimit`:
```json
{
"settings": {
"universis": {
"rateLimit": {
"profiles": [
[
"userRateLimitProfile",
{
"windowMs": 300000,
"limit": 50,
"legacyHeaders": true
}
]
],
"paths": [
[
"/users/me",
{
"profile": "userRateLimitProfile"
}
]
]
}
}
}
}
```
`RateLimitService` service configuration consists of a collection of rate limit `profiles` that are going to used as options during request validation and a collection of `paths` for defining rate limited route paths.
Read more about rate limit configuration at [express-rate-limit documentation](https://github.com/express-rate-limit/express-rate-limit#configuration)
### Enable rate limit headers over CORS
Each `RateLimitService` profile has an option to include a set of headers in the response. This can be done by setting the `headers` option to `true` in the profile configuration. This operation will add the following headers to the response:
- `X-RateLimit-Limit` - the maximum number of requests allowed in the current window
- `X-RateLimit-Remaining` - the number of requests remaining in the current window
- `X-RateLimit-Reset` - the number of milliseconds remaining until the window resets
Rate limit headers will be available only if the request is made from the same origin. If the request is made from a different origin, the headers will not be included in the response and should be configured to be exposed by CORS.
```json
{
"settings": {
"cors": {
"exposedHeaders": [
"X-Rate-Limit",
"X-Rate-Remaining",
"X-Rate-Reset"
]
}
}
}
```
## SpeedLimitService
`SpeedLimitService` is a configurable extension of [express-slow-down](https://www.npmjs.com/package/express-slow-down) for slowing down service responses.
Register service under application services:
```json
{
"services": [
{
"serviceType": "@universis/janitor#SpeedLimitService"
}
]
}
```
and start configuring speed limited endpoints under `settings/universis/speedLimit`:
```json
{
"settings": {
"universis": {
"speedLimit": {
"profiles": [
[
"userSpeedLimitProfile",
{
"windowMs": 300000,
"delayAfter": 5,
"delayMs": 500,
"maxDelayMs": 20000,
"headers": true
}
]
],
"paths": [
[
"/users/me",
{
"profile": "userSpeedLimitProfile"
}
]
]
}
}
}
}
```
`SpeedLimitService` configuration consists of a collection of speed limit `profiles` that are going to used as options during request validation and a collection of `paths` for defining speed limited route paths.
Read more about speed limit configuration at [express-slow-down documentation](https://github.com/express-rate-limit/express-slow-down#configuration)
`SpeedLimitService` offers two additional options for delaying response: `randomDelayMs` and `randomMaxDelayMs`. These options are used for adding random delay to the response. They define the range of random delay in milliseconds.
```json
{
"settings": {
"universis": {
"speedLimit": {
"profiles": [
[
"userSpeedLimitProfile",
{
"windowMs": 120000,
"delayAfter": 5,
"randomDelayMs": [
500,
1000
],
"headers": false
}
]
],
"paths": [
[
"/users/me",
{
"profile": "userSpeedLimitProfile"
}
]
]
}
}
}
}
```
If `randomDelayMs` is set, `delayMs` is ignored.
`randomMaxDelayMs` is used for setting the maximum random delay. If `randomMaxDelayMs` is not set, the maximum delay is set to `maxDelayMs` is ignored e.g.
```json
{
"settings": {
"universis": {
"speedLimit": {
"profiles": [
[
"userSpeedLimitProfile",
{
"windowMs": 120000,
"delayAfter": 5,
"delayMs": 500,
"randomMaxDelayMs": [
7000,
12000
],
"headers": false
}
]
],
"paths": [
[
"/users/me",
{
"profile": "userSpeedLimitProfile"
}
]
]
}
}
}
}
```
### Enable speed limit headers over CORS
Each `SpeedLimitService` profile has an option to include a set of headers in the response. This can be done by setting the `headers` option to `true` in the profile configuration. This operation will add the following headers to the response:
- `X-SlowDown-Limit` - the maximum number of requests allowed in the current window
- `X-SlowDown-Remaining` - the number of requests remaining in the current window
- `X-SlowDown-Reset` - the number of milliseconds remaining until the window resets
Speed limit headers will be available only if the request is made from the same origin. If the request is made from a different origin, the headers will not be included in the response and should be configured to be exposed by CORS.
```json
{
"settings": {
"cors": {
"exposedHeaders": [
"X-SlowDown-Limit",
"X-SlowDown-Remaining",
"X-SlowDown-Reset"
]
}
}
}
```
## ScopeAccessConfiguration
`ScopeAccessConfiguration` is a configurable application configuration strategy for limiting access to service endpoints based on user scopes.
Enable scope access configuration using `EnableScopeAccessConfiguration` service:
```json
{
"services": [
{
"serviceType": "@universis/janitor#EnableScopeAccessConfiguration"
}
]
}
```
add `<config directory>/scope.access.json` and start configuring scope limited endpoints:
```json
[
{
"scope": [
"registrar"
],
"resource": "/api/",
"access": [
"read",
"write"
]
},
{
"scope": [
"students",
"teachers",
"registrar"
],
"resource": "/api/workspaces/locales",
"access": [
"read"
]
}
]
```
Each configuration element consists of `scope` array, `resource` string and `access` array.
If the user has at least one of the scopes from the `scope` array,
and the user has at least one of the `access` array, the user will be granted access to the resource.
There are two different access types:
- `read` - grants access to read the resource (GET, HEAD, OPTIONS)
- `write` - grants access to write the resource (POST, PUT, PATCH, DELETE)
The resource string can be a path or a regular expression e.g. `/api/instructors/me/exams/(\d+)/types`
`validateScope` express.js middleware is available for validating user scope access to the resource:
```javascript
import { validateScope } from '/janitor';
app.use('/api', passport.authenticate('bearer', {session: false}), validateScope(), (req, res) => {
res.send('Hello World!')
});
```
A `403 - Access denied due to authorization scopes` error will be thrown if the user does not have access to the resource.
## ExtendScopeAccessConfiguration
`ExtendScopeAccessConfiguration` is a configurable application service for extending scope access configuration with additional scopes.
Register service under application services:
```json
{
"services": [
{
"serviceType": "@universis/janitor#ExtendScopeAccessConfiguration"
}
]
}
```
add `scopeAccess` section under `settings/universis/janitor` configuration and start configuring scope access extension:
```json
{
"settings": {
"universis": {
"janitor": {
"scopeAccess": {
"imports": [
"./custom.scope.access.json"
],
}
}
}
}
}
```
`ExtendScopeAccessConfiguration` service will import additional scope access configuration from the files listed in the `imports` array. The file path is relative to the application configuration directory e.g. `server/config/` where the main configuration file is located.
The given files should contain an array of scope access configuration elements as described in the `ScopeAccessConfiguration` section.
## RemoteAddressValidator
`RemoteAddressValidator` is a configurable application service for validating access to service endpoints based on remote address provided by OAuth2 token.
Register service under application services:
```json
{
"services": [
{
"serviceType": "@universis/janitor#RemoteAddressValidator"
}
]
}
```
`RemoteAddressValidator` validates the remote address of the request with the remote address provided by the OAuth2 token. If the addresses do not match, a `403 - Access denied due to remote address` error will be thrown. Token remote address is provided by the `remoteAddress` claim in the token payload. It can be configured in the OAuth2 server configuration and may have a different name. This name may be configured in the `settings/universis/janitor/remoteAddress` configuration e.g.
```json
{
"settings": {
"universis": {
"janitor": {
"remoteAddress": {
"claim": "ipAddress"
}
}
}
}
}
```
where `claim` is the name of the remote address claim in the token payload.
**Important Note**: If api server is served by a proxy, the remote address may be different from the client address. In this case, the proxy should be configured to forward the client address to the server. This scenario should be configured in application settings under `settings/universis/api/` section e.g.
```json
{
"settings": {
"universis": {
"api": {
"proxyAddressForwarding": true
}
}
}
}
```