can_play 0.2.4 → 0.2.5

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: 7eed223e4874c108017e81a8d0502041e3022e81
4
- data.tar.gz: 8a490f3b79ac9af3223e67f5b5ba7c6a6a9a0510
3
+ metadata.gz: 8defb79962f3b993057d0f3adcf9a10dc76b3b53
4
+ data.tar.gz: 7be32b34ecd58d7a57c03b6bd586868198d01092
5
5
  SHA512:
6
- metadata.gz: 3926d91df4c06593a9d521ffe5274304864a3c4fffb1fd58899ca044fe53e4cb4e2fd47e676637c38109d8a426a1aae3815ebe64055f3cabc2bb2f0b26f61192
7
- data.tar.gz: 13d6190e996237505d67a5c4abb74b46cbb963898b287dc9967abb6cef0a488ab505e4c5e00e2ae6e17fe3da94db1a459fa84a1a00046eab12ecac565dab3838
6
+ metadata.gz: cec401f5b1dd9a1fd5de254f720a1704f06fd8e482553b12d0637067c8b95782db584f65e72c001324196c2c66378e4774d5381479dcd3a69d3c0692a8a79d46
7
+ data.tar.gz: 8e913fc5a5d4dda021b0a03d2c1414884f02b4942fc7c4a97ac7d8e07bc169d4692d159ee99bddc7e2d62ad6e3f8d2898ea7768a42aa99371bf4dbe7f2a085d0
data/README.md CHANGED
@@ -1,33 +1,69 @@
1
- can_plan集成了cancancan和consul的功能,使用DSL描述用户对单个类的实例或某个类的操作权限,及可获取的条目的基础的relation对象.
1
+ 系统权限的管理,就是控制用户对某个资源的操作权限。这里说的资源并不仅仅是指ActiveRecord::Base的子类,也可以是ruby的其他类或者模型。can_plan集成了cancancan和consul的功能,使用简单的DSL描述用户对单个资源或某类资源的操作权限。它的权限管理方式是基于角色的,不同角色可以赋予不同的权限,我们可以让用户和资源的操作权限之间建立关联并保存进数据库.
2
2
 
3
3
  ## 安装方式
4
4
 
5
- ### 安装cancancan
6
5
 
7
- cancancan的使用请参见cancancan主页,在此我们安装后,不需要设置Ability文件,can_play在内部集成了这些设置。
8
- ### 安装consul
9
-
10
- consul的使用请参见consul主页,在此我们安装后,不需要设置power文件,也无需在controller中设置current_power,can_play在内部集成了这些设置。
6
+ can_play在内部集成了cancancan和consul两个gem,所以没必要再安装这两个gem。
11
7
  ### can_play安装
12
- 在gemfile中加入can_play的github地址来安装。
8
+ 在gemfile中加入can_play
9
+
10
+ ```
11
+ gem 'can_play'
12
+ ```
13
13
 
14
- 安装后执行如下命令
14
+ 运行bundle,安装完成后执行如下命令
15
15
 
16
16
  ```
17
17
  rails generate can_play:install
18
18
  ```
19
19
 
20
- 会在initializer和locales文件夹下生成文件。
20
+ 执行该命令会在initializer和locales文件夹下生成配置文件。
21
21
  initializer文件夹下的can_play.rb是can_play的基本配置文件。
22
22
  locales下的can_play.zh-Cn.yml文件用于描述权限名称。
23
-
24
- ### DSL文件描述权限
23
+
24
+ # path_to_config/initializers/can_play.rb
25
+ CanPlay::Config.setup do |config|
26
+ config.user_class_name = 'User'
27
+ config.role_class_name = 'Role'
28
+ config.role_resources_relation_name = 'role_resources'
29
+ config.super_roles = ['超级管理员']
30
+ end
31
+
32
+ can_play.rb配置文件中,可以传入用户类名(默认User)、角色类名(默认Role)、以及角色和权限的关联的名称(默认role_resources),role_resources_relation_name是角色类和操作权限资源之间的关联。
33
+
34
+ ---
35
+ zh-CN:
36
+ can_play:
37
+ class_name:
38
+ test: 测试
39
+ contract: 合同
40
+ authority_name:
41
+ common:
42
+ list: 列表查看
43
+ create: 新建
44
+ read: 查看
45
+ update: 修改
46
+ delete: 删除
47
+ crud: 管理
48
+ menus_roles: 菜单分配
49
+ role_resources: 权限分配
50
+ improve: 完善信息
51
+ contract:
52
+ terminate: 终止
53
+ purchaser_confirm: 采购人确认
54
+ supplier_confirm: 供应商确认
55
+
56
+ can_play.zh-CN.yml是中文翻译文件,在这里写下权限的英文和中文名称的对应,在前端就可以获取到权限的中文描述,其中common是一些常用权限名称,特别的权限名称,可以单独写,如contract下的terminate权限是合同独有的权限名称,必须单独写,而class_name也可以写上资源名称,如contract,如果不写,会默认去ActiveRecord的翻译文件下去取中文翻译。
57
+
58
+
59
+ ### DSL文件描述权限的方法
25
60
  dsl文件写法如下:
26
61
 
27
62
  #用哪个类用来描写权限,可在intializer下的can_play.rb文件下描写。
28
63
  class Resource
29
64
  include CanPlay
30
-
65
+ self.module_name = '核心模块'
66
+
31
67
  # 所有limit块、collection块和member块中都注入了user这个变量,指向当前登录用户,可直接使用。
32
68
 
33
69
  group Contract do |klass|
@@ -37,9 +73,9 @@ dsl文件写法如下:
37
73
  if user.is_admin?
38
74
  klass.all
39
75
  elsif user.role? '供应商'
40
- klass.where(emall: user.emall)
76
+ klass.where(supplier: user.supplier)
41
77
  elsif user.role? '采购人'
42
- klass.where(department: user.department)
78
+ klass.where(purchaser: user.purchaser)
43
79
  else
44
80
  klass.none
45
81
  end
@@ -55,9 +91,9 @@ dsl文件写法如下:
55
91
  if user.is_admin?
56
92
  true
57
93
  elsif user.role? '供应商'
58
- obj.emall.is? user.emall
94
+ obj.supplier.is? user.supplier
59
95
  elsif user.role? '采购人'
60
- obj.department.is? user.department
96
+ obj.purchaser.is? user.purchaser
61
97
  else
62
98
  false
63
99
  end
@@ -69,26 +105,25 @@ dsl文件写法如下:
69
105
  end
70
106
  end
71
107
 
72
- group Project do |klass|
108
+ end
109
+
110
+ `limit`方法用于控制某个用户可以查看的资源的额列表,如Contract类下的limit限制了管理员可以查看所有合同,供应商和采购人只能查看和自己相关的合同。limit方法会让在controller中生成一个动态方法,`current_power.contracts`,这个方法返回的是是我们再limit中写如的对象,这样就能根据用户的信息返回不同的资源数组。
73
111
 
74
- limit do
75
- if user.is_admin?
76
- klass.all
77
- else
78
- klass.none
79
- end
80
- end
112
+ `collection`方法,可以控制某个用户对某类资源的控制权限。如list和create权限,在controller中,我们可以用`authorize!(:read, Contract)`来限制用户的访问。
81
113
 
82
- collection [:list, :create], klass do
83
- user.is_admin?
84
- end
114
+ `member`方法,可以控制用户对某个资源的控制权限,如read权限,在controller中我们可以用authorize!(:read, @contract)来限制用户的访问。
85
115
 
86
- member [:read, :update, :delete, :create_later_documents], klass do |obj|
87
- user.is_admin?
88
- end
89
- end
90
- end
116
+ `self.module_name = '核心模块'`是用来处理在多模块开发的环境下,各个模块可能有自己的resource文件,并可能出现中文的重名,权限最终要集中管理,module_name可以做个简单的分隔,让用户清楚某个权限属于哪个模块。
117
+
118
+ 这些在controller中需要使用的current_power方法和authorize!方法分别是consul和cancancan中既有的方法。
91
119
 
92
120
  ### 和角色类之间建立关联
93
121
 
94
- 此处的DSL相当于在数据库中的resouces表,记录了所有权限。我们需要通过role_resources这样的中间表,建立角色和资源之间的关联。因此在数据库建立中间表role_resources,使用一个resource_name字段来跟DSL中的权限、资源进行关联。我们再前端页面,只需要调用Resource.grouped_resources_with_chinese_desc就可获取到所有DSL文件中描述的所有权限以及中文描述。再在controller和view中创建权限和role的关联即可(往role_resources中间表写条目)。
122
+ 此处的resouce文件相当于在数据库中的resouces表,以动态的语言记录了所有的权限。我们需要通过role_resources这样的中间表,建立角色和权限之间的关联。可以在数据库建立中间表role_resources
123
+
124
+ 在controller或view中只要调用 CanPlay.splat_grouped_resources_with_chinese_desc就能返回所有的权限hash,并按资源进行了分组。如果调用CanPlay.grouped_resources_with_chinese_desc则返回按module_name分组后,再按资源名称分组的权限hash。我们用这个hash在表单中呈现,让用户勾选,然后在controller中保存角色和资源权限的关联。
125
+
126
+
127
+ 其中roles_resources的表结构,至少要有role_id、resource_name字段,其中role_id关联角色,而 resource_name用于关联resource类(伪关联,实际不用加belongs_to这类关联)。
128
+
129
+ 有了这个关联,加载页面,就可以自动执行权限的限制了,当然前提是你在controller的每个action加入了cancancan的权限限制语句authorize!。
data/can_play.gemspec CHANGED
@@ -24,4 +24,5 @@ Gem::Specification.new do |spec|
24
24
  spec.add_dependency 'cancancan', "~> 1.12"
25
25
  spec.add_dependency 'consul', "~> 0.12"
26
26
  spec.add_dependency 'ror_hack', "~> 0.1 "
27
+ spec.add_dependency 'modularity', "~> 2.0 "
27
28
  end
@@ -5,12 +5,12 @@ class Ability
5
5
  def initialize(user)
6
6
  self.user = user||CanPlay::Config.user_class_name.constantize.new
7
7
  CanPlay::Config.super_roles.each do |role_name|
8
- can(:manage, :all) if user.role?(role_name)
8
+ can(:manage, :all) if user.send(CanPlay::Config.role_judge_method, role_name)
9
9
  end
10
10
  CanPlay::Config.role_class_name.constantize.all.each do |role|
11
11
  next unless user.role?(role.name)
12
12
  role.send(CanPlay::Config.role_resources_relation_name).each do |role_resource|
13
- resource = CanPlay.find_by_name(role_resource.resource_name)
13
+ resource = CanPlay.find_by_name_and_code(role_resource.resource_name, CanPlay.override_code)
14
14
  next unless resource
15
15
  if resource[:type] == 'collection'
16
16
  if resource[:behavior]
@@ -1,6 +1,29 @@
1
1
  class ActionController::Base
2
2
  include Consul::Controller
3
3
  current_power do
4
- Power.new(current_user)
4
+ CanPlay::Power.new(current_user)
5
5
  end
6
+
7
+ # 对current_power采用动态方法调用的装饰者。
8
+ class PlayResourceObject < BasicObject
9
+ def initialize(obj, klass)
10
+ @obj = obj
11
+ @klass = klass
12
+ end
13
+
14
+ def method_missing(method, *args, &block)
15
+ if @obj.respond_to? "#{method}_evaluate_in_#{@klass.override_code}_scope"
16
+ @obj.send("#{method}_evaluate_in_#{@klass.override_code}_scope", *args, &block)
17
+ elsif @obj.respond_to? method
18
+ @obj.send(method, *args, &block)
19
+ else
20
+ super
21
+ end
22
+ end
23
+ end
24
+
25
+ def play_resources
26
+ @play_resource_object ||= PlayResourceObject.new(current_power, CanPlay)
27
+ end
28
+
6
29
  end
@@ -1,9 +1,11 @@
1
- class Power
2
- include Consul::Power
3
- attr_accessor :user
1
+ module CanPlay
2
+ class Power
3
+ include Consul::Power
4
+ attr_accessor :user
4
5
 
5
- def initialize(user)
6
- self.user = user
7
- end
6
+ def initialize(user)
7
+ self.user = user
8
+ end
8
9
 
10
+ end
9
11
  end
@@ -1,3 +1,3 @@
1
1
  module CanPlay
2
- VERSION = "0.2.4"
2
+ VERSION = "0.2.5"
3
3
  end
data/lib/can_play.rb CHANGED
@@ -1,41 +1,41 @@
1
1
  require 'ror_hack'
2
2
  require 'consul'
3
3
  require 'cancancan'
4
- require "can_play/version"
5
- require "can_play/power"
6
- require "can_play/ability"
7
- require "can_play/controller"
4
+ require 'modularity'
8
5
 
9
6
  module CanPlay
10
- mattr_accessor :resources
11
- @@resources = []
7
+ mattr_accessor :resources, :override_resources, :override_code
8
+ self.override_code = nil
9
+ self.resources = []
10
+ self.override_resources = {}.with_indifferent_access
12
11
 
13
12
  class << self
14
13
  def included(base)
15
14
  base.class_eval <<-RUBY
16
- include RorHack::ClassLevelInheritableAttributes
17
- inheritable_attributes(:groups, :current_group, :module_name)
18
- @groups = []
19
- @current_group = nil
20
- @module_name = ''
15
+ singleton_attr_accessor(:groups, :current_group, :module_name)
16
+ self.groups = []
17
+ self.current_group = nil
18
+ self.module_name = ''
21
19
  RUBY
22
20
  base.extend ClassMethods
23
21
  end
24
22
 
25
- def find_by_name(name)
26
- CanPlay.resources.find { |r| r.name == name }
23
+ def find_by_name_and_code(name, code)
24
+ resource = CanPlay.override_resources[code].p2a.find { |r| r.name.to_s == name.to_s }
25
+ resource || CanPlay.resources.find { |r| r.name.to_s == name.to_s }
27
26
  end
28
27
 
29
- def grouped_resources
30
- @grouped_resources ||= CanPlay.resources.multi_group_by(:module_name, :group)
28
+ def conjunct_resources
29
+ resources = CanPlay.override_resources[CanPlay.override_code].p2a + CanPlay.resources
30
+ resources.uniq { |i| i.name }
31
31
  end
32
32
 
33
- def splat_grouped_resources
34
- @grouped_resources ||= CanPlay.resources.multi_group_by(:group)
33
+ def grouped_resources
34
+ conjunct_resources.multi_group_by :module_name, :group
35
35
  end
36
36
 
37
- def my_resources
38
- CanPlay.resources
37
+ def splat_grouped_resources
38
+ conjunct_resources.multi_group_by :group
39
39
  end
40
40
 
41
41
  def grouped_resources_with_chinese_desc
@@ -60,8 +60,8 @@ module CanPlay
60
60
  splat_grouped_resources.tap do |i|
61
61
  i.each do |group, resources|
62
62
  group[:chinese_desc] = begin
63
- name = I18n.t("can_play.class_name.#{group[:name].singularize}", default: '')
64
- name = group[:klass].model_name.human if name.blank?
63
+ name = I18n.t("can_play.class_name.#{group.name.singularize}", default: '')
64
+ name = group.klass.model_name.human if name.blank?
65
65
  name
66
66
  end
67
67
  resources.each do |resource|
@@ -74,47 +74,52 @@ module CanPlay
74
74
  end
75
75
 
76
76
  module Config
77
- mattr_accessor :user_class_name, :role_class_name, :role_resources_relation_name, :super_roles
78
- @@user_class_name = 'User'
79
- @@role_class_name = 'Role'
80
- @@role_resources_relation_name = 'role_resources'
81
- @@super_roles = []
77
+ mattr_accessor :user_class_name, :role_class_name, :role_resources_relation_name, :super_roles, :role_judge_method
78
+ self.user_class_name = 'User'
79
+ self.role_class_name = 'Role'
80
+ self.role_resources_relation_name = 'role_resources'
81
+ self.super_roles = []
82
+ self.role_judge_method = 'role?'
82
83
 
83
84
  def self.setup
84
85
  yield self
85
86
  end
86
87
  end
87
88
 
88
-
89
-
90
89
  module ClassMethods
91
90
 
91
+ class NameImportantOpenStruct < OpenStruct
92
+ def eql?(another)
93
+ self.name == another.name
94
+ end
95
+ end
96
+
92
97
  # 为每个 resource 添加一个 group, 方便管理
93
98
  def group(opts, &block)
94
99
  if opts.is_a?(Hash)
95
100
  opts = opts.with_indifferent_access
96
- group = OpenStruct.new(name: opts.delete(:name).to_s, klass: opts.delete(:klass))
101
+ group = NameImportantOpenStruct.new(name: opts.delete(:name).to_s, klass: opts.delete(:klass))
97
102
  elsif opts.is_a?(Module)
98
- name = opts.try(:table_name).presence || opts.to_s.underscore.gsub('/', '_').pluralize
99
- group = OpenStruct.new(name: name, klass: opts)
103
+ name = opts.try(:table_name).presence || opts.to_s.underscore.gsub('/', '_').pluralize
104
+ group = NameImportantOpenStruct.new(name: name, klass: opts)
100
105
  else
101
106
  # do nothing
102
107
  end
103
- @groups << group
104
- @groups = @groups.uniq(&:name)
105
- @current_group = group
108
+ self.groups << group
109
+ self.groups = groups.uniq(&:name)
110
+ self.current_group = group
106
111
  block.call(group.klass)
107
- @current_group = nil
112
+ self.current_group = nil
108
113
  end
109
114
 
110
115
  def limit(name=nil, &block)
111
- raise "Need define group first" if @current_group.nil?
112
- Power.power(name||@current_group.name, &block)
116
+ raise "Need define group first" if current_group.nil?
117
+ CanPlay::Power.power(name||current_group.name, &block)
113
118
  end
114
119
 
115
- def collection(verb_or_verbs, &block)
116
- raise "Need define group first" if @current_group.nil?
117
- group = @current_group
120
+ def collection(verb_or_verbs, opts={}.with_indifferent_access, &block)
121
+ raise "Need define group first" if current_group.nil?
122
+ group = current_group
118
123
  behavior = nil
119
124
  if block
120
125
  behavior = lambda do |user|
@@ -127,16 +132,16 @@ module CanPlay
127
132
 
128
133
  if verb_or_verbs.kind_of?(Array)
129
134
  verb_or_verbs.each do |verb|
130
- add_resource(group, verb, group.klass, 'collection', behavior)
135
+ add_resource(group, verb, group.klass, 'collection', behavior, opts)
131
136
  end
132
137
  else
133
- add_resource(group, verb_or_verbs, group.klass, 'collection', behavior)
138
+ add_resource(group, verb_or_verbs, group.klass, 'collection', behavior, opts)
134
139
  end
135
140
  end
136
141
 
137
- def member(verb_or_verbs, &block)
138
- raise "Need define group first" if @current_group.nil?
139
- group = @current_group
142
+ def member(verb_or_verbs, opts={}.with_indifferent_access, &block)
143
+ raise "Need define group first" if current_group.nil?
144
+ group = current_group
140
145
  behavior = nil
141
146
  if block
142
147
  behavior = lambda do |user, obj|
@@ -149,26 +154,65 @@ module CanPlay
149
154
 
150
155
  if verb_or_verbs.kind_of?(Array)
151
156
  verb_or_verbs.each do |verb|
152
- add_resource(group, verb, group.klass, 'member', behavior)
157
+ add_resource(group, verb, group.klass, 'member', behavior, opts)
153
158
  end
154
159
  else
155
- add_resource(group, verb_or_verbs, group.klass, 'member', behavior)
160
+ add_resource(group, verb_or_verbs, group.klass, 'member', behavior, opts)
156
161
  end
157
162
  end
158
163
 
159
- def add_resource(group, verb, object, type, behavior)
164
+ def add_resource(group, verb, object, type, behavior, opts)
160
165
  name = "#{verb}_#{group.name}"
161
166
  resource = OpenStruct.new(
162
167
  module_name: module_name,
163
- name: name,
164
- group: group,
165
- verb: verb,
166
- object: object,
167
- type: type,
168
- behavior: behavior
168
+ name: name,
169
+ group: group,
170
+ verb: verb,
171
+ object: object,
172
+ type: type,
173
+ behavior: behavior,
174
+ opts: opts
169
175
  )
170
176
  CanPlay.resources.keep_if { |i| i.name != name }
171
177
  CanPlay.resources << resource
172
178
  end
173
179
  end
180
+
181
+ module Override
182
+ as_trait do |code|
183
+ extend ClassMethods
184
+ singleton_attr_accessor(:groups, :current_group, :module_name)
185
+ self.groups = []
186
+ self.current_group = nil
187
+ self.module_name = ''
188
+
189
+ define_singleton_method(:limit) do |name=nil, &block|
190
+ raise "Need define group first" if current_group.nil?
191
+ method_name = name ? "#{name}_evaluate_in_#{code}_scope" : "#{current_group.name}_evaluate_in_#{code}_scope"
192
+ Power.power(method_name, &block)
193
+ end
194
+
195
+ define_singleton_method(:add_resource) do |group, verb, object, type, behavior, opts|
196
+ super(group, verb, object, type, behavior, opts) and return if code.blank?
197
+ CanPlay.override_resources[code] ||= []
198
+ name = "#{verb}_#{group.name}"
199
+ resource = OpenStruct.new(
200
+ module_name: module_name,
201
+ name: name,
202
+ group: group,
203
+ verb: verb,
204
+ object: object,
205
+ type: type,
206
+ behavior: behavior,
207
+ opts: opts
208
+ )
209
+ CanPlay.override_resources[code].keep_if { |i| i.name != name }
210
+ CanPlay.override_resources[code] << resource
211
+ end
212
+ end
213
+ end
174
214
  end
215
+
216
+ require "can_play/power"
217
+ require "can_play/controller"
218
+ require "can_play/ability"
@@ -13,4 +13,7 @@ CanPlay::Config.setup do |config|
13
13
 
14
14
  # super_roles表示无需分配权限既可拥有所有权限的角色。
15
15
  config.super_roles = ['超级管理员']
16
+
17
+ # 判断角色是否符合的方法。
18
+ config.role_judge_method = 'role?'
16
19
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: can_play
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - happyming9527
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-08-30 00:00:00.000000000 Z
11
+ date: 2015-09-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ~>
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0.1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: modularity
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '2.0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '2.0'
83
97
  description: control user's permissions based on role and resource.
84
98
  email:
85
99
  - happyming9527@gmail.com