module Models::Authorized class << self attr_writer :custom_permissions def custom_permissions; @custom_permissions ||= {} end def permissions @default_permissions ||= YAML.load_file("#{__FILE__.dirname}/default_permissions.yml").freeze @default_permissions.merge(rad.config.permissions).merge(custom_permissions) end end rad.extension :model_authorization, self do define_method(:roles){@roles ||= []} attr_writer :roles # field :roles, type: Array, protected: true, default: [] alias_method :mm_roles, :roles alias_method :mm_roles=, :roles= attr_accessor :admin # field :admin, type: Boolean, protected: true, default: false end inherited do validate :validate_anonymous validates_exclusion_of :name, in: Role::PRESERVED_USER_NAMES, if: lambda{|u| u.new_record?} end module ClassMethods def anonymous Models::User.by_name('anonymous') || raise("You probably don't create Anonymous User!") end end # # Owner # def owner_name; anonymous? ? nil : name end def owner? object !object.blank? and !name.blank? and !anonymous? and object.respond_to(:owner_name) == self.name end # # Roles # def self.anonymous? name; name == 'anonymous' end def anonymous?; Models::Authorized.anonymous?(name) end def registered? !anonymous? end def add_role role role = role.to_s unless roles.include? role if role == 'admin' self.admin = true else self.mm_roles -= Role.denormalize_to_lower_roles [role] self.mm_roles += [role] end _cache.clear end roles end def remove_role role role = role.to_s if roles.include? role if role == 'admin' self.admin = false else self.mm_roles -= Role.denormalize_to_higher_roles [role] end _cache.clear end roles end def handy_roles unless roles = _cache[:roles] roles = if self.mm_roles.empty? ['user'] else Role.denormalize_to_lower_roles self.mm_roles end if anonymous? roles << 'anonymous' else roles << 'registered' end roles << "user:#{name}" unless name.blank? if admin roles << 'admin' %w(manager member).each{|r| roles << r unless roles.include? r} end roles.must_be == roles.uniq roles = HandyRoles.new roles.sort _cache[:roles] = roles end roles end alias_method :roles, :handy_roles def major_roles _cache[:major_roles] ||= Role.major_roles roles end def has_role? role roles.include? role end # # can? # def can? operation, object = nil operation = operation.to_s return true if has_role?(:admin) custom_method = "able_#{operation}?" return object.send custom_method, self if object.respond_to? custom_method ( effective_permissions[operation] or (owner?(object) and effective_permissions_as_owner[operation]) ) end def can_view? object can? :view, object end # # Effective Permissions # def effective_permissions unless ep = _cache[:effective_permissions] ep = calculate_effective_roles_for roles _cache[:effective_permissions] = ep end ep end def effective_permissions_as_owner unless epo = _cache[:effective_permissions_as_owner] epo = calculate_effective_roles_for ['owner'] _cache[:effective_permissions_as_owner] = epo end epo end protected def calculate_effective_roles_for roles effective_permissions = {} permissions = ::Models::Authorized.permissions permissions.each do |operation, allowed_roles| operation = operation.to_s effective_permissions[operation.to_s] = roles.any?{|role| allowed_roles.include? role} end effective_permissions end def validate_anonymous errors.add :base, "Anonymous can't have any roles!" if anonymous? and !self.mm_roles.blank? end class HandyRoles < Array def include? role super role.to_s end alias_method :has?, :include? protected def method_missing m, *args, &block m = m.to_s super unless m.last == '?' self.include? m[0..-2] end end end