sinatra-bouncer 1.3.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6b8592539c3824fa1985a4c02b877bd7d53e6ab2635f4227b173325f16484465
4
- data.tar.gz: 1aac0ef35f8584e83cbafd5843172798f8967ad217235f9048e476cf3cc8945f
3
+ metadata.gz: 849fed52904df15b1cc036685383f197e47aae52bef8283a81decc281fc95660
4
+ data.tar.gz: 3a3ba41d0d3c2ad70b1c4e48b81558213111d86a750717e2b8f44dea9072ca1f
5
5
  SHA512:
6
- metadata.gz: 83ed25a0acff1d864f978dbbd3667d20f53be0bea36fa5db5abd55eeb4353cad53e29ebeb424f1c462e4a8790444f4a5850cad33c579729aab684ff4f0d482aa
7
- data.tar.gz: a3caa0c02709faa60e5512f73f2b4c4f21066f519999e014ffac2545271198d8a98a7a73d430494a185a00e0ea255170e1693274c7fb8bca51b56c6108fcd8a6
6
+ metadata.gz: d63e6a5437ef4d71e7183dce458241a8efaebace1c6f95e77766412f2faf2d5d490107a4e925588509dfb8e73b0c2e3a21064e73aae4cb3f98ae0c8465f19dd5
7
+ data.tar.gz: ca6d627ae51d9ecf6c88ffa21621117adb3af2eee6c83d14846f8ac7a5aa314f9ce4d2d10c30baf192e4a7c435f750bfb5b9bc3c37ed308ee4a5255a8df0eacf
data/.gitignore CHANGED
@@ -4,9 +4,6 @@
4
4
  # or operating system, you probably want to add a global ignore instead:
5
5
  # git config --global core.excludesfile ~/.gitignore_global
6
6
 
7
- # Ignore bundler config
8
- /.bundle
9
-
10
7
  # Ignore all logfiles and tempfiles.
11
8
  *.log
12
9
  tmp/*
@@ -17,7 +14,5 @@ tmp/*
17
14
  # Ignore all gem files.
18
15
  *.gem
19
16
 
20
- core/config.yml
21
- persist/database.yml
22
-
23
- Gemfile.lock
17
+ # Ignore simplecov results
18
+ .coverage/
data/.rubocop.yml CHANGED
@@ -3,6 +3,7 @@ inherit_from: ~/.config/rubocop/config.yml
3
3
  AllCops:
4
4
  Exclude:
5
5
  - 'bin/*'
6
+ - 'benchmarks/*'
6
7
 
7
8
  Layout/LineLength:
8
9
  Exclude:
@@ -11,12 +12,3 @@ Layout/LineLength:
11
12
  # setting to 6 to match RubyMine autoformat
12
13
  Layout/FirstArrayElementIndentation:
13
14
  IndentationWidth: 6
14
-
15
- # rspec blocks are huge by design
16
- Metrics/BlockLength:
17
- Exclude:
18
- - 'tests/spec/**/*.rb'
19
-
20
- Metrics/ModuleLength:
21
- Exclude:
22
- - 'tests/spec/**/*.rb'
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at robin@tenjin.ca. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile CHANGED
@@ -7,13 +7,14 @@ gemspec
7
7
 
8
8
  group :development do
9
9
  gem 'bundler', '~> 2.4'
10
- gem 'rake', '~> 13.0'
10
+ gem 'rake', '~> 13.1'
11
+ gem 'rubocop', '~> 1.57'
12
+ gem 'rubocop-performance', '~> 1.19'
11
13
  gem 'yard', '~> 0.9'
12
14
  end
13
15
 
14
16
  group :test do
15
- gem 'capybara', '~> 3.39'
16
- gem 'cucumber', '~> 8.0'
17
+ gem 'rack-test', '~> 2.1'
17
18
  gem 'rspec', '~> 3.12'
18
19
  gem 'simplecov', '~> 0.22'
19
20
  end
data/Gemfile.lock ADDED
@@ -0,0 +1,92 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sinatra-bouncer (3.0.0)
5
+ sinatra (>= 2.2)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.2)
11
+ diff-lcs (1.5.0)
12
+ docile (1.4.0)
13
+ json (2.6.3)
14
+ language_server-protocol (3.17.0.3)
15
+ mustermann (3.0.0)
16
+ ruby2_keywords (~> 0.0.1)
17
+ parallel (1.23.0)
18
+ parser (3.2.2.4)
19
+ ast (~> 2.4.1)
20
+ racc
21
+ racc (1.7.3)
22
+ rack (2.2.8)
23
+ rack-protection (3.1.0)
24
+ rack (~> 2.2, >= 2.2.4)
25
+ rack-test (2.1.0)
26
+ rack (>= 1.3)
27
+ rainbow (3.1.1)
28
+ rake (13.1.0)
29
+ regexp_parser (2.8.2)
30
+ rexml (3.2.6)
31
+ rspec (3.12.0)
32
+ rspec-core (~> 3.12.0)
33
+ rspec-expectations (~> 3.12.0)
34
+ rspec-mocks (~> 3.12.0)
35
+ rspec-core (3.12.2)
36
+ rspec-support (~> 3.12.0)
37
+ rspec-expectations (3.12.3)
38
+ diff-lcs (>= 1.2.0, < 2.0)
39
+ rspec-support (~> 3.12.0)
40
+ rspec-mocks (3.12.6)
41
+ diff-lcs (>= 1.2.0, < 2.0)
42
+ rspec-support (~> 3.12.0)
43
+ rspec-support (3.12.1)
44
+ rubocop (1.57.2)
45
+ json (~> 2.3)
46
+ language_server-protocol (>= 3.17.0)
47
+ parallel (~> 1.10)
48
+ parser (>= 3.2.2.4)
49
+ rainbow (>= 2.2.2, < 4.0)
50
+ regexp_parser (>= 1.8, < 3.0)
51
+ rexml (>= 3.2.5, < 4.0)
52
+ rubocop-ast (>= 1.28.1, < 2.0)
53
+ ruby-progressbar (~> 1.7)
54
+ unicode-display_width (>= 2.4.0, < 3.0)
55
+ rubocop-ast (1.30.0)
56
+ parser (>= 3.2.1.0)
57
+ rubocop-performance (1.19.1)
58
+ rubocop (>= 1.7.0, < 2.0)
59
+ rubocop-ast (>= 0.4.0)
60
+ ruby-progressbar (1.13.0)
61
+ ruby2_keywords (0.0.5)
62
+ simplecov (0.22.0)
63
+ docile (~> 1.1)
64
+ simplecov-html (~> 0.11)
65
+ simplecov_json_formatter (~> 0.1)
66
+ simplecov-html (0.12.3)
67
+ simplecov_json_formatter (0.1.4)
68
+ sinatra (3.1.0)
69
+ mustermann (~> 3.0)
70
+ rack (~> 2.2, >= 2.2.4)
71
+ rack-protection (= 3.1.0)
72
+ tilt (~> 2.0)
73
+ tilt (2.2.0)
74
+ unicode-display_width (2.5.0)
75
+ yard (0.9.34)
76
+
77
+ PLATFORMS
78
+ x86_64-linux
79
+
80
+ DEPENDENCIES
81
+ bundler (~> 2.4)
82
+ rack-test (~> 2.1)
83
+ rake (~> 13.1)
84
+ rspec (~> 3.12)
85
+ rubocop (~> 1.57)
86
+ rubocop-performance (~> 1.19)
87
+ simplecov (~> 0.22)
88
+ sinatra-bouncer!
89
+ yard (~> 0.9)
90
+
91
+ BUNDLED WITH
92
+ 2.4.19
data/README.md CHANGED
@@ -1,108 +1,285 @@
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.
1
+ # Sinatra-Bouncer
2
+
3
+ Simple permissions extension for [Sinatra](http://www.sinatrarb.com/). Require the gem, then declare which
4
+ routes are permitted based on your own logic.
5
+
6
+ ## Big Picture
7
+
8
+ Bouncer's syntax looks like:
3
9
 
4
- **Gemfile**
5
10
  ```ruby
6
- gem 'sinatra-bouncer'
11
+ # You can define roles to collect permissions together
12
+ bouncer.role :admins do
13
+ current_user&.admin?
14
+ end
15
+
16
+ bouncer.rules do
17
+ # Routes match based on one or more strings.
18
+ anyone.can get: '/',
19
+ post: ['/user/sign-in',
20
+ '/user/sign-out']
21
+
22
+ # Wildcards match anything directly under that path
23
+ anyone.can get: '/lib/js/*'
24
+
25
+ admins.can get: '/admin/*',
26
+ post: '/admin/actions/*'
27
+
28
+ # ... etc ...
29
+ end
7
30
  ```
8
31
 
9
- **Terminal**
10
- ```sh
11
- gem install sinatra-bouncer
32
+ ### Features
33
+
34
+ Here's what this Gem provides:
35
+
36
+ * **Block-by-default**
37
+ * Any route must be explicitly allowed
38
+ * **Role-Oriented**
39
+ * The [DSL](https://en.wikipedia.org/wiki/Domain-specific_language) is constructed to be easily readable
40
+ * **Declarative Syntax**
41
+ * Straightforward syntax reduces complexity of layered permissions
42
+ * Keeps permissions together for clarity
43
+ * **Conditional Logic Via Blocks**
44
+ * Often additional checks must be performed to know if a route is allowed.
45
+ * **Grouping**
46
+ * Routes can be matched by wildcard
47
+ * **Forced Boolean Affirmation**
48
+ * Condition blocks must explicitly return `true`, avoiding accidental truthy values
49
+
50
+ ### Anti-Features
51
+
52
+ Bouncer intentionally does not support some concepts.
53
+
54
+ * **No User Identification** *(aka Authentication)*
55
+ * Bouncer is not a user identification gem. It assumes you already have an existing solution
56
+ like [Warden](https://github.com/wardencommunity/warden). Knowing *who* someone is is already a complex enough
57
+ problem and should be separate from what they may do.
58
+ * **No Rails Integration**
59
+ * Bouncer is not intended to work with Rails. Use one of the Rails-based permissions gems for these situations.
60
+ * **No Negation**
61
+ * There is intentionally no negation (eg. `cannot`) to preserve the default-deny frame of mind
62
+
63
+ ## Installation
64
+
65
+ Add this to your gemfile:
66
+
67
+ ```ruby
68
+ gem 'sinatra-bouncer'
12
69
  ```
13
70
 
14
- ##Quickstart
15
- ###Step 1: Require/Register Bouncer
71
+ Then run:
16
72
 
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`
73
+ ```shell
74
+ bundle install
75
+ ```
76
+
77
+ **Sinatra Modular Style**
20
78
 
21
- **Sinatra Classic**
22
79
  ```ruby
23
- require 'sinatra'
80
+ require 'sinatra/base'
24
81
  require 'sinatra/bouncer'
25
82
 
83
+ class MyApp < Sinatra::Base
84
+ register Sinatra::Bouncer
85
+
86
+ bouncer.rules do
87
+ # ... can statements ...
88
+ end
89
+
26
90
  # ... routes and other config
91
+ end
27
92
  ```
28
93
 
29
- **Modular**
94
+ **Sinatra Classic Style**
95
+
30
96
  ```ruby
31
- require 'sinatra/base'
97
+ require 'sinatra'
32
98
  require 'sinatra/bouncer'
33
99
 
34
- class MyApp < Sinatra::Base
35
- register Sinatra::Bouncer
36
-
37
- # ... routes and other config
100
+ bouncer.rules do
101
+ # ... can statements ...
38
102
  end
103
+
104
+ # ... routes and other config
39
105
  ```
40
106
 
41
- ###Step 2: Declare Bouncer Rules
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,
43
- the `request` object, and `params`.
107
+ ## Usage
108
+
109
+ Define roles using the `role` method and provide each one with a condition block. Then call `rules` with a block that
110
+ uses your defined roles with the `#can` or `#can_sometimes` DSL methods to declare which
111
+ paths are allowed.
112
+
113
+ The rules block is run once as part of the configuration phase but the condition blocks are evaluated in the context of
114
+ the request handler. This means they have access to Sinatra helpers, the `request` object, and `params`.
44
115
 
45
116
  ```ruby
46
117
  require 'sinatra'
47
118
  require 'sinatra/bouncer'
48
119
 
49
- rules do
50
- # example: allow any GET request
51
- can(:get, :all)
52
-
53
- # example: logged in users can edit their account
54
- if(current_user)
55
- can(:post, '/user_edits_account')
56
- end
120
+ bouncer.role :members do
121
+ !current_user.nil?
122
+ end
123
+
124
+ bouncer.role :bots do
125
+ !request.get_header('X-CUSTOM-BOT').nil?
126
+ end
127
+
128
+ bouncer.rules do
129
+ # example: always allow GET requests to '/' and '/about'; and POST requests to '/sign-in'
130
+ anyone.can get: ['/', '/about'],
131
+ post: '/sign-in'
132
+
133
+ # example: logged in users can view (GET) member restricted paths and edit their account (POST)
134
+ members.can get: '/members/*'
135
+ members.can_sometimes post: '/members/edit-account' do
136
+ current_user && current_user.id == params[:id]
137
+ end
138
+
139
+ # example: require presence of arbitrary request header in the role condition
140
+ bots.can get: '/bots/*'
57
141
  end
58
142
 
59
- # ... route declarations as normal below
143
+ # ... Sinatra route declarations as normal ...
60
144
  ```
61
145
 
62
- ## API
63
- #### can
64
- Any route declared with #can will be accepted without further challenge.
146
+ ### Role Declarations
147
+
148
+ Roles are declared using the `#role` method in your Sinatra config. Each one must be provided a condition block that
149
+ must return exactly `true` when the role applies.
65
150
 
66
151
  ```ruby
67
- rules do
68
- can(:post, '/user_posts_blog')
152
+ # let's pretend that current_user is a helper that returns the user from Warden
153
+ bouncer.role :admins do
154
+ current_user&.admin?
69
155
  end
70
156
  ```
71
157
 
72
- ####can_sometimes
73
- `can_sometimes` is for occasions that you to check further, but want to defer that choice until the path is actually attempted.
74
- `can_sometimes` takes a block that will be run once the path is attempted. This block **must return an explicit boolean**
75
- (ie. `true` or `false`) to avoid any accidental truthy values creating unwanted access.
158
+ > **Note:** There is a default role called `anyone` that is always declared for you.
159
+
160
+ ### HTTP Method and Route Matching
161
+
162
+ Both `#can` and `#can_sometimes` accept symbol
163
+ [HTTP methods](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) as keys
164
+ and each key is paired with one or more path strings.
165
+
166
+ ```ruby
167
+ # example: single method, single route
168
+ anyone.can get: '/'
169
+
170
+ # example: multiple methods, single route each
171
+ anyone.can get: '/',
172
+ post: '/blog/action/save'
173
+
174
+ # example: multiple methods, multiple routes (using string array syntax)
175
+ anyone.can get: %w[/
176
+ /sign-in
177
+ /blog/editor],
178
+ post: %w[/blog/action/save
179
+ /blog/action/delete]
180
+ ```
181
+
182
+ > **Note** Allowing GET implies allowing HEAD, since HEAD is by spec a GET without a response body. The reverse is not
183
+ > true, however; allowing HEAD will not also allow GET.
184
+
185
+ #### Wildcards and Special Symbols
186
+
187
+ Provide a wildcard `*` to match any string excluding slash. There is intentionally no syntax for matching wildcards
188
+ recursively, so nested paths will also need to be declared.
189
+
190
+ ```ruby
191
+ # example: match anything directly under the /members/ path
192
+ members.can get: '/members/*'
193
+ ```
194
+
195
+ There is also a special symbol, `:all` that matches all paths. It is intended for rare use
196
+ with superadmin-type accounts.
197
+
198
+ ```ruby
199
+ # this allows GET on all paths to those in the admin group
200
+ admins.can get: :all
201
+ ```
202
+
203
+ > **Warning** Always be cautious when using wildcards and special symbols to avoid accidentally opening up pathways that
204
+ > should remain private.
205
+
206
+ ### Always Allow: `can`
207
+
208
+ Any route declared with `#can` will be accepted without further challenge.
76
209
 
77
210
  ```ruby
78
- rules do
79
- can_sometimes('/login') # Anyone can access this path
211
+ bouncer.rules do
212
+ # Anyone can access this path over GET
213
+ anyone.can get: '/login'
80
214
  end
81
215
  ```
82
216
 
83
- #### :any and :all special parameters
84
- Passing `can` or `can_sometimes`:
85
- * `:any` to the first parameter will match any HTTP method.
86
- * `:all` to the second parameter will match any path.
217
+ ### Conditionally Allow: `can_sometimes`
218
+
219
+ `can_sometimes` takes a condition block that will be run once the path is attempted. This block **must return an
220
+ explicit boolean** (ie. `true` or `false`) to avoid any accidental truthy values creating unwanted access.
87
221
 
88
222
  ```ruby
89
- # this allows get on all paths
90
- can(:get, :all)
223
+ bouncer.role :users do
224
+ !current_user.nil?
225
+ end
91
226
 
92
- # this allows any method type to run on the /login path
93
- can(:any, '/login')
227
+ bouncer.rules do
228
+ users.can_sometimes post: '/user/save' do
229
+ current_user.id == params[:id]
230
+ end
231
+ end
94
232
  ```
95
233
 
96
234
  ### 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.
235
+
236
+ The default bounce action is to `halt 403`. Call `bounce_with` with a block to specify your own behaviour. The block is
237
+ also run in a Sinatra request handler context, so you can use helpers here as well.
98
238
 
99
239
  ```ruby
100
240
  require 'sinatra'
101
241
  require 'sinatra/bouncer'
102
242
 
103
- bounce_with do
104
- redirect '/login'
243
+ bounce_with do
244
+ redirect '/login'
105
245
  end
106
246
 
107
247
  # bouncer rules, routes, etc...
108
248
  ```
249
+
250
+ ## Alternatives
251
+
252
+ The syntax for Bouncer is largely influenced by the now-deprecated [CanCan](https://github.com/ryanb/cancan) gem.
253
+
254
+ ## Contributing
255
+
256
+ Bug reports and pull requests are welcome on GitHub at https://github.com/TenjinInc/Sinatra-Bouncer.
257
+
258
+ Valued topics:
259
+
260
+ * Error messages (clarity, hinting)
261
+ * Documentation
262
+ * API
263
+ * Security correctness
264
+
265
+ This project is intended to be a friendly space for collaboration, and contributors are expected to adhere to the
266
+ [Contributor Covenant](https://www.contributor-covenant.org/) code of conduct. Play nice.
267
+
268
+ ### Core Developers
269
+
270
+ After checking out the repo, run `bundle install` to install dependencies. Then, run `rake spec` to run the tests. You
271
+ can also run `bin/console` for an interactive prompt that will allow you to experiment.
272
+
273
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the
274
+ version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version,
275
+ push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
276
+
277
+ Documentation is produced by Yard. Run `bundle exec rake yard`. The goal is to have 100% documentation coverage and 100%
278
+ test coverage.
279
+
280
+ Release notes are provided in `RELEASE_NOTES.md`, and should vaguely
281
+ follow [Keep A Changelog](https://keepachangelog.com/en/1.0.0/) recommendations.
282
+
283
+ ## License
284
+
285
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/license/mit/).