プロローグ
私はSqueelを採用しました–そしてすべてのステップを楽しんでいます!アーニーミラー、共有していただきありがとうございます!
私はruby1.9.2とSqueel1.0.2とRails3.2.5で開発しています
(質問を完全に再構成したことを告白します-読みやすさを向上させ、答えを得る可能性を高めることを望んでいます)<:)
使用事例
(スーパー)ユーザーがこのような承認と権限を割り当てることができるようにしたいと思います
- user_groupは複数の権限を持つことができる必要があります
- 承認は複数の権限を持つことができる必要があります
- 権限は、データへのアクセス(操作)を制御できる必要があります
- コントローラ経由(リクエストパス)
- クラスのインスタンスで
- 特定のインスタンスで
ACLシステムは怠惰である必要があります。つまり、役割/承認が与えられていない場合、ユーザーは明らかにACLにまったく関心がありません。
移行
ユースケースから役割と(多形の)役割のあるエンティティを特定したので、
普通ではない役割
create_table :roles do |t|
t.references :ox
t.string :name
t.boolean :active, default: true
t.timestamps
end
ともう少し説明的なRoleable
create_table :roleables do |t|
t.references :ox
t.references :role
t.references :roleable, polymorphic: true
t.string :authorization
t.string :controller
t.boolean :active, default: true
t.timestamps
end
クラス
システムには、ActiveRecord:Baseから継承し、すべてのクラスから継承するジェネリッククラス(AbstractActionBase)があります(システム全体の属性とメソッドを1か所に追加できます)
つまり、部分的には、AbstractActionBaseは次のようになります。
class AbstractActionBase < ActiveRecord::Base
self.abstract_class=true
require 'tempfile'
belongs_to :ox
has_many :roleables, as: :roleable
attr_accessible :ox_id
validates_presence_of :ox_id
#
# all models inheriting from this will have versions
has_paper_trail
#
#
#
# Class method to providing for index SELECT's being married with roleables (permissions)
# used from abstraction_actions_controller where build_collection calls this method
# the result 'should' be an ActiveRelation - used for the Kamanari 'result' call to readying pagination
#
def self.with_authorizations
#
# SELECT * FROM any_table at
# left join (
# select r.roleable_id, r.roleable_type, group_concat( r.authorization )
# from roleables r
# where r.authorization is not null
# and r.roleable_id=at.id
# and r.roleable_type=at.base_class
# and r.role_id not in (1,2,3) <--- ID's are current_user.roles
# ) rm on rm.roleable_id=at.id and rm.roleable_type=at.base_class
#
# which will provide for this:
#
# |.......| last column in table 'at' | roleable_id | roleable_type | authorizations |
# |.......| some value | 1 | 'UserGroup' | 'insert,create'|
# |.......| yet another value | 92 | 'UserGroup' | 'read' |
#
#
self.where{ active==true }
end
# compile a collection of records - regard search using Ransack
def base.collection( params, resource_set )
#
# kaminari (and continous scrolling)
#
params[:page] ||= 1
params[:per_page] ||= self.per_page
params[:o] ||= self.resource_order_by
distinct = params[:distinct].nil? ? false : params[:distinct].to_i.zero?
resource_set = (resource_set.respond_to?( "result")) ? resource_set.result(:distinct => distinct) : resource_set
(resource_set.respond_to?( "page")) ? resource_set.order(params[:o]).page( params[:page] ).per( params[:per_page] ) : resource_set.order(params[:o])
end
end
Roleクラスの一部は次のようになります
class Role < AbstractActionBase
has_many :roleables
scope :active, where{ active.eq true }
#
# what does this role allow
def permissions
roleables.permissions.scoped
end
#
# to whom does this role allow
def authorizations
roleables.authorizations.scoped
end
# returns true if the roleables (permissions) authorizes the options
# options are { controller: "", action: "", record: Instance, is_class: boolean }
def authorizes?( options={} )
coll = permissions
coll = coll.on_action(options.delete(:action)) if options.keys.include? :action
coll = coll.on_entity( options.delete(:record), options.delete(:is_class) || false ) if options.keys.include? :record
coll = coll.on_controller(options.delete(:controller)) if options.keys.include? :controller
(coll.count>0) === true
end
end
Roleableクラスは次のようになります
class Roleable < AbstractActionBase
belongs_to :role
belongs_to :roleable, polymorphic: true
# roleables authorizes users through user_groups
# (in which case the authorization is "-")
# providing them permissions on controllers, actions and instances
scope :authorizations, where{ authorization == nil }
scope :permissions, where{ authorization != nil }
# using Squeel, find roleables on a particular controller or any controller
def self.on_controller(ctrl)
where{ (controller==ctrl) | (controller==nil) }
end
# using Squeel, find roleables on a particular authorization or allowed 'all'
def self.on_action(action)
where{ (authorization=~ "%#{action}%") | (authorization=="all") }
end
# using Squeel, find roleables on a particular instance/record or class
def self.on_entity(entity, is_class=false)
if is_class
where{ ((roleable_type==entity.base_class.to_s ) & ( roleable_id==nil)) | ((roleable_type==nil) & (roleable_id==nil)) }
else
where{ ((roleable_type==entity.class.to_s ) & ( roleable_id==entity.id)) | ((roleable_type==nil) & (roleable_id==nil)) }
end
end
end
論理
作成
これにより、承認(誰か/何かに役割を割り当てる)が可能になります。この場合、承認文字列はnilです。
user_group salesには 、Roleable.create({role:@sales、roleable:@user_group})を使用してロールsalesが割り当てられます。
同時に、私は許可を行うことができます-任意の役割の詳細を説明します-のように
役割salesには、OrderHeadテーブルとOrderDetailテーブルに対するインデックス、作成、編集、および削除の権限があります。
- Roleable.create({role:@sales、authorization: "index、create、edit、delete"、roleable:@user_group、controller: "order_heads"})
- Roleable.create({role:@sales、authorization: "index、create、edit、delete"、roleable:@user_group、controller: "order_details"})
これらの「詳細」は、次のようにエーテル的である可能性があります
Roleable.create({role:@sales、authorization: "index"})
ややリアル
Roleable.create({role:@sales、authorization: "index"、roleable_type:'OrderHead'})
または非常に表現された
Roleable.create({role:@sales、authorization: "index"、roleable:OrderHead.first})
選択
ほとんどすべてのコントローラーは、インデックス(およびその他のアクション)が定義されているAbstractActionsControllerから継承します。このように、そのコントローラーは、AttachedResources:Baseから自己継承します。
class AbstractActionsController < InheritedResources::Base # < ApplicationController
append_view_path ViewTemplate::Resolver.instance
respond_to :html, :xml, :json, :js, :pdf
belongs_to :ox, :optional => true
before_filter :authorize!
before_filter :authenticate!
before_filter :warn_unless_confirmed!
before_filter :fix_money_params, :only => [:create,:update]
# GET /collection - printers
def index
# session[:params] = params
#
# preparing for Ransack
unless params[:q].nil?
params[:q]= { :"#{params[:q_fields]}" => params[:q] }
end
super do |format|
format.html
format.js { render layout: false }
format.pdf{ render :pdf => generate_pdf(false) and return }
format.xml { render layout: false }
format.json do
# field lookup request?
unless params[:lookup].nil?
render layout: false, :json => collection.map(&:select_mapping)
else
render json: collection.map { |p| view_context.grow_mustache_for_index(p, collection, (parent? ? collection : resource_class.order(:id)), @selected ) }
end
end
end
end
# the collection method on inherited_resources
# gets overloaded with Ransack search and Kaminari pagination (on the model)
def collection
# @collection ||= build_collection
# TODO - test whether caching the collection is possible
build_collection
end
def build_collection
unless params[:belongs].nil?
# debugger
parent = params[:belongs].constantize.find(params[:belongs_id])
@selected = parent.nil? ? [] : parent.send( rewrite_association(params[:assoc],parent) )
@search_resource = core_entity(params[:assoc].constantize)
@search_resource = @search_resource.search(params[:q]) unless params[:q].nil?
else
@search_resource = rewrite_end_of_association_chain(resource_class)
@search_resource = core_entity(@search_resource)
@search_resource = @search_resource.search(params[:q]) unless params[:q].nil?
end
# authorize rows
@search_resource = @search_resource.with_authorizations # left joins roleables coalescing a "authorization" field from roles ID's not owned by current_user through his user_groups
@resources ||= resource_class.collection( params, @search_resource )
end
end
チャレンジ
短い質問を提示するのはなんと長い話でしょう<:)
with_authorizations
ActiveRelationを返すメソッドを作成するにはどうすればよいですか(できればSqueelを使用して)