simon_says 0.1.6 → 0.2.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
  SHA1:
3
- metadata.gz: eb74ffaa3fbf69fbab2c3534393df41d91f8fccd
4
- data.tar.gz: 8c96349d0aa76b9617b095cb17a4e91d05bd3829
3
+ metadata.gz: 6c3b6bd7983eb136f044c2653430143493637871
4
+ data.tar.gz: a66d00fe12a96f48e8865a61aaf61f145feb5007
5
5
  SHA512:
6
- metadata.gz: 71da79481390650b93d0045f379206ed05a1882de58afdb603a88f7996ce717e9fd46941ece13ac926d5901411e9435731760d0d3511194f90df0bf32efe6920
7
- data.tar.gz: 0c8e4ff2fcc7a0932d0a794e48fb3382c3d083d65b0cf53a326adcbaf2e2e1d23a919354b3c81682b24ce5ca229580da29460dc25f51e26b09534059dd27d6c8
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! Take a look at the
8
- [docs](http://www.rubydoc.info/github/SimplyBuilt/SimonSays/) for more
9
- details.
7
+ Rails that works great with devise!
10
8
 
11
9
  [![Travis Build Status](https://travis-ci.org/SimplyBuilt/SimonSays.svg)](https://travis-ci.org/SimplyBuilt/SimonSays)
12
10
  [![Gem Version](https://badge.fury.io/rb/simon_says.svg)](https://badge.fury.io/rb/simon_says)
13
11
  [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./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 a given resource, such as User or on join through model.
37
- 2. An [Authorizer](#authorizer) concern which provides a lightweight,
38
- declarative API to controllers for finding and authorizing these
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. Generally speaking roles will exist
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 authorization logic. When using `Roleable` you need
49
- to add a `roles_mask` column to the model.
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 Membership < ActiveRecord::Base
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
- # > Membership.new(roles: Membership::ROLES).roles
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
- Membership example. Take a look at the [roleable source
111
- code](https://github.com/SimplyBuilt/SimonSays/blob/master/lib/simon_says/roleable.rb)
112
- to see how features are dynamically generated when using `has_roles`.
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
- `Membership` through model:
156
+ `Permission` through model:
158
157
 
159
158
  ```ruby
160
159
  class DocumentsController < ApplicationController
161
160
  authenticate :user
162
161
 
163
- find_and_authorize :documents, :edit, through: :memberships, only: [:edit, :update]
164
- find_and_authorize :documents, :delete, through: :memberships, only: :destroy
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 resources and assign them to the
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 membership with an `:edit` role. For the `:destroy` method, a
171
- memberships with the `:delete` role is required. It is possible for a
172
- given User to have both, one, or neither of those roles.
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 by via the `:from`
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
@@ -0,0 +1,5 @@
1
+ class SimonSaysAddTo<%= table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ add_column :<%= table_name %>, :<%= role_attribute_name %>_mask, :integer, default: 0, null: false
4
+ end
5
+ 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
- actual = record.send(attr)
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(attr, required, actual) unless res
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module SimonSays
2
- VERSION = '0.1.6'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -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.1"
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 = Document.all
12
+ @documents = current_user.documents
13
13
 
14
14
  respond_with @documents
15
15
  end
16
16
 
17
17
  def create
18
- @document = Document.create(document_params)
18
+ @document = current_user.documents.create(document_params)
19
19
 
20
20
  respond_with @document
21
21
  end
@@ -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 "title"
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 "access_mask"
23
- t.datetime "created_at", null: false
24
- t.datetime "updated_at", null: false
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 "title"
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 "token"
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 "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
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 is added to registry" do
148
- assert_includes SimonSays::Roleable.registry, :membership
147
+ test "Membership defines role_attribute_name" do
148
+ assert_equal :roles, Membership.role_attribute_name
149
149
  end
150
150
 
151
- test "Admin is added to registry" do
152
- assert_includes SimonSays::Roleable.registry, :admin
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.1.6
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-09-18 00:00:00.000000000 Z
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.1'
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.1'
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.12
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