sinatra-bouncer 1.3.0 → 3.0.0
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/.gitignore +2 -7
- data/.rubocop.yml +1 -9
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -3
- data/Gemfile.lock +92 -0
- data/README.md +231 -54
- data/RELEASE_NOTES.md +170 -0
- data/lib/sinatra/bouncer/basic_bouncer.rb +34 -37
- data/lib/sinatra/bouncer/rule.rb +114 -22
- data/lib/sinatra/bouncer/version.rb +1 -1
- data/lib/sinatra/bouncer.rb +6 -37
- data/sinatra_bouncer.gemspec +4 -1
- metadata +5 -14
- data/tests/integrations/dev_defines_legal_routes.feature +0 -57
- data/tests/integrations/dev_installs_bouncer.feature +0 -12
- data/tests/integrations/step_definitions/given.rb +0 -36
- data/tests/integrations/step_definitions/then.rb +0 -9
- data/tests/integrations/step_definitions/when.rb +0 -11
- data/tests/integrations/support/env.rb +0 -30
- data/tests/integrations/support/helpers.rb +0 -55
- data/tests/integrations/support/types.rb +0 -21
- data/tests/spec/basic_bouncer_spec.rb +0 -148
- data/tests/spec/rule_spec.rb +0 -67
- data/tests/spec/spec_helper.rb +0 -11
- data/tests/test_app.rb +0 -9
data/RELEASE_NOTES.md
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
# Release Notes
|
2
|
+
|
3
|
+
All notable changes to this project will be documented below.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project loosely follows
|
6
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [Unreleased]
|
9
|
+
|
10
|
+
### Major Changes
|
11
|
+
|
12
|
+
* none
|
13
|
+
|
14
|
+
### Minor Changes
|
15
|
+
|
16
|
+
* none
|
17
|
+
|
18
|
+
### Bugfixes
|
19
|
+
|
20
|
+
* none
|
21
|
+
|
22
|
+
## [3.0.0] - 2023-11-21
|
23
|
+
|
24
|
+
### Major Changes
|
25
|
+
|
26
|
+
* Changed rules DSL to be role-oriented
|
27
|
+
* Removed the `:any` HTTP method wildcard
|
28
|
+
* Changed Sinatra API to require calling methods on bouncer object
|
29
|
+
* eg. `rules do ... end` should now be `bouncer.rules do ... end`
|
30
|
+
|
31
|
+
### Minor Changes
|
32
|
+
|
33
|
+
* none
|
34
|
+
|
35
|
+
### Bugfixes
|
36
|
+
|
37
|
+
* none
|
38
|
+
|
39
|
+
## [2.0.0] - 2023-11-13
|
40
|
+
|
41
|
+
### Major Changes
|
42
|
+
|
43
|
+
* Converted to hash syntax in `can` and `can_sometimes` statements
|
44
|
+
|
45
|
+
### Minor Changes
|
46
|
+
|
47
|
+
* Converted Cucumber tests to Rspec integration tests for consistency
|
48
|
+
* HEAD requests are evaluated like GET requests due to semantic equivalence
|
49
|
+
* Falsey values are now acceptable rule block results
|
50
|
+
|
51
|
+
### Bugfixes
|
52
|
+
|
53
|
+
* none
|
54
|
+
|
55
|
+
## [1.3.0] - 2023-09-13
|
56
|
+
|
57
|
+
### Major Changes
|
58
|
+
|
59
|
+
* none
|
60
|
+
|
61
|
+
### Minor Changes
|
62
|
+
|
63
|
+
* Increased minimum Ruby to 3.1
|
64
|
+
* Cleaned up development dependencies
|
65
|
+
* Rubocop cleanups
|
66
|
+
* Installed Simplecov
|
67
|
+
* Created Rakefile
|
68
|
+
|
69
|
+
### Bugfixes
|
70
|
+
|
71
|
+
* none
|
72
|
+
|
73
|
+
## [1.2.0] - 2016-10-02
|
74
|
+
|
75
|
+
### Major Changes
|
76
|
+
|
77
|
+
* none
|
78
|
+
|
79
|
+
### Minor Changes
|
80
|
+
|
81
|
+
* Supports wildcard matches in route strings
|
82
|
+
|
83
|
+
### Bugfixes
|
84
|
+
|
85
|
+
* none
|
86
|
+
|
87
|
+
## [1.1.1] - 2015-06-02
|
88
|
+
|
89
|
+
### Major Changes
|
90
|
+
|
91
|
+
* none
|
92
|
+
|
93
|
+
### Minor Changes
|
94
|
+
|
95
|
+
* none
|
96
|
+
|
97
|
+
### Bugfixes
|
98
|
+
|
99
|
+
* Runs `bounce_with` in context of web request
|
100
|
+
|
101
|
+
## [1.1.0] - 2015-05-28
|
102
|
+
|
103
|
+
### Major Changes
|
104
|
+
|
105
|
+
* none
|
106
|
+
|
107
|
+
### Minor Changes
|
108
|
+
|
109
|
+
* none
|
110
|
+
|
111
|
+
### Bugfixes
|
112
|
+
|
113
|
+
* Correctly forgets rules between routes
|
114
|
+
|
115
|
+
## [1.0.2] - 2015-05-24
|
116
|
+
|
117
|
+
### Major Changes
|
118
|
+
|
119
|
+
* none
|
120
|
+
|
121
|
+
### Minor Changes
|
122
|
+
|
123
|
+
* Changed default halt to 403
|
124
|
+
* Application available in rule block
|
125
|
+
|
126
|
+
### Bugfixes
|
127
|
+
|
128
|
+
* Fixed sinatra module registration
|
129
|
+
|
130
|
+
## [1.0.1] - 2015-05-21
|
131
|
+
|
132
|
+
### Major Changes
|
133
|
+
|
134
|
+
* none
|
135
|
+
|
136
|
+
### Minor Changes
|
137
|
+
|
138
|
+
* none
|
139
|
+
|
140
|
+
### Bugfixes
|
141
|
+
|
142
|
+
* none
|
143
|
+
|
144
|
+
## [1.0.0] - Unreleased Prototype
|
145
|
+
|
146
|
+
### Major Changes
|
147
|
+
|
148
|
+
* none
|
149
|
+
|
150
|
+
### Minor Changes
|
151
|
+
|
152
|
+
* none
|
153
|
+
|
154
|
+
### Bugfixes
|
155
|
+
|
156
|
+
* none
|
157
|
+
|
158
|
+
## [0.1.0] - Unreleased Prototype
|
159
|
+
|
160
|
+
### Major Changes
|
161
|
+
|
162
|
+
* Initial prototype
|
163
|
+
|
164
|
+
### Minor Changes
|
165
|
+
|
166
|
+
* none
|
167
|
+
|
168
|
+
### Bugfixes
|
169
|
+
|
170
|
+
* none
|
@@ -4,65 +4,62 @@ require_relative 'rule'
|
|
4
4
|
|
5
5
|
module Sinatra
|
6
6
|
module Bouncer
|
7
|
+
# Core implementation of Bouncer logic
|
7
8
|
class BasicBouncer
|
8
|
-
attr_accessor :
|
9
|
+
attr_accessor :rules_initializer
|
9
10
|
|
10
11
|
def initialize
|
11
|
-
|
12
|
-
# method_to_paths[method] = Hash.new do |path_to_rules, path|
|
13
|
-
# path_to_rules[path] = []
|
14
|
-
# end
|
15
|
-
# end
|
12
|
+
@rules = []
|
16
13
|
|
17
|
-
|
18
|
-
|
14
|
+
role :anyone do
|
15
|
+
true
|
19
16
|
end
|
20
17
|
|
21
18
|
@rules_initializer = proc {}
|
19
|
+
@bounce_strategy = proc do
|
20
|
+
halt 403
|
21
|
+
end
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
25
|
-
|
24
|
+
def rules(&block)
|
25
|
+
instance_exec(&block)
|
26
|
+
@rules.each do |rule|
|
27
|
+
raise BouncerError, 'rules block error: missing #can or #can_sometimes call' if rule.incomplete?
|
28
|
+
end
|
26
29
|
end
|
27
30
|
|
28
|
-
def can(method,
|
29
|
-
|
30
|
-
hint = 'If you wish to conditionally allow, use #can_sometimes instead.'
|
31
|
-
raise BouncerError, "You cannot provide a block to #can. #{ hint }"
|
32
|
-
end
|
31
|
+
def can?(method, path, context)
|
32
|
+
method = method.downcase.to_sym unless method.is_a? Symbol
|
33
33
|
|
34
|
-
|
35
|
-
|
34
|
+
@rules.any? do |rule|
|
35
|
+
rule.allow? method, path, context
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
def
|
40
|
-
|
41
|
-
hint = 'If you wish to always allow, use #can instead.'
|
42
|
-
raise BouncerError, "You must provide a block to #can_sometimes. #{ hint }"
|
43
|
-
end
|
44
|
-
|
45
|
-
paths.each do |path|
|
46
|
-
@ruleset[method] += [Rule.new(path, &block)]
|
47
|
-
end
|
39
|
+
def bounce_with(&block)
|
40
|
+
@bounce_strategy = block
|
48
41
|
end
|
49
42
|
|
50
|
-
def
|
51
|
-
|
43
|
+
def bounce(instance)
|
44
|
+
instance.instance_exec(&@bounce_strategy)
|
45
|
+
end
|
52
46
|
|
53
|
-
|
54
|
-
|
47
|
+
def role(identifier, &block)
|
48
|
+
raise ArgumentError, 'must provide a role identifier to #role' unless identifier
|
49
|
+
raise ArgumentError, 'must provide a role condition block to #role' unless block
|
50
|
+
raise ArgumentError, "role called '#{ identifier }' already defined" if respond_to? identifier
|
55
51
|
|
56
|
-
|
52
|
+
define_singleton_method identifier do
|
53
|
+
add_rule(&block)
|
57
54
|
end
|
58
55
|
end
|
59
56
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
57
|
+
private
|
58
|
+
|
59
|
+
def add_rule(&block)
|
60
|
+
rule = Rule.new(&block)
|
61
|
+
@rules << rule
|
62
|
+
rule
|
66
63
|
end
|
67
64
|
end
|
68
65
|
|
data/lib/sinatra/bouncer/rule.rb
CHANGED
@@ -2,42 +2,122 @@
|
|
2
2
|
|
3
3
|
module Sinatra
|
4
4
|
module Bouncer
|
5
|
+
# Defines a RuleSet to be evaluated with each request
|
5
6
|
class Rule
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
# Enumeration of HTTP method strings based on:
|
8
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
|
9
|
+
# Ignoring CONNECT and TRACE due to rarity
|
10
|
+
HTTP_METHODS = %w[GET HEAD PUT POST DELETE OPTIONS PATCH].freeze
|
11
|
+
|
12
|
+
# Symbol versions of HTTP_METHODS constant
|
13
|
+
#
|
14
|
+
# @see HTTP_METHODS
|
15
|
+
HTTP_METHOD_SYMBOLS = HTTP_METHODS.collect do |http_method|
|
16
|
+
http_method.downcase.to_sym
|
17
|
+
end.freeze
|
11
18
|
|
12
|
-
|
19
|
+
def initialize(&block)
|
20
|
+
raise ArgumentError, 'must provide a block to Bouncer::Rule' unless block
|
21
|
+
|
22
|
+
@routes = Hash.new do
|
23
|
+
[]
|
13
24
|
end
|
14
25
|
|
15
|
-
@
|
26
|
+
@conditions = [block]
|
16
27
|
end
|
17
28
|
|
18
|
-
def
|
19
|
-
|
29
|
+
def can_sometimes(**method_routes, &block)
|
30
|
+
if method_routes.empty?
|
31
|
+
raise ArgumentError,
|
32
|
+
'must provide a hash where keys are HTTP method symbols and values are one or more path matchers'
|
33
|
+
end
|
20
34
|
|
21
|
-
|
35
|
+
unless block
|
36
|
+
hint = 'If you wish to always allow, use #can instead.'
|
37
|
+
raise BouncerError, "You must provide a block to #can_sometimes. #{ hint }"
|
38
|
+
end
|
22
39
|
|
23
|
-
|
24
|
-
|
40
|
+
method_routes.each do |method, paths|
|
41
|
+
validate_http_method! method
|
25
42
|
|
26
|
-
|
27
|
-
allowed_segment = @path[i]
|
28
|
-
given_segment = split_path[i]
|
43
|
+
paths = [paths] unless paths.respond_to? :collect
|
29
44
|
|
30
|
-
|
45
|
+
@routes[method] += paths.collect { |path| normalize_path path }
|
31
46
|
end
|
32
47
|
|
33
|
-
|
48
|
+
@conditions << block
|
34
49
|
end
|
35
50
|
|
36
|
-
def
|
37
|
-
|
51
|
+
def can(**method_routes)
|
52
|
+
if block_given?
|
53
|
+
hint = 'If you wish to conditionally allow, use #can_sometimes instead.'
|
54
|
+
raise BouncerError, "You cannot provide a block to #can. #{ hint }"
|
55
|
+
end
|
56
|
+
|
57
|
+
can_sometimes(**method_routes) do
|
58
|
+
true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def allow?(method, path, context)
|
63
|
+
match_path?(method, path) && @conditions.all? do |condition|
|
64
|
+
rule_passes?(context, &condition)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def incomplete?
|
69
|
+
@routes.empty?
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def validate_http_method!(method)
|
75
|
+
return if HTTP_METHOD_SYMBOLS.include?(method)
|
76
|
+
|
77
|
+
raise BouncerError, "'#{ method }' is not a known HTTP method key. Must be one of: #{ HTTP_METHOD_SYMBOLS }"
|
78
|
+
end
|
38
79
|
|
39
|
-
|
40
|
-
|
80
|
+
# Determines if the path matches the exact path or wildcard.
|
81
|
+
#
|
82
|
+
# @return `true` if the path matches
|
83
|
+
def match_path?(method, trial_path)
|
84
|
+
trial_path = normalize_path trial_path
|
85
|
+
|
86
|
+
matchers_for(method).any? do |matcher|
|
87
|
+
return true if matcher == :all
|
88
|
+
|
89
|
+
matcher_parts = matcher.split '/'
|
90
|
+
trial_parts = trial_path.split '/'
|
91
|
+
matches = matcher_parts.length == trial_parts.length
|
92
|
+
|
93
|
+
matcher_parts.each_index do |i|
|
94
|
+
allowed_segment = matcher_parts[i]
|
95
|
+
given_segment = trial_parts[i]
|
96
|
+
|
97
|
+
matches &= given_segment == allowed_segment || allowed_segment == '*'
|
98
|
+
end
|
99
|
+
|
100
|
+
matches
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def matchers_for(method)
|
105
|
+
matchers = @routes[method]
|
106
|
+
|
107
|
+
matchers += @routes[:get] if method == :head
|
108
|
+
|
109
|
+
matchers
|
110
|
+
end
|
111
|
+
|
112
|
+
# Evaluates the rule's block. Defensively prevents truthy values from the block from allowing a route.
|
113
|
+
#
|
114
|
+
# @raise BouncerError when the rule block is a truthy value but not exactly `true`
|
115
|
+
# @return Exactly `true` or `false`, depending on the result of the rule block
|
116
|
+
def rule_passes?(context, &rule)
|
117
|
+
ruling = context.instance_exec(&rule)
|
118
|
+
|
119
|
+
unless !ruling || ruling.is_a?(TrueClass)
|
120
|
+
source = rule.source_location.join(':')
|
41
121
|
msg = <<~ERR
|
42
122
|
Rule block at does not return explicit true/false.
|
43
123
|
Rules must return explicit true or false to prevent accidental truthy values.
|
@@ -47,7 +127,19 @@ module Sinatra
|
|
47
127
|
raise BouncerError, msg
|
48
128
|
end
|
49
129
|
|
50
|
-
ruling
|
130
|
+
!!ruling
|
131
|
+
end
|
132
|
+
|
133
|
+
def normalize_path(path)
|
134
|
+
if path == :all
|
135
|
+
path
|
136
|
+
else
|
137
|
+
if path.start_with?('/')
|
138
|
+
path
|
139
|
+
else
|
140
|
+
"/#{ path }"
|
141
|
+
end.downcase
|
142
|
+
end
|
51
143
|
end
|
52
144
|
end
|
53
145
|
end
|
data/lib/sinatra/bouncer.rb
CHANGED
@@ -23,52 +23,21 @@
|
|
23
23
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
24
24
|
#++
|
25
25
|
|
26
|
+
require 'sinatra/base'
|
26
27
|
require_relative 'bouncer/basic_bouncer'
|
27
28
|
|
29
|
+
# Namespace module
|
28
30
|
module Sinatra
|
31
|
+
# Namespace module
|
29
32
|
module Bouncer
|
30
33
|
def self.registered(base_class)
|
31
|
-
base_class.
|
32
|
-
|
33
|
-
bouncer = BasicBouncer.new
|
34
|
-
|
35
|
-
# TODO: can we instead store it somehow on the actual temp request object?
|
36
|
-
base_class.set :bouncer, bouncer
|
34
|
+
base_class.set :bouncer, BasicBouncer.new
|
37
35
|
|
38
36
|
base_class.before do
|
39
|
-
bouncer.
|
40
|
-
|
41
|
-
instance_exec(&bouncer.rules_initializer)
|
42
|
-
|
43
|
-
http_method = request.request_method.downcase.to_sym
|
44
|
-
path = request.path.downcase
|
45
|
-
|
46
|
-
bouncer.bounce(self) unless bouncer.can?(http_method, path)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
# Start ExtensionMethods
|
51
|
-
def bounce_with(&block)
|
52
|
-
bouncer.bounce_with = block
|
53
|
-
end
|
54
|
-
|
55
|
-
def rules(&block)
|
56
|
-
bouncer.rules_initializer = block
|
57
|
-
end
|
58
|
-
# End ExtensionMethods
|
59
|
-
|
60
|
-
module HelperMethods
|
61
|
-
def can(*args)
|
62
|
-
settings.bouncer.can(*args)
|
63
|
-
end
|
64
|
-
|
65
|
-
def can_sometimes(...)
|
66
|
-
settings.bouncer.can_sometimes(...)
|
37
|
+
settings.bouncer.bounce(self) unless settings.bouncer.can?(request.request_method, request.path, self)
|
67
38
|
end
|
68
39
|
end
|
69
40
|
end
|
70
41
|
|
71
|
-
|
72
|
-
register Bouncer
|
73
|
-
end
|
42
|
+
register Sinatra::Bouncer
|
74
43
|
end
|
data/sinatra_bouncer.gemspec
CHANGED
@@ -19,7 +19,10 @@ Gem::Specification.new do |spec|
|
|
19
19
|
'rubygems_mfa_required' => 'true'
|
20
20
|
}
|
21
21
|
|
22
|
-
spec.files
|
22
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
23
|
+
f.match(%r{^(test|spec|features|integrations|benchmarks)/})
|
24
|
+
end
|
25
|
+
|
23
26
|
spec.require_paths = ['lib']
|
24
27
|
|
25
28
|
spec.required_ruby_version = '>= 3.1'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sinatra-bouncer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tenjin Inc
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2023-
|
12
|
+
date: 2023-11-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sinatra
|
@@ -37,9 +37,12 @@ files:
|
|
37
37
|
- ".rubocop.yml"
|
38
38
|
- ".ruby-version"
|
39
39
|
- ".simplecov"
|
40
|
+
- CODE_OF_CONDUCT.md
|
40
41
|
- Gemfile
|
42
|
+
- Gemfile.lock
|
41
43
|
- MIT-LICENSE
|
42
44
|
- README.md
|
45
|
+
- RELEASE_NOTES.md
|
43
46
|
- Rakefile
|
44
47
|
- cucumber.yml
|
45
48
|
- lib/sinatra/bouncer.rb
|
@@ -47,18 +50,6 @@ files:
|
|
47
50
|
- lib/sinatra/bouncer/rule.rb
|
48
51
|
- lib/sinatra/bouncer/version.rb
|
49
52
|
- sinatra_bouncer.gemspec
|
50
|
-
- tests/integrations/dev_defines_legal_routes.feature
|
51
|
-
- tests/integrations/dev_installs_bouncer.feature
|
52
|
-
- tests/integrations/step_definitions/given.rb
|
53
|
-
- tests/integrations/step_definitions/then.rb
|
54
|
-
- tests/integrations/step_definitions/when.rb
|
55
|
-
- tests/integrations/support/env.rb
|
56
|
-
- tests/integrations/support/helpers.rb
|
57
|
-
- tests/integrations/support/types.rb
|
58
|
-
- tests/spec/basic_bouncer_spec.rb
|
59
|
-
- tests/spec/rule_spec.rb
|
60
|
-
- tests/spec/spec_helper.rb
|
61
|
-
- tests/test_app.rb
|
62
53
|
homepage: https://github.com/TenjinInc/Sinatra-Bouncer
|
63
54
|
licenses:
|
64
55
|
- MIT
|
@@ -1,57 +0,0 @@
|
|
1
|
-
Feature: Developer defines legal routes
|
2
|
-
As a developer
|
3
|
-
So that clients can safely access my server
|
4
|
-
I will allow specific routes
|
5
|
-
|
6
|
-
Scenario Outline: it should allows access to whitelist routes
|
7
|
-
Given a sinatra server with bouncer and routes:
|
8
|
-
| method | path | allowed |
|
9
|
-
| get | <path> | yes |
|
10
|
-
When I visit /<path>
|
11
|
-
Then it should be at /<path>
|
12
|
-
And it should have status code 200
|
13
|
-
Examples:
|
14
|
-
| path |
|
15
|
-
| some_path |
|
16
|
-
| another_path |
|
17
|
-
|
18
|
-
Scenario: it should NOT allow access to other routes
|
19
|
-
Given a sinatra server with bouncer and routes:
|
20
|
-
| method | path | allowed |
|
21
|
-
| get | some_path | yes |
|
22
|
-
| get | illegal_path | no |
|
23
|
-
When I visit /illegal_path
|
24
|
-
Then it should have status code 403
|
25
|
-
|
26
|
-
Scenario Outline: it should allow multiple routes with a splat
|
27
|
-
Given a sinatra server with bouncer and routes:
|
28
|
-
| method | path | allowed |
|
29
|
-
| get | admin/* | yes |
|
30
|
-
When I visit /admin/<sub_path>
|
31
|
-
Then it should be at /admin/<sub_path>
|
32
|
-
And it should have status code 200
|
33
|
-
Examples:
|
34
|
-
| sub_path |
|
35
|
-
| dashboard |
|
36
|
-
| users |
|
37
|
-
|
38
|
-
Scenario Outline: it should allow splat to be in the middle of the route
|
39
|
-
Given a sinatra server with bouncer and routes:
|
40
|
-
| method | path | allowed |
|
41
|
-
| get | admin/*/create | yes |
|
42
|
-
When I visit /admin/<sub_path>/create
|
43
|
-
Then it should be at /admin/<sub_path>/create
|
44
|
-
And it should have status code 200
|
45
|
-
Examples:
|
46
|
-
| sub_path |
|
47
|
-
| tasks |
|
48
|
-
| users |
|
49
|
-
|
50
|
-
Scenario: it should forget rules between requests
|
51
|
-
Given a sinatra server with bouncer and routes:
|
52
|
-
| method | path | allowed |
|
53
|
-
| get | some_path | once |
|
54
|
-
When I double visit /some_path
|
55
|
-
Then it should be at /some_path
|
56
|
-
And it should have status code 403
|
57
|
-
|
@@ -1,12 +0,0 @@
|
|
1
|
-
Feature: Developer installs Bouncer
|
2
|
-
As a developer
|
3
|
-
So that I can secure my sinatra server
|
4
|
-
I will install bouncer
|
5
|
-
|
6
|
-
Scenario: it should auto protect all routes
|
7
|
-
Given a sinatra server with bouncer and routes:
|
8
|
-
| method | path |
|
9
|
-
| get | some_path |
|
10
|
-
When I visit /some_path
|
11
|
-
Then it should have status code 403
|
12
|
-
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
Given 'a sinatra server with bouncer and routes:' do |table|
|
4
|
-
app = Capybara.app
|
5
|
-
|
6
|
-
allowed_paths = []
|
7
|
-
|
8
|
-
table.hashes.each do |row|
|
9
|
-
path = row[:path]
|
10
|
-
path = "/#{ path }" if path[0] != '/' # ensure leading slash
|
11
|
-
|
12
|
-
method = row[:method].to_sym
|
13
|
-
# build the routes
|
14
|
-
app.send(method, path) do
|
15
|
-
'The result of path'
|
16
|
-
end
|
17
|
-
|
18
|
-
is_once = row[:allowed] =~ /once/i
|
19
|
-
|
20
|
-
next unless is_once || parse_bool(row[:allowed])
|
21
|
-
|
22
|
-
allowed_paths << path
|
23
|
-
|
24
|
-
@allowed_once_paths << path if is_once
|
25
|
-
end
|
26
|
-
|
27
|
-
onces = @allowed_once_paths
|
28
|
-
|
29
|
-
app.rules do
|
30
|
-
allowed_paths.each do |path|
|
31
|
-
can_sometimes(:any_method, path) do
|
32
|
-
!onces.include?(path)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|