diff --git a/src/storage.cr b/src/storage.cr index 4885e38..fee984b 100644 --- a/src/storage.cr +++ b/src/storage.cr @@ -164,9 +164,11 @@ class DNSManager::Storage domain = @domains_by_name.get domain_name if domain.share_key.nil? - domain.share_key = UUID.random.to_s - @domains_by_name.update_or_create domain_name, domain - Response::DomainChanged.new domain + # Clone the domain so the database doesn't try to remove the new `shared_key`. + domain_cloned = domain.clone + domain_cloned.share_key = UUID.random.to_s + @domains_by_name.update domain_name, domain_cloned + Response::DomainChanged.new domain_cloned else Response::Error.new "The domain already have a share key." end @@ -177,9 +179,11 @@ class DNSManager::Storage domain = @domains_by_name.get domain_name if domain.transfer_key.nil? - domain.transfer_key = UUID.random.to_s - @domains_by_name.update_or_create domain_name, domain - Response::DomainChanged.new domain + # Clone the domain so the database doesn't try to remove the new `transfer_key`. + domain_cloned = domain.clone + domain_cloned.transfer_key = UUID.random.to_s + @domains_by_name.update domain_name, domain_cloned + Response::DomainChanged.new domain_cloned else Response::Error.new "The domain already have a transfer key." end @@ -192,9 +196,11 @@ class DNSManager::Storage domain = @domains_by_name.get domain_name if domain.owners.size == 1 && domain.owners[0] == user_id - domain.share_key = nil - @domains_by_name.update domain - Response::DomainChanged.new domain + # Clone the domain so the old `shared_key` still exists in the old version. + domain_cloned = domain.clone + domain_cloned.share_key = nil + @domains_by_name.update domain_cloned + Response::DomainChanged.new domain_cloned else Response::Error.new "You are not the only owner." end @@ -205,17 +211,20 @@ class DNSManager::Storage if domain.owners.includes? user_id return Response::Error.new "You already own this domain." end - domain.owners << user_id - @domains_by_name.update domain - Response::DomainChanged.new domain + domain_cloned = domain.clone + domain_cloned.owners << user_id + @domains_by_name.update domain_cloned + Response::DomainChanged.new domain_cloned elsif domain = @domains_by_transfer_key.get? uuid if domain.owners.includes? user_id return Response::Error.new "You already own this domain." end - domain.transfer_key = nil - domain.owners = [user_id] - @domains_by_name.update domain - Response::DomainChanged.new domain + # Clone the domain so the old `transfer_key` still exists in the old version. + domain_cloned = domain.clone + domain_cloned.transfer_key = nil + domain_cloned.owners = [user_id] + @domains_by_name.update domain_cloned + Response::DomainChanged.new domain_cloned else Response::Error.new "There is no key with this UUID." end @@ -274,6 +283,8 @@ class DNSManager::Storage zone.rr_not_readonly! rr.rrid + zone_clone = zone.clone + # Test RR validity. rr.get_errors.tap do |errors| unless errors.empty? @@ -282,9 +293,9 @@ class DNSManager::Storage end end - zone.update_rr rr + zone_clone.update_rr rr - update_zone zone + update_zone zone_clone Response::RRUpdated.new domain, rr end @@ -300,8 +311,10 @@ class DNSManager::Storage tokens_by_uuid.delete token_uuid end - zone.delete_rr rrid - update_zone zone + zone_clone = zone.clone + + zone_clone.delete_rr rrid + update_zone zone_clone Response::RRDeleted.new rrid end @@ -313,8 +326,9 @@ class DNSManager::Storage domain = @domains_by_name.get domain_name # The user isn't the only owner, just remove him from the list of owners. if domain.owners.size > 1 - domain.owners.select! { |o| o != user_id } - @domains_by_name.update_or_create domain_name, domain + domain_cloned = domain.clone + domain_cloned.owners.select! { |o| o != user_id } + @domains_by_name.update domain_cloned # Not really a domain deletion, but that'll do the trick. return Response::DomainDeleted.new domain_name @@ -363,15 +377,16 @@ class DNSManager::Storage def wipe_user_data(user_id : UserDataID) domains_by_owners.get(user_id.to_s).each do |domain| - domain.owners.delete user_id + domain_cloned = domain.clone + domain_cloned.owners.delete user_id # Remove the user's domain when he is the only owner. - if domain.owners.empty? - @domains_by_name.delete domain.name - @tokens_by_domain.delete domain.name - @zones_by_domain.delete domain.name + if domain_cloned.owners.empty? + @domains_by_name.delete domain_cloned.name + @tokens_by_domain.delete domain_cloned.name + @zones_by_domain.delete domain_cloned.name else - @domains_by_name.update_or_create domain.name, domain + @domains_by_name.update domain_cloned end end rescue e @@ -441,10 +456,11 @@ class DNSManager::Storage # 2. add token to the RR. rr.token = token.uuid - zone.update_rr rr + zone_clone = zone.clone + zone_clone.update_rr rr # 3. update the zone (no need to call `update_zone` to change the zone serial). - zones_by_domain.update_or_create zone.domain, zone + zones_by_domain.update zone_clone # 4. if there is an old token, remove it. if old_token_uuid = old_token @@ -476,7 +492,7 @@ class DNSManager::Storage return Response::Error.new "invalid ipv4" unless Zone.is_ipv4_address_valid? address rr.target = address zone.update_rr rr - zones_by_domain.update_or_create zone.domain, zone + update_zone zone Response::Success.new when Zone::AAAA return Response::Error.new "invalid ipv6" unless Zone.is_ipv6_address_valid? address