lib/kojac/kojac_rails.rb in kojac-0.13.0 vs lib/kojac/kojac_rails.rb in kojac-0.15.0

- old
+ new

@@ -211,33 +211,21 @@ public def results @results ||= {} end - - def deduce_model_class - KojacUtils.model_class_for_key(self.kojac_resource) - end - - def kojac_resource - self.class.to_s.chomp('Controller').snake_case - end - - def kojac_current_user - self.current_user - end - + def current_ring - kojac_current_user.try(:ring).to_i + current_user.try(:ring).to_i end def create_on_association(aItem,aAssoc,aValues,aRing) raise "User does not have permission for create on #{aAssoc}" unless aItem.class.ring_can?(aRing,:create_on,aAssoc.to_sym) return nil unless ma = aItem.class.reflect_on_association(aAssoc.to_sym) a_model_class = ma.klass - policy = Kojac.policy!(kojac_current_user,a_model_class) + policy = Pundit.policy!(current_user,a_model_class) aValues = KojacUtils.upgrade_hashes_to_params(aValues || {}) case ma.macro when :belongs_to @@ -275,75 +263,112 @@ #else # next #end end + # Unknown resource : + # CREATE: already there with given id => error + # READ: isn't there, or we don't have access => nil + # UPDATE: isn't there, or we don't have access => nil + # DESTROY: isn't there, or we don't have access => nil + def create_op ring = current_ring - op = params[:op] + op = (self.respond_to?(:op) && self.op || params[:op]) + #op = params[:op] unless self.respond_to? :op options = op[:options] || {} - model_class = deduce_model_class resource,id,assoc = op['key'].split_kojac_key - if assoc # create operation on an association eg. {verb: "CREATE", key: "order.items"} - raise "User does not have permission for #{op[:verb]} operation on #{model_class.to_s}.#{assoc}" unless model_class.ring_can?(ring,:create_on,assoc.to_sym) - item = KojacUtils.model_for_key(key_join(resource,id)) - ma = model_class.reflect_on_association(assoc.to_sym) - a_value = op[:value] # get data for this association, assume {} - raise "create multiple not yet implemented for associations" unless a_value.is_a?(Hash) + if model_class = KojacUtils.model_class_for_key(resource) + if assoc # create operation on an association eg. {verb: "CREATE", key: "order.items"} + if model_class.ring_can?(ring,:create_on,assoc.to_sym) + item = KojacUtils.model_for_key(key_join(resource,id)) + ma = model_class.reflect_on_association(assoc.to_sym) + a_value = op[:value] # get data for this association, assume {} + raise "create multiple not yet implemented for associations" unless a_value.is_a?(Hash) - a_model_class = ma.klass - policy = Kojac.policy!(kojac_current_user,a_model_class) - p_fields = policy.permitted_fields(:write) - fields = a_value.permit( *p_fields ) - new_sub_item = nil - case ma.macro - when :has_many - a_model_class.write_op_filter(current_user,fields,a_value) if a_model_class.respond_to? :write_op_filter - new_sub_item = item.send(assoc.to_sym).create(fields) + a_model_class = ma.klass + policy = Pundit.policy!(current_user,a_model_class) + p_fields = policy.permitted_fields(:write) + fields = a_value.permit( *p_fields ) + new_sub_item = nil + case ma.macro + when :has_many + a_model_class.write_op_filter(current_user,fields,a_value) if a_model_class.respond_to? :write_op_filter + new_sub_item = item.send(assoc.to_sym).create(fields) + else + raise "#{ma.macro} association unsupported in CREATE" + end + result_key = op[:result_key] || new_sub_item.kojac_key + merge_model_into_results(new_sub_item) else - raise "#{ma.macro} association unsupported in CREATE" - end - result_key = op[:result_key] || new_sub_item.kojac_key - merge_model_into_results(new_sub_item) - else # create operation on a resource eg. {verb: "CREATE", key: "order_items"} but may have embedded association values - raise "User does not have permission for #{op[:verb]} operation on #{model_class.to_s}" unless model_class.ring_can?(:create,ring) - policy = Kojac.policy!(kojac_current_user,model_class) - p_fields = policy.permitted_fields(:write) + error = { + code: 403, + status: "Forbidden", + message: "User does not have permission for #{op[:verb]} operation on #{model_class.to_s}.#{assoc}" + } + end + else # create operation on a resource eg. {verb: "CREATE", key: "order_items"} but may have embedded association values + if model_class.ring_can?(:create,ring) + policy = Pundit.policy!(current_user,model_class) + p_fields = policy.permitted_fields(:write) - p_fields = op[:value].permit( *p_fields ) - model_class.write_op_filter(current_user,p_fields,op[:value]) if model_class.respond_to? :write_op_filter - item = model_class.create!(p_fields) + p_fields = op[:value].permit( *p_fields ) + model_class.write_op_filter(current_user,p_fields,op[:value]) if model_class.respond_to? :write_op_filter + item = model_class.create!(p_fields) - options_include = options['include'] || [] - included_assocs = [] - p_assocs = policy.permitted_associations(:write) - if p_assocs - p_assocs.each do |a| - next unless (a_value = op[:value][a]) || options_include.include?(a.to_s) - create_on_association(item,a,a_value,ring) - included_assocs << a.to_sym + options_include = options['include'] || [] + included_assocs = [] + p_assocs = policy.permitted_associations(:write) + if p_assocs + p_assocs.each do |a| + next unless (a_value = op[:value][a]) || options_include.include?(a.to_s) + create_on_association(item,a,a_value,ring) + included_assocs << a.to_sym + end + end + item.save! + result_key = op[:result_key] || item.kojac_key + merge_model_into_results(item,result_key,:include => included_assocs) + else + error = { + code: 403, + status: "Forbidden", + message: "User does not have permission for #{op[:verb]} operation on #{model_class.to_s}" + } end end - item.save! - result_key = op[:result_key] || item.kojac_key - merge_model_into_results(item,result_key,:include => included_assocs) + else + error = { + code: 501, + status: "Not Implemented", + message: "model class not found" + } end - { + response = { key: op[:key], verb: op[:verb], - result_key: result_key, - results: results } + if error + response[:error] = error + else + response[:results] = results + response[:result_key] = result_key + end + response end protected + def rails_controller? + self.is_a? ActionController::Base + end + def merge_model_into_results(aItem,aResultKey=nil,aOptions=nil) ring = current_ring - aResultKey ||= aItem.g? :kojac_key - results[aResultKey] = (aItem && KojacUtils.to_jsono(aItem,scope: kojac_current_user)) - if policy = Kojac.policy!(kojac_current_user,aItem) + aResultKey ||= aItem.g?(:kojac_key) + results[aResultKey] = (aItem && KojacUtils.to_jsono(aItem,scope: current_user)) + if policy = Pundit.policy!(current_user,aItem) aOptions ||= {} if included_assocs = aOptions[:include] included_assocs = included_assocs.split(',') if included_assocs.is_a?(String) included_assocs = [included_assocs] unless included_assocs.is_a?(Array) included_assocs.map!(&:to_sym) if included_assocs.is_a?(Array) @@ -358,113 +383,140 @@ use_assocs.each do |a| next unless a_contents = aItem.send(a) if a_contents.is_a? Array contents_h = [] a_contents.each do |sub_item| - results[sub_item.kojac_key] = KojacUtils.to_jsono(sub_item,scope: kojac_current_user) + results[sub_item.kojac_key] = KojacUtils.to_jsono(sub_item,scope: current_user) end else - results[a_contents.kojac_key] = KojacUtils.to_jsono(a_contents,scope: kojac_current_user) + results[a_contents.kojac_key] = KojacUtils.to_jsono(a_contents,scope: current_user) end end end end results_insert_filter(results,aItem,aResultKey,aOptions) if respond_to?(:results_insert_filter) results end + public + def kojac_setup(aCurrentUser,aOp) + self.current_user = aCurrentUser if self.respond_to? :current_user + self.op = aOp if self.respond_to? :op + self.verb = aOp['verb'] if self.respond_to? :verb + self.key = aOp['key'] if self.respond_to? :key + self.value = aOp['value'] if self.respond_to? :value + self.params = aOp['params'] || {} if self.respond_to? :params + self.options = aOp['options'] || {} if self.respond_to? :options + self.error = aOp['error'] if self.respond_to? :error + self + end + def read_op - op = params[:op] + op = (self.respond_to?(:op) && self.op || params[:op]) key = op[:key] result_key = nil + error = nil resource,id = key.split '__' - model = deduce_model_class - scope = Kojac.policy_scope(current_user, model, op) || model - if id # item - if scope - item = scope.load_by_key(key,op) - #item = item.first - #item.prepare(key,op) if item.respond_to? :prepare - result_key = op[:result_key] || (item && item.kojac_key) || op[:key] - merge_model_into_results(item,result_key,op[:options]) - else - result_key = op[:result_key] || op[:key] - results[result_key] = null - end - else # collection - result_key = op[:result_key] || op[:key] - results[result_key] = [] - if scope - items = scope - items = send(:after_scope,items,op) if respond_to? :after_scope - items = items.load_by_key(key,op) - #items = scope.by_key(key,op) - #items = items.all - items.each do |item| - item.prepare(key,op) if item.respond_to? :prepare + model = KojacUtils.model_class_for_key(key) + #raise "model class not found" unless + if scope = Pundit.policy_scope(current_user, model) || model + if id # item + scope = scope.where(id: id) + scope = after_scope(scope) if respond_to?(:after_scope) + if item = scope.first + #item.prepare(key,op) if item.respond_to? :prepare + result_key = op[:result_key] || (item && item.kojac_key) || op[:key] + merge_model_into_results(item,result_key,op[:options]) + else + result_key = op[:result_key] || op[:key] + results[result_key] = nil end + else # collection + if rails_controller? # deprecated + items = scope.respond_to?(:all) ? scope.all : scope.to_a + result_key = op[:result_key] || op[:key] + results[result_key] = [] + items = after_scope(items) if respond_to?(:after_scope) + else + scope = after_scope(scope) if respond_to?(:after_scope) + items = scope.respond_to?(:all) ? scope.all : scope.to_a + result_key = op[:result_key] || op[:key] + results[result_key] = [] + end if op[:options] and op[:options][:atomise]==false items_json = [] - items_json = items.map {|i| KojacUtils.to_jsono(i,scope: kojac_current_user) } + items_json = items.map {|i| KojacUtils.to_jsono(i,scope: current_user) } results[result_key] = items_json else items.each do |m| item_key = m.kojac_key results[result_key] << item_key.split_kojac_key[1] merge_model_into_results(m,item_key,op[:options]) end end end + else + error = { + code: 501, + status: "Not Implemented", + message: "model class not found" + } end - { + response = { key: op[:key], verb: op[:verb], - results: results, - result_key: result_key } + if error + response[:error] = error + else + response[:results] = results + response[:result_key] = result_key + end + response end def update_op result = nil ring = current_ring - op = params[:op] + op = (self.respond_to?(:op) && self.op || params[:op]) result_key = nil - model = deduce_model_class - scope = Kojac.policy_scope(current_user, model, op) || model - - if item = scope.load_by_key(op[:key],op) - + model = KojacUtils.model_class_for_key(op[:key].base_key) + scope = Pundit.policy_scope(current_user, model) || model + scope = after_scope(scope) if scope && respond_to?(:after_scope) + if scope and item = scope.load_by_key(op[:key],op) #run_callbacks :update_op do - policy = Kojac.policy!(kojac_current_user,item,op) + policy = Pundit.policy!(current_user,item) item.update_permitted_attributes!(op[:value], policy) associations = policy.permitted_associations(:write) associations.each do |k| next unless assoc = model.reflect_on_association(k) next unless op[:value][k] case assoc.macro when :belongs_to if leaf = (item.send(k) || item.send("build_#{k}".to_sym)) - policy = Kojac.policy!(kojac_current_user,leaf) + policy = Pundit.policy!(current_user,leaf) leaf.update_permitted_attributes!(op[:value][k], policy) end end end - result_key = item.kojac_key - #results[result_key] = item + result_key = op[:result_key] || (item && item.kojac_key) || op[:key] merge_model_into_results(item,result_key,op[:options]) associations.each do |a| next unless assoc_item = item.send(a) next unless key = assoc_item.respond_to?(:kojac_key) && assoc_item.kojac_key #results[key] = assoc_item merge_model_into_results(assoc_item,key) end #end + else + result_key = op[:result_key] || op[:key] + results[result_key] = nil end { key: op[:key], verb: op[:verb], result_key: result_key, @@ -472,14 +524,23 @@ } end def destroy_op ring = current_ring - op = params[:op] + op = (self.respond_to?(:op) && self.op || params[:op]) result_key = op[:result_key] || op[:key] - item = KojacUtils.model_for_key(op[:key]) - item.destroy if item + # item = KojacUtils.model_for_key(op[:key]) + # item.destroy if item + r,id,a = op[:key].split_kojac_key + + if id + model = KojacUtils.model_class_for_key(op[:key].base_key) + scope = Pundit.policy_scope(current_user, model) || model + scope = after_scope(scope) if scope && respond_to?(:after_scope) + item = scope.where(id: id).first + item.destroy if item + end results[result_key] = nil { key: op[:key], verb: op[:verb], result_key: result_key, @@ -491,12 +552,12 @@ # puts 'execute_op' #end def add_op ring = current_ring - op = params[:op] - model = deduce_model_class + op = (self.respond_to?(:op) && self.op || params[:op]) + model = KojacUtils.model_class_for_key(op[:key].base_key) raise "ADD only supports associated collections at present eg order.items" unless op[:key].index('.') item = KojacUtils.model_for_key(op[:key].base_key) assoc = (assoc=op[:key].key_assoc) && assoc.to_sym id = op[:value]['id'] @@ -524,12 +585,12 @@ } end def remove_op ring = current_ring - op = params[:op] - model = deduce_model_class + op = (self.respond_to?(:op) && self.op || params[:op]) + model = KojacUtils.model_class_for_key(op[:key].base_key) raise "REMOVE only supports associated collections at present eg order.items" unless op[:key].key_assoc item = KojacUtils.model_for_key(op[:key].base_key) assoc = (assoc=op[:key].key_assoc) && assoc.to_sym id = op[:value]['id'] @@ -557,25 +618,26 @@ results: results } end def execute_op - op = params[:op] + op = (self.respond_to?(:op) && self.op || params[:op]) resource,action = op[:key].split_kojac_key raise "action not given" unless action.is_a? String - action = "execute_#{action}" + action = rails_controller? ? "execute_#{action}" : "execute__#{action}" raise "action #{action} not implemented on #{resource}" unless respond_to? action.to_sym - result = send(action.to_sym,op) - if op[:error] + result = rails_controller? ? send(action.to_sym,op) : send(action) + error = rails_controller? ? op[:error] : (respond_to?(:error).to_nil && send(:error)) + if error { key: op[:key], verb: op[:verb], - error: op[:error] + error: error } else result_key = op[:result_key] || op[:key] results = op[:results] || {} # look at op[:results][result_key]. If empty, fill with returned value from action - results[result_key] = KojacUtils.to_jsono(result,scope: kojac_current_user) unless results.has_key? result_key + results[result_key] = KojacUtils.to_jsono(result,scope: current_user) unless results.has_key? result_key { key: op[:key], verb: op[:verb], result_key: result_key, results: results \ No newline at end of file