1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: AGPL-3.0
6 def self.included(base)
7 base.before_validation :set_validation_timestamp
8 base.before_validation :ensure_trash_at_not_in_past
9 base.before_validation :sync_trash_state
10 base.before_validation :default_trash_interval
11 base.validate :validate_trash_and_delete_timing
14 # Use a single timestamp for all validations, even though each
15 # validation runs at a different time.
16 def set_validation_timestamp
17 @validation_timestamp = db_current_time
20 # If trash_at is being changed to a time in the past, change it to
21 # now. This allows clients to say "expires {client-current-time}"
22 # without failing due to clock skew, while avoiding odd log entries
23 # like "expiry date changed to {1 year ago}".
24 def ensure_trash_at_not_in_past
25 if trash_at_changed? && trash_at
26 self.trash_at = [@validation_timestamp, trash_at].max
30 # Caller can move into/out of trash by setting/clearing is_trashed
31 # -- however, if the caller also changes trash_at, then any changes
32 # to is_trashed are ignored.
34 if is_trashed_changed? && !trash_at_changed?
36 self.trash_at = @validation_timestamp
42 self.is_trashed = trash_at && trash_at <= @validation_timestamp || false
46 def default_trash_interval
47 if trash_at_changed? && !delete_at_changed?
48 # If trash_at is updated without touching delete_at,
49 # automatically update delete_at to a sensible value.
53 self.delete_at = trash_at + Rails.configuration.default_trash_lifetime.seconds
55 elsif !trash_at || !delete_at || trash_at > delete_at
56 # Not trash, or bogus arguments? Just validate in
57 # validate_trash_and_delete_timing.
58 elsif delete_at_changed? && delete_at >= trash_at
59 # Fix delete_at if needed, so it's not earlier than the expiry
60 # time on any permission tokens that might have been given out.
62 # In any case there are no signatures expiring after now+TTL.
63 # Also, if the existing trash_at time has already passed, we
64 # know we haven't given out any signatures since then.
66 @validation_timestamp,
68 ].compact.min + Rails.configuration.blob_signature_ttl.seconds
70 # The previous value of delete_at is also an upper bound on the
71 # longest-lived permission token. For example, if TTL=14,
72 # trash_at_was=now-7, delete_at_was=now+7, then it is safe to
73 # set trash_at=now+6, delete_at=now+8.
74 earliest_delete = [earliest_delete, delete_at_was].compact.min
76 # If delete_at is too soon, use the earliest possible time.
77 if delete_at < earliest_delete
78 self.delete_at = earliest_delete
83 def validate_trash_and_delete_timing
84 if trash_at.nil? != delete_at.nil?
85 errors.add :delete_at, "must be set if trash_at is set, and must be nil otherwise"
86 elsif delete_at && delete_at < trash_at
87 errors.add :delete_at, "must not be earlier than trash_at"
93 module TrashableController
95 if !@object.is_trashed
96 @object.update_attributes!(trash_at: db_current_time)
98 earliest_delete = (@object.trash_at +
99 Rails.configuration.blob_signature_ttl.seconds)
100 if @object.delete_at > earliest_delete
101 @object.update_attributes!(delete_at: earliest_delete)
107 if !@object.is_trashed
108 @object.update_attributes!(trash_at: db_current_time)
114 if @object.is_trashed
115 @object.trash_at = nil
117 if params[:ensure_unique_name]
118 @object.save_with_unique_name!
123 raise InvalidStateTransitionError