strongbolt 0.3.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +33 -0
- data/.gitignore +18 -0
- data/.rspec +1 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +130 -0
- data/LICENSE.txt +22 -0
- data/README.md +182 -0
- data/Rakefile +1 -0
- data/app/assets/javascripts/strongbolt.js +1 -0
- data/app/assets/javascripts/strongbolt/role-capabilities.js +80 -0
- data/app/controllers/strongbolt/capabilities_controller.rb +77 -0
- data/app/controllers/strongbolt/roles_controller.rb +92 -0
- data/app/controllers/strongbolt/security_controller.rb +8 -0
- data/app/controllers/strongbolt/user_groups_controller.rb +76 -0
- data/app/controllers/strongbolt/user_groups_users_controller.rb +35 -0
- data/app/controllers/strongbolt_controller.rb +2 -0
- data/app/views/strongbolt/_menu.html.erb +13 -0
- data/app/views/strongbolt/capabilities/index.html.erb +53 -0
- data/app/views/strongbolt/capabilities/show.html.erb +53 -0
- data/app/views/strongbolt/roles/_capabilities.html.erb +47 -0
- data/app/views/strongbolt/roles/_capability.html.erb +21 -0
- data/app/views/strongbolt/roles/_form.html.erb +12 -0
- data/app/views/strongbolt/roles/edit.html.erb +14 -0
- data/app/views/strongbolt/roles/index.html.erb +54 -0
- data/app/views/strongbolt/roles/new.html.erb +11 -0
- data/app/views/strongbolt/roles/show.html.erb +52 -0
- data/app/views/strongbolt/user_groups/_form.html.erb +12 -0
- data/app/views/strongbolt/user_groups/edit.html.erb +14 -0
- data/app/views/strongbolt/user_groups/index.html.erb +46 -0
- data/app/views/strongbolt/user_groups/new.html.erb +13 -0
- data/app/views/strongbolt/user_groups/show.html.erb +88 -0
- data/lib/generators/strongbolt/fix_generator.rb +23 -0
- data/lib/generators/strongbolt/indexes_generator.rb +19 -0
- data/lib/generators/strongbolt/install_generator.rb +29 -0
- data/lib/generators/strongbolt/templates/fix.rb +5 -0
- data/lib/generators/strongbolt/templates/indexes.rb +21 -0
- data/lib/generators/strongbolt/templates/migration.rb +73 -0
- data/lib/generators/strongbolt/templates/strongbolt.rb +45 -0
- data/lib/generators/strongbolt/views_generator.rb +26 -0
- data/lib/strongbolt.rb +219 -0
- data/lib/strongbolt/base.rb +7 -0
- data/lib/strongbolt/bolted.rb +125 -0
- data/lib/strongbolt/bolted_controller.rb +297 -0
- data/lib/strongbolt/capabilities_role.rb +15 -0
- data/lib/strongbolt/capability.rb +165 -0
- data/lib/strongbolt/configuration.rb +111 -0
- data/lib/strongbolt/controllers/url_helpers.rb +37 -0
- data/lib/strongbolt/engine.rb +44 -0
- data/lib/strongbolt/errors.rb +38 -0
- data/lib/strongbolt/generators/migration.rb +35 -0
- data/lib/strongbolt/helpers.rb +18 -0
- data/lib/strongbolt/rails/routes.rb +20 -0
- data/lib/strongbolt/role.rb +46 -0
- data/lib/strongbolt/roles_user_group.rb +15 -0
- data/lib/strongbolt/rspec.rb +29 -0
- data/lib/strongbolt/rspec/user.rb +90 -0
- data/lib/strongbolt/tenantable.rb +304 -0
- data/lib/strongbolt/user_abilities.rb +292 -0
- data/lib/strongbolt/user_group.rb +24 -0
- data/lib/strongbolt/user_groups_user.rb +16 -0
- data/lib/strongbolt/users_tenant.rb +12 -0
- data/lib/strongbolt/version.rb +3 -0
- data/lib/tasks/strongbolt_tasks.rake +29 -0
- data/spec/controllers/strongbolt/capabilities_controller_spec.rb +254 -0
- data/spec/controllers/strongbolt/roles_controller_spec.rb +228 -0
- data/spec/controllers/strongbolt/user_groups_controller_spec.rb +216 -0
- data/spec/controllers/strongbolt/user_groups_users_controller_spec.rb +69 -0
- data/spec/controllers/without_authorization_controller_spec.rb +20 -0
- data/spec/dummy/.rspec +2 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/controllers/posts_controller.rb +18 -0
- data/spec/dummy/app/controllers/test_controller.rb +3 -0
- data/spec/dummy/app/controllers/without_authorization_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +29 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +78 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/assets.rb +8 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/strongbolt.rb +32 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +12 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20150630212236_create_strongbolt_tables.rb +54 -0
- data/spec/dummy/db/migrate/20150630212251_create_strongbolt_tables_indexes.rb +21 -0
- data/spec/dummy/db/schema.rb +84 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/fabricators/capability_fabricator.rb +4 -0
- data/spec/fabricators/role_fabricator.rb +9 -0
- data/spec/fabricators/user_fabricator.rb +3 -0
- data/spec/fabricators/user_group_fabricator.rb +9 -0
- data/spec/fixtures/application.rb +28 -0
- data/spec/fixtures/controllers.rb +5 -0
- data/spec/spec_helper.rb +89 -0
- data/spec/strongbolt/bolted_controller_spec.rb +706 -0
- data/spec/strongbolt/bolted_spec.rb +136 -0
- data/spec/strongbolt/capability_spec.rb +251 -0
- data/spec/strongbolt/configuration_spec.rb +119 -0
- data/spec/strongbolt/controllers/url_helpers_spec.rb +34 -0
- data/spec/strongbolt/helpers_spec.rb +43 -0
- data/spec/strongbolt/role_spec.rb +90 -0
- data/spec/strongbolt/tenantable_spec.rb +281 -0
- data/spec/strongbolt/user_abilities_spec.rb +509 -0
- data/spec/strongbolt/user_group_spec.rb +37 -0
- data/spec/strongbolt/users_tenant_spec.rb +36 -0
- data/spec/strongbolt_spec.rb +274 -0
- data/spec/support/controller_macros.rb +11 -0
- data/spec/support/db_setup.rb +134 -0
- data/spec/support/helpers.rb +62 -0
- data/spec/support/transactional_specs.rb +17 -0
- data/strongbolt.gemspec +32 -0
- metadata +407 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
module Strongbolt
|
2
|
+
|
3
|
+
module Configuration
|
4
|
+
|
5
|
+
#
|
6
|
+
# A placeholder class for logger when not defined
|
7
|
+
# Just print what's given
|
8
|
+
#
|
9
|
+
class DefaultLogger
|
10
|
+
def method_missing method_name, text = nil, &block
|
11
|
+
puts "[#{method_name}] #{block.present? ? block.call : text}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
#
|
16
|
+
# Sets the class name of the user if different then default 'User'
|
17
|
+
#
|
18
|
+
mattr_accessor :user_class
|
19
|
+
@@user_class = 'User'
|
20
|
+
|
21
|
+
#
|
22
|
+
# Returns the constantize version of user class
|
23
|
+
#
|
24
|
+
def self.user_class_constant() self.user_class.constantize; end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Sets the logger used by Strongbolt
|
28
|
+
#
|
29
|
+
mattr_accessor :logger
|
30
|
+
@@logger = DefaultLogger.new
|
31
|
+
|
32
|
+
#
|
33
|
+
# Sets the tenants of the application
|
34
|
+
#
|
35
|
+
@@tenants = []
|
36
|
+
def self.tenants= tenants
|
37
|
+
@@tenants = []
|
38
|
+
[*tenants].each {|t| add_tenant t}
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Returns the tenants
|
43
|
+
#
|
44
|
+
def self.tenants() @@tenants; end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Adds a tenant if not in the list
|
48
|
+
#
|
49
|
+
def self.add_tenant tenant
|
50
|
+
tenant = tenant.constantize if tenant.is_a? String
|
51
|
+
unless @@tenants.any? { |m| m.name == tenant.name }
|
52
|
+
tenant.send :tenant
|
53
|
+
@@tenants << tenant
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
#
|
59
|
+
# Allows to configure what happens when the access is denied,
|
60
|
+
# or call the block that has been given
|
61
|
+
#
|
62
|
+
# The block access denied receives as arguments:
|
63
|
+
# user, instance, action, request_path
|
64
|
+
#
|
65
|
+
def self.access_denied *args, &block
|
66
|
+
if block.present?
|
67
|
+
@@access_denied_block = block
|
68
|
+
else
|
69
|
+
@@access_denied_block.call(*args) if defined?(@@access_denied_block)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# Allows to set Capability Models list
|
75
|
+
#
|
76
|
+
def self.models= models
|
77
|
+
Strongbolt::Capability.add_models models
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Controllers to skip controller authorization check ups
|
82
|
+
#
|
83
|
+
def self.skip_controller_authorization_for *controllers
|
84
|
+
ActiveSupport.on_load :action_controller do
|
85
|
+
controllers.each do |controller|
|
86
|
+
begin
|
87
|
+
"#{controller.camelize}Controller".constantize.send :skip_controller_authorization
|
88
|
+
rescue NameError => e
|
89
|
+
raise NameError, "Controller #{controller} doesn't correspond to a valid controller name"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
#
|
96
|
+
# Default permissions
|
97
|
+
#
|
98
|
+
@@default_capabilities = []
|
99
|
+
def self.default_capabilities= list
|
100
|
+
@@default_capabilities = list.inject([]) do |capabilities, hash|
|
101
|
+
capabilities.concat Capability.from_hash(hash)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.default_capabilities
|
106
|
+
@@default_capabilities
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
|
3
|
+
module Strongbolt
|
4
|
+
module Controllers
|
5
|
+
#
|
6
|
+
# Creates the url helpers without the 'strongbolt_' prefix,
|
7
|
+
# that both Strongbolt views and the app views can use
|
8
|
+
#
|
9
|
+
# It's not very nice like that but would be too complicated to do like Devise for now...
|
10
|
+
#
|
11
|
+
module UrlHelpers
|
12
|
+
URLS = %w{role user_group user_group_user role_capability}
|
13
|
+
|
14
|
+
#
|
15
|
+
# Creates the url helpers for the specific url and scope
|
16
|
+
#
|
17
|
+
def self.create_url_helper url, scope=nil
|
18
|
+
[:path, :url].each do |path_or_url|
|
19
|
+
class_eval <<-URL_HELPERS
|
20
|
+
def #{scope.present? ? "#{scope}_" : ''}#{url}_#{path_or_url} *args
|
21
|
+
send(:main_app).send("#{scope.present? ? "#{scope}_" : ''}strongbolt_#{url}_#{path_or_url}", *args)
|
22
|
+
end
|
23
|
+
URL_HELPERS
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Loads all the required helpers
|
29
|
+
#
|
30
|
+
URLS.each do |url|
|
31
|
+
create_url_helper url
|
32
|
+
create_url_helper url.pluralize
|
33
|
+
[:new, :edit].each { |scope| create_url_helper url, scope }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "strongbolt/rails/routes"
|
2
|
+
|
3
|
+
require "strongbolt/helpers"
|
4
|
+
require "strongbolt/controllers/url_helpers"
|
5
|
+
|
6
|
+
module Strongbolt
|
7
|
+
class Engine < ::Rails::Engine
|
8
|
+
|
9
|
+
initializer 'strongbolt.assets.precompile' do |app|
|
10
|
+
%w(javascripts).each do |sub|
|
11
|
+
app.config.assets.paths << root.join('app', 'assets', sub).to_s
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
initializer "strongbolt.helpers" do
|
16
|
+
ActionView::Base.send :include, Strongbolt::Helpers
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# Session Store should be accessible anytime
|
21
|
+
#
|
22
|
+
initializer "strongbolt.session" do
|
23
|
+
if defined? ActiveRecord::SessionStore::Session
|
24
|
+
ActiveRecord::SessionStore::Session.grant(:find, :create, :update, :destroy) { true }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# Avoids authorization checking in the middleware
|
30
|
+
#
|
31
|
+
initializer "strongbolt.devise_integration" do
|
32
|
+
if defined?(Warden) && defined?(Warden::SessionSerializer)
|
33
|
+
Warden::SessionSerializer.perform_without_authorization :store, :fetch, :delete
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Initialize our custom url helpers
|
39
|
+
#
|
40
|
+
initializer "strongbolt.url_helpers" do
|
41
|
+
Strongbolt.include_helpers Strongbolt::Controllers
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Strongbolt
|
2
|
+
StrongboltError = Class.new StandardError
|
3
|
+
|
4
|
+
#
|
5
|
+
# Copy & Paste of Grant Error
|
6
|
+
#
|
7
|
+
class Unauthorized < StrongboltError
|
8
|
+
attr_reader :user, :action, :model
|
9
|
+
|
10
|
+
def initialize(*args)
|
11
|
+
if args.size == 3
|
12
|
+
@user, @action, @model = args
|
13
|
+
else
|
14
|
+
@message = args[0]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
if @message
|
20
|
+
@message
|
21
|
+
else
|
22
|
+
user_str = user == nil ? 'Anonymous' : "#{user.try(:class).try(:name)}:#{user.try :id}"
|
23
|
+
model_str = model.is_a?(Class) ? "#{model.try :name}" : "#{model.try(:class).try(:name)}:#{model.try :id}"
|
24
|
+
"#{action} permission not granted to #{user_str} for resource #{model_str}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
ModelNotFound = Class.new StrongboltError
|
30
|
+
ActionNotConfigured = Class.new StrongboltError
|
31
|
+
|
32
|
+
WrongUserClass = Class.new StrongboltError
|
33
|
+
ModelNotOwned = Class.new StrongboltError
|
34
|
+
|
35
|
+
TenantError = Class.new StrongboltError
|
36
|
+
InverseAssociationNotConfigured = Class.new TenantError
|
37
|
+
DirectAssociationNotConfigured = Class.new TenantError
|
38
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rails/generators/active_record'
|
2
|
+
|
3
|
+
module Strongbolt
|
4
|
+
module Generators
|
5
|
+
module Migration
|
6
|
+
def self.included receiver
|
7
|
+
receiver.send :include, Rails::Generators::Migration
|
8
|
+
receiver.send :include, InstanceMethods
|
9
|
+
receiver.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
#
|
14
|
+
# Need to add this here... Don't know why it's not in a Rails module
|
15
|
+
#
|
16
|
+
def next_migration_number(dirname)
|
17
|
+
next_migration_number = current_migration_number(dirname) + 1
|
18
|
+
ActiveRecord::Migration.next_migration_number(next_migration_number)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
module InstanceMethods
|
24
|
+
|
25
|
+
def copy_migration(source, target)
|
26
|
+
if self.class.migration_exists?("db/migrate", "#{target}")
|
27
|
+
say_status "skipped", "Migration #{target}.rb already exists"
|
28
|
+
else
|
29
|
+
migration_template "#{source}.rb", "db/migrate/#{target}.rb"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Strongbolt
|
2
|
+
module Helpers
|
3
|
+
def can? *args, &block
|
4
|
+
# Block can be used when testing an instance
|
5
|
+
Strongbolt.without_authorization do
|
6
|
+
if block.present?
|
7
|
+
args.insert 1, block.call
|
8
|
+
end
|
9
|
+
|
10
|
+
return current_user.can? *args
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def cannot? *args, &block
|
15
|
+
! can? *args, &block
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ActionDispatch::Routing
|
2
|
+
#
|
3
|
+
# Creates the strongbolt route helper method
|
4
|
+
#
|
5
|
+
class Mapper
|
6
|
+
def strongbolt
|
7
|
+
scope :module => :strongbolt do
|
8
|
+
resources :user_groups do
|
9
|
+
resources :user_groups_users, as: :users, path: 'users', only: [:create, :destroy]
|
10
|
+
end
|
11
|
+
|
12
|
+
resources :roles do
|
13
|
+
resources :capabilities, only: [:create, :destroy] do
|
14
|
+
delete :destroy, on: :collection
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Strongbolt
|
2
|
+
class Role < Base
|
3
|
+
|
4
|
+
acts_as_nested_set
|
5
|
+
|
6
|
+
validates :name, presence: true
|
7
|
+
|
8
|
+
has_many :roles_user_groups,
|
9
|
+
:class_name => "Strongbolt::RolesUserGroup",
|
10
|
+
:dependent => :restrict_with_exception,
|
11
|
+
:inverse_of => :role
|
12
|
+
has_many :user_groups, :through => :roles_user_groups
|
13
|
+
|
14
|
+
has_many :users, through: :user_groups
|
15
|
+
|
16
|
+
has_many :capabilities_roles,
|
17
|
+
:class_name => "Strongbolt::CapabilitiesRole",
|
18
|
+
:dependent => :delete_all,
|
19
|
+
:inverse_of => :role
|
20
|
+
has_many :capabilities, :through => :capabilities_roles
|
21
|
+
|
22
|
+
before_destroy :should_not_have_children
|
23
|
+
|
24
|
+
# We SHOULD NOT destroy descendants in our case
|
25
|
+
skip_callback :destroy, :before, :destroy_descendants
|
26
|
+
|
27
|
+
#
|
28
|
+
# Returns inherited capabilities
|
29
|
+
#
|
30
|
+
def inherited_capabilities
|
31
|
+
Strongbolt::Capability.joins(:roles)
|
32
|
+
.where("strongbolt_roles.lft < :lft AND strongbolt_roles.rgt > :rgt", lft: lft, rgt: rgt)
|
33
|
+
.distinct
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def should_not_have_children
|
39
|
+
if children.count > 0
|
40
|
+
raise ActiveRecord::DeleteRestrictionError.new :children
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
Role = Strongbolt::Role unless defined? Role
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Strongbolt
|
2
|
+
class RolesUserGroup < Base
|
3
|
+
authorize_as "Strongbolt::UserGroup"
|
4
|
+
|
5
|
+
belongs_to :user_group,
|
6
|
+
:class_name => "Strongbolt::UserGroup",
|
7
|
+
:inverse_of => :roles_user_groups
|
8
|
+
|
9
|
+
belongs_to :role,
|
10
|
+
:class_name => "Strongbolt::Role",
|
11
|
+
:inverse_of => :roles_user_groups
|
12
|
+
|
13
|
+
validates_presence_of :user_group, :role
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#
|
2
|
+
# Setups Rspec related stuff to handle correctly Strongbolt
|
3
|
+
#
|
4
|
+
require 'strongbolt'
|
5
|
+
|
6
|
+
Strongbolt.switch_to_multithread
|
7
|
+
|
8
|
+
if defined?(RSpec)
|
9
|
+
# We load the class that overrides user behavior,
|
10
|
+
# more convenient for tests
|
11
|
+
require 'strongbolt/rspec/user'
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
#
|
15
|
+
# When creating stuff in before :all, avoid leaks
|
16
|
+
#
|
17
|
+
config.before(:all) do
|
18
|
+
Strongbolt.current_user = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
config.around(:each) do |example|
|
22
|
+
Strongbolt.without_authorization &example
|
23
|
+
# Clear all the User authorizations that could have been defined
|
24
|
+
User.clear_authorizations
|
25
|
+
# Removes the user from current_user to avoid leaks
|
26
|
+
Strongbolt.current_user = nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'rspec/mocks'
|
2
|
+
#
|
3
|
+
# We define a can! that allows to quickly stub a user authorization
|
4
|
+
#
|
5
|
+
Strongbolt.user_class_constant.class_eval do
|
6
|
+
#
|
7
|
+
# Best to use a class context and use class instance variables
|
8
|
+
#
|
9
|
+
class << self
|
10
|
+
|
11
|
+
def authorizations
|
12
|
+
@authorizations ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def set_authorization_for user, authorized, *args
|
16
|
+
return if user.new_record?
|
17
|
+
|
18
|
+
self.authorizations[user.id] ||= {}
|
19
|
+
self.authorizations[user.id][key_for(*args)] = authorized
|
20
|
+
end
|
21
|
+
|
22
|
+
def clear_authorizations
|
23
|
+
@authorizations = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
def authorized? user, *args
|
27
|
+
# Cannot do if user not saved
|
28
|
+
return false if user.new_record?
|
29
|
+
key = key_for(*args)
|
30
|
+
if self.authorizations[user.id].present? && self.authorizations[user.id][key].present?
|
31
|
+
return self.authorizations[user.id][key]
|
32
|
+
else
|
33
|
+
user._can? *args
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def key_for *args
|
38
|
+
action = args[0]
|
39
|
+
instance = args[1]
|
40
|
+
attrs = args[2] || :any
|
41
|
+
all_instances = (args[3] || false) ? "all" : "tenanted"
|
42
|
+
if instance.is_a?(ActiveRecord::Base)
|
43
|
+
model = instance.class.name
|
44
|
+
if instance.new_record?
|
45
|
+
"#{action}-#{model}-#{attrs}-#{all_instances}"
|
46
|
+
else
|
47
|
+
"#{action}-#{model}-#{attrs}-#{instance.id}"
|
48
|
+
end
|
49
|
+
else
|
50
|
+
model = instance.class.name
|
51
|
+
"#{action}-#{model}-#{attrs}-#{all_instances}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# 2 methods to setup mocking and stubs
|
59
|
+
#
|
60
|
+
def init
|
61
|
+
if RSpec::Mocks::Version::STRING >= "3.0"
|
62
|
+
require "rspec/mocks/standalone"
|
63
|
+
else
|
64
|
+
RSpec::Mocks::setup(self) unless self.respond_to? :allow
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def setup_stub authorized, arguments
|
69
|
+
init
|
70
|
+
# Set the authorizations on a class level
|
71
|
+
self.class.set_authorization_for self, authorized, *arguments
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# Mocked methods
|
76
|
+
#
|
77
|
+
alias_method :_can?, :can?
|
78
|
+
|
79
|
+
def can? *args
|
80
|
+
self.class.authorized? self, *args
|
81
|
+
end
|
82
|
+
|
83
|
+
def can! *args
|
84
|
+
setup_stub true, args
|
85
|
+
end
|
86
|
+
|
87
|
+
def cannot! *args
|
88
|
+
setup_stub false, args
|
89
|
+
end
|
90
|
+
end
|