rack-block 0.0.1.pre1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.travis.yml +8 -0
- data/Gemfile.lock +11 -1
- data/LICENSE +18 -0
- data/README.md +121 -0
- data/index.html +108 -0
- data/lib/rack/block.rb +14 -2
- data/lib/rack/block/dsl.rb +2 -3
- data/lib/rack/block/dsl/bot_ua_pattern.rb +0 -0
- data/lib/rack/block/dsl/builtin_bot_pattern.rb +21 -0
- data/lib/rack/block/dsl/matchers.rb +35 -2
- data/lib/rack/block/dsl/responses.rb +61 -3
- data/lib/rack/block/version.rb +1 -1
- data/rack-block.gemspec +5 -3
- data/spec/integrations/bot_pattern_spec.rb +43 -0
- data/spec/integrations/ip_blocking_spec.rb +68 -0
- data/spec/integrations/path_matching_spec.rb +53 -0
- data/spec/integrations/response_verbs_spec.rb +133 -0
- data/spec/integrations/ua_blocking_spec.rb +44 -20
- metadata +50 -16
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rack-block (0.0.1)
|
4
|
+
rack-block (0.0.1.pre1)
|
5
5
|
rack (>= 1.3)
|
6
6
|
|
7
7
|
GEM
|
@@ -13,8 +13,11 @@ GEM
|
|
13
13
|
guard-rspec (0.5.9)
|
14
14
|
guard (>= 0.8.4)
|
15
15
|
rack (1.3.5)
|
16
|
+
rack-protection (1.1.4)
|
17
|
+
rack
|
16
18
|
rack-test (0.6.1)
|
17
19
|
rack (>= 1.0)
|
20
|
+
rake (0.9.2.2)
|
18
21
|
rspec (2.7.0)
|
19
22
|
rspec-core (~> 2.7.0)
|
20
23
|
rspec-expectations (~> 2.7.0)
|
@@ -23,7 +26,12 @@ GEM
|
|
23
26
|
rspec-expectations (2.7.0)
|
24
27
|
diff-lcs (~> 1.1.2)
|
25
28
|
rspec-mocks (2.7.0)
|
29
|
+
sinatra (1.3.1)
|
30
|
+
rack (~> 1.3, >= 1.3.4)
|
31
|
+
rack-protection (~> 1.1, >= 1.1.2)
|
32
|
+
tilt (~> 1.3, >= 1.3.3)
|
26
33
|
thor (0.14.6)
|
34
|
+
tilt (1.3.3)
|
27
35
|
|
28
36
|
PLATFORMS
|
29
37
|
ruby
|
@@ -32,4 +40,6 @@ DEPENDENCIES
|
|
32
40
|
guard-rspec
|
33
41
|
rack-block!
|
34
42
|
rack-test (> 0)
|
43
|
+
rake (> 0)
|
35
44
|
rspec (>= 2)
|
45
|
+
sinatra (> 1.0)
|
data/LICENSE
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright (c) 2011 Uchio Kondo <udzura@udzura.jp>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
4
|
+
software and associated documentation files (the "Software"), to deal in the Software
|
5
|
+
without restriction, including without limitation the rights to use, copy, modify, merge,
|
6
|
+
publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
7
|
+
to whom the Software is furnished to do so, subject to the following conditions:
|
8
|
+
|
9
|
+
The above copyright notice and this permission notice shall be included in all copies or
|
10
|
+
substantial portions of the Software.
|
11
|
+
|
12
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
13
|
+
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
14
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
15
|
+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
16
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
17
|
+
|
18
|
+
http://mit-license.org/
|
data/README.md
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
# rack-block
|
2
|
+
|
3
|
+
A rack middleware for controlling accesses by search bot or not, remote ip address, etc.
|
4
|
+
|
5
|
+
## Install
|
6
|
+
|
7
|
+
```bash
|
8
|
+
$ gem install rack-block
|
9
|
+
```
|
10
|
+
|
11
|
+
No doubt it depends on `rack` (>= 1.3).
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
### block all bot accesses:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
use Rack::Block do
|
19
|
+
bot_access do
|
20
|
+
halt 404
|
21
|
+
end
|
22
|
+
end
|
23
|
+
run App.new
|
24
|
+
```
|
25
|
+
|
26
|
+
### block all bot accesses on a specific path:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
use Rack::Block do
|
30
|
+
bot_access do
|
31
|
+
path '/secret/*' do
|
32
|
+
halt 404
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
run App.new
|
37
|
+
```
|
38
|
+
|
39
|
+
### block some patterns of accesses:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
use Rack::Block do
|
43
|
+
ua_pattern /googlebot/i do
|
44
|
+
halt 404
|
45
|
+
end
|
46
|
+
end
|
47
|
+
run App.new
|
48
|
+
```
|
49
|
+
|
50
|
+
### block accesses from specific IP(s):
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
use Rack::Block do
|
54
|
+
ip_pattern '192.0.0.0' do
|
55
|
+
# expressions like '192.0.0.' also available
|
56
|
+
halt 404
|
57
|
+
end
|
58
|
+
end
|
59
|
+
run App.new
|
60
|
+
```
|
61
|
+
|
62
|
+
### redirect accesses:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
use Rack::Block do
|
66
|
+
bot_access do
|
67
|
+
path '/secret/*' do
|
68
|
+
redirect '/'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
run App.new
|
73
|
+
```
|
74
|
+
|
75
|
+
### redirect accesses to a double app:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
use Rack::Block do
|
79
|
+
bot_access do
|
80
|
+
path '/secret/*' do
|
81
|
+
double { Dummy.new # is a Rack-compatible app }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
run App.new
|
86
|
+
```
|
87
|
+
|
88
|
+
More usage on RDoc: [http://rubydoc.info/github/udzura/rack-block/master/frames]
|
89
|
+
|
90
|
+
Or please look into `spec/*`
|
91
|
+
|
92
|
+
## Related Sites
|
93
|
+
|
94
|
+
* (official)[http://udzura.jp/rack-block]
|
95
|
+
* (rubygems.org)[https://rubygems.org/gems/rack-block]
|
96
|
+
* (github)[https://github.com/udzura/rack-block]
|
97
|
+
* (travis-ci)[http://travis-ci.org/udzura/rack-block] / <img src="https://secure.travis-ci.org/udzura/rack-block.png" alt="build status" />
|
98
|
+
* (author's blog)[http://blog.udzura.jp] (Japanese)
|
99
|
+
|
100
|
+
## Todo
|
101
|
+
|
102
|
+
* Make it more DRY
|
103
|
+
* More test cases
|
104
|
+
* Refactoring internal classes
|
105
|
+
* Proxying accesses to another server
|
106
|
+
* Passing IP patterns like `'192.0.0.0/24'`...
|
107
|
+
|
108
|
+
## Contributing to rack-block
|
109
|
+
|
110
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
111
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
112
|
+
* Fork the project
|
113
|
+
* Start a feature/bugfix branch
|
114
|
+
* Commit and push until you are happy with your contribution
|
115
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
116
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
117
|
+
|
118
|
+
## Copyright
|
119
|
+
|
120
|
+
Copyright (c) 2011 Uchio Kondo <udzura@udzura.jp>. See LICENSE for
|
121
|
+
further details.
|
data/index.html
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset='utf-8'>
|
5
|
+
|
6
|
+
<title>udzura/rack-block @ GitHub</title>
|
7
|
+
|
8
|
+
<style type="text/css">
|
9
|
+
body {
|
10
|
+
margin-top: 1.0em;
|
11
|
+
background-color: #d25b93;
|
12
|
+
font-family: Helvetica, Arial, FreeSans, san-serif;
|
13
|
+
color: #000000;
|
14
|
+
}
|
15
|
+
#container {
|
16
|
+
margin: 0 auto;
|
17
|
+
width: 700px;
|
18
|
+
}
|
19
|
+
h1 { font-size: 3.8em; color: #2da46c; margin-bottom: 3px; }
|
20
|
+
h1 .small { font-size: 0.4em; }
|
21
|
+
h1 a { text-decoration: none }
|
22
|
+
h2 { font-size: 1.5em; color: #2da46c; }
|
23
|
+
h3 { text-align: center; color: #2da46c; }
|
24
|
+
a { color: #2da46c; }
|
25
|
+
.description { font-size: 1.2em; margin-bottom: 30px; margin-top: 30px; font-style: italic;}
|
26
|
+
.download { float: right; }
|
27
|
+
pre { background: #000; color: #fff; padding: 15px;}
|
28
|
+
hr { border: 0; width: 80%; border-bottom: 1px solid #aaa}
|
29
|
+
.footer { text-align:center; padding-top:30px; font-style: italic; }
|
30
|
+
</style>
|
31
|
+
</head>
|
32
|
+
|
33
|
+
<body>
|
34
|
+
<a href="https://github.com/udzura/rack-block"><img style="position: absolute; top: 0; right: 0; border: 0;" src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
|
35
|
+
|
36
|
+
<div id="container">
|
37
|
+
|
38
|
+
<div class="download">
|
39
|
+
<a href="https://github.com/udzura/rack-block/zipball/master">
|
40
|
+
<img border="0" width="90" src="https://github.com/images/modules/download/zip.png"></a>
|
41
|
+
<a href="https://github.com/udzura/rack-block/tarball/master">
|
42
|
+
<img border="0" width="90" src="https://github.com/images/modules/download/tar.png"></a>
|
43
|
+
</div>
|
44
|
+
|
45
|
+
<h1><a href="https://github.com/udzura/rack-block">rack-block</a>
|
46
|
+
<span class="small">by <a href="https://github.com/udzura">udzura</a></span></h1>
|
47
|
+
|
48
|
+
<div class="description">
|
49
|
+
A rack middleware for handling search bot access, ip block, etc.
|
50
|
+
</div>
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
<h2>Dependencies</h2>
|
56
|
+
<p>rack (>= 1.3)</p>
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
<h2>Install</h2>
|
61
|
+
<p> gem install rack-block
|
62
|
+
|
63
|
+
then edit `config.ru`:
|
64
|
+
use Rack::Block do
|
65
|
+
bot_access do
|
66
|
+
path '/foo' do
|
67
|
+
halt 404
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
run YourApp.new
|
72
|
+
</p>
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
<h2>License</h2>
|
77
|
+
<p>MIT License.</p>
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
<h2>Authors</h2>
|
82
|
+
<p>Uchio Kondo (udzura@udzura.jp)
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
<h2>Contact</h2>
|
87
|
+
<p>Uchio Kondo (udzura@udzura.jp)
|
88
|
+
|
89
|
+
|
90
|
+
<h2>Download</h2>
|
91
|
+
<p>
|
92
|
+
You can download this project in either
|
93
|
+
<a href="https://github.com/udzura/rack-block/zipball/master">zip</a> or
|
94
|
+
<a href="https://github.com/udzura/rack-block/tarball/master">tar formats.
|
95
|
+
</p>
|
96
|
+
<p>You can also clone the project with <a href="http://git-scm.com">Git</a>
|
97
|
+
by running:
|
98
|
+
<pre>$ git clone git://github.com/udzura/rack-block</pre>
|
99
|
+
</p>
|
100
|
+
|
101
|
+
<div class="footer">
|
102
|
+
get the source code on GitHub : <a href="https://github.com/udzura/rack-block">udzura/rack-block</a>
|
103
|
+
</div>
|
104
|
+
|
105
|
+
</div>
|
106
|
+
|
107
|
+
</body>
|
108
|
+
</html>
|
data/lib/rack/block.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'rack'
|
2
2
|
module Rack
|
3
3
|
class Block
|
4
|
-
|
4
|
+
require 'rack/block/dsl'
|
5
5
|
|
6
6
|
include DSL::Matchers
|
7
7
|
include DSL::Responses
|
@@ -26,11 +26,23 @@ module Rack
|
|
26
26
|
path_matchers.each_pair do |path, action_with_args|
|
27
27
|
if path =~ req.path_info
|
28
28
|
action, *args = action_with_args
|
29
|
-
return send action, *args
|
29
|
+
return send action, req, *args
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
34
|
+
|
35
|
+
self.ip_matchers.each_pair do |pattern, path_matchers|
|
36
|
+
if pattern =~ req.ip
|
37
|
+
path_matchers.each_pair do |path, action_with_args|
|
38
|
+
if path =~ req.path_info
|
39
|
+
action, *args = action_with_args
|
40
|
+
return send action, req, *args
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
34
46
|
app.call(env)
|
35
47
|
end
|
36
48
|
# Your code goes here...
|
data/lib/rack/block/dsl.rb
CHANGED
File without changes
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# patterns barrowed from CodeIgniter's helper
|
2
|
+
# thanks:
|
3
|
+
# https://github.com/EllisLab/CodeIgniter/blob/develop/application/config/user_agents.php#L200
|
4
|
+
# http://www.useragentstring.com/pages/useragentstring.php
|
5
|
+
# TODO more more bot sample
|
6
|
+
module Rack::Block::DSL
|
7
|
+
module Matchers
|
8
|
+
BUILTIN_BOT_PATTERN = /#{
|
9
|
+
["Googlebot",
|
10
|
+
"MSNBot",
|
11
|
+
"Bing",
|
12
|
+
"Inktomi Slurp",
|
13
|
+
"Yahoo",
|
14
|
+
"AskJeeves",
|
15
|
+
"FastCrawler",
|
16
|
+
"InfoSeek Robot 1.0",
|
17
|
+
"Baiduspider",
|
18
|
+
"Lycos"].join("|")
|
19
|
+
}/i
|
20
|
+
end
|
21
|
+
end
|
@@ -1,11 +1,11 @@
|
|
1
|
+
require 'rack/block/dsl/builtin_bot_pattern'
|
1
2
|
module Rack::Block::DSL
|
2
3
|
module Matchers
|
3
|
-
BUILTIN_BOT_PATTERN = /googlebot/i
|
4
|
-
|
5
4
|
def bot_access(&block)
|
6
5
|
ua_pattern BUILTIN_BOT_PATTERN, &block
|
7
6
|
end
|
8
7
|
|
8
|
+
# TODO it's NO DRY
|
9
9
|
def ua_pattern(pattern, &block)
|
10
10
|
@_current_matching_type, orig = :by_UA, @_current_matching_type
|
11
11
|
@_current_matching_ua_pattern, orig_ptn = pattern, @_current_matching_ua_pattern
|
@@ -15,11 +15,44 @@ module Rack::Block::DSL
|
|
15
15
|
@_current_matching_type = orig
|
16
16
|
end
|
17
17
|
|
18
|
+
def ip_pattern(pattern, &block)
|
19
|
+
@_current_matching_type, orig = :by_IP, @_current_matching_type
|
20
|
+
@_current_matching_ip_pattern, orig_ptn = ip_to_pattern(pattern), @_current_matching_ip_pattern
|
21
|
+
yield
|
22
|
+
ensure
|
23
|
+
@_current_matching_ip_pattern = orig_ptn
|
24
|
+
@_current_matching_type = orig
|
25
|
+
end
|
26
|
+
|
27
|
+
alias block_ua ua_pattern
|
28
|
+
alias block_ip ip_pattern
|
29
|
+
|
18
30
|
def path(pattern, &block)
|
19
31
|
@_current_matching_path, orig = pattern, @_current_matching_path
|
20
32
|
yield
|
21
33
|
ensure
|
22
34
|
@_current_matching_path = orig
|
23
35
|
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def ip_to_pattern(ip_pattern)
|
39
|
+
case ip_pattern
|
40
|
+
when /^(\d+)(\.\d+){3}$/
|
41
|
+
Regexp.compile("^" + ip_pattern.gsub('.', '\\.') + "$")
|
42
|
+
when /^(\d+)(\.\d+){0,2}\.?$/
|
43
|
+
ip_pattern = ip_pattern.sub(/\.$/, '')
|
44
|
+
Regexp.compile("^" + ip_pattern.gsub('.', '\\.') + '(\\.\\d+)+' + "$")
|
45
|
+
else
|
46
|
+
raise ArgumentError, 'passed invalid IP string'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def in_ua_block?
|
51
|
+
@_current_matching_type == :by_UA
|
52
|
+
end
|
53
|
+
|
54
|
+
def in_ip_block?
|
55
|
+
@_current_matching_type == :by_IP
|
56
|
+
end
|
24
57
|
end
|
25
58
|
end
|
@@ -1,9 +1,18 @@
|
|
1
1
|
module Rack::Block::DSL
|
2
2
|
module Responses
|
3
|
+
def detect_matching_pattern
|
4
|
+
if in_ua_block?
|
5
|
+
ua_matchers[@_current_matching_ua_pattern] ||= {}
|
6
|
+
elsif in_ip_block?
|
7
|
+
ip_matchers[@_current_matching_ip_pattern] ||= {}
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# TODO non DRY on handling `@_current_matching_path' !!!! it is terrible!!
|
3
12
|
def halt(code, opts={})
|
4
13
|
if @_current_matching_path
|
5
|
-
path = Regexp.compile("^" + @_current_matching_path.
|
6
|
-
current_matchers =
|
14
|
+
path = Regexp.compile("^" + @_current_matching_path.gsub(/\./, '\\.').gsub(/\*/, '.*'))
|
15
|
+
current_matchers = detect_matching_pattern
|
7
16
|
current_matchers[path] = [:do_halt, code, opts]
|
8
17
|
else
|
9
18
|
path '*' do
|
@@ -12,10 +21,59 @@ module Rack::Block::DSL
|
|
12
21
|
end
|
13
22
|
end
|
14
23
|
|
15
|
-
def
|
24
|
+
def redirect(dest_path, opts={})
|
25
|
+
if @_current_matching_path
|
26
|
+
path = Regexp.compile("^" + @_current_matching_path.gsub(/\./, '\\.').gsub(/\*/, '.*'))
|
27
|
+
current_matchers = detect_matching_pattern
|
28
|
+
current_matchers[path] = [:do_redirect, dest_path, opts]
|
29
|
+
else
|
30
|
+
path '*' do
|
31
|
+
redirect dest_path, opts
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def dummy_app(opts={}, &app_proc)
|
37
|
+
if @_current_matching_path
|
38
|
+
path = Regexp.compile("^" + @_current_matching_path.gsub(/\./, '\\.').gsub(/\*/, '.*'))
|
39
|
+
current_matchers = detect_matching_pattern
|
40
|
+
current_matchers[path] = [:do_dummy_app, app_proc, opts]
|
41
|
+
else
|
42
|
+
path '*' do
|
43
|
+
dummy_app opts, &app_proc
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
alias_method :double, :dummy_app
|
49
|
+
|
50
|
+
private
|
51
|
+
def do_halt(req, code, opts={})
|
16
52
|
headers = {"Content-Type" => "text/plain"}.merge(opts[:headers] || {})
|
17
53
|
body = opts[:body] || "Halt!"
|
18
54
|
return [code, headers, [body]]
|
19
55
|
end
|
56
|
+
|
57
|
+
def do_redirect(req, dest_path, opts={})
|
58
|
+
dest_full_path = case dest_path
|
59
|
+
when /^https?:\/\//
|
60
|
+
dest_path
|
61
|
+
else
|
62
|
+
uri = URI.parse req.url
|
63
|
+
uri.path = dest_path
|
64
|
+
uri.to_s
|
65
|
+
end
|
66
|
+
code = opts[:status_code] || 301
|
67
|
+
return [code, {"Location" => dest_full_path}, []]
|
68
|
+
end
|
69
|
+
|
70
|
+
def do_dummy_app(req, app_proc, opts={})
|
71
|
+
case app_proc.arity
|
72
|
+
when 0
|
73
|
+
return app_proc.call.call(req.env)
|
74
|
+
else
|
75
|
+
return app_proc.call(req.env)
|
76
|
+
end
|
77
|
+
end
|
20
78
|
end
|
21
79
|
end
|
data/lib/rack/block/version.rb
CHANGED
data/rack-block.gemspec
CHANGED
@@ -7,9 +7,9 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.version = Rack::Block::VERSION
|
8
8
|
s.authors = ["Uchio Kondo"]
|
9
9
|
s.email = ["udzura@udzura.jp"]
|
10
|
-
s.homepage = ""
|
11
|
-
s.summary = %q{A rack middleware for
|
12
|
-
s.description = %q{A rack middleware for
|
10
|
+
s.homepage = "http://udzura.jp/rack-block"
|
11
|
+
s.summary = %q{A rack middleware for controlling accesses by search bot or not, remote ip address, etc.}
|
12
|
+
s.description = %q{A rack middleware for controlling accesses by search bot or not, remote ip address, etc.}
|
13
13
|
|
14
14
|
s.rubyforge_project = "rack-block"
|
15
15
|
|
@@ -20,7 +20,9 @@ Gem::Specification.new do |s|
|
|
20
20
|
|
21
21
|
s.add_runtime_dependency "rack", '>= 1.3'
|
22
22
|
|
23
|
+
s.add_development_dependency "rake", '> 0'
|
23
24
|
s.add_development_dependency "rspec", '>= 2'
|
24
25
|
s.add_development_dependency "rack-test", '> 0'
|
26
|
+
s.add_development_dependency "sinatra", '> 1.0'
|
25
27
|
s.add_development_dependency "guard-rspec"
|
26
28
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/mock_app_helper')
|
3
|
+
|
4
|
+
describe "Blocking search bots" do
|
5
|
+
def access_root_as(ua)
|
6
|
+
header "User-Agent", ua
|
7
|
+
get '/'
|
8
|
+
end
|
9
|
+
|
10
|
+
before do
|
11
|
+
mock_app do
|
12
|
+
use Rack::Block do
|
13
|
+
bot_access { halt 404 }
|
14
|
+
end
|
15
|
+
run DEFAULT_APP
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'blocks google bot' do
|
20
|
+
access_root_as 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'
|
21
|
+
last_response.should be_not_found
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'blocks msn bot' do
|
25
|
+
access_root_as 'msnbot/1.1 (+http://search.msn.com/msnbot.htm)'
|
26
|
+
last_response.should be_not_found
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'blocks bing bot' do
|
30
|
+
access_root_as 'Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)'
|
31
|
+
last_response.should be_not_found
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'blocks yahoo! slurp' do
|
35
|
+
access_root_as 'Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)'
|
36
|
+
last_response.should be_not_found
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'blocks baiduspider' do
|
40
|
+
access_root_as 'Baiduspider+(+http://www.baidu.com/search/spider_jp.html)'
|
41
|
+
last_response.should be_not_found
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/mock_app_helper')
|
3
|
+
|
4
|
+
describe "Blocking by IP" do
|
5
|
+
it 'blocks accesses from a specific IP' do
|
6
|
+
mock_app {
|
7
|
+
use Rack::Block do
|
8
|
+
ip_pattern '10.20.30.40' do
|
9
|
+
halt 404
|
10
|
+
end
|
11
|
+
end
|
12
|
+
run DEFAULT_APP
|
13
|
+
}
|
14
|
+
|
15
|
+
header "X-Forwarded-For", "10.20.30.40"
|
16
|
+
['/', '/any', '/path/blocked'].each do |path|
|
17
|
+
get path
|
18
|
+
last_response.should be_not_found
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'blocks accesses from a specific IP pattern' do
|
23
|
+
mock_app {
|
24
|
+
use Rack::Block do
|
25
|
+
ip_pattern '10.20.30.' do
|
26
|
+
halt 404
|
27
|
+
end
|
28
|
+
end
|
29
|
+
run DEFAULT_APP
|
30
|
+
}
|
31
|
+
|
32
|
+
header "X-Forwarded-For", "10.20.30.40"
|
33
|
+
['/', '/any', '/path/blocked'].each do |path|
|
34
|
+
get path
|
35
|
+
last_response.should be_not_found
|
36
|
+
end
|
37
|
+
|
38
|
+
header "X-Forwarded-For", "10.20.30.50"
|
39
|
+
['/', '/any', '/path/blocked'].each do |path|
|
40
|
+
get path
|
41
|
+
last_response.should be_not_found
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'blocks accesses from a specific IP pattern(with a netmask)' do
|
46
|
+
pending "Not yet implemented, netmask expressions to Regexp or some other matching methods..."
|
47
|
+
mock_app {
|
48
|
+
use Rack::Block do
|
49
|
+
ip_pattern '10.20.30.0/24' do
|
50
|
+
halt 404
|
51
|
+
end
|
52
|
+
end
|
53
|
+
run DEFAULT_APP
|
54
|
+
}
|
55
|
+
|
56
|
+
header "X-Forwarded-For", "10.20.30.40"
|
57
|
+
['/', '/any', '/path/blocked'].each do |path|
|
58
|
+
get path
|
59
|
+
last_response.should be_not_found
|
60
|
+
end
|
61
|
+
|
62
|
+
header "X-Forwarded-For", "10.20.30.50"
|
63
|
+
['/', '/any', '/path/blocked'].each do |path|
|
64
|
+
get path
|
65
|
+
last_response.should be_not_found
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/mock_app_helper')
|
3
|
+
|
4
|
+
describe "Path matching patterns" do
|
5
|
+
it 'matching path should specified with `/\' starting' do
|
6
|
+
mock_app {
|
7
|
+
use Rack::Block do
|
8
|
+
bot_access { path('/foo'){ halt 404 } }
|
9
|
+
end
|
10
|
+
run DEFAULT_APP
|
11
|
+
}
|
12
|
+
|
13
|
+
header "User-Agent", "Googlebot"
|
14
|
+
get '/foo'
|
15
|
+
last_response.should be_not_found
|
16
|
+
|
17
|
+
get '/bar/foo'
|
18
|
+
last_response.should be_ok
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'matching glob `*\' should be a wildcard' do
|
22
|
+
mock_app {
|
23
|
+
use Rack::Block do
|
24
|
+
bot_access { path('/foo/*/bar'){ halt 500 } }
|
25
|
+
end
|
26
|
+
run DEFAULT_APP
|
27
|
+
}
|
28
|
+
|
29
|
+
header "User-Agent", "Googlebot"
|
30
|
+
get '/foo/123/bar'
|
31
|
+
last_response.should be_server_error
|
32
|
+
|
33
|
+
get '/foo/hogehoge/bar/4'
|
34
|
+
last_response.should be_server_error
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'path including `.\' should be sane' do
|
38
|
+
mock_app {
|
39
|
+
use Rack::Block do
|
40
|
+
bot_access { path('/favicon.ico'){ halt 404 } }
|
41
|
+
end
|
42
|
+
run DEFAULT_APP
|
43
|
+
}
|
44
|
+
|
45
|
+
header "User-Agent", "Googlebot"
|
46
|
+
get '/favicon.ico'
|
47
|
+
last_response.should be_not_found
|
48
|
+
|
49
|
+
header "User-Agent", "Googlebot"
|
50
|
+
get '/favicon_ico'
|
51
|
+
last_response.should be_ok
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/mock_app_helper')
|
3
|
+
|
4
|
+
describe "Response value verbs" do
|
5
|
+
before do
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'can halt with status code 404' do
|
9
|
+
mock_app {
|
10
|
+
use Rack::Block do
|
11
|
+
bot_access { path('*'){ halt 404 } }
|
12
|
+
end
|
13
|
+
run DEFAULT_APP
|
14
|
+
}
|
15
|
+
|
16
|
+
header "User-Agent", "Googlebot"
|
17
|
+
get '/'
|
18
|
+
last_response.should be_not_found
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'can halt with status code 500' do
|
22
|
+
mock_app {
|
23
|
+
use Rack::Block do
|
24
|
+
bot_access { path('*'){ halt 500 } }
|
25
|
+
end
|
26
|
+
run DEFAULT_APP
|
27
|
+
}
|
28
|
+
|
29
|
+
header "User-Agent", "Googlebot"
|
30
|
+
get '/'
|
31
|
+
last_response.status.should eq(500)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'can redirect' do
|
35
|
+
mock_app {
|
36
|
+
use Rack::Block do
|
37
|
+
bot_access { path('*'){ redirect 'http://www.google.com/' } }
|
38
|
+
end
|
39
|
+
run DEFAULT_APP
|
40
|
+
}
|
41
|
+
|
42
|
+
header "User-Agent", "Googlebot"
|
43
|
+
get '/'
|
44
|
+
last_response.should be_redirect
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'can redirect internal' do
|
48
|
+
mock_app {
|
49
|
+
use Rack::Block do
|
50
|
+
bot_access { path('/foo/*'){ redirect '/' } }
|
51
|
+
end
|
52
|
+
run DEFAULT_APP
|
53
|
+
}
|
54
|
+
|
55
|
+
header "User-Agent", "Googlebot"
|
56
|
+
get '/foo/bar'
|
57
|
+
last_response.should be_redirect
|
58
|
+
|
59
|
+
follow_redirect!
|
60
|
+
last_response.body.should match /It is summer/
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'doubling application' do
|
64
|
+
require 'sinatra/base'
|
65
|
+
let :dummy do
|
66
|
+
Class.new(Sinatra::Base) do
|
67
|
+
get '/' do
|
68
|
+
"This is a dummy access to: #{request.path_info}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'can point access to a dummy application' do
|
74
|
+
dummy = dummy()
|
75
|
+
mock_app {
|
76
|
+
use Rack::Block do
|
77
|
+
bot_access do
|
78
|
+
path('*') do
|
79
|
+
dummy_app { dummy.new }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
run DEFAULT_APP
|
84
|
+
}
|
85
|
+
|
86
|
+
header "User-Agent", "Googlebot"
|
87
|
+
get '/'
|
88
|
+
last_response.body.should match /This is a dummy access to: \//
|
89
|
+
|
90
|
+
get '/not-found'
|
91
|
+
last_response.should be_not_found
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should be called with env' do
|
95
|
+
dummy = dummy()
|
96
|
+
mock_app {
|
97
|
+
use Rack::Block do
|
98
|
+
bot_access do
|
99
|
+
path('*') do
|
100
|
+
double do |env|
|
101
|
+
# Sinatra::Base and its subclasses have a #call method in his class methods
|
102
|
+
dummy.call(env)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
run DEFAULT_APP
|
108
|
+
}
|
109
|
+
|
110
|
+
header "User-Agent", "Googlebot"
|
111
|
+
get '/'
|
112
|
+
last_response.body.should match /This is a dummy access to: \//
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'should be aliased by #double' do
|
116
|
+
dummy = dummy()
|
117
|
+
mock_app {
|
118
|
+
use Rack::Block do
|
119
|
+
bot_access do
|
120
|
+
path('*') do
|
121
|
+
double { dummy.new }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
run DEFAULT_APP
|
126
|
+
}
|
127
|
+
|
128
|
+
header "User-Agent", "Googlebot"
|
129
|
+
get '/'
|
130
|
+
last_response.body.should match /This is a dummy access to: \//
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -2,23 +2,6 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
|
2
2
|
require File.expand_path(File.dirname(__FILE__) + '/mock_app_helper')
|
3
3
|
|
4
4
|
describe "Blocking by UA" do
|
5
|
-
before do
|
6
|
-
mock_app {
|
7
|
-
use Rack::Block do
|
8
|
-
bot_access do
|
9
|
-
path '/foo' do
|
10
|
-
halt 404
|
11
|
-
end
|
12
|
-
|
13
|
-
path '/bar' do
|
14
|
-
redirect '/'
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
run DEFAULT_APP
|
19
|
-
}
|
20
|
-
end
|
21
|
-
|
22
5
|
describe "Blocking built-in bot UA pattern" do
|
23
6
|
|
24
7
|
it "blocks all bot access if no nesting block" do
|
@@ -50,14 +33,55 @@ describe "Blocking by UA" do
|
|
50
33
|
run DEFAULT_APP
|
51
34
|
}
|
52
35
|
|
36
|
+
header "User-Agent", "Googlebot"
|
37
|
+
get '/foo'
|
38
|
+
last_response.should be_not_found
|
39
|
+
end
|
40
|
+
|
41
|
+
it "does not block excepting specified paths" do
|
42
|
+
mock_app {
|
43
|
+
use Rack::Block do
|
44
|
+
bot_access do
|
45
|
+
path '/foo' do
|
46
|
+
halt 404
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
run DEFAULT_APP
|
51
|
+
}
|
52
|
+
|
53
53
|
header "User-Agent", "Googlebot"
|
54
54
|
get '/'
|
55
55
|
last_response.should be_ok
|
56
|
+
end
|
57
|
+
end
|
56
58
|
|
57
|
-
|
58
|
-
|
59
|
+
describe "Blocking customized UA pattern" do
|
60
|
+
before do
|
61
|
+
mock_app {
|
62
|
+
use Rack::Block do
|
63
|
+
ua_pattern /MSIE [678]\.[05].*Windows/ do
|
64
|
+
halt 404
|
65
|
+
end
|
66
|
+
end
|
67
|
+
run DEFAULT_APP
|
68
|
+
}
|
59
69
|
end
|
60
70
|
|
61
|
-
it "
|
71
|
+
it "blocks customized UA" do
|
72
|
+
header "User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"
|
73
|
+
['/', '/any', '/path/blocked'].each do |path|
|
74
|
+
get path
|
75
|
+
last_response.should be_not_found
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
it "doesn't block non-matching UA" do
|
80
|
+
header "User-Agent", "Mozilla/5.0 (Ubuntu; X11; Linux i686; rv:8.0) Gecko/20100101 Firefox/8.0"
|
81
|
+
['/', '/any', '/path/not-blocked'].each do |path|
|
82
|
+
get path
|
83
|
+
last_response.should be_ok
|
84
|
+
end
|
85
|
+
end
|
62
86
|
end
|
63
87
|
end
|
metadata
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-block
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Uchio Kondo
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-12-
|
12
|
+
date: 2011-12-24 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
16
|
-
requirement: &
|
16
|
+
requirement: &85009500 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,21 @@ dependencies:
|
|
21
21
|
version: '1.3'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *85009500
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rake
|
27
|
+
requirement: &85009210 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>'
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *85009210
|
25
36
|
- !ruby/object:Gem::Dependency
|
26
37
|
name: rspec
|
27
|
-
requirement: &
|
38
|
+
requirement: &85008940 !ruby/object:Gem::Requirement
|
28
39
|
none: false
|
29
40
|
requirements:
|
30
41
|
- - ! '>='
|
@@ -32,10 +43,10 @@ dependencies:
|
|
32
43
|
version: '2'
|
33
44
|
type: :development
|
34
45
|
prerelease: false
|
35
|
-
version_requirements: *
|
46
|
+
version_requirements: *85008940
|
36
47
|
- !ruby/object:Gem::Dependency
|
37
48
|
name: rack-test
|
38
|
-
requirement: &
|
49
|
+
requirement: &85008680 !ruby/object:Gem::Requirement
|
39
50
|
none: false
|
40
51
|
requirements:
|
41
52
|
- - ! '>'
|
@@ -43,10 +54,21 @@ dependencies:
|
|
43
54
|
version: '0'
|
44
55
|
type: :development
|
45
56
|
prerelease: false
|
46
|
-
version_requirements: *
|
57
|
+
version_requirements: *85008680
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: sinatra
|
60
|
+
requirement: &85008300 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>'
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '1.0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *85008300
|
47
69
|
- !ruby/object:Gem::Dependency
|
48
70
|
name: guard-rspec
|
49
|
-
requirement: &
|
71
|
+
requirement: &85008070 !ruby/object:Gem::Requirement
|
50
72
|
none: false
|
51
73
|
requirements:
|
52
74
|
- - ! '>='
|
@@ -54,8 +76,9 @@ dependencies:
|
|
54
76
|
version: '0'
|
55
77
|
type: :development
|
56
78
|
prerelease: false
|
57
|
-
version_requirements: *
|
58
|
-
description: A rack middleware for
|
79
|
+
version_requirements: *85008070
|
80
|
+
description: A rack middleware for controlling accesses by search bot or not, remote
|
81
|
+
ip address, etc.
|
59
82
|
email:
|
60
83
|
- udzura@udzura.jp
|
61
84
|
executables: []
|
@@ -64,22 +87,32 @@ extra_rdoc_files: []
|
|
64
87
|
files:
|
65
88
|
- .gitignore
|
66
89
|
- .rspec
|
90
|
+
- .travis.yml
|
67
91
|
- Gemfile
|
68
92
|
- Gemfile.lock
|
69
93
|
- Guardfile
|
94
|
+
- LICENSE
|
95
|
+
- README.md
|
70
96
|
- Rakefile
|
97
|
+
- index.html
|
71
98
|
- lib/rack-block.rb
|
72
99
|
- lib/rack/block.rb
|
73
100
|
- lib/rack/block/dsl.rb
|
101
|
+
- lib/rack/block/dsl/bot_ua_pattern.rb
|
102
|
+
- lib/rack/block/dsl/builtin_bot_pattern.rb
|
74
103
|
- lib/rack/block/dsl/matchers.rb
|
75
104
|
- lib/rack/block/dsl/responses.rb
|
76
105
|
- lib/rack/block/version.rb
|
77
106
|
- rack-block.gemspec
|
107
|
+
- spec/integrations/bot_pattern_spec.rb
|
108
|
+
- spec/integrations/ip_blocking_spec.rb
|
78
109
|
- spec/integrations/mock_app_helper.rb
|
79
110
|
- spec/integrations/mock_app_works_spec.rb
|
111
|
+
- spec/integrations/path_matching_spec.rb
|
112
|
+
- spec/integrations/response_verbs_spec.rb
|
80
113
|
- spec/integrations/ua_blocking_spec.rb
|
81
114
|
- spec/spec_helper.rb
|
82
|
-
homepage:
|
115
|
+
homepage: http://udzura.jp/rack-block
|
83
116
|
licenses: []
|
84
117
|
post_install_message:
|
85
118
|
rdoc_options: []
|
@@ -94,13 +127,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
94
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
128
|
none: false
|
96
129
|
requirements:
|
97
|
-
- - ! '
|
130
|
+
- - ! '>='
|
98
131
|
- !ruby/object:Gem::Version
|
99
|
-
version:
|
132
|
+
version: '0'
|
100
133
|
requirements: []
|
101
134
|
rubyforge_project: rack-block
|
102
135
|
rubygems_version: 1.8.10
|
103
136
|
signing_key:
|
104
137
|
specification_version: 3
|
105
|
-
summary: A rack middleware for
|
138
|
+
summary: A rack middleware for controlling accesses by search bot or not, remote ip
|
139
|
+
address, etc.
|
106
140
|
test_files: []
|