├── .editorconfig ├── .gitignore ├── .travis.yml ├── .verb.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── index.js ├── package.json └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | insert_final_newline = false 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Always-ignore dirs # 2 | # #################### 3 | _gh_pages 4 | node_modules 5 | jspm_packages 6 | bower_components 7 | components 8 | vendor 9 | build 10 | dest 11 | src 12 | lib-cov 13 | coverage 14 | nbproject 15 | cache 16 | temp 17 | tmp 18 | koa-ip-filter 19 | 20 | # Packages # 21 | # ########## 22 | *.7z 23 | *.dmg 24 | *.gz 25 | *.iso 26 | *.jar 27 | *.rar 28 | *.tar 29 | *.zip 30 | 31 | # OS, Logs and databases # 32 | # ######################### 33 | logs 34 | *.pid 35 | *.dat 36 | *.log 37 | *.sql 38 | *.sqlite 39 | *~ 40 | ~* 41 | 42 | # Another files # 43 | # ############### 44 | Icon? 45 | .DS_Store* 46 | Thumbs.db 47 | ehthumbs.db 48 | Desktop.ini 49 | npm-debug.log 50 | .directory 51 | ._* 52 | lcov.info 53 | 54 | # Runtime data 55 | pids 56 | *.pid 57 | *.seed 58 | *.pid.lock 59 | 60 | 61 | # nyc test coverage 62 | .nyc_output 63 | 64 | # Grunt intermediate storage 65 | # see here: http://gruntjs.com/creating-plugins#storing-task-files 66 | .grunt 67 | 68 | # Optional npm cache directory 69 | .npm 70 | 71 | # Optional REPL history 72 | .node_repl_history 73 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | 4 | node_js: 5 | - "node" 6 | - "6" 7 | - "5" 8 | - "4" 9 | - "0.12" 10 | - "0.10" 11 | 12 | matrix: 13 | fast_finish: true 14 | allow_failures: 15 | - node_js: "4" 16 | - node_js: "0.10" 17 | - node_js: "0.12" 18 | 19 | notifications: 20 | email: false 21 | 22 | after_success: npm run report-coverage 23 | -------------------------------------------------------------------------------- /.verb.md: -------------------------------------------------------------------------------- 1 | # [{%= name %}][author-www-url] [![npmjs.com][npmjs-img]][npmjs-url] [![The MIT License][license-img]][license-url] [![npm downloads][downloads-img]][downloads-url] 2 | 3 | > {%= description %} 4 | 5 | [![code climate][codeclimate-img]][codeclimate-url] [![standard code style][standard-img]][standard-url] [![travis build status][travis-img]][travis-url] [![coverage status][coveralls-img]][coveralls-url] [![dependency status][david-img]][david-url] 6 | 7 | ## Install 8 | 9 | ``` 10 | npm i {%= name %} --save 11 | ``` 12 | 13 | ## Features 14 | - custom message when `403 Forbidden` response, through `opts.forbidden` 15 | - custom identifier different than default `this.ip`, through `opts.id` 16 | + you may want to add `opts.strict: false` if it's not IP 17 | - filter IP using glob patterns, regexp, string, array or function 18 | - blacklist with negative glob patterns, whitelist with positive 19 | - would restrict all to `403 Forbidden` that not match to filter 20 | 21 | > **Notice:** In the next middleware you will have `this.filter` method which is [ip-filter][] 22 | > and `this.identifier` - the IP/ID that passed the given filter 23 | 24 | ## Usage 25 | > For more use-cases see the [tests](./test.js) 26 | 27 | ```js 28 | const {%= varname %} = require('{%= name %}') 29 | ``` 30 | 31 | {%= apidocs('index.js') %} 32 | 33 | 34 | ### One more example 35 | > If you want to allow all IPs, but want to restrict only some range 36 | 37 | ```js 38 | 'use strict' 39 | 40 | var koa = require('koa') 41 | var ipFilter = require('koa-ip-filter') 42 | var helloWorld = require('koa-hello-world') 43 | 44 | var app = koa() 45 | 46 | app 47 | .use(ipFilter({ 48 | forbidden: '403: Get out of here!', 49 | filter: ['*', '!213.15.*'] 50 | })) 51 | .use(helloWorld()) 52 | 53 | app.listen(1234) 54 | console.log('koa server start listening on http://localhost:1234') 55 | 56 | // only user with IP starting with `213.15.*` 57 | // will see the message `403: Get out of here!` 58 | ``` 59 | 60 | {% if (verb.related && verb.related.list && verb.related.list.length) { %} 61 | ## Related 62 | {%= related(verb.related.list, {words: 12}) %} 63 | {% } %} 64 | 65 | ## Contributing 66 | Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/{%= repository %}/issues/new). 67 | But before doing anything, please read the [CONTRIBUTING.md](./CONTRIBUTING.md) guidelines. 68 | 69 | ## [Charlike Make Reagent](http://j.mp/1stW47C) [![new message to charlike][new-message-img]][new-message-url] [![freenode #charlike][freenode-img]][freenode-url] 70 | 71 | [![{%= author.username %}.tk][author-www-img]][author-www-url] [![keybase {%= author.username %}][keybase-img]][keybase-url] [![{%= author.username %} npm][author-npm-img]][author-npm-url] [![{%= author.username %} twitter][author-twitter-img]][author-twitter-url] [![{%= author.username %} github][author-github-img]][author-github-url] 72 | 73 | {%= reflinks(verb.reflinks) %} 74 | 75 | [npmjs-url]: https://www.npmjs.com/package/{%= name %} 76 | [npmjs-img]: https://img.shields.io/npm/v/{%= name %}.svg?label={%= name %} 77 | 78 | [license-url]: https://github.com/{%= repository %}/blob/master/LICENSE 79 | [license-img]: https://img.shields.io/npm/l/{%= name %}.svg 80 | 81 | [downloads-url]: https://www.npmjs.com/package/{%= name %} 82 | [downloads-img]: https://img.shields.io/npm/dm/{%= name %}.svg 83 | 84 | 85 | [codeclimate-url]: https://codeclimate.com/github/{%= repository %} 86 | [codeclimate-img]: https://img.shields.io/codeclimate/github/{%= repository %}.svg 87 | 88 | [travis-url]: https://travis-ci.org/{%= repository %} 89 | [travis-img]: https://img.shields.io/travis/{%= repository %}/master.svg 90 | 91 | [coveralls-url]: https://coveralls.io/r/{%= repository %} 92 | [coveralls-img]: https://img.shields.io/coveralls/{%= repository %}.svg 93 | 94 | [david-url]: https://david-dm.org/{%= repository %} 95 | [david-img]: https://img.shields.io/david/{%= repository %}.svg 96 | 97 | [standard-url]: https://github.com/feross/standard 98 | [standard-img]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg 99 | 100 | 101 | [author-www-url]: http://www.{%= author.username.toLowerCase() %}.tk 102 | [author-www-img]: https://img.shields.io/badge/www-{%= author.username.toLowerCase() %}.tk-fe7d37.svg 103 | 104 | [keybase-url]: https://keybase.io/{%= author.username.toLowerCase() %} 105 | [keybase-img]: https://img.shields.io/badge/keybase-{%= author.username.toLowerCase() %}-8a7967.svg 106 | 107 | [author-npm-url]: https://www.npmjs.com/~{%= author.username.toLowerCase() %} 108 | [author-npm-img]: https://img.shields.io/badge/npm-~{%= author.username.toLowerCase() %}-cb3837.svg 109 | 110 | [author-twitter-url]: https://twitter.com/{%= author.username %} 111 | [author-twitter-img]: https://img.shields.io/badge/twitter-@{%= author.username %}-55acee.svg 112 | 113 | [author-github-url]: https://github.com/{%= author.username %} 114 | [author-github-img]: https://img.shields.io/badge/github-@{%= author.username %}-4183c4.svg 115 | 116 | [freenode-url]: http://webchat.freenode.net/?channels=charlike 117 | [freenode-img]: https://img.shields.io/badge/freenode-%23charlike-5654a4.svg 118 | 119 | [new-message-url]: https://github.com/{%= author.username %}/ama 120 | [new-message-img]: https://img.shields.io/badge/ask%20me-anything-green.svg -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 3.0.0 - 2016-10-07 4 | 5 | **POSSIBLY BREAKING CHANGES** 6 | 7 | Mainly bump to `ip-filter@2` which fixes some wrong behaving. Also you should notice 8 | that signle `*` would not work in some cases, so you should `**`, because basically `ip-filter` 9 | converts IPs to filepaths to be able to use `micromatch` as matching library behind the scenes. 10 | 11 | So one way is to pass such `filter: ['*.*.*.*', '!111.??.244.*']`, another is 12 | the `filter: ['**', '!111.??.244.*']` to match any IPs except `111.22.244.31` for example. 13 | 14 | **MISC STUFF** 15 | 16 | Boilerplate stuff. Update contributing guide, dotfiles, license year, npm scripts, release/publish flow and etc. Replace `assertit` with `mukla` which is drop-in replacement. Using `verb` for generating the README.md and API docs. 17 | 18 | ## 2.0.0 - 2015-05-27 19 | - Release v2.0.0 / npm@v2.0.0 20 | - add `related` section 21 | - fix complexity of tests, close [#2](https://github.com/tunnckoCore/koa-ip-filter/issues/2), thanks @codeclimate 22 | - remove `upcoming v2` notice, close [#1](https://github.com/tunnckoCore/koa-ip-filter/issues/1) 23 | - update description/keywords 24 | - refactor tests 25 | - bump devDeps 26 | - add test to ensure next middleware 27 | - update docs/example, add notice 28 | - update docs and features list 29 | - notice for upcoming v2 30 | - refactor - see [#1](https://github.com/tunnckoCore/koa-ip-filter/issues/1 "upcoming v2") 31 | 32 | ## 1.0.0 - 2015-05-23 33 | - Release v1.0.0 / npm@v1.0.0 34 | - update example 35 | - add docs and comments 36 | - update readme example, editorconfig 37 | - add example.js from readme 38 | - update readme 39 | - add test `opts.forbidden` to be function 40 | - change `accessForbidden` to `forbidden` 41 | - add test for custom msg when 403 42 | - update usage example 43 | 44 | ## 0.0.0 - 2015-05-22 45 | - first commits -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to koa-ip-filter 2 | 3 | :sparkles: Thanks for your contribution in advance! :tada: 4 | 5 | First and foremost, thank you! We appreciate that you want to contribute to `koa-ip-filter`, your time is valuable, and your contributions mean a lot to us. 6 | 7 | ## What does "contributing" mean? 8 | 9 | There are many ways to contribute to an open source project, including: 10 | 11 | - Updating or correcting documentation 12 | - Feature requests 13 | - Submitting bug reports 14 | 15 | But you aren't limited to these things. Use your imagination. If you like a project, and you see something that can or should be improved, then you have an opportunity (but not an obligation) to contribute. 16 | 17 | ### Improve documentation 18 | 19 | As a user of `koa-ip-filter` you're the perfect candidate to help us improve our documentation. Typo corrections, error fixes, better explanations, more examples, etc. Open issues for things that could be improved. Anything. Even improvements to this document. 20 | 21 | Use the [`docs` label](https://github.com/tunnckoCore/koa-ip-filter/labels/docs) to find suggestions for what we'd love to see more documentation on. 22 | 23 | ### Improve issues 24 | 25 | Some issues are created with missing information, not reproducible, or plain invalid. Help make them easier to resolve. Handling issues takes a lot of time that we could rather spend on fixing bugs and adding features. 26 | 27 | ### Give feedback on issues 28 | 29 | We're always looking for more opinions on discussions in the issue tracker. It's a good opportunity to influence the future direction of AVA. 30 | 31 | The [`question` label](https://github.com/tunnckoCore/koa-ip-filter/labels/question) is a good place to find ongoing discussions. 32 | 33 | 34 | ## Why should I contribute? 35 | 36 | Regardless of the details, being an effective contributor means that you're adding _adding value_ to a project. 37 | 38 | Here are just a few of the advantages of adding value to a project: 39 | 40 | - you gain the appreciation and respect of the project's maintainers and community 41 | - you gain valuable experience 42 | - you get noticed by job recruiters 43 | - you become more attrative to potential employers. 44 | 45 | ## Getting familiarized with a project 46 | 47 | Before you attempt to contribute to a project, take a moment to get familiarized with it. In most cases you can learn all you need to know within a couple of minutes. 48 | 49 | ### Required 50 | 51 | The following items are a pre-requisite for contributing to any project. Avoid creating issues or doing pull requests until you've done all of these things: 52 | 53 | - **Review the readme**: Oftentimes a project readme has links to documentation, advice on creating issues or bug reports, and so on. 54 | - **Read contributing guidelines**: look for a `CONTRIBUTING.md` file and, if one exists, read it in its entirety before creating issues or doing a pull request. Typically this is in the root of the project, but it might be in `.github/CONTRIBUTING.md`. 55 | - **Search issues**: Before creating bug reports, feature requests, or submitting issues of any kind, you should always search for existing issues (closed or open) that address the same thing. 56 | 57 | ### Recommended 58 | 59 | - **Review unit tests** - one of the best ways to get familiarized with a project is through its unit tests. Of course, this depends on the type of project, complexity, test coverage, and so on. But when applicable, test are often a good source of insight. 60 | - **Get familiarized with the code** - If the codebase is small, and you're familiar with the language, take a moment to review the code to see if you find anything that can be improved. If the codebase is large, you might be able to provide domain expertise or fixes for specific areas. 61 | - **Ask questions** - Depending the project type and size, it might be good to start by searching google to find anwers to your questions. Then, check to see if the project uses [gitter](https://gitter.im) or has a [slack](https://slack.com) channel, or something similar. Also visit [stackoverflow](https://stackoverflow.com) and do a search to see if others have already asked the same question. As a last resort, create an issue on the project's GitHub repository. 62 | 63 | 64 | ## Details of Highly Effective Bug Reports 65 | 66 | ### Rationale 67 | 68 | The easier you make it for a maintainter or members of the community to react, the more likely it is for them to react quickly. 69 | 70 | Like you, maintainers have to make decisions about where to spend their time. Not only within a given project, but oftentimes across multiple projects. If you're experiencing a bug and you want to make a report, bug reports that are clearly described and organized are much more likely to get addressed by the maintainers or member of the community. 71 | 72 | Providing these details up front will make everyone happy. If you don't provide these details, maintainers will have to ask you for them, which can be annoying for experienced maintainers who have had to ask for these crucial details many times. 73 | 74 | ### The details 75 | 76 | Always include the following essential details in every bug report: 77 | 78 | 1. **version**: what version of `koa-ip-filter` were you using when you experienced the bug? 79 | 2. **description**: clear description of the bug, and minimum steps to reproduce it. 80 | 3. **error messages**: paste any error messages into the issue or a [github gist](https://gist.github.com/), use [gfm code blocks][gfm]. 81 | 4. **code**: paste any code necessary for reproducing the bug and use [gfm code blocks][gfm] to wrap the code. 82 | 5. **title**: use a clear and descriptive title. 83 | 84 | See GitHub's guide to [Creating and highlighting code blocks][gfm] for more details. 85 | 86 | ## Submitting a pull requests 87 | 88 | **Working on your first Pull Request?** 89 | 90 | You can learn how from this *free* video series ["How to Contribute to an Open Source Project on GitHub"][howto-oss-github] 91 | 92 | **Details** 93 | 94 | - Non-trivial changes are often best discussed in an issue first, to prevent you from doing unnecessary work. 95 | - For ambitious tasks, you should try to get your work in front of the community for feedback as soon as possible. Open a pull request as soon as you have done the minimum needed to demonstrate your idea. At this early stage, don't worry about making things perfect, or 100% complete. Add a [WIP] prefix to the title, and describe what you still need to do. This lets reviewers know not to nit-pick small details or point out improvements you already know you need to make. 96 | - New features should be accompanied with tests and documentation. 97 | - Don't include unrelated changes. 98 | - Lint and test immediately after you fork by running `$ npm test`. 99 | - Lint and test before submitting the pull request by running `$ npm test`. 100 | - Make the pull request from a [topic branch](https://github.com/dchelimsky/rspec/wiki/Topic-Branches), not master. 101 | - Use a clear and descriptive title for the pull request and commits. 102 | - Write a convincing description of why we should land your pull request. It's your job to convince us. Answer "why" it's needed and provide use-cases. 103 | - You might be asked to do changes to your pull request. There's never a need to open another pull request. [Just update the existing one.][amending] 104 | 105 | ## Other ways to contribute 106 | 107 | ### Show your support 108 | 109 | Sometimes we find a project we like but just don't have time to contribute. That's okay, there are other ways to show support: 110 | 111 | - Star the project 112 | - Tweet about it 113 | - Tell your friends 114 | 115 | ### Show your appreciation 116 | 117 | Maintainers are people too. You can make someone's day by letting them know you appreciate their work. If you use a library in one of your own projects, let the author know you care: 118 | 119 | - Add a link to the project on your project's readme 120 | - Say "thanks" on twitter 121 | 122 | ## Attribution 123 | 124 | This document is adapted from a few Contributing Guides. It is more general and can apply in most cases. Everyone is free to re-use it or re-adapt it. 125 | 126 | ### Good to read 127 | 128 | - [Awesome Contributing][awesomelist] 129 | - [Idiomatic Contributing][idiomatic] 130 | - [AVA's Contributing Guide][avajs] 131 | - [Amending a commit Guide][amending] 132 | - [Creating and highlighting code blocks][gfm] 133 | - [Contributing to Open Source (GitHub)][os-on-github] 134 | - [How to contribute to Open Source Project (Egghead.io videos)][howto-oss-github] 135 | 136 | ### Authors 137 | 138 | **Charlike Mike Reagent** 139 | 140 | * [github/tunnckoCore](https://github.com/tunnckoCore) 141 | * [twitter/tunnckoCore](http://twitter.com/tunnckoCore) 142 | 143 | ## License 144 | 145 | Released under [Creative Commons](https://creativecommons.org/licenses/by/3.0/). 146 | Copyright © 2016, [Charlike Mike Reagent](http://www.tunnckocore.tk). 147 | 148 | [gfm]: https://help.github.com/articles/creating-and-highlighting-code-blocks/ 149 | [avajs]: https://github.com/avajs/ava/blob/master/contributing.md 150 | [idiomatic]: https://github.com/jonschlinkert/idiomatic-contributing 151 | [awesomelist]: https://github.com/jonschlinkert/awesome-contributing 152 | [amending]: https://github.com/RichardLitt/docs/blob/master/amending-a-commit-guide.md 153 | [os-on-github]: https://guides.github.com/activities/contributing-to-open-source/ 154 | [howto-oss-github]: http://j.mp/how-to-contrib-on-github -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2016 Charlike Mike Reagent 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [koa-ip-filter][author-www-url] [![npmjs.com][npmjs-img]][npmjs-url] [![The MIT License][license-img]][license-url] [![npm downloads][downloads-img]][downloads-url] 2 | 3 | > Middleware for [koa][] that filters IPs against glob patterns, RegExp, string or array of globs. Support custom `403 Forbidden` message and custom ID. 4 | 5 | [![code climate][codeclimate-img]][codeclimate-url] [![standard code style][standard-img]][standard-url] [![travis build status][travis-img]][travis-url] [![coverage status][coveralls-img]][coveralls-url] [![dependency status][david-img]][david-url] 6 | 7 | ## Install 8 | 9 | ``` 10 | npm i koa-ip-filter --save 11 | ``` 12 | 13 | ## Features 14 | - custom message when `403 Forbidden` response, through `opts.forbidden` 15 | - custom identifier different than default `this.ip`, through `opts.id` 16 | + you may want to add `opts.strict: false` if it's not IP 17 | - filter IP using glob patterns, regexp, string, array or function 18 | - blacklist with negative glob patterns, whitelist with positive 19 | - would restrict all to `403 Forbidden` that not match to filter 20 | 21 | > **Notice:** In the next middleware you will have `this.filter` method which is [ip-filter][] 22 | > and `this.identifier` - the IP/ID that passed the given filter 23 | 24 | ## Usage 25 | > For more use-cases see the [tests](./test.js) 26 | 27 | ```js 28 | const koaIpFilter = require('koa-ip-filter') 29 | ``` 30 | 31 | ### [koaIpFilter](index.js#L51) 32 | > Filtering incoming request with glob patterns array, regexp, string or matcher function 33 | 34 | **Params** 35 | 36 | * `options` **{Object}** 37 | - `id` **{Function}**: custom identifier, defaults to `this.ip` 38 | - `strict` **{Boolean}**: to throw when not valid IPv4/IPv6? default `true` 39 | - `filter` **{Array|String|RegExp|Function}**: black/white list filter 40 | - `forbidden` **{String|Function}**: custom message when `403 Forbidden` response 41 | * `returns` **{GeneratorFunction}** 42 | 43 | **Example** 44 | 45 | ```js 46 | 'use strict' 47 | 48 | var koa = require('koa') 49 | var ipFilter = require('koa-ip-filter') 50 | var helloWorld = require('koa-hello-world') 51 | 52 | var app = koa() 53 | 54 | app 55 | .use(ipFilter({ 56 | forbidden: '403: Get out of here!', 57 | filter: ['127.??.6*.12', '!1.2.*.4'] 58 | })) 59 | .use(helloWorld()) 60 | 61 | app.listen(1234) 62 | console.log('koa server start listening on http://localhost:1234') 63 | 64 | // if your IP is `127.43.65.12` you will see `Hello World` 65 | // otherwise you will see `403: Get out of here!` 66 | ``` 67 | 68 | ### One more example 69 | > If you want to allow all IPs, but want to restrict only some range 70 | 71 | ```js 72 | 'use strict' 73 | 74 | var koa = require('koa') 75 | var ipFilter = require('koa-ip-filter') 76 | var helloWorld = require('koa-hello-world') 77 | 78 | var app = koa() 79 | 80 | app 81 | .use(ipFilter({ 82 | forbidden: '403: Get out of here!', 83 | filter: ['*', '!213.15.*'] 84 | })) 85 | .use(helloWorld()) 86 | 87 | app.listen(1234) 88 | console.log('koa server start listening on http://localhost:1234') 89 | 90 | // only user with IP starting with `213.15.*` 91 | // will see the message `403: Get out of here!` 92 | ``` 93 | 94 | ## Related 95 | - [ip-filter](https://www.npmjs.com/package/ip-filter): Filters valid IPv4 or IPv6 against glob pattern, array, string and etc… [more](https://github.com/tunnckocore/ip-filter#readme) | [homepage](https://github.com/tunnckocore/ip-filter#readme "Filters valid IPv4 or IPv6 against glob pattern, array, string and etc. If match returns passed `ip`, otherwise null is returned. Have no strict mode to check no IP values.") 96 | - [is-match-ip](https://www.npmjs.com/package/is-match-ip): Matching IPs using [micromatch][] and [ip-filter][] - glob patterns, RegExp, string or… [more](https://github.com/tunnckocore/is-match-ip#readme) | [homepage](https://github.com/tunnckocore/is-match-ip#readme "Matching IPs using [micromatch][] and [ip-filter][] - glob patterns, RegExp, string or array of globs. Returns matcher function.") 97 | - [is-match](https://www.npmjs.com/package/is-match): Create a matching function from a glob pattern, regex, string, array, object… [more](https://github.com/jonschlinkert/is-match) | [homepage](https://github.com/jonschlinkert/is-match "Create a matching function from a glob pattern, regex, string, array, object or function.") 98 | - [koa-better-body](https://www.npmjs.com/package/koa-better-body): Full-featured [koa][] body parser! Support parsing text, buffer, json, json patch, json… [more](https://github.com/tunnckocore/koa-better-body#readme) | [homepage](https://github.com/tunnckocore/koa-better-body#readme "Full-featured [koa][] body parser! Support parsing text, buffer, json, json patch, json api, csp-report, multipart, form and urlencoded bodies. Works for koa@1, koa@2 and will work for koa@3.") 99 | - [koa-ip-filter](https://www.npmjs.com/package/koa-ip-filter): koa middleware to filter request IPs or custom ID with glob patterns… [more](https://github.com/tunnckocore/koa-ip-filter#readme) | [homepage](https://github.com/tunnckocore/koa-ip-filter#readme "koa middleware to filter request IPs or custom ID with glob patterns, array, string, regexp or matcher function. Support custom `403 Forbidden` message and custom ID.") 100 | - [micromatch](https://www.npmjs.com/package/micromatch): Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch… [more](https://github.com/jonschlinkert/micromatch) | [homepage](https://github.com/jonschlinkert/micromatch "Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch.") 101 | 102 | ## Contributing 103 | Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/tunnckoCore/koa-ip-filter/issues/new). 104 | But before doing anything, please read the [CONTRIBUTING.md](./CONTRIBUTING.md) guidelines. 105 | 106 | ## [Charlike Make Reagent](http://j.mp/1stW47C) [![new message to charlike][new-message-img]][new-message-url] [![freenode #charlike][freenode-img]][freenode-url] 107 | 108 | [![tunnckoCore.tk][author-www-img]][author-www-url] [![keybase tunnckoCore][keybase-img]][keybase-url] [![tunnckoCore npm][author-npm-img]][author-npm-url] [![tunnckoCore twitter][author-twitter-img]][author-twitter-url] [![tunnckoCore github][author-github-img]][author-github-url] 109 | 110 | [ip-filter]: https://github.com/tunnckocore/ip-filter 111 | [is-match]: https://github.com/jonschlinkert/is-match 112 | [koa]: https://github.com/koajs/koa 113 | [micromatch]: https://github.com/jonschlinkert/micromatch 114 | 115 | [npmjs-url]: https://www.npmjs.com/package/koa-ip-filter 116 | [npmjs-img]: https://img.shields.io/npm/v/koa-ip-filter.svg?label=koa-ip-filter 117 | 118 | [license-url]: https://github.com/tunnckoCore/koa-ip-filter/blob/master/LICENSE 119 | [license-img]: https://img.shields.io/npm/l/koa-ip-filter.svg 120 | 121 | [downloads-url]: https://www.npmjs.com/package/koa-ip-filter 122 | [downloads-img]: https://img.shields.io/npm/dm/koa-ip-filter.svg 123 | 124 | [codeclimate-url]: https://codeclimate.com/github/tunnckoCore/koa-ip-filter 125 | [codeclimate-img]: https://img.shields.io/codeclimate/github/tunnckoCore/koa-ip-filter.svg 126 | 127 | [travis-url]: https://travis-ci.org/tunnckoCore/koa-ip-filter 128 | [travis-img]: https://img.shields.io/travis/tunnckoCore/koa-ip-filter/master.svg 129 | 130 | [coveralls-url]: https://coveralls.io/r/tunnckoCore/koa-ip-filter 131 | [coveralls-img]: https://img.shields.io/coveralls/tunnckoCore/koa-ip-filter.svg 132 | 133 | [david-url]: https://david-dm.org/tunnckoCore/koa-ip-filter 134 | [david-img]: https://img.shields.io/david/tunnckoCore/koa-ip-filter.svg 135 | 136 | [standard-url]: https://github.com/feross/standard 137 | [standard-img]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg 138 | 139 | [author-www-url]: http://www.tunnckocore.tk 140 | [author-www-img]: https://img.shields.io/badge/www-tunnckocore.tk-fe7d37.svg 141 | 142 | [keybase-url]: https://keybase.io/tunnckocore 143 | [keybase-img]: https://img.shields.io/badge/keybase-tunnckocore-8a7967.svg 144 | 145 | [author-npm-url]: https://www.npmjs.com/~tunnckocore 146 | [author-npm-img]: https://img.shields.io/badge/npm-~tunnckocore-cb3837.svg 147 | 148 | [author-twitter-url]: https://twitter.com/tunnckoCore 149 | [author-twitter-img]: https://img.shields.io/badge/twitter-@tunnckoCore-55acee.svg 150 | 151 | [author-github-url]: https://github.com/tunnckoCore 152 | [author-github-img]: https://img.shields.io/badge/github-@tunnckoCore-4183c4.svg 153 | 154 | [freenode-url]: http://webchat.freenode.net/?channels=charlike 155 | [freenode-img]: https://img.shields.io/badge/freenode-%23charlike-5654a4.svg 156 | 157 | [new-message-url]: https://github.com/tunnckoCore/ama 158 | [new-message-img]: https://img.shields.io/badge/ask%20me-anything-green.svg 159 | 160 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * koa-ip-filter 3 | * 4 | * Copyright (c) 2015-2016 Charlike Mike Reagent, contributors. 5 | * Released under the MIT license. 6 | */ 7 | 8 | 'use strict' 9 | 10 | var ipFilter = require('ip-filter') 11 | 12 | /** 13 | * > Filtering incoming request with glob patterns 14 | * array, regexp, string or matcher function 15 | * 16 | * **Example** 17 | * 18 | * ```js 19 | * 'use strict' 20 | * 21 | * var koa = require('koa') 22 | * var ipFilter = require('koa-ip-filter') 23 | * var helloWorld = require('koa-hello-world') 24 | * 25 | * var app = koa() 26 | * 27 | * app 28 | * .use(ipFilter({ 29 | * forbidden: '403: Get out of here!', 30 | * filter: ['127.??.6*.12', '!1.2.*.4'] 31 | * })) 32 | * .use(helloWorld()) 33 | * 34 | * app.listen(1234) 35 | * console.log('koa server start listening on http://localhost:1234') 36 | * 37 | * // if your IP is `127.43.65.12` you will see `Hello World` 38 | * // otherwise you will see `403: Get out of here!` 39 | * ``` 40 | * 41 | * @name koaIpFilter 42 | * @param {Object} `options` 43 | * @option {Function} [options] `id` custom identifier, defaults to `this.ip` 44 | * @option {Boolean} [options] `strict` to throw when not valid IPv4/IPv6? default `true` 45 | * @option {Array|String|RegExp|Function} [options] `filter` black/white list filter 46 | * @option {String|Function} [options] `forbidden` custom message when `403 Forbidden` response 47 | * @return {GeneratorFunction} 48 | * @api public 49 | */ 50 | 51 | module.exports = function koaIpFilter (options) { 52 | options = typeof options === 'object' ? options : {} 53 | 54 | return function * (next) { 55 | var id = typeof options.id === 'function' 56 | ? options.id.call(this, this) 57 | : this.ip 58 | 59 | if (!id || !options.filter) { 60 | return yield * next 61 | } 62 | 63 | var forbidden = options.forbidden || '403 Forbidden' 64 | 65 | var identifier = ipFilter(id, options.filter, options) 66 | if (identifier === null) { 67 | this.status = 403 68 | this.body = typeof forbidden === 'function' 69 | ? forbidden.call(this, this) 70 | : forbidden 71 | return 72 | } 73 | 74 | this.filter = ipFilter 75 | this.identifier = identifier 76 | 77 | return yield * next 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "koa-ip-filter", 3 | "version": "3.0.0", 4 | "description": "Middleware for [koa][] that filters IPs against glob patterns, RegExp, string or array of globs. Support custom `403 Forbidden` message and custom ID.", 5 | "repository": "tunnckoCore/koa-ip-filter", 6 | "author": "Charlike Mike Reagent <@tunnckoCore> (http://www.tunnckocore.tk)", 7 | "precommit.silent": true, 8 | "main": "index.js", 9 | "license": "MIT", 10 | "scripts": { 11 | "lint": "standard --verbose", 12 | "pretest": "npm run lint", 13 | "test": "npm run coverage", 14 | "posttest": "npm run lint:coverage", 15 | "coverage": "nyc node test.js", 16 | "lint:coverage": "nyc check-coverage --lines 100 --branches 100 --statements 100 --functions 100", 17 | "report-coverage": "nyc report --reporter=text-lcov | coveralls", 18 | "prerelease": "npm test", 19 | "release": "standard-version --sign --no-verify", 20 | "precommit": "git add --all", 21 | "commit": "git-cz" 22 | }, 23 | "dependencies": { 24 | "ip-filter": "^2.0.0" 25 | }, 26 | "devDependencies": { 27 | "commitizen": "^2.8.6", 28 | "coveralls": "^2.11.12", 29 | "cz-conventional-changelog": "^1.2.0", 30 | "koa": "^1.2.4", 31 | "koa-hello-world": "^1.0.1", 32 | "mukla": "^0.4.1", 33 | "nyc": "^8.1.0", 34 | "pre-commit": "^1.1.3", 35 | "standard": "^8.0.0", 36 | "standard-version": "^2.4.0", 37 | "supertest": "^2.0.0" 38 | }, 39 | "files": [ 40 | "index.js" 41 | ], 42 | "keywords": [ 43 | "array", 44 | "blacklist", 45 | "custom", 46 | "filter", 47 | "function", 48 | "glob", 49 | "id", 50 | "ip", 51 | "ips", 52 | "koa", 53 | "koajs", 54 | "match", 55 | "matcher", 56 | "micromatch", 57 | "middleware", 58 | "minimatch", 59 | "options", 60 | "pattern", 61 | "regex", 62 | "regexp", 63 | "string", 64 | "support", 65 | "whitelist" 66 | ], 67 | "config": { 68 | "commitizen": { 69 | "path": "./node_modules/cz-conventional-changelog" 70 | } 71 | }, 72 | "verb": { 73 | "run": true, 74 | "toc": false, 75 | "layout": "empty", 76 | "tasks": [ 77 | "readme" 78 | ], 79 | "related": { 80 | "list": [ 81 | "is-match", 82 | "ip-filter", 83 | "koa-ip-filter", 84 | "koa-better-body", 85 | "micromatch", 86 | "is-match-ip" 87 | ] 88 | }, 89 | "reflinks": [ 90 | "ip-filter", 91 | "micromatch", 92 | "is-match", 93 | "koa" 94 | ], 95 | "lint": { 96 | "reflinks": true 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * koa-ip-filter 3 | * 4 | * Copyright (c) 2015-2016 Charlike Mike Reagent, contributors. 5 | * Released under the MIT license. 6 | */ 7 | 8 | /* jshint asi:true, esnext:true */ 9 | 10 | 'use strict' 11 | 12 | var test = require('mukla') 13 | var ipFilter = require('./index') 14 | var request = require('supertest') 15 | var helloWorld = require('koa-hello-world') 16 | var koa = require('koa') 17 | 18 | function middleware (fn) { 19 | return koa().use(fn).use(helloWorld()).callback() 20 | } 21 | 22 | test('should yield next middleware if no `opts.filter` given', function (done) { 23 | var server = middleware(ipFilter()) 24 | 25 | request(server) 26 | .get('/') 27 | .expect(200, 'Hello World') 28 | .end(done) 29 | }) 30 | 31 | test('should have `opts.id` and it should be binded to koa this', function (done) { 32 | var server = middleware(ipFilter({ 33 | id: function _id_ (ctx) { 34 | this.set('x-github-username', 'tunnckoCore') 35 | return ctx.request.header['x-koaip'] 36 | } 37 | })) 38 | 39 | request(server).get('/') 40 | .expect('x-github-username', 'tunnckoCore') 41 | .expect(200) 42 | .expect('Hello World') 43 | .end(done) 44 | }) 45 | 46 | test('should `403 Forbidden` if not match to `opts.filter`', function (done) { 47 | var server = middleware(ipFilter({ 48 | id: function () { 49 | return this.request.header['x-koaip'] 50 | }, 51 | filter: '1.2.3.*' 52 | })) 53 | 54 | request(server).get('/').set('x-koaip', '4.4.8.8') 55 | .expect('403 Forbidden').expect(403, done) 56 | }) 57 | 58 | test('should `403 Forbidden` if IP is in blacklist', function (done) { 59 | request(middleware(ipFilter({ 60 | id: function () { 61 | return this.request.header['x-koaip'] 62 | }, 63 | filter: ['*', '!89.???.30.*'] 64 | }))) 65 | .get('/').set('x-koaip', '89.111.30.8') 66 | .expect(403, '403 Forbidden') 67 | .end(done) 68 | }) 69 | 70 | test('should `200 OK` if not in blacklist range', function (done) { 71 | var mw = ipFilter({ 72 | id: function () { 73 | var header = this.request.header['x-koaip'] 74 | return header 75 | }, 76 | // it can be double star - globstar 77 | // or *.*.*.* 78 | filter: ['*.*.*.*', '!111.??.244.*'] 79 | }) 80 | request(middleware(mw)) 81 | .get('/') 82 | .set('x-koaip', '4.4.8.8') 83 | .expect(200, 'Hello World') 84 | .end(done) 85 | }) 86 | 87 | test('should support custom message for 403 Forbidden', function (done) { 88 | var server = middleware(ipFilter({ 89 | id: function () { 90 | return this.request.header['x-koaip'] 91 | }, 92 | filter: ['*', '!89.???.30.*'], 93 | forbidden: '403, Get out of here!' 94 | })) 95 | 96 | request(server) 97 | .get('/') 98 | .set('x-koaip', '89.111.30.8') 99 | .expect(403, '403, Get out of here!') 100 | .end(done) 101 | }) 102 | 103 | test('should be able `opts.forbidden` to be function', function (done) { 104 | var server = middleware(ipFilter({ 105 | id: function () { 106 | return this.request.header['x-koaip'] 107 | }, 108 | filter: '123.225.23.120', 109 | forbidden: function (ctx) { 110 | this.set('X-Forbidden', 'Can be function') 111 | ctx.set('X-Seriously', 'yes') 112 | return 'opts.forbidden can be function' 113 | } 114 | })) 115 | 116 | request(server) 117 | .get('/') 118 | .set('x-koaip', '55.55.55.55') 119 | .expect('X-Forbidden', 'Can be function') 120 | .expect('X-Seriously', 'yes') 121 | .expect(403, 'opts.forbidden can be function') 122 | .end(done) 123 | }) 124 | 125 | test('should have `this.filter` and `this.identifier` in next middleware', function (done) { 126 | var ok = false 127 | var server = koa() 128 | .use(ipFilter({ 129 | // it can be double star - globstar 130 | // or *.*.*.* 131 | filter: ['**', '!213.15.*'] 132 | })) 133 | .use(helloWorld()) 134 | .use(function * (next) { 135 | test.ok(this.filter, 'should have `this.filter` in next') 136 | test.ok(this.identifier, 'should have `this.identifier` in next') 137 | test.equal(typeof this.filter, 'function', 'should have `this.filter` method') 138 | test.equal(typeof this.identifier, 'string', 'should have `this.identifier`') 139 | ok = true 140 | yield next 141 | }) 142 | 143 | request(server.callback()) 144 | .get('/') 145 | .set('x-koaip', '7.7.7.7') 146 | .expect(200, 'Hello World') 147 | .end(function (err) { 148 | test.ifError(err) 149 | test.equal(ok, true) 150 | done() 151 | }) 152 | }) 153 | 154 | test('should not have `this.filter` if no `opts.filter` given', function (done) { 155 | var ok = false 156 | var server = koa().use(ipFilter()).use(helloWorld()).use(function * (next) { 157 | test.ok(!this.filter, 'should not have `this.filter` in next') 158 | test.ok(!this.identifier, 'should not have `this.identifier` in next') 159 | ok = true 160 | yield next 161 | }).callback() 162 | 163 | request(server) 164 | .get('/') 165 | .expect('Hello World') 166 | .expect(200, function (err) { 167 | test.ifError(err) 168 | test.equal(ok, true) 169 | done() 170 | }) 171 | }) 172 | --------------------------------------------------------------------------------