Module: Zena::Acts::SecureNode::InstanceMethods

Includes:
RubyLess
Defined in:
lib/zena/acts/secure_node.rb

Instance Method Summary collapse

Instance Method Details

#can_drive?(vis = visitor, ugps = visitor.group_ids) ⇒ Boolean

Can alter node (move around, name, rwp groups, etc).

  • super user

  • members of drive_group if member status is at least ‘user’

Returns:

  • (Boolean)


103
104
105
# File 'lib/zena/acts/secure_node.rb', line 103

def can_drive?(vis=visitor, ugps=visitor.group_ids)
  ( vis.user? && (ugps.include?(dgroup_id) || draft?) )
end

#can_drive_was_true?(vis = visitor, ugps = visitor.group_ids) ⇒ Boolean

‘can_drive?’ before attribute change

Returns:

  • (Boolean)


108
109
110
# File 'lib/zena/acts/secure_node.rb', line 108

def can_drive_was_true?(vis=visitor, ugps=visitor.group_ids)
  ( vis.user? && (ugps.include?(dgroup_id_was) || draft_was_true?) )
end

#can_read?(vis = visitor, ugps = visitor.group_ids) ⇒ Boolean

people who can read:

  • super user

  • members of read_group if the node is published and the current date is greater or equal to the publication date

  • members of write_group

Returns:

  • (Boolean)


66
67
68
69
# File 'lib/zena/acts/secure_node.rb', line 66

def can_read?(vis = visitor, ugps=visitor.group_ids)
  ( ugps.include?(rgroup_id) && publish_from && Time.now >= publish_from ) ||
  ( ugps.include?(wgroup_id) )
end

#can_see_redactions?(ugps = visitor.group_ids) ⇒ Boolean

Returns true if the current visitor can see redactions (unpublished versions) of the node.

Returns:

  • (Boolean)


80
81
82
# File 'lib/zena/acts/secure_node.rb', line 80

def can_see_redactions?(ugps = visitor.group_ids)
  ugps.include?(wgroup_id)
end

#can_write?(vis = visitor, ugps = visitor.group_ids) ⇒ Boolean

people who can write:

  • super user

  • members of write_group if there status is at least ‘user’.

Returns:

  • (Boolean)


74
75
76
# File 'lib/zena/acts/secure_node.rb', line 74

def can_write?(vis=visitor, ugps=visitor.group_ids)
  ( ugps.include?(wgroup_id) && visitor.user?)  # write group
end

#draft?Boolean

The node has just been created so the creator can still delete it or move it around.

Returns:

  • (Boolean)


86
87
88
89
90
# File 'lib/zena/acts/secure_node.rb', line 86

def draft?
  !publish_from && visitor.id == user_id &&
  visitor.user? && visitor.id == version.user_id &&
  versions.count == 1
end

#draft_was_true?Boolean

The node has just been created so the creator can still delete it or move it around.

Returns:

  • (Boolean)


94
95
96
97
98
# File 'lib/zena/acts/secure_node.rb', line 94

def draft_was_true?
  !publish_from_was && visitor.id == user_id_was &&
  visitor.user? && visitor.id == version.user_id_was &&
  versions.count == 1
end

#empty?Boolean

Return true if the node is not a reference for any other nodes

Returns:

  • (Boolean)


57
58
59
60
# File 'lib/zena/acts/secure_node.rb', line 57

def empty?
  return true if new_record?
  0 == self.class.count_by_sql("SELECT COUNT(*) FROM #{self.class.table_name} WHERE #{ref_field} = #{self[:id]}")
end

#full_drive?(vis = visitor, ugps = visitor.group_ids) ⇒ Boolean

‘can_drive?’ without draft? exceptions

Returns:

  • (Boolean)


113
114
115
# File 'lib/zena/acts/secure_node.rb', line 113

def full_drive?(vis=visitor, ugps=visitor.group_ids)
  ( vis.user? && ugps.include?(dgroup_id) )
end

#full_drive_was_true?(vis = visitor, ugps = visitor.group_ids) ⇒ Boolean

‘full_drive?’ before attribute change

Returns:

  • (Boolean)


118
119
120
# File 'lib/zena/acts/secure_node.rb', line 118

def full_drive_was_true?(vis=visitor, ugps=visitor.group_ids)
  ( vis.user? && ugps.include?(dgroup_id_was) )
end

#in_circular_reference?Boolean

Make sure there is no circular reference (any way to do this faster ?)

Returns:

  • (Boolean)


296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/zena/acts/secure_node.rb', line 296

def in_circular_reference?
  loop_ids = [self[:id]]
  curr_ref = ref_field_id
  in_loop  = false
  while curr_ref != 0
    if loop_ids.include?(curr_ref) # detect loops
      in_loop = true
      break
    end
    loop_ids << curr_ref
    curr_ref = Zena::Db.fetch_attribute("SELECT #{ref_field} FROM #{self.class.table_name} WHERE id=#{curr_ref}").to_i
  end

  errors.add(ref_field, 'circular reference') if in_loop
  in_loop
end

#public?Boolean

Return true if the node can be viewed by all (public)

Returns:

  • (Boolean)


46
47
48
49
# File 'lib/zena/acts/secure_node.rb', line 46

def public?
  anon = visitor.site.anon
  can_read?(anon, anon.group_ids) # visible by anonymous
end

#record_must_be_securedObject



35
36
37
# File 'lib/zena/acts/secure_node.rb', line 35

def record_must_be_secured
  errors.add(:base, 'record not secured') unless secured?
end

#refObject

Reference to validate access rights



328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/zena/acts/secure_node.rb', line 328

def ref
  # new record and self as reference (creating root node)
  return self if ref_field == :id && new_record?
  if !@ref || (@ref.id != ref_field_id)
    # no ref or ref changed
    @ref = secure(ref_class) { ref_class.find_by_id(ref_field_id) }
  end
  if @ref && (self.new_record? || (:id == ref_field) || (self[:id] != @ref[:id] ))
    # reference is accepted only if it is not the same as self or self is root (ref_field==:id set by Node)
    @ref.freeze
  else
    nil
  end
end

#ref_field_valid?Boolean

Verify validity of the reference field.

Returns:

  • (Boolean)


268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/zena/acts/secure_node.rb', line 268

def ref_field_valid?
  return true unless ref_field_id_changed?
  # reference changed
  if published_in_heirs_was_true?
    # node or some children node was published, moves must be made with drive rights in both
    # source and destination
    if ref_field_id == self.id ||
       secure_drive(ref_class) {
         ref_class.count(:conditions => ['id IN (?)', [ref_field_id, ref_field_id_was]]) != 2
       }
      errors.add(ref_field, "invalid reference")
      return false
    end
  else
    # node was not visible to others, we need write access to both source and destination
    if ref_field_id == self.id ||
        secure_write(ref_class) {
          ref_class.count(:conditions => ['id IN (?)', [ref_field_id, ref_field_id_was]]) != 2
        }
      errors.add(ref_field, "invalid reference")
      return false
    end
  end
  in_circular_reference? ? false : true
end

#secure_before_saveObject

Prepare after save callbacks



262
263
264
265
# File 'lib/zena/acts/secure_node.rb', line 262

def secure_before_save
  @needs_inheritance_spread = !new_record? && (rgroup_id_changed? || wgroup_id_changed? || dgroup_id_changed? || skin_id_changed?)
  true
end

#secure_before_validationObject

Secure before validation on create (we use the ‘before_validation’ hook instead of ‘before_validation_on_create’ to trigger secure_before_validation before multiversion’s before_validation).



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/zena/acts/secure_node.rb', line 124

def secure_before_validation
  return true unless new_record?
  
  # Secure before validation on create.
  # set defaults before validation
  self[:site_id]  = visitor.site.id
  self[:user_id]  = visitor.id
  self[:ref_lang] = visitor.lang

  [:rgroup_id, :wgroup_id, :dgroup_id, :skin_id].each do |sym|
    # not defined => inherit
    self[sym] ||= ref[sym]
  end

  if inherit.nil?
    if rgroup_id == ref.rgroup_id && wgroup_id == ref.wgroup_id && dgroup_id == ref.dgroup_id
      self[:inherit] = 1
    else
      self[:inherit] = 0
    end
  end
  true
end

#secure_before_validation_on_updateObject



148
149
150
# File 'lib/zena/acts/secure_node.rb', line 148

def secure_before_validation_on_update
  true
end

#secure_on_createObject

  1. validate the presence of a valid project (one in which the visitor has write access and project<>self !)

  2. validate the presence of a valid reference (project or parent) (in which the visitor has write access and ref<>self !)

  3. validate drive_group value (same as parent or ref.can_drive? and valid)

  4. validate rw groups :

    a. if can_drive? : valid groups
    b. else inherit or private
    
  5. validate the rest



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
# File 'lib/zena/acts/secure_node.rb', line 168

def secure_on_create
  case inherit
  when 1
    # force inheritance
    self[:rgroup_id] = ref.rgroup_id
    self[:wgroup_id] = ref.wgroup_id
    self[:dgroup_id] = ref.dgroup_id
    self[:skin_id  ] = ref.skin_id
  when 0
    # custom access rights
    if ref.full_drive?
      errors.add('rgroup_id', "unknown group") unless visitor.group_ids.include?(rgroup_id)
      errors.add('wgroup_id', "unknown group") unless visitor.group_ids.include?(wgroup_id)
      errors.add('dgroup_id', "unknown group") unless visitor.group_ids.include?(dgroup_id)
    else
      errors.add('inherit', "custom access rights not allowed")
      errors.add('rgroup_id', "you cannot change this") unless rgroup_id == ref.rgroup_id
      errors.add('wgroup_id', "you cannot change this") unless wgroup_id == ref.wgroup_id
      errors.add('dgroup_id', "you cannot change this") unless dgroup_id == ref.dgroup_id
      errors.add('skin_id' , "you cannot change this") unless skin_id  == ref.skin_id
    end
  else
    errors.add(:inherit, "bad inheritance mode")
  end

end

#secure_on_destroyObject



313
314
315
316
317
318
319
320
321
322
323
324
325
# File 'lib/zena/acts/secure_node.rb', line 313

def secure_on_destroy
  if new_record? || can_drive_was_true?
    unless empty?
      errors.add(:base, 'cannot be removed (contains subpages or data)')
      false
    else
      true
    end
  else
    errors.add(:base, 'You do not have the rights to destroy.')
    false
  end
end

#secure_on_updateObject

  1. if dgroup changed from old, make sure user could do this and new group is valid

  2. if owner changed from old, make sure only a user in ‘admin’ can do this

  3. error if user cannot publish nor manage

  4. parent/project changed ? verify ‘publish access to new and old’

  5. validate rw groups :

    a. can change to 'inherit' if can_drive? or can_drive? and max_status < pub and does not have children
    b. can change to 'private' if can_drive?
    c. can change to 'custom' if can_drive?
    
  6. validate the rest



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
# File 'lib/zena/acts/secure_node.rb', line 204

def secure_on_update
  if !changed_without_properties?
    # Node hasn't been changed (only version edits)
    return true
  end
  
  if !can_drive_was_true?
    errors.add(:base, 'You do not have the rights to do this.') unless errors[:base]
    return false
  end

  if user_id_changed?
    if visitor.is_admin?
      # only admin can change owners
      unless secure(User) { User.find_by_id(user_id) }
        errors.add(:user_id, 'unknown user')
      end
    else
      errors.add(:user_id, 'Only admins can change owners')
    end
  end

  return false unless ref_field_valid?

  # verify groups
  if inherit_changed? && !full_drive_was_true?
    errors.add(:inherit, 'cannot be changed')
  else
    case inherit
    when 1
      # inherit rights
      [:rgroup_id, :wgroup_id, :dgroup_id, :skin_id].each do |sym|
        if self.send("#{sym}_changed?") && self[sym] != ref[sym]
          # manual change of value not allowed without changing inherit mode
          if !full_drive_was_true?
            errors.add(sym.to_s, 'cannot be changed')
          else
            errors.add(sym.to_s, 'cannot be changed without changing inherit mode')
          end
        else
          # in case parent changed, keep in sync
          self[sym] = ref[sym]
        end
      end
    when 0
      # custom rights
      [:rgroup_id, :wgroup_id, :dgroup_id].each do |sym|
        if self.send("#{sym}_changed?") && !visitor.group_ids.include?(self[sym])
          errors.add(sym.to_s, 'unknown group')
        end
      end
    else
      errors.add('inherit', 'bad inheritance mode')
    end
  end
end

#secure_reference_before_validationObject

Make sure the reference object (the one from which this object inherits) exists before validating.



153
154
155
156
157
158
159
# File 'lib/zena/acts/secure_node.rb', line 153

def secure_reference_before_validation
  if ref == nil
    errors.add(ref_field, "invalid reference")
    return false
  end
  true
end

#secured?Boolean

Return true if the node is properly secured (was loaded with secure)

Returns:

  • (Boolean)


52
53
54
# File 'lib/zena/acts/secure_node.rb', line 52

def secured?
  @visitor && @visitor == Thread.current[:visitor]
end

#visitor=(visitor) ⇒ Object

Store visitor to produce scope when needed and to retrieve correct editions.



40
41
42
43
# File 'lib/zena/acts/secure_node.rb', line 40

def visitor=(visitor)
  @visitor = visitor
  self
end