rails-acu 1.2.1
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 +8 -0
- data/.project +18 -0
- data/.rspec +3 -0
- data/.travis.yml +30 -0
- data/Gemfile +30 -0
- data/Gemfile.lock +169 -0
- data/MIT-LICENSE +20 -0
- data/README.md +221 -0
- data/Rakefile +26 -0
- data/bin/rails +13 -0
- data/lib/acu/configs.rb +30 -0
- data/lib/acu/engine.rb +9 -0
- data/lib/acu/errors.rb +37 -0
- data/lib/acu/helpers/helpers.rb +9 -0
- data/lib/acu/injectors.rb +15 -0
- data/lib/acu/listeners.rb +18 -0
- data/lib/acu/monitor.rb +201 -0
- data/lib/acu/rules.rb +134 -0
- data/lib/acu/utilities.rb +14 -0
- data/lib/acu/version.rb +3 -0
- data/lib/generators/acu/install_generator.rb +20 -0
- data/lib/generators/templates/rules.rb +34 -0
- data/lib/generators/templates/setup.rb +30 -0
- data/lib/rails-acu.rb +26 -0
- data/rails-acu-1.2.0.gem +0 -0
- data/rails-acu.gemspec +23 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/config/manifest.js +5 -0
- data/spec/dummy/app/assets/javascripts/admin/manage.js +2 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/javascripts/books.js +2 -0
- data/spec/dummy/app/assets/javascripts/cable.js +12 -0
- data/spec/dummy/app/assets/javascripts/comments.js +2 -0
- data/spec/dummy/app/assets/javascripts/home.js +2 -0
- data/spec/dummy/app/assets/stylesheets/admin/manage.css +4 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/assets/stylesheets/books.css +4 -0
- data/spec/dummy/app/assets/stylesheets/comments.css +4 -0
- data/spec/dummy/app/assets/stylesheets/home.css +4 -0
- data/spec/dummy/app/assets/stylesheets/scaffold.css +84 -0
- data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
- data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
- data/spec/dummy/app/controllers/admin/manage_controller.rb +19 -0
- data/spec/dummy/app/controllers/application_controller.rb +4 -0
- data/spec/dummy/app/controllers/home_controller.rb +7 -0
- data/spec/dummy/app/helpers/admin/manage_helper.rb +2 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/helpers/home_helper.rb +2 -0
- data/spec/dummy/app/jobs/application_job.rb +2 -0
- data/spec/dummy/app/mailers/application_mailer.rb +4 -0
- data/spec/dummy/app/models/application_record.rb +3 -0
- data/spec/dummy/app/models/user.rb +8 -0
- data/spec/dummy/app/models/user_type.rb +3 -0
- data/spec/dummy/app/views/admin/manage/add.html.erb +2 -0
- data/spec/dummy/app/views/admin/manage/delete.html.erb +2 -0
- data/spec/dummy/app/views/admin/manage/index.html.erb +2 -0
- data/spec/dummy/app/views/admin/manage/list.html.erb +2 -0
- data/spec/dummy/app/views/admin/manage/prove.html.erb +2 -0
- data/spec/dummy/app/views/admin/manage/show.html.erb +2 -0
- data/spec/dummy/app/views/home/contact.html.erb +2 -0
- data/spec/dummy/app/views/home/index.html.erb +21 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/spec/dummy/app/views/layouts/mailer.text.erb +1 -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/bin/setup +34 -0
- data/spec/dummy/bin/update +29 -0
- data/spec/dummy/config.ru +5 -0
- data/spec/dummy/config/application.rb +23 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/cable.yml +9 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +54 -0
- data/spec/dummy/config/environments/production.rb +86 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/acu_rules.rb +31 -0
- data/spec/dummy/config/initializers/acu_setup.rb +14 -0
- data/spec/dummy/config/initializers/application_controller_renderer.rb +6 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/spec/dummy/config/initializers/devise.rb +277 -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/new_framework_defaults.rb +24 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/devise.en.yml +64 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/puma.rb +47 -0
- data/spec/dummy/config/routes.rb +19 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/config/spring.rb +6 -0
- data/spec/dummy/db/migrate/20170329111257_create_books.rb +9 -0
- data/spec/dummy/db/migrate/20170329111323_create_comments.rb +10 -0
- data/spec/dummy/db/migrate/20170329114943_devise_create_users.rb +42 -0
- data/spec/dummy/db/migrate/20170329120950_create_admin_user_types.rb +15 -0
- data/spec/dummy/db/migrate/20170329121612_add_user_type_id_to_users.rb +5 -0
- data/spec/dummy/db/schema.rb +59 -0
- data/spec/dummy/db/seeds.rb +39 -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/apple-touch-icon-precomposed.png +0 -0
- data/spec/dummy/public/apple-touch-icon.png +0 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/spec/controllers/admin/manage_controller_spec.rb +72 -0
- data/spec/dummy/spec/controllers/application_controller_spec.rb +14 -0
- data/spec/dummy/spec/controllers/home_controller_spec.rb +560 -0
- data/spec/rails_helper.rb +59 -0
- data/spec/spec_helper.rb +104 -0
- metadata +268 -0
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Acu'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
|
21
|
+
load 'rails/tasks/statistics.rake'
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
require 'bundler/gem_tasks'
|
26
|
+
|
data/bin/rails
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# This command will automatically be run when you run "rails" with Rails gems
|
3
|
+
# installed from the root of your application.
|
4
|
+
|
5
|
+
ENGINE_ROOT = File.expand_path('../..', __FILE__)
|
6
|
+
ENGINE_PATH = File.expand_path('../../lib/acu/engine', __FILE__)
|
7
|
+
|
8
|
+
# Set up gems listed in the Gemfile.
|
9
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
|
10
|
+
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
11
|
+
|
12
|
+
require 'rails/all'
|
13
|
+
require 'rails/engine/commands'
|
data/lib/acu/configs.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Acu
|
2
|
+
module Configs
|
3
|
+
|
4
|
+
mattr_accessor :base_controller
|
5
|
+
@@base_controller = 'ApplicationController'
|
6
|
+
|
7
|
+
mattr_accessor :allow_by_default
|
8
|
+
@@allow_by_default = false
|
9
|
+
|
10
|
+
mattr_accessor :audit_log_file
|
11
|
+
@@audit_log_file = nil
|
12
|
+
|
13
|
+
mattr_accessor :use_cache
|
14
|
+
@@use_cache = false
|
15
|
+
|
16
|
+
mattr_accessor :cache_namespace
|
17
|
+
@@cache_namespace = 'acu'
|
18
|
+
|
19
|
+
mattr_accessor :cache_expires_in
|
20
|
+
@@cache_expires_in = nil
|
21
|
+
|
22
|
+
mattr_accessor :cache_race_condition_ttl
|
23
|
+
@@cache_race_condition_ttl = nil
|
24
|
+
|
25
|
+
# for getting options
|
26
|
+
def self.get name
|
27
|
+
eval("@@#{name}")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/acu/engine.rb
ADDED
data/lib/acu/errors.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module Acu
|
2
|
+
module Errors
|
3
|
+
|
4
|
+
class AccessDenied < StandardError
|
5
|
+
end
|
6
|
+
|
7
|
+
class UncheckedPermissions < StandardError
|
8
|
+
end
|
9
|
+
|
10
|
+
class InvalidSyntax < StandardError
|
11
|
+
end
|
12
|
+
|
13
|
+
class AmbiguousRule < StandardError
|
14
|
+
end
|
15
|
+
|
16
|
+
class InvalidData < StandardError
|
17
|
+
end
|
18
|
+
|
19
|
+
class MissingData < InvalidData
|
20
|
+
end
|
21
|
+
|
22
|
+
class MissingEntity < MissingData
|
23
|
+
end
|
24
|
+
|
25
|
+
class MissingUser < MissingData
|
26
|
+
end
|
27
|
+
|
28
|
+
class MissingAction < MissingData
|
29
|
+
end
|
30
|
+
|
31
|
+
class MissingController < MissingData
|
32
|
+
end
|
33
|
+
|
34
|
+
class MissingNamespace < MissingData
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative 'helpers/helpers'
|
2
|
+
|
3
|
+
module Acu
|
4
|
+
module Injectors
|
5
|
+
class << self
|
6
|
+
|
7
|
+
ActiveSupport::Notifications.subscribe "start_processing.action_controller" do |**args|
|
8
|
+
eval((Acu::Configs.get :base_controller).to_s).class_eval do
|
9
|
+
before_action { Monitor::gaurd }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Acu
|
2
|
+
module Listeners
|
3
|
+
|
4
|
+
@@collective_data = { }
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
attr_reader :collective_data
|
9
|
+
|
10
|
+
ActiveSupport::Notifications.subscribe "start_processing.action_controller" do |**args|
|
11
|
+
# collects the request parameters for the given [controller, action to be used in Monitor]
|
12
|
+
@@collective_data = @@collective_data.merge([:request, :parameters].reverse.inject(args[:params] || { }) { |v, k| {k => v}})
|
13
|
+
end
|
14
|
+
# get access to collected data
|
15
|
+
def data; @@collective_data end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/acu/monitor.rb
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
require_relative 'errors'
|
2
|
+
|
3
|
+
module Acu
|
4
|
+
|
5
|
+
class Monitor
|
6
|
+
|
7
|
+
@kwargs = { }
|
8
|
+
|
9
|
+
class << self
|
10
|
+
|
11
|
+
protected :new
|
12
|
+
attr_reader :kwargs
|
13
|
+
|
14
|
+
def by kwargs
|
15
|
+
@kwargs = @kwargs.merge(kwargs)
|
16
|
+
end
|
17
|
+
|
18
|
+
def clear_args
|
19
|
+
@kwargs = { }
|
20
|
+
end
|
21
|
+
|
22
|
+
def gaurd
|
23
|
+
# fetch the request & process it
|
24
|
+
_info = process Acu::Listeners.data[:request]
|
25
|
+
|
26
|
+
# return if we hit the cache
|
27
|
+
return if hit_cache _info
|
28
|
+
|
29
|
+
rules = Rules.rules.select do |cond, _|
|
30
|
+
flag = true;
|
31
|
+
|
32
|
+
# check if this is a global rule!
|
33
|
+
next true if cond.empty?
|
34
|
+
|
35
|
+
{namespace: nil, controller: :namespace, action: :controller}.each do |current, parent|
|
36
|
+
t = -1
|
37
|
+
# either mentioned explicitly
|
38
|
+
if cond[current]
|
39
|
+
t = (cond[current][:name].to_s == eval("_info.#{current}").to_s) ? 1 : 0
|
40
|
+
# or in `only|except` tags
|
41
|
+
elsif parent and cond[parent]
|
42
|
+
# if nothing mentioned in parent, assume for all
|
43
|
+
t = 1 if not(cond[parent][:only] or cond[parent][:except])
|
44
|
+
# flag true if it checked in namespace's only tag
|
45
|
+
{only: {on_true: 1, on_false: 0} , except: {on_true: 0, on_false: 1}}.each do |tag, val|
|
46
|
+
if cond[parent][tag]
|
47
|
+
case cond[parent][tag].include? eval("_info.#{current}").to_sym
|
48
|
+
when true
|
49
|
+
t = val[:on_true]
|
50
|
+
break
|
51
|
+
when false
|
52
|
+
t = val[:on_false]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
flag &= (t == 1) if t.between? 0, 1;
|
58
|
+
break if not flag
|
59
|
+
end
|
60
|
+
flag
|
61
|
+
end
|
62
|
+
# flag so we can process all the related rule and it all passed this should be false
|
63
|
+
# if any failed, and exception will be raised
|
64
|
+
_granted = -1
|
65
|
+
_entitled_entities = [];
|
66
|
+
# for each mached rule
|
67
|
+
rules.each do |_, rule|
|
68
|
+
# for each entity and it's actions in the rule
|
69
|
+
rule.each do |entity, action|
|
70
|
+
# check it the current request can relay to the entity?
|
71
|
+
if valid_for? entity
|
72
|
+
_entitled_entities << entity.to_s
|
73
|
+
# current entity is granted to have the access?
|
74
|
+
if is_allowed? action
|
75
|
+
# grant the access if already not denied
|
76
|
+
_granted = 1 if _granted == -1
|
77
|
+
else
|
78
|
+
# deny it, period!
|
79
|
+
_granted = 0
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
# if the access is granted? i.e if all the rules are satisfied with the request
|
85
|
+
return if _granted == 1 and access_granted _info, _entitled_entities
|
86
|
+
# if the access is denied? i.e at least one of rules are NOT satisfied with the request
|
87
|
+
return if _granted == 0 and access_denied _info, _entitled_entities
|
88
|
+
# if we reached here it measn that have found no rule to deny/allow the request and we have to fallback to the defaults
|
89
|
+
access_denied _info, [:__ACU_BY_DEFAULT__], by_default: true if not Configs.get :allow_by_default
|
90
|
+
access_granted _info, [:__ACU_BY_DEFAULT__], by_default: true
|
91
|
+
end
|
92
|
+
|
93
|
+
def valid_for? entity
|
94
|
+
# check for existance
|
95
|
+
raise Errors::MissingEntity.new("whois :#{entity}?") if not Rules.entities[entity]
|
96
|
+
# fetch the entity's identity
|
97
|
+
e = Rules.entities[entity]
|
98
|
+
# fetch the related args to the entity from the `kwargs`
|
99
|
+
kwargs = @kwargs.reject { |x| !e[:args].include?(x) }
|
100
|
+
# if fetched args and pre-defined arg didn't match?
|
101
|
+
raise Errors::MissingData.new("at least one of arguments for `whois :#{entity}` is not provided!") if kwargs.length != e[:args].length
|
102
|
+
# send varibles in order the have defined
|
103
|
+
e[:callback].call(*e[:args].map { |i| kwargs[i] })
|
104
|
+
end
|
105
|
+
|
106
|
+
def clear_cache
|
107
|
+
return if not Configs.get :use_cache
|
108
|
+
Rails.cache.clear namespace: (Configs.get :cache_namespace)
|
109
|
+
end
|
110
|
+
|
111
|
+
protected
|
112
|
+
|
113
|
+
def hit_cache _info
|
114
|
+
# return [didn't hit] if not allowed to use cache
|
115
|
+
return false if not Configs.get :use_cache
|
116
|
+
# fetch the relative entities to this request
|
117
|
+
_entitled_entities = Rules.entities.select { |name, _| valid_for? name }
|
118
|
+
# fetch the cache-name
|
119
|
+
cname = cache_name _info, _entitled_entities
|
120
|
+
# return [didn't hit] if not found in cache
|
121
|
+
return false if not Rails.cache.exist? cname, cache_options
|
122
|
+
# check if the request is allowed in cache?
|
123
|
+
if is_allowed?(Rails.cache.read(cname, cache_options).to_s.to_sym)
|
124
|
+
# grant the access
|
125
|
+
access_granted _info, _entitled_entities.keys, from_cache: true
|
126
|
+
else
|
127
|
+
# deny the access
|
128
|
+
access_denied _info, _entitled_entities.keys, from_cache: true
|
129
|
+
end
|
130
|
+
# hit the cache
|
131
|
+
return true
|
132
|
+
end
|
133
|
+
|
134
|
+
def cache_name _info, entities
|
135
|
+
"%s-%s" %[_info.to_a.join('::'), (entities.kind_of?(Array) ? entities : entities.keys).join("-")]
|
136
|
+
end
|
137
|
+
|
138
|
+
def is_allowed? action
|
139
|
+
case action
|
140
|
+
when Rules.GRANT_SYMBOL
|
141
|
+
return true
|
142
|
+
when Rules.DENY_SYMBOL
|
143
|
+
return false
|
144
|
+
else
|
145
|
+
log_audit "> access DENIED to undefined action as `:#{action}`"
|
146
|
+
raise Exception.new("action `#{action}` is undefined!")
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def cache_options
|
151
|
+
out = { }
|
152
|
+
# fetch cache options from config
|
153
|
+
[:namespace, :expires_in, :race_condition_ttl].each { |k| out[k] = Configs.get "cache_#{k}".to_sym }
|
154
|
+
return out
|
155
|
+
end
|
156
|
+
|
157
|
+
def log_audit log
|
158
|
+
# fetch the log file from configuration
|
159
|
+
file = Configs.get :audit_log_file
|
160
|
+
# log if allowed?
|
161
|
+
Logger.new(Configs.get :audit_log_file).info(log) if file and not file.blank?
|
162
|
+
end
|
163
|
+
|
164
|
+
def access_granted _info, entities, by_default: false, from_cache: false
|
165
|
+
# log the event
|
166
|
+
log_audit ("[-]" + (from_cache ? '[c]' : '') + " access GRANTED to `#{_info}` as `:#{entities.uniq.join(", :")}`" + (by_default ? " [autherized by :allow_by_default]" : ""))
|
167
|
+
# cache the event if not already from cache
|
168
|
+
Rails.cache.write(cache_name(_info, entities), Rules.GRANT_SYMBOL, cache_options) if not from_cache and Configs.get :use_cache
|
169
|
+
# grant the access
|
170
|
+
true
|
171
|
+
end
|
172
|
+
|
173
|
+
def access_denied _info, entities, by_default: false, from_cache: false
|
174
|
+
# log the event
|
175
|
+
log_audit ("[x]" + (from_cache ? '[c]' : '') + " access DENIED to `#{_info}` as `:#{entities.uniq.join(", :")}`" + (by_default ? " [autherized by :allow_by_default]" : ""))
|
176
|
+
# cache the event if not already from cache
|
177
|
+
Rails.cache.write(cache_name(_info, entities), Rules.DENY_SYMBOL, cache_options) if not from_cache and Configs.get :use_cache
|
178
|
+
# deny the access
|
179
|
+
raise Errors::AccessDenied.new("you don't have the enough access for process this request!")
|
180
|
+
end
|
181
|
+
|
182
|
+
def process request
|
183
|
+
# validate the request parameters
|
184
|
+
raise Errors::InvalidData.new("the request object needs to provided!") if not(request and request[:parameters])
|
185
|
+
# fetch the params
|
186
|
+
p = request[:parameters]
|
187
|
+
# try find the namespace/controller set
|
188
|
+
nc = p["controller"].split('/');
|
189
|
+
|
190
|
+
n = nc.length > 1 ? nc.first : nil
|
191
|
+
c = nc.length > 1 ? nc.second : nc.first
|
192
|
+
a = p["action"]
|
193
|
+
|
194
|
+
# return it with structure
|
195
|
+
Struct.new(:namespace, :controller, :action).new(n, c, a)
|
196
|
+
end
|
197
|
+
|
198
|
+
end # /class << self
|
199
|
+
end # /class Monitor
|
200
|
+
|
201
|
+
end # /module Acu
|
data/lib/acu/rules.rb
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
require_relative 'utilities'
|
2
|
+
|
3
|
+
module Acu
|
4
|
+
class Rules
|
5
|
+
|
6
|
+
@rules = { }
|
7
|
+
@entities = { }
|
8
|
+
|
9
|
+
@GRANT_SYMBOL = :allow
|
10
|
+
@DENY_SYMBOL = :deny
|
11
|
+
|
12
|
+
class << self
|
13
|
+
|
14
|
+
protected :new
|
15
|
+
attr_reader :rules
|
16
|
+
attr_reader :entities
|
17
|
+
attr_reader :GRANT_SYMBOL
|
18
|
+
attr_reader :DENY_SYMBOL
|
19
|
+
|
20
|
+
include Utilities
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
reset
|
24
|
+
end
|
25
|
+
|
26
|
+
def reset
|
27
|
+
@rules = { }
|
28
|
+
@entities = { }
|
29
|
+
end
|
30
|
+
|
31
|
+
# only: only the defined `controllers` in the `namespace`
|
32
|
+
# except: except the defined `controllers` in the `namespace`
|
33
|
+
def namespace name = nil, except: nil, only: nil
|
34
|
+
only = nil if only and not (only.kind_of?(Array) or only.length == 0)
|
35
|
+
except = nil if except and not (except.kind_of?(Array) or except.length == 0)
|
36
|
+
raise Errors::AmbiguousRule.new('cannot have both `only` and `except` options at the same time for namespace `%s`' %name) if only and except
|
37
|
+
pass namespace: { name: name ? name.downcase : name, except: except, only: only } do
|
38
|
+
yield
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# only: only the defined `actions` in the `controller`
|
43
|
+
# except: except the defined `actions` in the `controller`
|
44
|
+
def controller name, except: nil, only: nil
|
45
|
+
only = nil if only and not (only.kind_of?(Array) or only.length == 0)
|
46
|
+
except = nil if except and not (except.kind_of?(Array) or except.length == 0)
|
47
|
+
raise Errors::AmbiguousRule.new("there is already an `except` or `only` constraints defined in container namespace `#{@_params[:namespace][:name]}`") if @_params[:namespace] and (@_params[:namespace][:except] || @_params[:namespace][:only])
|
48
|
+
raise Errors::AmbiguousRule.new('cannot have both `only` and `except` options at the same time for controller `%s`' %name) if only and except
|
49
|
+
pass controller: { name: name.downcase, except: except, only: only } do
|
50
|
+
yield
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def action name
|
55
|
+
raise Errors::AmbiguousRule.new("at least one of the parent `controller` or `namespace` needs to be defined for the this action") if not (@_params[:namespace] || @_params[:controller])
|
56
|
+
raise Errors::AmbiguousRule.new("there is already an `except` or `only` constraints defined in container controller `#{@_params[:controller][:name]}`") if @_params[:controller] and (@_params[:controller][:except] || @_params[:controller][:only])
|
57
|
+
pass action: { name: name.downcase } do
|
58
|
+
yield
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def define(&block)
|
63
|
+
helper_initialize
|
64
|
+
self.instance_eval(&block)
|
65
|
+
end
|
66
|
+
|
67
|
+
# locks the rules to be read-only
|
68
|
+
def lock
|
69
|
+
self.freeze
|
70
|
+
end
|
71
|
+
|
72
|
+
def whois(symbol, args: nil, &block)
|
73
|
+
@entities[symbol] = {
|
74
|
+
args: [args || []].flatten,
|
75
|
+
callback: block
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
#################### the ops ######################
|
80
|
+
# at this point we assign the class varible rules #
|
81
|
+
###################################################
|
82
|
+
|
83
|
+
def allow symbol, on: []
|
84
|
+
op symbol, @GRANT_SYMBOL, on
|
85
|
+
end
|
86
|
+
|
87
|
+
def deny symbol, on: []
|
88
|
+
op symbol, @DENY_SYMBOL, on
|
89
|
+
end
|
90
|
+
|
91
|
+
################### end of ops ####################
|
92
|
+
|
93
|
+
protected
|
94
|
+
|
95
|
+
def op symbol, opr, on
|
96
|
+
symbol = [symbol].flatten if symbol
|
97
|
+
raise Errors::InvalidData.new("invalid argument") if not symbol or symbol.to_s.blank? or opr.to_s.blank?
|
98
|
+
raise Errors::AmbiguousRule.new("cannot have `on` argument inside the action `#{@_params[:action][:name]}`") if not on.empty? and @_params[:action]
|
99
|
+
raise Errors::InvalidData.new("the symbol `#{symbol}` is not defined by `whois`") if not symbol.all? { |s| @entities.include? s }
|
100
|
+
return if on.empty? and symbol.each { |s| build_rule({"#{s}": opr}) }
|
101
|
+
# for each action in the `on` create a new rule
|
102
|
+
on.each do |a|
|
103
|
+
action a do
|
104
|
+
symbol.each { |s| build_rule({"#{s}": opr}) }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def build_rule rule
|
110
|
+
@rules[@_params.clone] ||= {}
|
111
|
+
@rules[@_params.clone] = rules[@_params.clone].merge(rule);
|
112
|
+
end
|
113
|
+
|
114
|
+
def build_rule_entry
|
115
|
+
n = @_params[:namespace]
|
116
|
+
c = @_params[:controller]
|
117
|
+
a = @_params[:action]
|
118
|
+
raise Errors::AmbiguousRule.new('invalid input') if not ( n or c or a )
|
119
|
+
raise Errors::AmbiguousRule.new('cannot have rule for controller `%s` inside the namespace `%s` that `except`ed it!' %[c[:name], n[:name]]) if n and n[:except] and c and n[:except].include? c[:name]
|
120
|
+
raise Errors::AmbiguousRule.new('cannot have rule for action `%s` inside the controler `%s` that `except`ed it!' %[a[:name], c[:name]]) if c and c[:except] and a and c[:except].include? a[:name]
|
121
|
+
raise Errors::AmbiguousRule.new('cannot have rule for controller `%s` inside the namespace `%s` that has bounded to `only` some other controllers!' %[c[:name], n[:name]]) if n and n[:only] and c and not(n[:only].include? c[:name])
|
122
|
+
raise Errors::AmbiguousRule.new('cannot have rule for action `%s` inside the controller `%s` that has bounded to `only` some other actions!' %[a[:name], c[:name]]) if c and c[:only] and a and not(c[:only].include? a[:name])
|
123
|
+
|
124
|
+
entries = [];
|
125
|
+
|
126
|
+
entries << :namespace if n
|
127
|
+
entries << :controller if c
|
128
|
+
entries << :action if a
|
129
|
+
|
130
|
+
return entries
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|