app/models/ability.rb
require 'cancan/model_adapters/mongoff_adapter'
require 'setup/storage'
class Ability
include CanCan::Ability
attr_reader :user
attr_reader :deferred_abilities
def initialize(user)
@deferred_abilities = []
deferred_abilities <<
if (@user = user)
deferred_abilities <<
if user.super_admin?
SuperUser
else
can [:read, :update], User, id: user.id
can [:update, :delete], Setup::CrossSharedCollection, owner_id: user.id
can [:read, :update], Account, { '$or' => [
{ 'owner_id' => user.id },
{ '_id' => { '$in' => user.member_account_ids || [] } }
] }
can :delete, Account, :id.in => user.account_ids - [user.account_id]
can :delete, ::Cenit::OauthAccessToken
StandardUser
end
if user.roles.any? { |role| Cenit.file_stores_roles.include?(role.name) }
can :update, Setup::FileStoreConfig
end
creator_id =
if Account.current && Account.current.owner
Account.current.owner.id
else
user.id
end
UserCommon.prepare.const_get(:SHARED_ALLOWED).each do |keys, models|
can keys, models, { '$or' => [{ 'origin' => 'default' }, { 'creator_id' => creator_id }] }
end
UserCommon.prepare
else
Anonymous
end
end
CROSSING_MODELS_NO_ORIGIN = [Setup::Collection]
CROSSING_MODELS_WITH_ORIGIN =
[
Setup::RemoteOauthClient,
Setup::GenericAuthorizationClient,
Setup::Oauth2Scope,
Setup::Algorithm,
Setup::Resource,
Setup::Operation,
Setup::PlainWebhook,
Setup::Connection,
Setup::Flow,
Setup::Snippet,
Setup::ApiSpec
] +
Setup::Translator.class_hierarchy +
Setup::AuthorizationProvider.class_hierarchy +
Setup::DataType.class_hierarchy +
Setup::Validator.class_hierarchy
CROSSING_MODELS = CROSSING_MODELS_WITH_ORIGIN + CROSSING_MODELS_NO_ORIGIN
UNCONDITIONAL_ADMIN_CROSSING_MODELS = CROSSING_MODELS + [Setup::Scheduler]
ADMIN_CROSSING_MODELS = UNCONDITIONAL_ADMIN_CROSSING_MODELS + [Setup::CrossSharedCollection]
def can?(action, subject, *extra_args)
return true if action == :digest
if action == :json_edit
subject.is_a?(Mongoff::Record) && !subject.is_a?(Mongoff::GridFs::File)
elsif (action == :simple_cross && crossing_models.exclude?(subject.is_a?(Class) ? subject : subject.class)) ||
(subject == ScriptExecution && (user.nil? || !user.super_admin?))
false
else
super || deferred_abilities.any? { |ability| ability.can?(action, subject, *extra_args) }
end
end
def crossing_models
if user
if user.super_admin?
ADMIN_CROSSING_MODELS
else
CROSSING_MODELS
end
else
[]
end
end
def relevant_rules_for_query(action, subject)
[super, deferred_abilities.collect { |ability| ability.get_relevant_rules_for_query(action, subject) }].flatten
end
class UserCommon
extend CanCan::Ability
can :delete, Setup::Execution, :status.in => Setup::Task::FINISHED_STATUS
can :delete, Setup::Task,
:status.in => Setup::Task::NON_ACTIVE_STATUS,
:scheduler_id.in => Setup::Scheduler.where(activated: false).collect(&:id) + [nil]
can :read, Setup::FileStoreConfig
can :manage, Mongoff::Model
can :manage, Mongoff::Record
def self.prepare
return self if @init_done
@init_done = true
actions = Setup::Models.all.reduce(Set.new) do |set, model|
set.merge(model.allowed_actions).merge(model.denied_actions)
end.merge(Cenit::Access::DEFAULT) - [:all]
allowed_hash = Hash.new { |h, k| h[k] = Set.new }
Setup::Models.all.each do |model|
model_denied = model.denied_actions
actions.each do |action|
models = allowed_hash[action]
denied = model_denied.include?(:all) || model_denied.include?(action)
if relevant_rules_for_match(action, model).empty? && !denied && model.can?(action)
models << model
elsif denied
models.delete(model)
end
end
end
Setup::Models.all.each do |model|
model_allowed = model.allowed_actions
actions.each do |action|
models = allowed_hash[action]
models << model if model_allowed.include?(action)
end
end
Setup::Models.all.each do |model|
next unless model < Setup::ClassHierarchyAware
if model.abstract_class
allowed_hash[:create].delete(model)
allowed_hash[:update].delete(model)
end
end
allowed_hash.each do |key, models|
allowed_hash[key] = models.reject { |model| models.any? { |m| model < m } }
end
{
shared_denied_actions: shared_denied_hash = Hash.new { |h, k| h[k] = [] },
shared_allowed_actions: shared_allowed_hash = Hash.new { |h, k| h[k] = [] }
}.each do |collector_method, hash|
Setup::Models.all.each do |model|
next unless model < Setup::CrossOriginShared
model.send(collector_method) do |model_actions|
model_actions.each do |action|
next if model.denied_actions.include?(action)
models = allowed_hash[action]
root = model
stack = []
while root && models.exclude?(root)
stack << root
root = root.superclass
root = nil unless root.include?(Mongoid::Document)
end
if root
while root
models.delete(root)
if stack.empty?
hash[key] << root
else
models.concat(root.subclasses)
end
root = stack.pop
end
else
hash[key] << model
end
end
end
end
end
[
allowed_hash,
shared_denied_hash,
shared_allowed_hash
].each do |hash|
new_hash = {}
hash.each do |key, models|
a = (new_hash[models] ||= [])
a << key
end
hash.clear
new_hash.each do |models, keys|
if (models = models.to_a).present?
hash[keys] = models
end
end
end
[
allowed_hash,
shared_denied_hash,
shared_allowed_hash
].each do |hash|
hash.each do |keys, models|
keys.delete(:simple_cross)
if [:update, :delete].any? { |key| keys.include?(key) }
models.delete(Setup::CrossSharedCollection)
end
end
end
const_set(:ALLOWED, allowed_hash)
const_set(:SHARED_DENIED, shared_denied_hash)
const_set(:SHARED_ALLOWED, shared_allowed_hash)
allowed_hash.each do |keys, models|
can keys, models
end
shared_denied_hash.each do |keys, models|
can keys, models, { 'origin' => 'default' }
end
self
end
end
class StandardUser < self
extend CanCan::Ability
cannot :access, [
Setup::Configuration,
::Role,
::Script,
::ScriptExecution,
Cenit::ActiveTenant,
::RabbitConsumer,
Setup::DelayedMessage,
Setup::SystemReport
]
cannot :delete, Setup::Storage
can :read, Setup::CrossSharedCollection
# can :simple_cross, CROSSING_MODELS_NO_ORIGIN
# can :simple_cross, CROSSING_MODELS_WITH_ORIGIN, :origin.in => [:default, :owner]
can :create, Account unless Cenit.tenant_creation_disabled
end
class SuperUser < self
extend CanCan::Ability
can :manage,
[
Role,
User,
Account,
Cenit::BasicToken,
Script,
Setup::DelayedMessage,
Setup::SystemReport,
Setup::Operation,
Setup::Category,
TourTrack
]
can [:read, :create], Cenit::ActiveTenant
can [:read, :update], Setup::Configuration
can :delete, [Setup::Storage, Setup::CrossSharedCollection]
can :delete, Setup::CenitDataType, origin: :tmp
can :read, RabbitConsumer
can :read, :update, Setup::CrossSharedCollection
can :read, Cenit::ApplicationId
can(:delete, Cenit::ApplicationId) do |app_id|
app_id.app.nil?
end
can [:read, :update], Cenit::BuildInApp
# can :simple_cross, Setup::CrossSharedCollection, installed: true
# can :simple_cross, UNCONDITIONAL_ADMIN_CROSSING_MODELS
end
class Anonymous < self
extend CanCan::Ability
can :read, Setup::CrossSharedCollection
end
end