Skip to content

Commit

Permalink
Added new features
Browse files Browse the repository at this point in the history
- Custom status code support;
- Redirect support.
  • Loading branch information
douglasrafael committed Jul 22, 2019
1 parent d31c54c commit abca90e
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 24 deletions.
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ A middleware for access permissions based on IP/host addresses. Customers who ar
* Create a list of permissions with hostnames and IP addresses and control who can access the resources of your API;
* Support IPv4, IPv6, CIDR format & IPv4 mapped IPv6 addresses;
* Custom log function;
* Custom message function.
* Custom message function;
* Set request code status or use default;
* Set URL to redirect.

## Installation
> `npm i ip-allowed --save`
Expand Down Expand Up @@ -39,7 +41,9 @@ const options = {
},
message: function (err, clientIp) {
return {error: `Client with IP address ${clientIp} is not allowed!`}
}
},
statusCode: 401,
redirectTo: ''
};
```

Expand All @@ -62,7 +66,12 @@ const options = {
}
}
```
- **statusCode**: The status code sent when the request was denied.
- Valor default: `401`
- **redirectTo**: URL to redirect when request is denied. Be sure to set the statusCode to 301 or 302 as it is the HTTP status codes that apply in this situation. Otherwise, the default 401 will be used.
- Valor default: `""`


[//]: # (These are reference links used in the body of this note.)
[license-image]: https://img.shields.io/badge/license-Apache%202-blue.svg
[license-url]: https://github.com/nutes-uepb/ip-allowed/blob/master/LICENSE
Expand Down
19 changes: 8 additions & 11 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ async function clientMatch(clientIp, whitelist) {
isMatch = whitelist.some((result) => {
// IPv6 address has 128 bits and IPv4 has 32 bits.
// Setting the routing prefix to all bits in a CIDR address means only the specified address is allowed.
result = result || ''
result = result.indexOf('/') === -1 ? result + '/128' : result
result = (result && result.indexOf('/') === -1) ? result + '/128' : result || ''

const range = result.split('/')
if (range.length === 2 && Address.isValid(range[0]) && isNumeric(range[1])) {
Expand Down Expand Up @@ -80,24 +79,22 @@ function ipAllowed(allowedList, options) {
const _options = {
// log: Pass a log function or `false` to disable log.
// `Function(String clientIp, Boolean access)`
log: (clientIp, accessDenied) => {
log: options && options.log !== undefined ? options.log : (clientIp, accessDenied) => {
console.log(`Access ${accessDenied ? 'denied' : 'allowed'} for ip address ${clientIp}`)
},
// Message sent when the request is denied, can be a string or JSON.
// `Function(String clientIp)`
message: (err, clientIp) => {
message: options && options.message ? options.message : (err, clientIp) => {
return {
code: 401,
message: 'Unauthorized',
description: `Access denied for IP address ${clientIp}`
}
}
},
statusCode: options && options.statusCode ? parseInt(options.statusCode, 10) : 401,
redirectTo: options && options.redirectTo ? options.redirectTo : ''
}

// Override default options.
_options.log = options && options.log !== undefined ? options.log : _options.log
_options.message = options && options.message ? options.message : _options.message

// Express middleware.
return (req, res, next) => {
const clientIp = req.ip || req.connection.remoteAddress
Expand All @@ -118,8 +115,8 @@ function ipAllowed(allowedList, options) {
}

if (!isMatch && typeof _options.message === 'function') {
res.status(401)
res.send(_options.message(null, clientIp))
if (_options.redirectTo.length > 0) res.redirect(_options.statusCode, _options.redirectTo)
else res.status(_options.statusCode).send(_options.message(null, clientIp))
} else {
next()
}
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ip-allowed",
"version": "1.0.0",
"version": "1.1.0",
"description": "A middleware for access permissions based on IP/host addresses. Customers who are not on the whitelist have their requests blocked.",
"main": "index.js",
"directories": {
Expand Down Expand Up @@ -30,7 +30,7 @@
"security"
],
"author": "NUTES/UEPB",
"license": "MIT",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/nutes-uepb/ip-allowed/issues"
},
Expand Down
80 changes: 72 additions & 8 deletions test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,24 @@ describe('ipAllowed()', () => {
.expect(200)
})

it('Should accept requests only from the ::ffff:127.0.0.1', () => {
const app = createServer(ipAllowed('::ffff:127.0.0.1', {log: false}))
it('Should accept requests only from the IPv6', () => {
const app = createServer(ipAllowed('0:0:0:0:0:ffff:7f00:1', {log: false}))

return request(app)
.get('/')
.expect(200)
})

it('Should accept requests of any client', () => {
it('Should accept requests of any origin - *', () => {
const app = createServer(ipAllowed('*', {log: false}))

return request(app)
.get('/')
.expect(200)
})

it('Should accept requests of any client', () => {
const app = createServer(ipAllowed('*', {log: false}))
it('Should accept requests of any origin - 0.0.0.0', () => {
const app = createServer(ipAllowed('0.0.0.0', {log: false}))

return request(app)
.get('/')
Expand Down Expand Up @@ -234,8 +234,7 @@ describe('ipAllowed()', () => {
})
})

it('Should print default log (no override)', () => {
let logDefault = ''
it('Should print default log (no override) access allowed', () => {
const app = createServer(ipAllowed('151.81.131.239'))
app.enable('trust proxy')

Expand All @@ -245,7 +244,15 @@ describe('ipAllowed()', () => {
.expect(200)
})

it('Should print default log for denied access', () => {
it('Should print default log (no override) access denied', () => {
const app = createServer(ipAllowed('151.81.131.239'))

return request(app)
.get('/')
.expect(401)
})

it('Should return parameters clientIp and accessDenied on message callback', () => {
let logDefault = ''
const app = createServer(ipAllowed('127.0.0.1', {
log: (clientIp, accessDenied) => {
Expand Down Expand Up @@ -280,6 +287,63 @@ describe('ipAllowed()', () => {
})
})
})

describe('Status code', () => {
it('Should return status code 500 for access denied', () => {
const app = createServer(ipAllowed(['google.com'], {log: false, statusCode: '500'}))

return request(app)
.get('/')
.expect(500)
})


it('Should return status code 301 for access denied', () => {
const app = createServer(ipAllowed(['google.com'], {log: false, statusCode: 301}))

return request(app)
.get('/')
.expect(301)
})
})

describe('Redirect to', () => {
it('Should return status code 301 and redirect to /access-denied', () => {
const app = createServer(ipAllowed(
['google.com'], {
log: false,
statusCode: 301,
redirectTo: '/access-denied.html'
}
))

Object.defineProperty(app.request, 'ip', {
configurable: true,
enumerable: true,
get: () => {
}
})

return request(app)
.get('/')
.expect(301)
.expect('Location', '/access-denied.html')
})

it('Should return status code default (401) and redirect to /access-denied', () => {
const app = createServer(ipAllowed(
['151.81.131.400'], {
log: false,
redirectTo: '/access-denied.html'
}
))

return request(app)
.get('/')
.expect(401)
.expect('Location', '/access-denied.html')
})
})
})

function createServer(middleware) {
Expand Down

0 comments on commit abca90e

Please sign in to comment.