simon_says 0.1.6 → 0.2.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/README.md +38 -38
- data/lib/generators/active_record/simon_says_generator.rb +23 -0
- data/lib/generators/active_record/templates/migration.rb +5 -0
- data/lib/simon_says/authorizer.rb +6 -8
- data/lib/simon_says/roleable.rb +4 -7
- data/lib/simon_says/version.rb +1 -1
- data/simon_says.gemspec +1 -1
- data/test/rails_app/app/controllers/documents_controller.rb +2 -2
- data/test/rails_app/db/schema.rb +11 -11
- data/test/simon_says/roleable_test.rb +4 -4
- metadata +7 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6c3b6bd7983eb136f044c2653430143493637871
|
|
4
|
+
data.tar.gz: a66d00fe12a96f48e8865a61aaf61f145feb5007
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 284e3a2a74a36d1ae52aa78849a347f38071b4c5838dc43b60cdba794fd9bed021054941b44b44d28744511a3af57eb6764108ae087b7bf1d17e96b03f1f1e8a
|
|
7
|
+
data.tar.gz: dea0bc94572281d6b505ef4a3eb2ed0fb502e916483e122cf19f34444dfaf12947e374065495f2b2edd3c8a0e370b45a5454a1075c56daec896f642a02ae451e
|
data/README.md
CHANGED
|
@@ -4,22 +4,12 @@
|
|
|
4
4
|
Logo](https://raw.githubusercontent.com/SimplyBuilt/SimonSays/master/SimonSays.png)
|
|
5
5
|
|
|
6
6
|
This gem is a simple, declarative, role-based access control system for
|
|
7
|
-
Rails that works great with devise!
|
|
8
|
-
[docs](http://www.rubydoc.info/github/SimplyBuilt/SimonSays/) for more
|
|
9
|
-
details.
|
|
7
|
+
Rails that works great with devise!
|
|
10
8
|
|
|
11
9
|
[](https://travis-ci.org/SimplyBuilt/SimonSays)
|
|
12
10
|
[](https://badge.fury.io/rb/simon_says)
|
|
13
11
|
[](./LICENSE)
|
|
14
12
|
|
|
15
|
-
## About
|
|
16
|
-
|
|
17
|
-
A ruby gem for simple, declarative, role-based access control system for
|
|
18
|
-
[Rails](https://github.com/rails/rails) that works great with
|
|
19
|
-
[Devise](https://github.com/plataformatec/devise)! Take a look at the
|
|
20
|
-
[docs](http://www.rubydoc.info/github/SimplyBuilt/SimonSays/) for more
|
|
21
|
-
details!
|
|
22
|
-
|
|
23
13
|
### Installation
|
|
24
14
|
|
|
25
15
|
SimonSays can be installed via your Gemfile or using Ruby gems directly.
|
|
@@ -32,23 +22,24 @@ gem 'simon_says'
|
|
|
32
22
|
|
|
33
23
|
SimonSays consists of two parts:
|
|
34
24
|
|
|
35
|
-
1. A [Roleable](#roleable) concern provides a way to define access roles
|
|
36
|
-
on
|
|
37
|
-
2. An [Authorizer](#authorizer) concern which provides a
|
|
38
|
-
|
|
39
|
-
resources in relation to an already authenticated resource, like a
|
|
40
|
-
User or Admin.
|
|
25
|
+
1. A [Roleable](#roleable) concern which provides a way to define access roles
|
|
26
|
+
on User models or on join through models.
|
|
27
|
+
2. An [Authorizer](#authorizer) concern which provides a declarative API
|
|
28
|
+
to controllers for finding and authorizing model resources.
|
|
41
29
|
|
|
42
30
|
#### Roleable
|
|
43
31
|
|
|
44
|
-
First, we need to define some roles.
|
|
45
|
-
on either "User" models or on relationship models (such as a through
|
|
46
|
-
model linking a User to another resource). Roles are stored as an
|
|
32
|
+
First, we need to define some roles on a model. Roles are stored as an
|
|
47
33
|
integer and [bitmasking](https://en.wikipedia.org/wiki/Mask_(computing))
|
|
48
|
-
is used to determine
|
|
49
|
-
|
|
34
|
+
is used to determine the roles assigned for that model. SimonSays
|
|
35
|
+
provides a generator for creating a new migration for this required
|
|
36
|
+
attribute:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
rails g active_record:simon_says User
|
|
40
|
+
```
|
|
50
41
|
|
|
51
|
-
For example:
|
|
42
|
+
Now we can define some roles in our User model. For example:
|
|
52
43
|
|
|
53
44
|
```ruby
|
|
54
45
|
class User < ActiveRecord::Base
|
|
@@ -87,12 +78,19 @@ end
|
|
|
87
78
|
# => [:support]
|
|
88
79
|
```
|
|
89
80
|
|
|
81
|
+
Make sure to generate a migration using the correct attribute name if
|
|
82
|
+
`:as` is used. For example:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
rails g active_record:simon_says Admin access
|
|
86
|
+
```
|
|
87
|
+
|
|
90
88
|
We can also use `has_roles` to define roles on a join through model
|
|
91
89
|
which is used to associate a User with a resource.
|
|
92
90
|
|
|
93
91
|
```ruby
|
|
94
92
|
|
|
95
|
-
class
|
|
93
|
+
class Permission < ActiveRecord::Base
|
|
96
94
|
include SimonSays::Roleable
|
|
97
95
|
|
|
98
96
|
belongs_to :user
|
|
@@ -101,20 +99,20 @@ class Membership < ActiveRecord::Base
|
|
|
101
99
|
has_roles :download, :edit, :delete,
|
|
102
100
|
end
|
|
103
101
|
|
|
104
|
-
# >
|
|
102
|
+
# > Permission.new(roles: Permission::ROLES).roles
|
|
105
103
|
# => [:download, :edit, :delete]
|
|
106
104
|
```
|
|
107
105
|
|
|
108
106
|
It is useful to note the dynamically generated `has_` methods as shown
|
|
109
107
|
in the User model as well the `ROLES` constant which is used in the
|
|
110
|
-
|
|
111
|
-
code](https://github.com/SimplyBuilt/SimonSays/blob/master/lib/simon_says/roleable.rb)
|
|
112
|
-
to see how features are dynamically generated
|
|
108
|
+
Permission example. Take a look at the `Roleable`
|
|
109
|
+
[source code](https://github.com/SimplyBuilt/SimonSays/blob/master/lib/simon_says/roleable.rb)
|
|
110
|
+
to see how features are dynamically generated with `has_roles`.
|
|
113
111
|
|
|
114
112
|
#### Authorizer
|
|
115
113
|
|
|
116
114
|
The `Authorizer` concern provides several methods that can be used within
|
|
117
|
-
your controllers in declarative manner.
|
|
115
|
+
your controllers in a declarative manner.
|
|
118
116
|
|
|
119
117
|
Please note, certain assumptions are made with `Authorizer`. Building
|
|
120
118
|
upon the above User and Admin model examples, `Authorizer` would assume
|
|
@@ -138,7 +136,8 @@ to be used in controllers. All of these methods accept the `:only` and
|
|
|
138
136
|
- `authorize_resource(resource, *roles)`: Authorize resource for given
|
|
139
137
|
roles
|
|
140
138
|
- `find_and_authorize(resource, *roles)`: Find a resource and then try
|
|
141
|
-
authorize it for the given roles
|
|
139
|
+
authorize it for the given roles. If successful, the resource is
|
|
140
|
+
assigned to an instance variable
|
|
142
141
|
|
|
143
142
|
When find resources, the `default_authorization_scope` is used. It can
|
|
144
143
|
be customized on a per-controller basis. For example:
|
|
@@ -154,22 +153,23 @@ end
|
|
|
154
153
|
To authorize resources against a given role, we use either `authorize`
|
|
155
154
|
or `find_and_authorize`. For example, consider this
|
|
156
155
|
`DocumentsController` which uses an authenticated `User` resource and a
|
|
157
|
-
`
|
|
156
|
+
`Permission` through model:
|
|
158
157
|
|
|
159
158
|
```ruby
|
|
160
159
|
class DocumentsController < ApplicationController
|
|
161
160
|
authenticate :user
|
|
162
161
|
|
|
163
|
-
find_and_authorize :
|
|
164
|
-
find_and_authorize :
|
|
162
|
+
find_and_authorize :document, :edit, through: :permissions, only: [:edit, :update]
|
|
163
|
+
find_and_authorize :document, :delete, through: :permissions, only: :destroy
|
|
165
164
|
end
|
|
166
165
|
```
|
|
167
166
|
|
|
168
|
-
This controller will find Document
|
|
167
|
+
This controller will find a Document resource and assign it to the
|
|
169
168
|
`@document` instance variable. For the `:edit` and `:update` actions,
|
|
170
|
-
it'll require
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
it'll require a permission with an `:edit` role. For the `:destroy`
|
|
170
|
+
method, a permission with the `:delete` role is required. Since the
|
|
171
|
+
`:through` option is used, a `@permission` instance variable will also
|
|
172
|
+
be created.
|
|
173
173
|
|
|
174
174
|
The `find_resource` method may raise an `ActiveRecord::RecordNotFound`
|
|
175
175
|
exception. The `authorize` method may raise a
|
|
@@ -177,7 +177,7 @@ exception. The `authorize` method may raise a
|
|
|
177
177
|
access. As a result, the `find_and_authorize` method may raise either
|
|
178
178
|
exception.
|
|
179
179
|
|
|
180
|
-
We can also use a different authorization scope
|
|
180
|
+
We can also use a different authorization scope with the `:from`
|
|
181
181
|
option for `find_resource` and `find_and_authorize`. For example:
|
|
182
182
|
|
|
183
183
|
```ruby
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require 'rails/generators/active_record'
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Generators
|
|
5
|
+
class SimonSaysGenerator < ActiveRecord::Generators::Base
|
|
6
|
+
source_root File.expand_path("../templates", __FILE__)
|
|
7
|
+
|
|
8
|
+
def copy_simon_says_migration
|
|
9
|
+
migration_template "migration.rb", "db/migrate/simon_says_add_to_#{table_name}.rb"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def migration_version
|
|
15
|
+
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" if Rails.version >= '5.0.0'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def role_attribute_name
|
|
19
|
+
args.first || 'roles'
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -151,11 +151,6 @@ module SimonSays
|
|
|
151
151
|
name = options[:resource]
|
|
152
152
|
end
|
|
153
153
|
|
|
154
|
-
attr = Roleable.registry[name]
|
|
155
|
-
|
|
156
|
-
required ||= options[attr.to_sym]
|
|
157
|
-
required = [required] unless Array === required
|
|
158
|
-
|
|
159
154
|
record = instance_variable_get("@#{name}")
|
|
160
155
|
|
|
161
156
|
if record.nil? # must be devise scope
|
|
@@ -163,12 +158,16 @@ module SimonSays
|
|
|
163
158
|
send "authenticate_#{name}!"
|
|
164
159
|
end
|
|
165
160
|
|
|
166
|
-
|
|
161
|
+
role_attr = record.class.role_attribute_name
|
|
162
|
+
actual = record.send(role_attr)
|
|
163
|
+
|
|
164
|
+
required ||= options[role_attr]
|
|
165
|
+
required = [required] unless Array === required
|
|
167
166
|
|
|
168
167
|
# actual roles must have at least
|
|
169
168
|
# one required role (array intersection)
|
|
170
169
|
((required & actual).size > 0).tap do |res|
|
|
171
|
-
raise Denied.new(
|
|
170
|
+
raise Denied.new(role_attr, required, actual) unless res
|
|
172
171
|
end
|
|
173
172
|
end
|
|
174
173
|
|
|
@@ -187,7 +186,6 @@ module SimonSays
|
|
|
187
186
|
|
|
188
187
|
else
|
|
189
188
|
klass = (options[:class_name] || resource).to_s
|
|
190
|
-
# TODO support array of namespaces?
|
|
191
189
|
klass = "#{options[:namespace]}/#{klass}" if options[:namespace]
|
|
192
190
|
|
|
193
191
|
scope = klass.classify.constantize
|
data/lib/simon_says/roleable.rb
CHANGED
|
@@ -2,11 +2,6 @@ module SimonSays
|
|
|
2
2
|
module Roleable
|
|
3
3
|
extend ActiveSupport::Concern
|
|
4
4
|
|
|
5
|
-
def self.registry # :nodoc:
|
|
6
|
-
# "global" registry we'll use when authorizing
|
|
7
|
-
@registry ||= {}
|
|
8
|
-
end
|
|
9
|
-
|
|
10
5
|
module ClassMethods
|
|
11
6
|
# Provides a declarative method to introduce role based
|
|
12
7
|
# access controller through a give integer mask.
|
|
@@ -65,8 +60,6 @@ module SimonSays
|
|
|
65
60
|
singular = name.singularize
|
|
66
61
|
const = name.upcase
|
|
67
62
|
|
|
68
|
-
Roleable.registry[model_name.to_s.downcase.to_sym] ||= name
|
|
69
|
-
|
|
70
63
|
roles.map!(&:to_sym)
|
|
71
64
|
|
|
72
65
|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__
|
|
@@ -88,6 +81,10 @@ module SimonSays
|
|
|
88
81
|
def has_#{name}?(*args)
|
|
89
82
|
(#{name} & args).size > 0
|
|
90
83
|
end
|
|
84
|
+
|
|
85
|
+
def self.role_attribute_name
|
|
86
|
+
:#{name}
|
|
87
|
+
end
|
|
91
88
|
RUBY_EVAL
|
|
92
89
|
|
|
93
90
|
if name != singular
|
data/lib/simon_says/version.rb
CHANGED
data/simon_says.gemspec
CHANGED
|
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
|
22
22
|
|
|
23
23
|
spec.add_development_dependency "bundler", "~> 1.9"
|
|
24
24
|
spec.add_development_dependency "rake", "~> 10.0"
|
|
25
|
-
spec.add_development_dependency "rails", ">= 4.0", "< 5.
|
|
25
|
+
spec.add_development_dependency "rails", ">= 4.0", "< 5.2"
|
|
26
26
|
spec.add_development_dependency "responders", "~> 2.0"
|
|
27
27
|
spec.add_development_dependency "mocha", "~> 1.1"
|
|
28
28
|
end
|
|
@@ -9,13 +9,13 @@ class DocumentsController < ApplicationController
|
|
|
9
9
|
find_and_authorize :document, :download, through: :memberships, only: :send_file
|
|
10
10
|
|
|
11
11
|
def index
|
|
12
|
-
@documents =
|
|
12
|
+
@documents = current_user.documents
|
|
13
13
|
|
|
14
14
|
respond_with @documents
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def create
|
|
18
|
-
@document =
|
|
18
|
+
@document = current_user.documents.create(document_params)
|
|
19
19
|
|
|
20
20
|
respond_with @document
|
|
21
21
|
end
|
data/test/rails_app/db/schema.rb
CHANGED
|
@@ -13,35 +13,35 @@
|
|
|
13
13
|
ActiveRecord::Schema.define(version: 20160823220959) do
|
|
14
14
|
|
|
15
15
|
create_table "admin_reports", force: :cascade do |t|
|
|
16
|
-
t.string
|
|
16
|
+
t.string "title"
|
|
17
17
|
t.datetime "created_at", null: false
|
|
18
18
|
t.datetime "updated_at", null: false
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
create_table "admins", force: :cascade do |t|
|
|
22
|
-
t.integer
|
|
23
|
-
t.datetime "created_at",
|
|
24
|
-
t.datetime "updated_at",
|
|
22
|
+
t.integer "access_mask"
|
|
23
|
+
t.datetime "created_at", null: false
|
|
24
|
+
t.datetime "updated_at", null: false
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
create_table "documents", force: :cascade do |t|
|
|
28
|
-
t.string
|
|
28
|
+
t.string "title"
|
|
29
29
|
t.datetime "created_at", null: false
|
|
30
30
|
t.datetime "updated_at", null: false
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
create_table "images", force: :cascade do |t|
|
|
34
|
-
t.string
|
|
34
|
+
t.string "token"
|
|
35
35
|
t.datetime "created_at", null: false
|
|
36
36
|
t.datetime "updated_at", null: false
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
create_table "memberships", force: :cascade do |t|
|
|
40
|
-
t.integer
|
|
41
|
-
t.integer
|
|
42
|
-
t.integer
|
|
43
|
-
t.datetime "created_at",
|
|
44
|
-
t.datetime "updated_at",
|
|
40
|
+
t.integer "user_id"
|
|
41
|
+
t.integer "document_id"
|
|
42
|
+
t.integer "roles_mask", default: 0
|
|
43
|
+
t.datetime "created_at", null: false
|
|
44
|
+
t.datetime "updated_at", null: false
|
|
45
45
|
t.index ["document_id"], name: "index_memberships_on_document_id"
|
|
46
46
|
t.index ["user_id"], name: "index_memberships_on_user_id"
|
|
47
47
|
end
|
|
@@ -144,11 +144,11 @@ class RoleableTest < ActiveSupport::TestCase
|
|
|
144
144
|
]
|
|
145
145
|
end
|
|
146
146
|
|
|
147
|
-
test "Membership
|
|
148
|
-
|
|
147
|
+
test "Membership defines role_attribute_name" do
|
|
148
|
+
assert_equal :roles, Membership.role_attribute_name
|
|
149
149
|
end
|
|
150
150
|
|
|
151
|
-
test "Admin
|
|
152
|
-
|
|
151
|
+
test "Admin defines role_attribute_name" do
|
|
152
|
+
assert_equal :access, Admin.role_attribute_name
|
|
153
153
|
end
|
|
154
154
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: simon_says
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michael Coyne
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date: 2017-
|
|
13
|
+
date: 2017-10-07 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: activesupport
|
|
@@ -63,7 +63,7 @@ dependencies:
|
|
|
63
63
|
version: '4.0'
|
|
64
64
|
- - "<"
|
|
65
65
|
- !ruby/object:Gem::Version
|
|
66
|
-
version: '5.
|
|
66
|
+
version: '5.2'
|
|
67
67
|
type: :development
|
|
68
68
|
prerelease: false
|
|
69
69
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -73,7 +73,7 @@ dependencies:
|
|
|
73
73
|
version: '4.0'
|
|
74
74
|
- - "<"
|
|
75
75
|
- !ruby/object:Gem::Version
|
|
76
|
-
version: '5.
|
|
76
|
+
version: '5.2'
|
|
77
77
|
- !ruby/object:Gem::Dependency
|
|
78
78
|
name: responders
|
|
79
79
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -120,6 +120,8 @@ files:
|
|
|
120
120
|
- README.md
|
|
121
121
|
- Rakefile
|
|
122
122
|
- SimonSays.png
|
|
123
|
+
- lib/generators/active_record/simon_says_generator.rb
|
|
124
|
+
- lib/generators/active_record/templates/migration.rb
|
|
123
125
|
- lib/simon_says.rb
|
|
124
126
|
- lib/simon_says/authorizer.rb
|
|
125
127
|
- lib/simon_says/roleable.rb
|
|
@@ -234,7 +236,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
234
236
|
version: '0'
|
|
235
237
|
requirements: []
|
|
236
238
|
rubyforge_project:
|
|
237
|
-
rubygems_version: 2.6.
|
|
239
|
+
rubygems_version: 2.6.13
|
|
238
240
|
signing_key:
|
|
239
241
|
specification_version: 4
|
|
240
242
|
summary: Light-weight, declarative authorization and access control for Rails
|