# File lib/hobo/model/permissions.rb, line 135 def _create_record_with_hobo_permission_check(*args, &b) if permission_check_required? create_permitted? or raise PermissionDeniedError, "#{self.class.name} #{self.id}#create" end _create_record_without_hobo_permission_check(*args, &b) end
# File lib/hobo/model/permissions.rb, line 142 def _update_record_with_hobo_permission_check(*args) if permission_check_required? update_permitted? or raise PermissionDeniedError, "#{self.class.name} #{self.id}#update" end _update_record_without_hobo_permission_check(*args) end
# File lib/hobo/model/permissions.rb, line 327 def all_changed?(*attributes) attributes = prepare_attributes_for_change_helpers(attributes) attributes.all? do |attr| with_attribute_or_belongs_to_keys(attr) do |a, ftype| if ftype changed.include?(a) || changed.include?(ftype) else changed.include?(a) end end end end
# File lib/hobo/model/permissions.rb, line 315 def any_changed?(*attributes) attributes.any? do |attr| with_attribute_or_belongs_to_keys(attr) do |a, ftype| if ftype changed.include?(a) || changed.include?(ftype) else changed.include?(a) end end end end
# File lib/hobo/model/permissions.rb, line 276 def association_editable_by?(user, reflection) # has_one and polymorphic associations are not editable (for now) return false if reflection.macro == :has_one || reflection.options[:polymorphic] return false unless reflection.options[:accessible] record = if (through = reflection.through_reflection) # For edit permission on a has_many :through, # the user needs create+destroy permission on the join model send(through.name).new_candidate else # For edit permission on a regular has_many, # the user needs create/destroy permission on the member model send(reflection.name).new_candidate end record.creatable_by?(user) && record.destroyable_by?(user) end
# File lib/hobo/model/permissions.rb, line 257 def attribute_protected?(attribute) return false if attribute.nil? attribute = attribute.to_s return true if self.class.send(:attributes_protected_by_default).include? attribute if !self.class.accessible_attributes.empty? return true if !self.class.accessible_attributes.include?(attribute) elsif self.class.protected_attributes return true if self.class.protected_attributes.include?(attribute) end # Readonly attributes can be set on creation but not thereafter return self.class.readonly_attributes.include?(attribute) if !new_record? && self.class.readonly_attributes false end
# File lib/hobo/model/permissions.rb, line 410 def changed? true end
# File lib/hobo/model/permissions.rb, line 418 def changes raise Hobo::UndefinedAccessError end
# File lib/hobo/model/permissions.rb, line 202 def creatable_by?(user) with_acting_user(user) { create_permitted? } end
Conservative default permissions #
# File lib/hobo/model/permissions.rb, line 360 def create_permitted?; false end
# File lib/hobo/model/permissions.rb, line 362 def destroy_permitted?; false end
# File lib/hobo/model/permissions.rb, line 149 def destroy_with_hobo_permission_check if permission_check_required? destroy_permitted? or raise PermissionDeniedError, "#{self.class.name} #{self.id}#.destroy" end destroy_without_hobo_permission_check end
# File lib/hobo/model/permissions.rb, line 210 def destroyable_by?(user) with_acting_user(user) { destroy_permitted? } end
Best. Name. Ever
# File lib/hobo/model/permissions.rb, line 427 def deunknownify_attribute(attr, remove_globals = true) attr = attr.to_sym metaclass.send :remove_method, attr if (refl = self.class.reflections[attr.to_s]) && refl.macro == :belongs_to # A belongs_to -- restore the underlying fields deunknownify_attribute refl.foreign_key deunknownify_attribute(refl.options[:foreign_type], false) if refl.options[:polymorphic] else # A regular field -- restore the dirty tracking methods # if remove_globals is false, skip the top-level methods, as we have already removed them to_remove = remove_globals ? [:changed?, :changed, :changes] : [] (["#{attr}_change", "#{attr}_was", "#{attr}_changed?"] + to_remove).each do |m| metaclass.send :remove_method, m.to_sym end end end
By default, attempt to derive edit permission from create/update permission
# File lib/hobo/model/permissions.rb, line 368 def edit_permitted?(attribute) unknownify_attribute(attribute) if attribute new_record? ? create_permitted? : update_permitted? rescue Hobo::UndefinedAccessError # The permission is dependent on the unknown value # so this attribute is not editable false ensure deunknownify_attribute(attribute) if attribute end
# File lib/hobo/model/permissions.rb, line 228 def editable_by?(user, attribute=nil) return false if attribute_protected?(attribute) return true if exempt_from_edit_checks? # Can't view implies can't edit return false unless viewable_by?(user, attribute) if attribute attribute = attribute.to_s.sub(/\?$/, '').to_sym # Try the attribute-specific edit-permission method if there is one if has_hobo_method?(meth = "#{attribute}_edit_permitted?") return with_acting_user(user) { send(meth) } end # No setter = no edit permission return false if !respond_to?("#{attribute}=") refl = self.class.reflections[attribute.to_s] if refl && refl.macro != :belongs_to # a belongs_to is handled the same as a regular attribute return association_editable_by?(user, refl) end end with_acting_user(user) { edit_permitted?(attribute) } end
# File lib/hobo/model/permissions.rb, line 214 def method_callable_by?(user, method) permission_method = "#{method}_permitted?" respond_to?(permission_method) && with_acting_user(user) { send(permission_method) } end
# File lib/hobo/model/permissions.rb, line 307 def none_changed?(*attributes) attributes = attributes.map do |attr| with_attribute_or_belongs_to_keys(attr) { |a, ftype| ftype ? [a, ftype] : a } end.flatten attributes.all? { |attr| !changed.include?(attr) } end
— Permission Declaration Helpers — #
# File lib/hobo/model/permissions.rb, line 299 def only_changed?(*attributes) attributes = attributes.map do |attr| with_attribute_or_belongs_to_keys(attr) { |a, ftype| ftype ? [a, ftype] : a } end.flatten changed.all? { |attr| attributes.include?(attr) } end
— Hook ActiveRecord CRUD actions — #
# File lib/hobo/model/permissions.rb, line 130 def permission_check_required? # Lifecycle steps are exempt from permission checks acting_user && !(self.class.has_lifecycle? && lifecycle.active_step) end
Add some singleton methods to record
to give the effect that
attribute
is unknown. That is, attempts to access the
attribute will result in a Hobo::UndefinedAccessError
# File lib/hobo/model/permissions.rb, line 382 def unknownify_attribute(attr) metaclass.class_eval do define_method attr do raise Hobo::UndefinedAccessError end end if (refl = self.class.reflections[attr.to_s]) && refl.macro == :belongs_to # A belongs_to -- also unknownify the underlying fields unknownify_attribute refl.foreign_key unknownify_attribute refl.options[:foreign_type] if refl.options[:polymorphic] else # A regular field -- hack the dirty tracking methods metaclass.class_eval do define_method "#{attr}_change" do raise Hobo::UndefinedAccessError end define_method "#{attr}_was" do read_attribute attr end define_method "#{attr}_changed?" do true end def changed? true end define_method :changed do changed_attributes.keys | [attr.to_s] end def changes raise Hobo::UndefinedAccessError end end end end
# File lib/hobo/model/permissions.rb, line 206 def updatable_by?(user) with_acting_user(user) { update_permitted? } end
# File lib/hobo/model/permissions.rb, line 361 def update_permitted?; false end
# File lib/hobo/model/permissions.rb, line 180 def user_destroy(user) with_acting_user(user) { destroy } end
# File lib/hobo/model/permissions.rb, line 172 def user_save(user, validate = true) with_acting_user(user) { save(:validate => validate) } end
# File lib/hobo/model/permissions.rb, line 176 def user_save!(user) with_acting_user(user) { save! } end
# File lib/hobo/model/permissions.rb, line 188 def user_update_attributes(user, attributes) with_acting_user(user) do self.attributes = attributes save end end
# File lib/hobo/model/permissions.rb, line 195 def user_update_attributes!(user, attributes) with_acting_user(user) do self.attributes = attributes save! end end
# File lib/hobo/model/permissions.rb, line 184 def user_view(user, attribute=nil) raise PermissionDeniedError unless viewable_by?(user, attribute) end
Allow viewing by default
# File lib/hobo/model/permissions.rb, line 365 def view_permitted?(attribute) true end
# File lib/hobo/model/permissions.rb, line 219 def viewable_by?(user, attribute=nil) if attribute attribute = attribute.to_s.sub(/\?$/, '').to_sym return false if attribute && self.class.never_show?(attribute) end with_acting_user(user) { view_permitted?(attribute) } end
— Permissions API — #
# File lib/hobo/model/permissions.rb, line 163 def with_acting_user(user) return yield if user == acting_user old = acting_user self.acting_user = user result = yield self.acting_user = old result end
# File lib/hobo/model/permissions.rb, line 340 def with_attribute_or_belongs_to_keys(attribute) if (refl = self.class.reflections[attribute.to_s]) && refl.macro == :belongs_to if refl.options[:polymorphic] yield refl.foreign_key, refl.options[:foreign_type] else yield refl.foreign_key, nil end else yield attribute.to_s, nil end end
# File lib/hobo/model/permissions.rb, line 25 def self.find_aliased_name(klass, method_name) # The method +method_name+ will have been aliased. We jump through some hoops to figure out # what it's new name is method_name = method_name.to_sym method = klass.instance_method method_name methods = (klass.private_instance_methods + klass.instance_methods).*.to_sym new_name = methods.select {|m| klass.instance_method(m) == method }.find { |m| m != method_name } end
# File lib/hobo/model/permissions.rb, line 6 def self.included(klass) klass.class_eval do extend ClassMethods alias_method_chain :_create_record, :hobo_permission_check alias_method_chain :_update_record, :hobo_permission_check alias_method_chain :destroy, :hobo_permission_check class << self alias_method_chain :has_many, :hobo_permission_check alias_method_chain :has_one, :hobo_permission_check alias_method_chain :belongs_to, :hobo_permission_check end attr_accessor :acting_user, :origin, :origin_attribute bool_attr_accessor :exempt_from_edit_checks end end