sinatra-bouncer 1.1.1 → 1.1.2

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8c2cecc7324f61545be2136b73b66a0e3965ac64
4
+ data.tar.gz: e83e24a0f44fedf13b0caab14378007f80a16d8a
5
+ SHA512:
6
+ metadata.gz: 2a9cf4365f81e73630bc845767d29bad3ed3e08f1c033c961eb28d5f52e10235b1611a1634626ffc1e581a833504ddd4987678677e157d28a1b110931aa756b5
7
+ data.tar.gz: 7eb46c0a4567a20d9c20952bb6da6eba78fb18fd45637b444c64c1a2e5cf61866b835b1e5952be7ddd08d4e9424db266b598715121c03767456205924ac71224
data/README.md CHANGED
@@ -1,25 +1,23 @@
1
- #Sinatra::Bouncer
2
- Simple authorization permissions extension for Sinatra.
3
-
4
- ## Installation
5
-
6
- **Prerequisites**
7
-
8
- Bouncer requires [Sinatra](http://www.sinatrarb.com/), and [ruby 1.9.3](https://www.ruby-lang.org/en/documentation/installation/).
1
+ #Sinatra-Bouncer
2
+ Simple authorization permissions extension for [Sinatra](http://www.sinatrarb.com/). Require the gem, then declare which routes are permitted based on your own logic.
9
3
 
10
4
  **Gemfile**
11
5
  ```ruby
12
6
  gem 'sinatra-bouncer'
13
7
  ```
14
8
 
15
- **Command Line**
9
+ **Terminal**
16
10
  ```sh
17
11
  gem install sinatra-bouncer
18
12
  ```
19
13
 
20
- ##Usage
14
+ ##Quickstart
21
15
  ###Step 1: Require/Register Bouncer
22
16
 
17
+ After registration, Bouncer will reject any request that either:
18
+ * has no rule associated with it, or
19
+ * has no associated rule that returns `true`
20
+
23
21
  **Sinatra Classic**
24
22
  ```ruby
25
23
  require 'sinatra'
@@ -40,24 +38,19 @@ class MyApp < Sinatra::Base
40
38
  end
41
39
  ```
42
40
 
43
- After registration, Bouncer will reject any request that either:
44
- * has no rule associated with it, or
45
- * has no associated rule that returns `true`
46
-
47
41
  ###Step 2: Declare Bouncer Rules
48
- Call `rules` with a block that uses `can` and `can_sometimes` to declare which paths legal.
49
- The rules block is run in the context of the request, which means you will have access to sinatra helpers,
42
+ Call `rules` with a block that uses `can` and `can_sometimes` to declare which paths are legalduring this request. The rules block is run in the context of the request, which means you will have access to sinatra helpers,
50
43
  the `request` object, and `params`.
51
44
 
52
- **Example**
53
45
  ```ruby
54
46
  require 'sinatra'
55
47
  require 'sinatra/bouncer'
56
48
 
57
49
  rules do
50
+ # example: allow any GET request
58
51
  can(:get, :all)
59
52
 
60
- # logged in users can edit their account
53
+ # example: logged in users can edit their account
61
54
  if(current_user)
62
55
  can(:post, '/user_edits_account')
63
56
  end
@@ -66,8 +59,9 @@ end
66
59
  # ... route declarations as normal below
67
60
  ```
68
61
 
62
+ ## API
69
63
  #### can
70
- Any route declared with #can will be accepted this request without further challenge.
64
+ Any route declared with #can will be accepted without further challenge.
71
65
 
72
66
  ```ruby
73
67
  rules do
@@ -80,7 +74,6 @@ end
80
74
  `can_sometimes` takes a block that will be run once the path is attempted. This block **must return an explicit boolean**
81
75
  (ie. `true` or `false`) to avoid any accidental truthy values creating unwanted access.
82
76
 
83
- **Example**
84
77
  ```ruby
85
78
  rules do
86
79
  can_sometimes('/login') # Anyone can access this path
@@ -92,7 +85,6 @@ Passing `can` or `can_sometimes`:
92
85
  * `:any` to the first parameter will match any HTTP method.
93
86
  * `:all` to the second parameter will match any path.
94
87
 
95
- **Examples**
96
88
  ```ruby
97
89
  # this allows get on all paths
98
90
  can(:get, :all)
@@ -101,12 +93,16 @@ can(:get, :all)
101
93
  can(:any, '/login')
102
94
  ```
103
95
 
104
- ###Bounce Customization
105
- The default bounce action is to `halt 401`. Call `bounce_with` with a block that takes the sinatra application to change that behaviour.
96
+ ### Custom Bounce Behaviour
97
+ The default bounce action is to `halt 403`. Call `bounce_with` with a block to specify your own behaviour. The block is also run in a sinatra request context, so you can use helpers here as well.
106
98
 
107
- **Example**
108
99
  ```ruby
109
- bounce_with do |application|
110
- application.redirect '/login'
100
+ require 'sinatra'
101
+ require 'sinatra/bouncer'
102
+
103
+ bounce_with do
104
+ redirect '/login'
111
105
  end
106
+
107
+ # bouncer rules, routes, etc...
112
108
  ```
@@ -0,0 +1,69 @@
1
+ require_relative 'rule'
2
+
3
+ module Sinatra
4
+ module Bouncer
5
+ class BasicBouncer
6
+ attr_accessor :bounce_with
7
+ attr_accessor :rules_initializer
8
+
9
+ def initialize
10
+ # @rules = Hash.new do |method_to_paths, method|
11
+ # method_to_paths[method] = Hash.new do |path_to_rules, path|
12
+ # path_to_rules[path] = []
13
+ # end
14
+ # end
15
+
16
+ @ruleset = Hash.new do
17
+ []
18
+ end
19
+ @rules_initializer = Proc.new {}
20
+ end
21
+
22
+ def reset!
23
+ @ruleset.clear
24
+ end
25
+
26
+ def can(method, *paths)
27
+ if block_given?
28
+ raise BouncerError.new('You cannot provide a block to #can. If you wish to conditionally allow, use #can_sometimes instead.')
29
+ end
30
+
31
+ can_sometimes(method, *paths) do
32
+ true
33
+ end
34
+ end
35
+
36
+ def can_sometimes(method, *paths, &block)
37
+ unless block_given?
38
+ raise BouncerError.new('You must provide a block to #can_sometimes. If you wish to always allow, use #can instead.')
39
+ end
40
+
41
+ paths.each do |path|
42
+ @ruleset[method] += [Rule.new(path, &block)]
43
+ end
44
+ end
45
+
46
+ def can?(method, path)
47
+ rules = (@ruleset[:any_method] + @ruleset[method]).select { |rule| rule.match_path?(path) }
48
+
49
+ rules.any? do |rule_block|
50
+ ruling = rule_block.rule_passes?
51
+
52
+ ruling
53
+ end
54
+ end
55
+
56
+ def bounce(instance)
57
+ if bounce_with
58
+ instance.instance_exec &bounce_with
59
+ else
60
+ instance.halt 403
61
+ end
62
+ end
63
+ end
64
+
65
+ class BouncerError < StandardError
66
+
67
+ end
68
+ end
69
+ end
@@ -21,122 +21,55 @@
21
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  #++
23
23
 
24
+ require_relative 'basic_bouncer'
25
+
24
26
  module Sinatra
25
- module Bouncer
26
- def self.registered(base_class)
27
- base_class.helpers HelperMethods
27
+ module Bouncer
28
+ def self.registered(base_class)
29
+ base_class.helpers HelperMethods
28
30
 
29
- bouncer = BasicBouncer.new
31
+ bouncer = BasicBouncer.new
30
32
 
31
- # TODO: can we instead store it somehow on the actual temp request object?
32
- base_class.set :bouncer, bouncer
33
+ # TODO: can we instead store it somehow on the actual temp request object?
34
+ base_class.set :bouncer, bouncer
33
35
 
34
- base_class.before do
35
- bouncer.reset! # must clear all rules otherwise will leave doors open
36
+ base_class.before do
37
+ bouncer.reset! # must clear all rules otherwise will leave doors open
36
38
 
37
- self.instance_exec &bouncer.rules_initializer
39
+ self.instance_exec &bouncer.rules_initializer
38
40
 
39
- http_method = request.request_method.downcase.to_sym
40
- path = request.path.downcase
41
+ http_method = request.request_method.downcase.to_sym
42
+ path = request.path.downcase
41
43
 
42
- unless bouncer.can?(http_method, path)
43
- bouncer.bounce(self)
44
- end
44
+ unless bouncer.can?(http_method, path)
45
+ bouncer.bounce(self)
46
+ end
47
+ end
45
48
  end
46
- end
47
-
48
- # Start ExtensionMethods
49
- def bounce_with(&block)
50
- bouncer.bounce_with = block
51
- end
52
-
53
- def rules(&block)
54
- bouncer.rules_initializer = block
55
- end
56
49
 
57
- # End ExtensionMethods
58
-
59
- module HelperMethods
60
- def can(*args)
61
- settings.bouncer.can(*args)
50
+ # Start ExtensionMethods
51
+ def bounce_with(&block)
52
+ bouncer.bounce_with = block
62
53
  end
63
54
 
64
- def can_sometimes(*args, &block)
65
- settings.bouncer.can_sometimes(*args, &block)
55
+ def rules(&block)
56
+ bouncer.rules_initializer = block
66
57
  end
67
- end
68
-
69
- # Data class
70
- class BasicBouncer
71
- attr_accessor :bounce_with
72
- attr_accessor :rules_initializer
73
58
 
74
- def initialize
75
- @rules = Hash.new do |method_to_paths, method|
76
- method_to_paths[method] = Hash.new do |path_to_rules, path|
77
- path_to_rules[path] = []
78
- end
79
- end
59
+ # End ExtensionMethods
80
60
 
81
- @rules_initializer = Proc.new {}
82
- end
61
+ module HelperMethods
62
+ def can(*args)
63
+ settings.bouncer.can(*args)
64
+ end
83
65
 
84
- def reset!
85
- @rules.clear
66
+ def can_sometimes(*args, &block)
67
+ settings.bouncer.can_sometimes(*args, &block)
68
+ end
86
69
  end
70
+ end
87
71
 
88
- def can(method, *paths)
89
- if block_given?
90
- raise BouncerError.new('You cannot provide a block to #can. If you wish to conditionally allow, use #can_sometimes instead.')
91
- end
92
-
93
- can_sometimes(method, *paths) do
94
- true
95
- end
96
- end
97
-
98
- def can_sometimes(method, *paths, &block)
99
- unless block_given?
100
- raise BouncerError.new('You must provide a block to #can_sometimes. If you wish to always allow, use #can instead.')
101
- end
102
-
103
- paths.each do |path|
104
- @rules[method][path] << block
105
- end
106
- end
107
-
108
- def can?(method, path)
109
- rules = @rules[:any_method][path] + @rules[method][:all] + @rules[method][path] #@rules[:all] + @rules[method]
110
-
111
- rules.any? do |rule_block|
112
- ruling = rule_block.call #(app)
113
-
114
- if ruling != true && ruling != false
115
- source = rule_block.source_location.join(':')
116
- raise BouncerError.new("Rule block at does not return explicit true/false.\n\n"+
117
- "Rules must return explicit true or false to prevent accidental truthy values.\n\n"+
118
- "Source: #{source}\n")
119
- end
120
-
121
- ruling
122
- end
123
- end
124
-
125
- def bounce(instance)
126
- if bounce_with
127
- instance.instance_exec &bounce_with
128
- else
129
- instance.halt 403
130
- end
131
- end
132
- end
133
-
134
- class BouncerError < StandardError
135
-
136
- end
137
- end
138
-
139
- if defined? register
140
- register Bouncer
141
- end
72
+ if defined? register
73
+ register Bouncer
74
+ end
142
75
  end
@@ -0,0 +1,48 @@
1
+ module Sinatra
2
+ module Bouncer
3
+ class Rule
4
+ def initialize(path, &ruleblock)
5
+ if path == :all
6
+ @path = :all
7
+ else
8
+ path = '/' + path unless path.start_with?('/')
9
+
10
+ @path = path.split('/')
11
+ end
12
+
13
+ @rule = ruleblock
14
+ end
15
+
16
+ def match_path?(path)
17
+ return true if @path == :all
18
+
19
+ path = '/' + path unless path.start_with?('/')
20
+
21
+ split_path = path.split('/')
22
+ matches = @path.length == split_path.length
23
+
24
+ @path.each_index do |i|
25
+ allowed_segment = @path[i]
26
+ given_segment = split_path[i]
27
+
28
+ matches &= given_segment == allowed_segment || allowed_segment == '*'
29
+ end
30
+
31
+ matches
32
+ end
33
+
34
+ def rule_passes?
35
+ ruling = @rule.call
36
+
37
+ unless ruling.is_a?(TrueClass)|| ruling.is_a?(FalseClass)
38
+ source = @rule.source_location.join(':')
39
+ raise BouncerError.new("Rule block at does not return explicit true/false.\n\n"+
40
+ "Rules must return explicit true or false to prevent accidental truthy values.\n\n"+
41
+ "Source: #{source}\n")
42
+ end
43
+
44
+ ruling
45
+ end
46
+ end
47
+ end
48
+ end
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sinatra-bouncer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
5
- prerelease:
4
+ version: 1.1.2
6
5
  platform: ruby
7
6
  authors:
8
7
  - Tenjin
@@ -10,118 +9,104 @@ authors:
10
9
  autorequire:
11
10
  bindir: bin
12
11
  cert_chain: []
13
- date: 2015-06-01 00:00:00.000000000 Z
12
+ date: 2016-10-02 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: sinatra
17
16
  requirement: !ruby/object:Gem::Requirement
18
- none: false
19
17
  requirements:
20
- - - ! '>='
18
+ - - ">="
21
19
  - !ruby/object:Gem::Version
22
20
  version: '0'
23
21
  type: :runtime
24
22
  prerelease: false
25
23
  version_requirements: !ruby/object:Gem::Requirement
26
- none: false
27
24
  requirements:
28
- - - ! '>='
25
+ - - ">="
29
26
  - !ruby/object:Gem::Version
30
27
  version: '0'
31
28
  - !ruby/object:Gem::Dependency
32
29
  name: simplecov
33
30
  requirement: !ruby/object:Gem::Requirement
34
- none: false
35
31
  requirements:
36
- - - ! '>='
32
+ - - ">="
37
33
  - !ruby/object:Gem::Version
38
34
  version: '0'
39
35
  type: :development
40
36
  prerelease: false
41
37
  version_requirements: !ruby/object:Gem::Requirement
42
- none: false
43
38
  requirements:
44
- - - ! '>='
39
+ - - ">="
45
40
  - !ruby/object:Gem::Version
46
41
  version: '0'
47
42
  - !ruby/object:Gem::Dependency
48
43
  name: rspec
49
44
  requirement: !ruby/object:Gem::Requirement
50
- none: false
51
45
  requirements:
52
- - - ~>
46
+ - - "~>"
53
47
  - !ruby/object:Gem::Version
54
48
  version: 2.14.1
55
49
  type: :development
56
50
  prerelease: false
57
51
  version_requirements: !ruby/object:Gem::Requirement
58
- none: false
59
52
  requirements:
60
- - - ~>
53
+ - - "~>"
61
54
  - !ruby/object:Gem::Version
62
55
  version: 2.14.1
63
56
  - !ruby/object:Gem::Dependency
64
57
  name: cucumber
65
58
  requirement: !ruby/object:Gem::Requirement
66
- none: false
67
59
  requirements:
68
- - - ~>
60
+ - - "~>"
69
61
  - !ruby/object:Gem::Version
70
62
  version: 1.3.19
71
63
  type: :development
72
64
  prerelease: false
73
65
  version_requirements: !ruby/object:Gem::Requirement
74
- none: false
75
66
  requirements:
76
- - - ~>
67
+ - - "~>"
77
68
  - !ruby/object:Gem::Version
78
69
  version: 1.3.19
79
70
  - !ruby/object:Gem::Dependency
80
71
  name: capybara
81
72
  requirement: !ruby/object:Gem::Requirement
82
- none: false
83
73
  requirements:
84
- - - ! '>='
74
+ - - ">="
85
75
  - !ruby/object:Gem::Version
86
76
  version: '0'
87
77
  type: :development
88
78
  prerelease: false
89
79
  version_requirements: !ruby/object:Gem::Requirement
90
- none: false
91
80
  requirements:
92
- - - ! '>='
81
+ - - ">="
93
82
  - !ruby/object:Gem::Version
94
83
  version: '0'
95
84
  - !ruby/object:Gem::Dependency
96
85
  name: launchy
97
86
  requirement: !ruby/object:Gem::Requirement
98
- none: false
99
87
  requirements:
100
- - - ! '>='
88
+ - - ">="
101
89
  - !ruby/object:Gem::Version
102
90
  version: '0'
103
91
  type: :development
104
92
  prerelease: false
105
93
  version_requirements: !ruby/object:Gem::Requirement
106
- none: false
107
94
  requirements:
108
- - - ! '>='
95
+ - - ">="
109
96
  - !ruby/object:Gem::Version
110
97
  version: '0'
111
98
  - !ruby/object:Gem::Dependency
112
99
  name: parallel_tests
113
100
  requirement: !ruby/object:Gem::Requirement
114
- none: false
115
101
  requirements:
116
- - - ! '>='
102
+ - - ">="
117
103
  - !ruby/object:Gem::Version
118
104
  version: '0'
119
105
  type: :development
120
106
  prerelease: false
121
107
  version_requirements: !ruby/object:Gem::Requirement
122
- none: false
123
108
  requirements:
124
- - - ! '>='
109
+ - - ">="
125
110
  - !ruby/object:Gem::Version
126
111
  version: '0'
127
112
  description: Bouncer brings simple authorization to Sinatra.
@@ -133,29 +118,30 @@ files:
133
118
  - Gemfile
134
119
  - MIT-LICENSE
135
120
  - README.md
121
+ - lib/sinatra/basic_bouncer.rb
136
122
  - lib/sinatra/bouncer.rb
123
+ - lib/sinatra/rule.rb
137
124
  homepage: http://www.tenjin.ca
138
125
  licenses: []
126
+ metadata: {}
139
127
  post_install_message:
140
128
  rdoc_options: []
141
129
  require_paths:
142
130
  - lib
143
131
  required_ruby_version: !ruby/object:Gem::Requirement
144
- none: false
145
132
  requirements:
146
- - - ! '>='
133
+ - - ">="
147
134
  - !ruby/object:Gem::Version
148
135
  version: 1.9.3
149
136
  required_rubygems_version: !ruby/object:Gem::Requirement
150
- none: false
151
137
  requirements:
152
- - - ! '>='
138
+ - - ">="
153
139
  - !ruby/object:Gem::Version
154
140
  version: '0'
155
141
  requirements: []
156
142
  rubyforge_project:
157
- rubygems_version: 1.8.24
143
+ rubygems_version: 2.4.8
158
144
  signing_key:
159
- specification_version: 3
145
+ specification_version: 4
160
146
  summary: Sinatra permissions plugin
161
147
  test_files: []