rack-cerberus 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +0 -2
- data/MIT_LICENSE +1 -1
- data/README.md +7 -8
- data/example/config.ru +4 -5
- data/lib/rack/cerberus/version.rb +1 -1
- data/lib/rack/cerberus.rb +105 -51
- data/rack_cerberus.gemspec +20 -11
- data/test/test_rack_cerberus.rb +155 -0
- metadata +52 -10
- data/spec/rack_cerberus_spec.rb +0 -149
- data/spec/spec_helper.rb +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f646a686e4b181584972006a33b8637f44ddaef7
|
4
|
+
data.tar.gz: 152f0132226b3dd7803bf2507fb57b6d4c6523d4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c2dfda81ee63b2de2a1fb52be5e4511493f62d9d3c7256e98796f7c5fd68cec598ded528c1e0a59bd76178b662945a0ce3c823939422cbc12a72823cb77d0a37
|
7
|
+
data.tar.gz: 189a19fff0b2bbb33e5202806244b035d693d04214f19631d95fbf9b6d88cb523e86b6560d790675f9dd50022bff023906e1ae14e042ca1247f689c11b15e07e
|
data/Gemfile
CHANGED
data/MIT_LICENSE
CHANGED
data/README.md
CHANGED
@@ -8,18 +8,18 @@ options in order to style the authentication page.
|
|
8
8
|
Install with:
|
9
9
|
|
10
10
|
```
|
11
|
-
#
|
11
|
+
# gem install rack-cerberus
|
12
12
|
```
|
13
13
|
|
14
14
|
Or in your `Gemfile`:
|
15
15
|
|
16
|
-
```
|
16
|
+
```ruby
|
17
17
|
gem 'rack-cerberus'
|
18
18
|
```
|
19
19
|
|
20
20
|
You can use it almost the same way you use `Rack::Auth::Basic`:
|
21
21
|
|
22
|
-
```
|
22
|
+
```ruby
|
23
23
|
require 'rack/cerberus'
|
24
24
|
use Rack::Session::Cookie, secret: 'change_me'
|
25
25
|
use Rack::Cerberus do |login, pass|
|
@@ -45,7 +45,7 @@ There is an optional hash you can add for customisation it. Options are:
|
|
45
45
|
|
46
46
|
Which is used that way:
|
47
47
|
|
48
|
-
```
|
48
|
+
```ruby
|
49
49
|
use Rack::Cerberus, {company_name: 'Nintendo'} do |login, pass|
|
50
50
|
pass=='secret'
|
51
51
|
end
|
@@ -66,15 +66,14 @@ return `true` or `false`.
|
|
66
66
|
|
67
67
|
You can also use the 3rd argument which is the request object:
|
68
68
|
|
69
|
-
```
|
69
|
+
```ruby
|
70
70
|
use Rack::Cerberus, {company_name: 'Nintendo'} do |login, pass, req|
|
71
71
|
pass=='secret' && req.xhr?
|
72
72
|
end
|
73
73
|
```
|
74
74
|
|
75
75
|
This is useful if you want to check other details of the request.
|
76
|
-
Like the referer or another parameter. But bear in mind that `cerberus_login`
|
77
|
-
and `cerberus_pass` are still mandatory.
|
76
|
+
Like the referer or another parameter. But bear in mind that `cerberus_login` and `cerberus_pass` are still mandatory.
|
78
77
|
|
79
78
|
Example
|
80
79
|
-------
|
@@ -103,5 +102,5 @@ or send patches.
|
|
103
102
|
Copyright
|
104
103
|
---------
|
105
104
|
|
106
|
-
(c) 2010-
|
105
|
+
(c) 2010-2016 Mickael Riga - see `MIT_LICENSE` for details
|
107
106
|
|
data/example/config.ru
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
lib = File.expand_path('../../lib', __FILE__)
|
2
|
+
$:.unshift lib
|
3
|
+
require 'rack/cerberus'
|
2
4
|
|
3
5
|
use Rack::Session::Cookie, secret: 'change_me'
|
4
6
|
|
@@ -19,10 +21,7 @@ map '/' do
|
|
19
21
|
end
|
20
22
|
|
21
23
|
map '/secret' do
|
22
|
-
use Rack::Cerberus, {
|
23
|
-
company_name: 'Nintendo',
|
24
|
-
fg_color: 'red',
|
25
|
-
} do |login,pass|
|
24
|
+
use Rack::Cerberus, { company_name: 'Nintendo' } do |login,pass|
|
26
25
|
[login,pass]==['mario','bros']
|
27
26
|
end
|
28
27
|
run lambda {|env|
|
data/lib/rack/cerberus.rb
CHANGED
@@ -5,63 +5,115 @@ module Rack
|
|
5
5
|
|
6
6
|
class Cerberus
|
7
7
|
|
8
|
-
VERSION = '1.0.2'
|
9
|
-
|
10
8
|
class NoSessionError < RuntimeError; end
|
11
9
|
|
12
10
|
def self.new(*); ::Rack::MethodOverride.new(super); end
|
13
11
|
|
14
|
-
def initialize
|
12
|
+
def initialize app, options={}, &block
|
15
13
|
@app = app
|
16
14
|
defaults = {
|
17
15
|
company_name: 'Cerberus',
|
18
|
-
bg_color: '#
|
19
|
-
fg_color: '#
|
20
|
-
text_color: '#
|
21
|
-
icon_url: nil,
|
16
|
+
bg_color: '#93a1a1',
|
17
|
+
fg_color: '#002b36',
|
18
|
+
text_color: '#fdf6e3',
|
22
19
|
session_key: 'cerberus_user'
|
23
20
|
}
|
24
21
|
@options = defaults.merge(options)
|
25
|
-
@options[:icon] = @options[:icon_url].nil? ?
|
26
|
-
|
22
|
+
@options[:icon] = @options[:icon_url].nil? ?
|
23
|
+
'' :
|
24
|
+
"<img src='#{@options[:icon_url]}' /><br />"
|
25
|
+
@options[:css] = @options[:css_location].nil? ?
|
26
|
+
'' :
|
27
|
+
"<link href='#{@options[:css_location]}' rel='stylesheet' type='text/css'>"
|
27
28
|
@block = block
|
28
29
|
end
|
29
30
|
|
30
|
-
def call
|
31
|
+
def call env
|
31
32
|
dup._call(env)
|
32
33
|
end
|
33
34
|
|
34
|
-
def _call
|
35
|
-
|
36
|
-
req = Rack::Request.new
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
if env['PATH_INFO']=='/logout'
|
42
|
-
res = Rack::Response.new(env)
|
43
|
-
res.redirect(env['SCRIPT_NAME']=='' ? '/' : env['SCRIPT_NAME'])
|
44
|
-
res.finish
|
35
|
+
def _call env
|
36
|
+
ensure_session env
|
37
|
+
req = Rack::Request.new env
|
38
|
+
if (logged?(req) and !logging_out?(req)) or authorized?(req)
|
39
|
+
ensure_logged! req
|
40
|
+
if logging_out? req
|
41
|
+
logout_response req
|
45
42
|
else
|
46
|
-
@app.call
|
43
|
+
@app.call env
|
47
44
|
end
|
48
45
|
else
|
49
|
-
|
50
|
-
error = "<p class='err'>Wrong login or password</p>"
|
51
|
-
end
|
52
|
-
env['rack.session'].delete(@options[:session_key])
|
53
|
-
[
|
54
|
-
401, {'Content-Type' => 'text/html'},
|
55
|
-
[AUTH_PAGE % @options.merge({
|
56
|
-
error: error, submit_path: env['REQUEST_URI'],
|
57
|
-
request_method: req.request_method,
|
58
|
-
login: Rack::Utils.escape_html(login),
|
59
|
-
pass: Rack::Utils.escape_html(pass)
|
60
|
-
})]
|
61
|
-
]
|
46
|
+
form_response req
|
62
47
|
end
|
63
48
|
end
|
64
|
-
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def ensure_session env
|
53
|
+
if env['rack.session'].nil?
|
54
|
+
raise(NoSessionError, 'Cerberus cannot work without Session')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def h text
|
59
|
+
Rack::Utils.escape_html text
|
60
|
+
end
|
61
|
+
|
62
|
+
def login req
|
63
|
+
req.params['cerberus_login']
|
64
|
+
end
|
65
|
+
|
66
|
+
def pass req
|
67
|
+
req.params['cerberus_pass']
|
68
|
+
end
|
69
|
+
|
70
|
+
def logged? req
|
71
|
+
req.env['rack.session'][@options[:session_key]]!=nil
|
72
|
+
end
|
73
|
+
|
74
|
+
def provided_fields? req
|
75
|
+
login(req) and pass(req)
|
76
|
+
end
|
77
|
+
|
78
|
+
def authorized? req
|
79
|
+
provided_fields?(req) and
|
80
|
+
@block.call login(req), pass(req), req
|
81
|
+
end
|
82
|
+
|
83
|
+
def ensure_logged! req
|
84
|
+
req.env['rack.session'][@options[:session_key]] ||= login(req)
|
85
|
+
end
|
86
|
+
|
87
|
+
def ensure_logged_out! req
|
88
|
+
req.env['rack.session'].delete @options[:session_key]
|
89
|
+
end
|
90
|
+
|
91
|
+
def logging_out? req
|
92
|
+
req.path_info=='/logout'
|
93
|
+
end
|
94
|
+
|
95
|
+
def logout_response req
|
96
|
+
res = Rack::Response.new
|
97
|
+
res.redirect(req.script_name=='' ? '/' : req.script_name)
|
98
|
+
res.finish
|
99
|
+
end
|
100
|
+
|
101
|
+
def form_response req
|
102
|
+
if provided_fields? req
|
103
|
+
error = "<p class='err'>Wrong login or password</p>"
|
104
|
+
end
|
105
|
+
ensure_logged_out! req
|
106
|
+
[
|
107
|
+
401, {'Content-Type' => 'text/html'},
|
108
|
+
[AUTH_PAGE % @options.merge({
|
109
|
+
error: error, submit_path: req.env['REQUEST_URI'],
|
110
|
+
request_method: req.request_method,
|
111
|
+
login: h(login(req)),
|
112
|
+
pass: h(pass(req))
|
113
|
+
})]
|
114
|
+
]
|
115
|
+
end
|
116
|
+
|
65
117
|
AUTH_PAGE = <<-PAGE
|
66
118
|
<!DOCTYPE html>
|
67
119
|
<html><head>
|
@@ -81,7 +133,7 @@ module Rack
|
|
81
133
|
-moz-border-radius: 3px;
|
82
134
|
-webkit-border-radius: 3px;
|
83
135
|
color: white;
|
84
|
-
background-color:
|
136
|
+
background-color: #dc322f;
|
85
137
|
}
|
86
138
|
div {
|
87
139
|
text-align: left;
|
@@ -101,7 +153,23 @@ module Rack
|
|
101
153
|
input[type=text], input[type=password] {
|
102
154
|
display: block; width: 100%%; padding: 0.5em;
|
103
155
|
border: 0px; font-size: 1.25em;
|
156
|
+
background-color: %{text_color};
|
157
|
+
}
|
158
|
+
input[type=submit] {
|
159
|
+
background-color: %{bg_color};
|
160
|
+
color: %{fg_color};
|
161
|
+
padding: 0.5em;
|
162
|
+
-webkit-appearance: none;
|
163
|
+
-moz-appearance: none;
|
164
|
+
appearance: none;
|
165
|
+
border: 0;
|
166
|
+
cursor: pointer;
|
104
167
|
}
|
168
|
+
input[type=submit]:hover { background-color: %{text_color}; }
|
169
|
+
::-webkit-input-placeholder { color: %{bg_color}; }
|
170
|
+
:-moz-placeholder { color: %{bg_color}; }
|
171
|
+
::-moz-placeholder { color: %{bg_color}; }
|
172
|
+
:-ms-input-placeholder { color: %{bg_color}; }
|
105
173
|
</style>
|
106
174
|
%{css}
|
107
175
|
</head><body>
|
@@ -116,20 +184,6 @@ module Rack
|
|
116
184
|
<input type="hidden" name="_method" value="%{request_method}">
|
117
185
|
<p><input type="submit" value="SIGN IN →"></p>
|
118
186
|
</form>
|
119
|
-
<script type="text/javascript" charset="utf-8">
|
120
|
-
var login = document.getElementById('login');
|
121
|
-
var pass = document.getElementById('pass');
|
122
|
-
var focus = function() {
|
123
|
-
if (this.value==this.id) this.value = '';
|
124
|
-
}
|
125
|
-
var blur = function() {
|
126
|
-
if (this.value=='') this.value = this.id;
|
127
|
-
}
|
128
|
-
login.onfocus = focus;
|
129
|
-
pass.onfocus = focus;
|
130
|
-
login.onblur = blur;
|
131
|
-
pass.onblur = blur;
|
132
|
-
</script>
|
133
187
|
</div>
|
134
188
|
</body></html>
|
135
189
|
PAGE
|
data/rack_cerberus.gemspec
CHANGED
@@ -1,22 +1,31 @@
|
|
1
|
-
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$:.unshift lib
|
5
|
+
require 'rack/cerberus/version'
|
2
6
|
|
3
7
|
Gem::Specification.new do |s|
|
4
8
|
|
9
|
+
s.authors = ['Mickael Riga']
|
10
|
+
s.email = ['mig@mypeplum.com']
|
11
|
+
s.homepage = 'http://github.com/mig-hub/cerberus'
|
12
|
+
s.licenses = ['MIT']
|
13
|
+
|
5
14
|
s.name = 'rack-cerberus'
|
6
15
|
s.version = Rack::Cerberus::VERSION
|
7
|
-
s.summary =
|
8
|
-
s.description =
|
9
|
-
s.licenses = ['MIT']
|
16
|
+
s.summary = 'A Rack middleware for form-based authentication'
|
17
|
+
s.description = 'A Rack middleware for form-based authentication. It works roughly like Basic HTTP Authentication except that the authentication page can be styled with the middleware options.'
|
10
18
|
|
11
|
-
s.files = `git ls-files`.split("\n").sort
|
12
|
-
s.require_path = './lib'
|
13
|
-
s.add_dependency('rack')
|
14
|
-
s.test_files = s.files.select { |p| p =~ /^spec\/.*_spec.rb/ }
|
15
19
|
s.platform = Gem::Platform::RUBY
|
20
|
+
s.files = `git ls-files`.split("\n").sort
|
21
|
+
s.test_files = s.files.select { |p| p =~ /^test\/test_.*\.rb$/ }
|
22
|
+
s.require_paths = ['lib']
|
23
|
+
|
24
|
+
s.add_dependency 'rack', '>= 1.0'
|
16
25
|
|
17
|
-
s.
|
18
|
-
s.
|
19
|
-
s.
|
26
|
+
s.add_development_dependency 'bundler', '~> 1.13'
|
27
|
+
s.add_development_dependency 'minitest', '~> 5.8'
|
28
|
+
s.add_development_dependency 'rack-test', '~> 0.6'
|
20
29
|
|
21
30
|
end
|
22
31
|
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'rack/test'
|
3
|
+
require 'rack/cerberus'
|
4
|
+
|
5
|
+
ENV['RACK_ENV'] = 'test'
|
6
|
+
|
7
|
+
class TestRackCerberus < Minitest::Test
|
8
|
+
parallelize_me!
|
9
|
+
|
10
|
+
include Rack::Test::Methods
|
11
|
+
|
12
|
+
def secret_app
|
13
|
+
lambda {|env|
|
14
|
+
[
|
15
|
+
200,
|
16
|
+
{'Content-Type'=>'text/plain'},
|
17
|
+
"#{env['REQUEST_METHOD']} #{env['rack.session'].inspect}"
|
18
|
+
]
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def cerberus_app cerberus_options={}
|
23
|
+
Rack::Cerberus.new(secret_app, cerberus_options) do |login,pass|
|
24
|
+
[login,pass]==['mario@nintendo.com','bros']
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def mounted_app mount_path='/', cerberus_options={}
|
29
|
+
Rack::URLMap.new({
|
30
|
+
mount_path => Rack::Session::Cookie.new(cerberus_app(cerberus_options), {secret: '42'})
|
31
|
+
})
|
32
|
+
end
|
33
|
+
|
34
|
+
def app; @app; end
|
35
|
+
|
36
|
+
def body
|
37
|
+
last_response.body
|
38
|
+
end
|
39
|
+
|
40
|
+
def correct_logins
|
41
|
+
{
|
42
|
+
'cerberus_login' => 'mario@nintendo.com',
|
43
|
+
'cerberus_pass' => 'bros'
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
def wrong_logins
|
48
|
+
{
|
49
|
+
'cerberus_login' => 'fake_login',
|
50
|
+
'cerberus_pass' => 'fake_pass'
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def setup
|
55
|
+
@app = mounted_app
|
56
|
+
end
|
57
|
+
|
58
|
+
def teardown
|
59
|
+
clear_cookies
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_no_session_raises
|
63
|
+
@app = cerberus_app
|
64
|
+
assert_raises(Rack::Cerberus::NoSessionError) do
|
65
|
+
get '/'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_unauthorized_when_not_logged_in
|
70
|
+
get '/'
|
71
|
+
assert_equal 401, last_response.status
|
72
|
+
assert_equal String, body.class
|
73
|
+
assert_match 'name="cerberus_login" value=""', body
|
74
|
+
assert_match 'name="cerberus_pass" value=""', body
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_unauthorized_when_logins_are_incorrect
|
78
|
+
get '/', wrong_logins
|
79
|
+
assert_equal 401, last_response.status
|
80
|
+
assert_match 'Wrong login or password', body
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_fields_filled_with_previous_info
|
84
|
+
post '/', wrong_logins
|
85
|
+
assert_match 'name="cerberus_login" value="fake_login"', body
|
86
|
+
assert_match 'name="cerberus_pass" value="fake_pass"', body
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_submitted_info_is_html_escaped
|
90
|
+
post('/', {
|
91
|
+
'cerberus_login' => '<script>bad</script>',
|
92
|
+
'cerberus_pass' => '<script>bad</script>'
|
93
|
+
})
|
94
|
+
assert_match 'bad', body
|
95
|
+
refute_match '<script>bad</script>', body
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_authorized_when_logins_are_correct
|
99
|
+
get '/', correct_logins
|
100
|
+
assert_equal 200, last_response.status
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_calls_final_page_with_original_method
|
104
|
+
get '/'
|
105
|
+
assert_match 'name="_method" value="GET"', body
|
106
|
+
post '/', correct_logins.merge({'_method'=>'GET'})
|
107
|
+
assert_match /^GET/, body
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_stay_authorized_once_logged
|
111
|
+
get '/', correct_logins
|
112
|
+
get '/'
|
113
|
+
assert_equal 200, last_response.status
|
114
|
+
assert_match '"cerberus_user"=>"mario@nintendo.com"', body
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_logout_with_logout_path
|
118
|
+
@app = mounted_app '/admin'
|
119
|
+
get '/admin/', correct_logins
|
120
|
+
assert_equal 200, last_response.status
|
121
|
+
get '/admin/logout'
|
122
|
+
assert_equal 401, last_response.status
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_never_redirects_to_logout_path
|
126
|
+
@app = mounted_app '/admin'
|
127
|
+
get '/admin/logout', correct_logins
|
128
|
+
assert_equal 302, last_response.status
|
129
|
+
assert_equal '/admin', last_response['Location']
|
130
|
+
end
|
131
|
+
|
132
|
+
# Options
|
133
|
+
|
134
|
+
def test_no_css_location
|
135
|
+
get '/'
|
136
|
+
refute_match '<link', body
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_css_location
|
140
|
+
@app = mounted_app '/', css_location: '/main.css'
|
141
|
+
get '/'
|
142
|
+
assert_match '<link', body
|
143
|
+
assert_match '/main.css', body
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_can_change_session_key
|
147
|
+
@app = mounted_app '/', session_key: 'different_user'
|
148
|
+
get '/', correct_logins
|
149
|
+
get '/'
|
150
|
+
assert_equal 200, last_response.status
|
151
|
+
assert_match '"different_user"=>"mario@nintendo.com"', body
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-cerberus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mickael Riga
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -16,18 +16,61 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
19
|
+
version: '1.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.13'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.13'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.8'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rack-test
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.6'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.6'
|
27
69
|
description: A Rack middleware for form-based authentication. It works roughly like
|
28
70
|
Basic HTTP Authentication except that the authentication page can be styled with
|
29
71
|
the middleware options.
|
30
|
-
email:
|
72
|
+
email:
|
73
|
+
- mig@mypeplum.com
|
31
74
|
executables: []
|
32
75
|
extensions: []
|
33
76
|
extra_rdoc_files: []
|
@@ -42,8 +85,7 @@ files:
|
|
42
85
|
- lib/rack/cerberus.rb
|
43
86
|
- lib/rack/cerberus/version.rb
|
44
87
|
- rack_cerberus.gemspec
|
45
|
-
-
|
46
|
-
- spec/spec_helper.rb
|
88
|
+
- test/test_rack_cerberus.rb
|
47
89
|
homepage: http://github.com/mig-hub/cerberus
|
48
90
|
licenses:
|
49
91
|
- MIT
|
@@ -51,7 +93,7 @@ metadata: {}
|
|
51
93
|
post_install_message:
|
52
94
|
rdoc_options: []
|
53
95
|
require_paths:
|
54
|
-
-
|
96
|
+
- lib
|
55
97
|
required_ruby_version: !ruby/object:Gem::Requirement
|
56
98
|
requirements:
|
57
99
|
- - ">="
|
@@ -64,9 +106,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
106
|
version: '0'
|
65
107
|
requirements: []
|
66
108
|
rubyforge_project:
|
67
|
-
rubygems_version: 2.
|
109
|
+
rubygems_version: 2.5.1
|
68
110
|
signing_key:
|
69
111
|
specification_version: 4
|
70
112
|
summary: A Rack middleware for form-based authentication
|
71
113
|
test_files:
|
72
|
-
-
|
114
|
+
- test/test_rack_cerberus.rb
|
data/spec/rack_cerberus_spec.rb
DELETED
@@ -1,149 +0,0 @@
|
|
1
|
-
require 'rack/cerberus'
|
2
|
-
|
3
|
-
RSpec.describe Rack::Cerberus do
|
4
|
-
|
5
|
-
let(:secret_app) {
|
6
|
-
lambda {|env|
|
7
|
-
[200, {'Content-Type'=>'text/plain'}, env['rack.session'].inspect]
|
8
|
-
}
|
9
|
-
}
|
10
|
-
|
11
|
-
let(:cerberus_app) {
|
12
|
-
Rack::Cerberus.new(secret_app, cerberus_options) do |login,pass|
|
13
|
-
[login,pass]==['mario@nintendo.com','bros']
|
14
|
-
end
|
15
|
-
}
|
16
|
-
|
17
|
-
let(:app) {
|
18
|
-
Rack::URLMap.new({
|
19
|
-
mount_path => Rack::Session::Cookie.new(cerberus_app, {secret: '42'})
|
20
|
-
})
|
21
|
-
}
|
22
|
-
|
23
|
-
let(:cerberus_options) { {} }
|
24
|
-
let(:mount_path) { '/' }
|
25
|
-
|
26
|
-
before :each do
|
27
|
-
clear_cookies
|
28
|
-
end
|
29
|
-
|
30
|
-
context 'No session is set' do
|
31
|
-
let(:app) { cerberus_app }
|
32
|
-
it 'Raises' do
|
33
|
-
expect{ get('/') }.to raise_error(Rack::Cerberus::NoSessionError)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
context 'Not logged in' do
|
38
|
-
it 'Stops requests' do
|
39
|
-
get '/'
|
40
|
-
expect(last_response.status).to eq 401
|
41
|
-
body = last_response.body
|
42
|
-
expect(body.class).to eq String
|
43
|
-
expect(body).to include('name="cerberus_login" value=""')
|
44
|
-
expect(body).to include('name="cerberus_pass" value=""')
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
describe 'Logging in' do
|
49
|
-
|
50
|
-
context 'Login details are incorrect' do
|
51
|
-
it 'Stops requests' do
|
52
|
-
get('/', {'cerberus_login' => 'fake_login', 'cerberus_pass' => 'fake_pass'})
|
53
|
-
expect(last_response.status).to eq 401
|
54
|
-
expect(last_response.body).to include('Wrong login or password')
|
55
|
-
end
|
56
|
-
it 'Keeps what was entered in the fields' do
|
57
|
-
post('/', {'cerberus_login' => 'fake_login', 'cerberus_pass' => 'fake_pass'})
|
58
|
-
expect(last_response.body).to include('name="cerberus_login" value="fake_login"')
|
59
|
-
expect(last_response.body).to include('name="cerberus_pass" value="fake_pass"')
|
60
|
-
end
|
61
|
-
it 'Escapes HTML on submitted info' do
|
62
|
-
expect(Rack::Utils).to receive(:escape_html).with('<script>bad</script>').twice
|
63
|
-
post('/', {'cerberus_login' => '<script>bad</script>', 'cerberus_pass' => '<script>bad</script>'})
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
context 'Login details are correct' do
|
68
|
-
let(:secret_app) {
|
69
|
-
lambda {|env|
|
70
|
-
[200, {'Content-Type'=>'text/plain'}, env['REQUEST_METHOD']]
|
71
|
-
}
|
72
|
-
}
|
73
|
-
it 'Gives access' do
|
74
|
-
get('/', {'cerberus_login' => 'mario@nintendo.com', 'cerberus_pass' => 'bros'})
|
75
|
-
expect(last_response.status).to eq 200
|
76
|
-
end
|
77
|
-
it 'Calls the final page with the original method' do
|
78
|
-
get('/')
|
79
|
-
expect(last_response.body).to include('name="_method" value="GET"')
|
80
|
-
post('/', {
|
81
|
-
'cerberus_login' => 'mario@nintendo.com',
|
82
|
-
'cerberus_pass' => 'bros',
|
83
|
-
'_method' => 'GET'
|
84
|
-
})
|
85
|
-
expect(last_response.body).to eq 'GET'
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
end
|
90
|
-
|
91
|
-
describe 'Already logged in' do
|
92
|
-
|
93
|
-
it 'Uses session for persistent login' do
|
94
|
-
get('/', {'cerberus_login' => 'mario@nintendo.com', 'cerberus_pass' => 'bros'})
|
95
|
-
get('/')
|
96
|
-
expect(last_response.status).to eq 200
|
97
|
-
expect(last_response.body).to include('"cerberus_user"=>"mario@nintendo.com"}')
|
98
|
-
end
|
99
|
-
|
100
|
-
end
|
101
|
-
|
102
|
-
describe 'Logout' do
|
103
|
-
|
104
|
-
let(:mount_path) { '/admin' }
|
105
|
-
|
106
|
-
it 'Happens via /logout path' do
|
107
|
-
get('/admin/', {'cerberus_login' => 'mario@nintendo.com', 'cerberus_pass' => 'bros'})
|
108
|
-
expect(last_response.status).to eq 200
|
109
|
-
get('/admin/logout')
|
110
|
-
expect(last_response.status).to eq 401
|
111
|
-
end
|
112
|
-
|
113
|
-
it 'Never redirects to the logout path' do
|
114
|
-
get('/admin/logout', {'cerberus_login' => 'mario@nintendo.com', 'cerberus_pass' => 'bros'})
|
115
|
-
expect(last_response.status).to eq 302
|
116
|
-
expect(last_response['Location']).to eq '/admin'
|
117
|
-
end
|
118
|
-
|
119
|
-
end
|
120
|
-
|
121
|
-
describe 'Options' do
|
122
|
-
|
123
|
-
it 'Does not link CSS by default' do
|
124
|
-
get('/')
|
125
|
-
expect(last_response.body).not_to match(/<link/)
|
126
|
-
end
|
127
|
-
|
128
|
-
context 'CSS option is used' do
|
129
|
-
let(:cerberus_options) { {:css_location=>'/main.css'} }
|
130
|
-
it 'Links the CSS file' do
|
131
|
-
get('/')
|
132
|
-
expect(last_response.body).to match(/<link/)
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
context 'Session key is different' do
|
137
|
-
let(:cerberus_options) { {session_key: 'different_user'} }
|
138
|
-
it 'Uses the session key of the options' do
|
139
|
-
get('/', {'cerberus_login' => 'mario@nintendo.com', 'cerberus_pass' => 'bros'})
|
140
|
-
get('/')
|
141
|
-
expect(last_response.status).to eq 200
|
142
|
-
expect(last_response.body).to include('"different_user"=>"mario@nintendo.com"}')
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
end
|
147
|
-
|
148
|
-
end
|
149
|
-
|
data/spec/spec_helper.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
ENV['RACK_ENV'] = 'test'
|
2
|
-
|
3
|
-
RSpec.configure do |config|
|
4
|
-
|
5
|
-
config.include Rack::Test::Methods
|
6
|
-
config.expect_with :rspec do |expectations|
|
7
|
-
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
8
|
-
end
|
9
|
-
config.mock_with :rspec do |mocks|
|
10
|
-
mocks.verify_partial_doubles = true
|
11
|
-
end
|
12
|
-
config.filter_run :focus
|
13
|
-
config.run_all_when_everything_filtered = true
|
14
|
-
config.disable_monkey_patching!
|
15
|
-
config.warnings = true
|
16
|
-
if config.files_to_run.one?
|
17
|
-
config.default_formatter = 'doc'
|
18
|
-
end
|
19
|
-
config.profile_examples = 10
|
20
|
-
config.order = :random
|
21
|
-
Kernel.srand config.seed
|
22
|
-
|
23
|
-
end
|
24
|
-
|