forked from theforeman/foreman
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtaxonomy.rb
More file actions
260 lines (223 loc) · 9.64 KB
/
Copy pathtaxonomy.rb
File metadata and controls
260 lines (223 loc) · 9.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
class Taxonomy < ApplicationRecord
validates_lengths_from_database
include Authorizable
include NestedAncestryCommon
include TopbarCacheExpiry
serialize :ignore_types, Array
before_create :assign_default_templates
after_create :assign_taxonomy_to_user
before_validation :sanitize_ignored_types
has_many :taxable_taxonomies, :dependent => :destroy
has_many :users, :through => :taxable_taxonomies, :source => :taxable, :source_type => 'User'
has_many :smart_proxies, :through => :taxable_taxonomies, :source => :taxable, :source_type => 'SmartProxy'
has_many :compute_resources, :through => :taxable_taxonomies, :source => :taxable, :source_type => 'ComputeResource'
has_many :media, :through => :taxable_taxonomies, :source => :taxable, :source_type => 'Medium'
has_many :provisioning_templates, -> { where(:type => 'ProvisioningTemplate') }, :through => :taxable_taxonomies, :source => :taxable, :source_type => 'ProvisioningTemplate'
has_many :ptables, -> { where(:type => 'Ptable') }, :through => :taxable_taxonomies, :source => :taxable, :source_type => 'Ptable'
has_many :report_templates, -> { where(:type => 'ReportTemplate') }, :through => :taxable_taxonomies, :source => :taxable, :source_type => 'ReportTemplate'
has_many :domains, :through => :taxable_taxonomies, :source => :taxable, :source_type => 'Domain'
has_many :http_proxies, :through => :taxable_taxonomies, :source => :taxable, :source_type => 'HttpProxy'
has_many :realms, :through => :taxable_taxonomies, :source => :taxable, :source_type => 'Realm'
has_many :hostgroups, :through => :taxable_taxonomies, :source => :taxable, :source_type => 'Hostgroup'
has_many :subnets, :through => :taxable_taxonomies, :source => :taxable, :source_type => 'Subnet'
has_many :auth_sources, :through => :taxable_taxonomies, :source => :taxable, :source_type => 'AuthSource'
validate :check_for_orphans, :unless => proc { |t| t.new_record? }
# the condition for parent_id != 0 is required because of our tests, should validate macros fill in attribute with values and it set 0 to this one
# which would lead to an error when we ask for parent object
validate :parent_id_does_not_escalate, :if => proc { |t| t.ancestry_changed? && t.parent_id != 0 && t.parent.present? }
validates :name, :presence => true, :uniqueness => {:scope => [:ancestry, :type], :case_sensitive => false}
def self.inherited(child)
child.instance_eval do
scoped_search :on => :description, :complete_enabled => :false, :only_explicit => true
scoped_search :on => :id, :validator => ScopedSearch::Validators::INTEGER
apipie :class do
sections only: %w[all additional]
name_exl, title_exl = class_scope.model_name.human == 'Location' ? ['Europe', 'Europe/Prague'] : ['Red Hat', 'Red Hat/Engineering']
prop_group :basic_model_props, ApplicationRecord, meta: { example: name_exl }
property :title, String, desc: "Title of the #{class_scope}. Comparing to the Name, Title contains also names of all parent #{class_scope}s, e.g. #{title_exl}"
property :description, String, desc: "Description of the #{class_scope}"
property :created_at, String, desc: "The time when the #{class_scope} was created"
property :updated_at, String, desc: "The last time when the #{class_scope} was updated"
end
jail_class = Class.new(::Safemode::Jail) do
allow :id, :name, :title, :created_at, :updated_at, :description
end
child.const_set('Jail', jail_class)
end
child.send(:include, NestedAncestryCommon::Search)
super
end
delegate :import_missing_ids, :inherited_ids, :used_and_selected_or_inherited_ids, :selected_or_inherited_ids,
:non_inherited_ids, :used_or_inherited_ids, :used_ids, :to => :tax_host
default_scope -> { order(:title) }
scope :completer_scope, lambda { |opts|
case opts[:controller]
when 'organizations'
Organization.completer_scope opts
when 'locations'
Location.completer_scope opts
end
}
def self.no_taxonomy_scope
as_taxonomy nil, nil do
yield if block_given?
end
end
def self.as_taxonomy(org, location)
Organization.as_org org do
Location.as_location location do
yield if block_given?
end
end
end
def self.types
[Organization, Location]
end
def self.ignore?(taxable_type)
current_taxonomies = if current.nil? && User.current.present?
# "Any context" - all available taxonomies"
User.current.public_send("my_#{to_s.underscore.pluralize}")
else
[current]
end
current_taxonomies.compact.any? do |current|
current.ignore?(taxable_type)
end
end
# if taxonomy e.g. organization was not set by current context (e.g. Any organization)
# then we have to compute what this context mean for current user (what organizations
# they are assigned to)
#
# if user is not assigned to any organization then empty relation is returned.
#
# if user is admin we we return the original value (even if nil).
def self.expand(value)
if value.blank? && User.current.present? && !User.current.admin?
value = send("my_#{to_s.underscore.pluralize}")
end
value
end
def ignore?(taxable_type)
ignore_types.include?(taxable_type.classify)
end
def self.all_import_missing_ids
all.find_each do |taxonomy|
taxonomy.import_missing_ids
end
end
def self.all_mismatcheds
includes(:hosts).map { |taxonomy| taxonomy.mismatches }
end
def dup
new = super
new.name = ""
new.users = users
new.smart_proxies = smart_proxies
new.subnets = subnets
new.compute_resources = compute_resources
new.provisioning_templates = provisioning_templates
new.ptables = ptables
new.report_templates = report_templates
new.media = media
new.domains = domains
new.realms = realms
new.hostgroups = hostgroups
new.auth_sources = auth_sources
new
end
# overwrite *_ids since need to check if ignored? - don't overwrite location_ids and organization_ids since these aren't ignored
(TaxHost::HASH_KEYS - [:location_ids, :organization_ids]).each do |key|
# def domain_ids
# if ignore?("Domain")
# Domain.pluck(:id)
# else
# super() # self.domain_ids
# end
define_method(key) do
klass = hash_key_to_class(key)
if ignore?(klass)
return User.unscoped.except_admin.except_hidden.map(&:id) if klass == "User"
return klass.constantize.pluck(:id)
else
super()
end
end
end
def expire_topbar_cache
(users + User.only_admin).each { |u| u.expire_topbar_cache }
end
def parent_params(include_source = false)
hash = {}
elements = parents_with_params
elements.each do |el|
el.send("#{type.downcase}_parameters".to_sym).authorized(:view_params).each do |p|
hash[p.name] = include_source ? p.hash_for_include_source(sti_name, el.title) : p.value
end
end
hash
end
# returns self and parent parameters as a hash
def parameters(include_source = false)
hash = parent_params(include_source)
send("#{type.downcase}_parameters".to_sym).authorized(:view_params).each do |p|
hash[p.name] = include_source ? p.hash_for_include_source(sti_name, el.title) : p.value
end
hash
end
def parents_with_params
self.class.sort_by_ancestry(self.class.includes("#{type.downcase}_parameters".to_sym).find(ancestor_ids))
end
def taxonomy_inherited_params_objects
# need to pull out the locations to ensure they are sorted first,
# otherwise we might be overwriting the hash in the wrong order.
parents = parents_with_params
parents_parameters = []
parents.each do |parent|
parents_parameters << parent.send("#{parent.type.downcase}_parameters".to_sym)
end
parents_parameters
end
def params_objects
(send("#{type.downcase}_parameters".to_sym).authorized(:view_params) + taxonomy_inherited_params_objects.to_a.reverse!).uniq { |param| param.name }
end
def notification_recipients_ids
subtree.flat_map(&:users).map(&:id).uniq
end
# NOTE: this method used by before_destroy callbacks in extension files from plugins
# audits for 'destroy' action on resources lead to taxable_taxonomies records.
# This will check if any taxable_taxonomies records present and apply destroy_all
# so that it nullifies all associated audit records
def destroy_taxable_taxonomies
TaxableTaxonomy.where(taxonomy_id: id).destroy_all
end
private
delegate :need_to_be_selected_ids, :selected_ids, :used_and_selected_ids, :mismatches, :missing_ids, :check_for_orphans,
:to => :tax_host
def assign_default_templates
Template.where(:default => true).group_by { |t| t.class.to_s.underscore.pluralize }.each do |association, templates|
send("#{association}=", send(association) + templates.select(&:valid?))
end
end
def sanitize_ignored_types
self.ignore_types ||= []
self.ignore_types = self.ignore_types.compact.uniq - ["0"]
end
def tax_host
@tax_host ||= TaxHost.new(self)
end
def hash_key_to_class(key)
key.to_s.gsub(/_ids?\Z/, '').classify
end
def assign_taxonomy_to_user
return if User.current.nil? || User.current.admin
User.current.public_send(self.class.to_s.downcase.pluralize) << self
User.current.reload
User.current.invalidate_cache
end
def parent_id_does_not_escalate
unless User.current.can?("edit_#{self.class.to_s.underscore.pluralize}", parent)
errors.add :parent_id, _("Missing a permission to edit parent %s") % self.class.to_s
false
end
end
end