simon_says 0.0.3
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 +7 -0
- data/.gitignore +18 -0
- data/.gitpublish +3 -0
- data/.travis.yml +9 -0
- data/Gemfile +13 -0
- data/Guardfile +13 -0
- data/LICENSE.txt +22 -0
- data/README.md +18 -0
- data/ROADMAP.md +9 -0
- data/Rakefile +24 -0
- data/SimonSays.png +0 -0
- data/lib/simon_says/authorizer.rb +157 -0
- data/lib/simon_says/roleable.rb +107 -0
- data/lib/simon_says/version.rb +3 -0
- data/lib/simon_says.rb +8 -0
- data/simon_says.gemspec +28 -0
- data/test/controllers/admin/reports_controller_test.rb +92 -0
- data/test/controllers/documents_controller_test.rb +87 -0
- data/test/models/admin_test.rb +7 -0
- data/test/models/membership_test.rb +7 -0
- data/test/rails_app/.gitignore +16 -0
- data/test/rails_app/README.rdoc +28 -0
- data/test/rails_app/Rakefile +6 -0
- data/test/rails_app/app/assets/images/.keep +0 -0
- data/test/rails_app/app/assets/javascripts/application.js +16 -0
- data/test/rails_app/app/assets/stylesheets/application.css +15 -0
- data/test/rails_app/app/controllers/admin/reports_controller.rb +40 -0
- data/test/rails_app/app/controllers/application_controller.rb +19 -0
- data/test/rails_app/app/controllers/concerns/.keep +0 -0
- data/test/rails_app/app/controllers/documents_controller.rb +48 -0
- data/test/rails_app/app/helpers/application_helper.rb +2 -0
- data/test/rails_app/app/mailers/.keep +0 -0
- data/test/rails_app/app/models/.keep +0 -0
- data/test/rails_app/app/models/admin/report.rb +2 -0
- data/test/rails_app/app/models/admin.rb +5 -0
- data/test/rails_app/app/models/concerns/.keep +0 -0
- data/test/rails_app/app/models/document.rb +4 -0
- data/test/rails_app/app/models/membership.rb +8 -0
- data/test/rails_app/app/models/user.rb +4 -0
- data/test/rails_app/app/views/layouts/application.html.erb +14 -0
- data/test/rails_app/bin/bundle +3 -0
- data/test/rails_app/bin/rails +8 -0
- data/test/rails_app/bin/rake +8 -0
- data/test/rails_app/bin/spring +18 -0
- data/test/rails_app/config/application.rb +29 -0
- data/test/rails_app/config/boot.rb +4 -0
- data/test/rails_app/config/database.yml +25 -0
- data/test/rails_app/config/environment.rb +5 -0
- data/test/rails_app/config/environments/development.rb +37 -0
- data/test/rails_app/config/environments/production.rb +78 -0
- data/test/rails_app/config/environments/test.rb +39 -0
- data/test/rails_app/config/initializers/assets.rb +8 -0
- data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/test/rails_app/config/initializers/cookies_serializer.rb +3 -0
- data/test/rails_app/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/rails_app/config/initializers/inflections.rb +16 -0
- data/test/rails_app/config/initializers/mime_types.rb +4 -0
- data/test/rails_app/config/initializers/session_store.rb +3 -0
- data/test/rails_app/config/initializers/wrap_parameters.rb +14 -0
- data/test/rails_app/config/locales/en.yml +23 -0
- data/test/rails_app/config/routes.rb +7 -0
- data/test/rails_app/config/secrets.yml +22 -0
- data/test/rails_app/config.ru +4 -0
- data/test/rails_app/db/migrate/20141016142638_create_admins.rb +9 -0
- data/test/rails_app/db/migrate/20141016183619_create_users.rb +8 -0
- data/test/rails_app/db/migrate/20141016183633_create_memberships.rb +12 -0
- data/test/rails_app/db/migrate/20141016183642_create_documents.rb +9 -0
- data/test/rails_app/db/migrate/20141017140833_create_admin_reports.rb +9 -0
- data/test/rails_app/db/schema.rb +47 -0
- data/test/rails_app/db/seeds.rb +7 -0
- data/test/rails_app/lib/assets/.keep +0 -0
- data/test/rails_app/lib/tasks/.keep +0 -0
- data/test/rails_app/log/.keep +0 -0
- data/test/rails_app/public/404.html +67 -0
- data/test/rails_app/public/422.html +67 -0
- data/test/rails_app/public/500.html +66 -0
- data/test/rails_app/public/favicon.ico +0 -0
- data/test/rails_app/public/robots.txt +5 -0
- data/test/rails_app/test/controllers/.keep +0 -0
- data/test/rails_app/test/fixtures/.keep +0 -0
- data/test/rails_app/test/fixtures/admin/reports.yml +2 -0
- data/test/rails_app/test/fixtures/admins.yml +11 -0
- data/test/rails_app/test/fixtures/documents.yml +8 -0
- data/test/rails_app/test/fixtures/memberships.yml +10 -0
- data/test/rails_app/test/fixtures/users.yml +2 -0
- data/test/rails_app/test/helpers/.keep +0 -0
- data/test/rails_app/test/integration/.keep +0 -0
- data/test/rails_app/test/mailers/.keep +0 -0
- data/test/rails_app/test/models/.keep +0 -0
- data/test/rails_app/test/models/admin/report_test.rb +7 -0
- data/test/rails_app/test/models/document_test.rb +7 -0
- data/test/rails_app/test/models/membership_test.rb +7 -0
- data/test/rails_app/test/models/user_test.rb +7 -0
- data/test/rails_app/test/test_helper.rb +10 -0
- data/test/rails_app/vendor/assets/javascripts/.keep +0 -0
- data/test/rails_app/vendor/assets/stylesheets/.keep +0 -0
- data/test/simon_says/authorizer_test.rb +143 -0
- data/test/simon_says/roleable_test.rb +200 -0
- data/test/simon_says_test.rb +7 -0
- data/test/test_helper.rb +48 -0
- metadata +312 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 8bea95d0e9c7c97c12f1f3dabcd8b419ed9f3e40
|
|
4
|
+
data.tar.gz: 6faef8c22e188dea0013ad8e41f425079c88284c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: e599afe61d51efe4e44d286e7fd5cb14954642e8af3855d115dd0aea3cf4eae12c8c316aa74dd322b45ea62ea689cb650c76e3448edbfe316bb6a2560e7ae510
|
|
7
|
+
data.tar.gz: db9ffc036f4cbcb123109214d60c97334d534f43202b614a777341baaf39a7d9bda35b00c4b116abee98d0c0bb6ef884bc45b9a2eda7a56aa36f100bde90848c
|
data/.gitignore
ADDED
data/.gitpublish
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# A sample Guardfile
|
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
|
3
|
+
|
|
4
|
+
guard :minitest do
|
|
5
|
+
# with Minitest::Unit
|
|
6
|
+
watch(%r{^test/(.*)\/?(.*)_test\.rb$})
|
|
7
|
+
watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}#{m[2]}_test.rb" }
|
|
8
|
+
watch(%r{^test/test_helper\.rb$}) { 'test' }
|
|
9
|
+
|
|
10
|
+
# watch test Rails app
|
|
11
|
+
watch(%r{^test/rails_app/app/models/(.*)\.rb$}) { |m| "test/models/#{m[1]}_test.rb" }
|
|
12
|
+
watch(%r{^test/rails_app/app/controllers/(.*)\.rb$}) { |m| "test/controllers/#{m[1]}_test.rb" }
|
|
13
|
+
end
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2014 Michael Coyne
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# SimonSays!
|
|
2
|
+
|
|
3
|
+

|
|
5
|
+
|
|
6
|
+
This gem is a simple, declarative, role-based access control system for Rails that
|
|
7
|
+
works great with devise! Take a look at the [website](http://simonsays.onsimplybuilt.com) or
|
|
8
|
+
[docs](http://www.rubydoc.info/github/SimplyBuilt/SimonSays/) for more details!
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+
|
|
12
|
+
## Contributing
|
|
13
|
+
|
|
14
|
+
1. Fork it ( https://github.com/SimplyBuilt/SimonSays/fork )
|
|
15
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
16
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
17
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
18
|
+
5. Create a new Pull Request
|
data/ROADMAP.md
ADDED
data/Rakefile
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
|
2
|
+
require "rake/testtask"
|
|
3
|
+
require 'rdoc/task'
|
|
4
|
+
|
|
5
|
+
Rake::TestTask.new :test do |t|
|
|
6
|
+
t.libs << 'test'
|
|
7
|
+
t.pattern = 'test/**/*_test.rb'
|
|
8
|
+
t.verbose = false
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
namespace :docs do
|
|
12
|
+
RDoc::Task.new :generate do |rdoc|
|
|
13
|
+
rdoc.title = "SimonSays RDOC"
|
|
14
|
+
rdoc.main = "README.md"
|
|
15
|
+
|
|
16
|
+
rdoc.rdoc_dir = "docs"
|
|
17
|
+
rdoc.rdoc_files.include("README.md", "lib/**/*.rb")
|
|
18
|
+
|
|
19
|
+
rdoc.options << "--all"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
task :default => :test
|
data/SimonSays.png
ADDED
|
Binary file
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
module SimonSays
|
|
2
|
+
module Authorizer
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
class Denied < Exception
|
|
6
|
+
def initialize(as, required, actual)
|
|
7
|
+
# TODO i18n for err message (as should be singluarized with 1 flag)
|
|
8
|
+
super "Access denied; #{required * ', '} role is required. Current access is #{actual * ', '}"
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
included do
|
|
13
|
+
class_attribute :default_authorization_scope
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Once +Authorizer+ is included these methods become
|
|
17
|
+
# available to your controllers.
|
|
18
|
+
module ClassMethods
|
|
19
|
+
# Authentication convenience method (to keep things declarative).
|
|
20
|
+
# This method just setups a +before_filter+
|
|
21
|
+
#
|
|
22
|
+
# * +scope+ is a symbol or string and should correspond to some sort
|
|
23
|
+
# of authentication scope (ie: +authenticate_user!+)
|
|
24
|
+
# * +opts+ filter options
|
|
25
|
+
#
|
|
26
|
+
# ====== Example
|
|
27
|
+
#
|
|
28
|
+
# authenticate :user, expect: :index
|
|
29
|
+
#
|
|
30
|
+
def authenticate(scope, opts = {})
|
|
31
|
+
before_filter :"authenticate_#{scope}!", filter_options(opts)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Find and authorize a resource.
|
|
35
|
+
#
|
|
36
|
+
# * +resource+ the name of resource to find
|
|
37
|
+
# * +roles+ one or more role symbols
|
|
38
|
+
# * the last argument may also be a filter options hash
|
|
39
|
+
#
|
|
40
|
+
# ====== Example
|
|
41
|
+
#
|
|
42
|
+
# find_and_authorize :document, :create, :update :publish, through: :memberships
|
|
43
|
+
def find_and_authorize(resource, *roles)
|
|
44
|
+
opts = roles.extract_options!
|
|
45
|
+
|
|
46
|
+
before_filter(filter_options(opts)) do
|
|
47
|
+
find_resource resource, opts
|
|
48
|
+
|
|
49
|
+
authorize roles, opts unless roles.empty?
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Find a resource
|
|
54
|
+
#
|
|
55
|
+
# * +resource+ the name of the resource to find
|
|
56
|
+
# * +opts+ filter options
|
|
57
|
+
def find_resource(resource, opts = {})
|
|
58
|
+
before_filter(filter_options(opts)) do
|
|
59
|
+
find_resource resource, opts
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Authorize against a given resource
|
|
64
|
+
#
|
|
65
|
+
# * +resource+ the name of the resource to authorize against. The
|
|
66
|
+
# resource should include +Roleable+ and define some set of
|
|
67
|
+
# roles. This method also expect the record to be available as
|
|
68
|
+
# an instance variable (which is the case if +find_resource+ is
|
|
69
|
+
# called before hand)
|
|
70
|
+
# * +roles+ one or more role symbols
|
|
71
|
+
# * the last argument may also be a filter options hash
|
|
72
|
+
def authorize_resource(resource, *roles)
|
|
73
|
+
before_filter(filter_options(roles.extract_options!)) do
|
|
74
|
+
authorize(roles, { resource: resource })
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def filter_options(options) # :nodoc:
|
|
79
|
+
{ except: options.delete(:except), only: options.delete(:only) }
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# @returns Primary resource found; need for +authorize+ calls
|
|
84
|
+
def find_resource(resource, options = {}) # :nodoc:
|
|
85
|
+
resource = resource.to_s
|
|
86
|
+
|
|
87
|
+
scope, query = resource_scope_and_query(resource, options)
|
|
88
|
+
through = options[:through] ? options[:through].to_s : nil
|
|
89
|
+
|
|
90
|
+
assoc = through || (options[:from] ? resource.pluralize : nil)
|
|
91
|
+
scope = scope.send(assoc) if assoc
|
|
92
|
+
|
|
93
|
+
record = scope.where(query).first!
|
|
94
|
+
|
|
95
|
+
if through
|
|
96
|
+
instance_variable_set "@#{through.singularize}", record
|
|
97
|
+
record = record.send(resource)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
instance_variable_set "@#{resource}", record
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def authorize(required = nil, options) # :nodoc:
|
|
105
|
+
if through = options[:through]
|
|
106
|
+
name = through.to_s.singularize.to_sym
|
|
107
|
+
else
|
|
108
|
+
name = options[:resource]
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
attr = Roleable.registry[name]
|
|
112
|
+
required ||= options[attr.to_sym]
|
|
113
|
+
|
|
114
|
+
required = [required] unless Array === required
|
|
115
|
+
record = instance_variable_get("@#{name}")
|
|
116
|
+
|
|
117
|
+
if record.nil? # must be devise scope
|
|
118
|
+
record = send("current_#{name}")
|
|
119
|
+
send "authenticate_#{name}!"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
actual = record.send(attr)
|
|
123
|
+
|
|
124
|
+
# actual roles must have at least
|
|
125
|
+
# one required role (array intersection)
|
|
126
|
+
((required & actual).size > 0).tap do |res|
|
|
127
|
+
raise Denied.new(attr, required, actual) unless res
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
private
|
|
132
|
+
|
|
133
|
+
def resource_scope_and_query(resource, options) # :nodoc:
|
|
134
|
+
if options[:through]
|
|
135
|
+
field = "#{resource}_id"
|
|
136
|
+
|
|
137
|
+
query = { field => params[field] } if params[field]
|
|
138
|
+
scope = send(self.class.default_authorization_scope)
|
|
139
|
+
|
|
140
|
+
elsif options[:from]
|
|
141
|
+
scope = instance_variable_get("@#{options[:from]}") || send(options[:from])
|
|
142
|
+
|
|
143
|
+
else
|
|
144
|
+
klass = (options[:class_name] || resource).to_s
|
|
145
|
+
# TODO support array of namespaces?
|
|
146
|
+
klass = "#{options[:namespace]}/#{klass}" if options[:namespace]
|
|
147
|
+
|
|
148
|
+
scope = klass.classify.constantize
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
field ||= :id
|
|
152
|
+
query ||= { field => params[:id] }
|
|
153
|
+
|
|
154
|
+
return scope, query
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
module SimonSays
|
|
2
|
+
module Roleable
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
def self.registry # :nodoc:
|
|
6
|
+
# "global" registry we'll use when authorizing
|
|
7
|
+
@registry ||= {}
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
##
|
|
12
|
+
# Provides a declarative method to introduce role based
|
|
13
|
+
# access controller through a give integer mask.
|
|
14
|
+
#
|
|
15
|
+
# By default it'll use an attributed named +role_mask+. You can
|
|
16
|
+
# use the +:as+ option to change the prefix for the +_mask+
|
|
17
|
+
# attribute. This will also alter the names of the dynamically
|
|
18
|
+
# generated methods.
|
|
19
|
+
#
|
|
20
|
+
# ===== Example
|
|
21
|
+
#
|
|
22
|
+
# class User < ActiveRecord::Base
|
|
23
|
+
# include SimonSays::Roleable
|
|
24
|
+
#
|
|
25
|
+
# has_roles :read, :write, :delete
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
# class Editor < ActiveRecord::Base
|
|
29
|
+
# include SimonSays::Roleable
|
|
30
|
+
#
|
|
31
|
+
# has_roles :create, :update, :publish, as: :access
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
# ===== Dynamic Methods
|
|
35
|
+
#
|
|
36
|
+
# Several methods are dynamically genreated when calling +has_roles+.
|
|
37
|
+
# The methods generated include a setter, a getter and a predicate
|
|
38
|
+
# method. For examples:
|
|
39
|
+
#
|
|
40
|
+
# User.new.roles
|
|
41
|
+
# => []
|
|
42
|
+
#
|
|
43
|
+
# User.new(roles: :read).roles
|
|
44
|
+
# => [:read]
|
|
45
|
+
#
|
|
46
|
+
# User.new.tap { |u| u.roles = :write, :read }.roles
|
|
47
|
+
# => [:read, :write]
|
|
48
|
+
#
|
|
49
|
+
# User.new(roles: [:read, :write]).has_roles? :read, :write
|
|
50
|
+
# => true
|
|
51
|
+
#
|
|
52
|
+
# User.new(roles: :read).has_role? :read
|
|
53
|
+
# => true
|
|
54
|
+
#
|
|
55
|
+
# Here's an example using the +:as+ prefix option:
|
|
56
|
+
#
|
|
57
|
+
# Editor.new(access: %w[create update publish]).access
|
|
58
|
+
# => [:create, :update, :publish]
|
|
59
|
+
#
|
|
60
|
+
# Editor.new(access: :publish).has_access? :create
|
|
61
|
+
# => false
|
|
62
|
+
#
|
|
63
|
+
def has_roles *roles
|
|
64
|
+
options = roles.extract_options!
|
|
65
|
+
|
|
66
|
+
name = (options[:as] || :roles).to_s
|
|
67
|
+
singular = name.singularize
|
|
68
|
+
const = name.upcase
|
|
69
|
+
|
|
70
|
+
Roleable.registry[model_name.to_s.downcase.to_sym] ||= name
|
|
71
|
+
|
|
72
|
+
roles.map!(&:to_sym)
|
|
73
|
+
|
|
74
|
+
class_eval <<-RUBY_EVAL, __FILE__, __LINE__
|
|
75
|
+
#{const} = %i[#{roles * ' '}]
|
|
76
|
+
|
|
77
|
+
def #{name}=(args)
|
|
78
|
+
args = [args] unless Array === args
|
|
79
|
+
args.map!(&:to_sym)
|
|
80
|
+
|
|
81
|
+
self[:#{name}_mask] = (args & #{const}).map { |i| 2 ** #{const}.index(i) }.sum
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def #{name}
|
|
85
|
+
#{const}.reject { |i| ((#{name}_mask || 0) & 2 ** #{const}.index(i)).zero? }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def has_#{name}?(*args)
|
|
89
|
+
(#{name} & args).size > 0
|
|
90
|
+
end
|
|
91
|
+
RUBY_EVAL
|
|
92
|
+
|
|
93
|
+
if name != singular
|
|
94
|
+
class_eval <<-RUBY_EVAL
|
|
95
|
+
alias has_#{singular}? has_#{name}?
|
|
96
|
+
RUBY_EVAL
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Declare a scope for finding records with a given role set
|
|
100
|
+
# TODO support an array roles (must match ALL)
|
|
101
|
+
scope "with_#{name}", ->(role) {
|
|
102
|
+
where("(#{name}_mask & ?) > 0", 2**roles.index(role))
|
|
103
|
+
}
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
data/lib/simon_says.rb
ADDED
data/simon_says.gemspec
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'simon_says/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "simon_says"
|
|
8
|
+
spec.version = SimonSays::VERSION
|
|
9
|
+
spec.authors = ["Michael Coyne"]
|
|
10
|
+
spec.email = ["mikeycgto@gmail.com"]
|
|
11
|
+
spec.summary = %q{Light-weight, declarative authorization and access control for Rails}
|
|
12
|
+
spec.description = %q{This gem is a simple, easy-to-use declarative role-based access control system for Rails}
|
|
13
|
+
spec.homepage = "https://github.com/SimplyBuilt/SimonSays"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
spec.add_dependency "activesupport", "~> 4.0"
|
|
22
|
+
|
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
|
25
|
+
spec.add_development_dependency "rails", "~> 4.1"
|
|
26
|
+
spec.add_development_dependency "responders", "~> 1.0"
|
|
27
|
+
spec.add_development_dependency "mocha", "~> 1.1"
|
|
28
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class Admin::ReportsControllerTest < ActionController::TestCase
|
|
4
|
+
setup do
|
|
5
|
+
@support = admins(:support)
|
|
6
|
+
@marketing = admins(:marketing)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
test "index with access" do
|
|
10
|
+
@controller.current_admin = @support
|
|
11
|
+
|
|
12
|
+
get :index, format: :json
|
|
13
|
+
|
|
14
|
+
assert_response :success
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
test "index without access" do
|
|
18
|
+
@controller.current_admin = @marketing
|
|
19
|
+
|
|
20
|
+
assert_raises SimonSays::Authorizer::Denied do
|
|
21
|
+
get :index, format: :json
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
test "create with access" do
|
|
26
|
+
@controller.current_admin = @support
|
|
27
|
+
|
|
28
|
+
assert_difference 'Admin::Report.count' do
|
|
29
|
+
post :create, report: { title: 'Test' }, format: :json
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
test "create without access" do
|
|
34
|
+
@controller.current_admin = @marketing
|
|
35
|
+
|
|
36
|
+
assert_raises SimonSays::Authorizer::Denied do
|
|
37
|
+
post :create, report: { title: 'Test' }, format: :json
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
test "show with access" do
|
|
42
|
+
@controller.current_admin = @support
|
|
43
|
+
|
|
44
|
+
get :show, id: admin_reports(:report_one), format: :json
|
|
45
|
+
|
|
46
|
+
refute_nil assigns(:report)
|
|
47
|
+
assert_response :success
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
test "show without access" do
|
|
51
|
+
@controller.current_admin = @marketing
|
|
52
|
+
|
|
53
|
+
assert_raises SimonSays::Authorizer::Denied do
|
|
54
|
+
get :show, id: admin_reports(:report_one), format: :json
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
test "update with access" do
|
|
59
|
+
@controller.current_admin = @support
|
|
60
|
+
|
|
61
|
+
patch :show, id: admin_reports(:report_one), report: { title: 'Test' }, format: :json
|
|
62
|
+
|
|
63
|
+
refute_nil assigns(:report)
|
|
64
|
+
assert_response :success
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
test "update without access" do
|
|
68
|
+
@controller.current_admin = @marketing
|
|
69
|
+
|
|
70
|
+
assert_raises SimonSays::Authorizer::Denied do
|
|
71
|
+
patch :show, id: admin_reports(:report_one), report: { title: 'Test' }, format: :json
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
test "destroy with access" do
|
|
76
|
+
@controller.current_admin = @support
|
|
77
|
+
|
|
78
|
+
assert_difference 'Admin::Report.count', -1 do
|
|
79
|
+
delete :destroy, id: admin_reports(:report_one), format: :json
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
refute_nil assigns(:report)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
test "destroy without access" do
|
|
86
|
+
@controller.current_admin = @marketing
|
|
87
|
+
|
|
88
|
+
assert_raises SimonSays::Authorizer::Denied do
|
|
89
|
+
delete :destroy, id: admin_reports(:report_one), format: :json
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class DocumentsControllerTest < ActionController::TestCase
|
|
4
|
+
setup do
|
|
5
|
+
@alpha = documents(:alpha)
|
|
6
|
+
@beta = documents(:beta)
|
|
7
|
+
|
|
8
|
+
@bob = users(:bob)
|
|
9
|
+
@jim = users(:jim)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def as_bob!
|
|
13
|
+
@controller.current_user = @bob
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def as_jim!
|
|
17
|
+
@controller.current_user = @jim
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
test "show" do
|
|
21
|
+
as_bob!
|
|
22
|
+
|
|
23
|
+
get :show, id: @alpha.id, format: :json
|
|
24
|
+
|
|
25
|
+
refute_nil assigns(:document)
|
|
26
|
+
assert_response :success
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
test "show without through relationship" do
|
|
30
|
+
as_jim!
|
|
31
|
+
|
|
32
|
+
assert_raises ActiveRecord::RecordNotFound do
|
|
33
|
+
get :show, id: @alpha.id, format: :json
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
test "update with access" do
|
|
38
|
+
as_bob!
|
|
39
|
+
|
|
40
|
+
patch :update, id: @alpha.id, document: { title: 'Test' }, format: :json
|
|
41
|
+
|
|
42
|
+
refute_nil assigns(:document)
|
|
43
|
+
assert_response :success
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
test "update without access" do
|
|
47
|
+
as_bob!
|
|
48
|
+
|
|
49
|
+
assert_raises SimonSays::Authorizer::Denied do
|
|
50
|
+
patch :update, id: @beta.id, document: { title: 'Test' }, format: :json
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
test "update without through relationship" do
|
|
55
|
+
as_jim!
|
|
56
|
+
|
|
57
|
+
assert_raises ActiveRecord::RecordNotFound do
|
|
58
|
+
patch :update, id: @alpha.id, document: { title: 'Test' }, format: :json
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
test "destroy with access" do
|
|
63
|
+
as_bob!
|
|
64
|
+
|
|
65
|
+
assert_difference 'Document.count', -1 do
|
|
66
|
+
delete :destroy, id: @alpha.id, format: :json
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
refute_nil assigns(:document)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
test "destroy without access" do
|
|
73
|
+
as_bob!
|
|
74
|
+
|
|
75
|
+
assert_raises SimonSays::Authorizer::Denied do
|
|
76
|
+
delete :destroy, id: @beta.id, format: :json
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
test "destroy without through relationship" do
|
|
81
|
+
as_jim!
|
|
82
|
+
|
|
83
|
+
assert_raises ActiveRecord::RecordNotFound do
|
|
84
|
+
delete :destroy, id: @beta.id, format: :json
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
|
|
2
|
+
#
|
|
3
|
+
# If you find yourself ignoring temporary files generated by your text editor
|
|
4
|
+
# or operating system, you probably want to add a global ignore instead:
|
|
5
|
+
# git config --global core.excludesfile '~/.gitignore_global'
|
|
6
|
+
|
|
7
|
+
# Ignore bundler config.
|
|
8
|
+
/.bundle
|
|
9
|
+
|
|
10
|
+
# Ignore the default SQLite database.
|
|
11
|
+
/db/*.sqlite3
|
|
12
|
+
/db/*.sqlite3-journal
|
|
13
|
+
|
|
14
|
+
# Ignore all logfiles and tempfiles.
|
|
15
|
+
/log/*.log
|
|
16
|
+
/tmp
|