module Hyperstack class InternalPolicy def self.accessible_attributes_for(model, acting_user) user_channels = ClassConnectionRegulation.connections_for(acting_user, false) + InstanceConnectionRegulation.connections_for(acting_user, false) internal_policy = InternalPolicy.new(model, model.attribute_names, user_channels) ChannelBroadcastRegulation.broadcast(internal_policy) InstanceBroadcastRegulation.broadcast(model, internal_policy) internal_policy.accessible_attributes_for end def accessible_attributes_for accessible_attributes = Set.new @channel_sets.each do |channel, attribute_set| accessible_attributes.merge attribute_set end accessible_attributes << :id unless accessible_attributes.empty? accessible_attributes end end end class ActiveRecord::Base attr_accessor :acting_user def view_permitted?(attribute) Hyperstack::InternalPolicy.accessible_attributes_for(self, acting_user).include? attribute.to_sym end def create_permitted? false end def update_permitted? false end def destroy_permitted? false end def only_changed?(*attributes) (self.attributes.keys + self.class.reactive_record_association_keys).each do |key| return false if self.send("#{key}_changed?") and !attributes.include? key end true end def none_changed?(*attributes) attributes.each do |key| return false if self.send("#{key}_changed?") end true end def any_changed?(*attributes) attributes.each do |key| return true if self.send("#{key}_changed?") end false end def all_changed?(*attributes) attributes.each do |key| return false unless self.send("#{key}_changed?") end true end class << self attr_reader :reactive_record_association_keys [:has_many, :belongs_to, :composed_of].each do |macro| define_method "#{macro}_with_reactive_record_add_changed_method".to_sym do |attr_name, *args, &block| define_method "#{attr_name}_changed?".to_sym do instance_variable_get "@reactive_record_#{attr_name}_changed".to_sym end (@reactive_record_association_keys ||= []) << attr_name send "#{macro}_without_reactive_record_add_changed_method".to_sym, attr_name, *args, &block end alias_method "#{macro}_without_reactive_record_add_changed_method".to_sym, macro alias_method macro, "#{macro}_with_reactive_record_add_changed_method".to_sym end alias belongs_to_without_reactive_record_add_is_method belongs_to def belongs_to(attr_name, *args) belongs_to_without_reactive_record_add_is_method(attr_name, *args).tap do define_method "#{attr_name}_is?".to_sym do |model| attributes[self.class.reflections[attr_name.to_s].foreign_key] == model.id end end end end def check_permission_with_acting_user(user, permission, *args) old = acting_user self.acting_user = user if self.send(permission, *args) self.acting_user = old self else acting_user_string = if acting_user id = user.respond_to?(:id) ? user.id : user "not allowed for acting_user: <##{user.class} id: #{user}>" else "not allowed without acting_user (acting_user = nil)" end message = "CRUD access violation: <##{self.class} id: #{self.id}> - #{permission}(#{args}) #{acting_user_string}" if permission == :view_permitted? details = Hyperstack::PolicyDiagnostics.policy_dump_for(self, user) end Hyperstack::InternalPolicy.raise_operation_access_violation(message, details || '') end end end class ActionController::Base def acting_user end end