From 2d64da170e3210381cc135e5cd404a8024429987 Mon Sep 17 00:00:00 2001 From: Philippe PITTOLI Date: Mon, 1 Jul 2024 23:48:26 +0200 Subject: [PATCH] Better typing, (minor) documentation improvements, remove orphans. --- src/storage.cr | 92 +++++++++++++++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 35 deletions(-) diff --git a/src/storage.cr b/src/storage.cr index bd7ee34..5c3a620 100644 --- a/src/storage.cr +++ b/src/storage.cr @@ -164,7 +164,8 @@ class DNSManager::Storage Response::DomainAdded.new domain end - def ask_share_token(user_id : UserDataID, domain_name : String) + # Asks for a "share token". + def ask_share_token(user_id : UserDataID, domain_name : String) : IPC::JSON user_should_own! user_id, domain_name domain = @domains_by_name.get domain_name @@ -179,7 +180,8 @@ class DNSManager::Storage end end - def ask_transfer_token(user_id : UserDataID, domain_name : String) + # Asks for a "transfer token". + def ask_transfer_token(user_id : UserDataID, domain_name : String) : IPC::JSON user_should_own! user_id, domain_name domain = @domains_by_name.get domain_name @@ -196,7 +198,7 @@ class DNSManager::Storage # Check the domain owners. # In case there's only the requesting user, allow him to gain full control. - def ask_unshare_domain(user_id : UserDataID, domain_name : String) + def ask_unshare_domain(user_id : UserDataID, domain_name : String) : IPC::JSON user_should_own! user_id, domain_name domain = @domains_by_name.get domain_name @@ -211,7 +213,8 @@ class DNSManager::Storage end end - def gain_ownership(user_id : UserDataID, uuid : String) + # Uses either a "share" or a "transfer" token. + def gain_ownership(user_id : UserDataID, uuid : String) : IPC::JSON if domain = @domains_by_share_key.get? uuid if domain.owners.includes? user_id return Response::Error.new "You already own this domain." @@ -282,7 +285,7 @@ class DNSManager::Storage # Any modification of the zone must be performed here. # This function updates the SOA serial before storing the modified zone. # Also, this function generate the Bind9 file. - def update_zone(zone : Zone) + def update_zone(zone : Zone) : Nil zone.update_serial zones_by_domain.update_or_create zone.domain, zone @@ -331,11 +334,12 @@ class DNSManager::Storage Response::RRDeleted.new rrid end - # TODO - def remove_bind9_zonefile(domain : String) + # TODO: removes a Bind9 zonefile. + def remove_bind9_zonefile(domain : String) : Nil Baguette::Log.info "Removing a Bind9 zone file." end + # Deletes a domain. def delete_domain(user_id : UserDataID, domain_name : String) : IPC::JSON zone_must_exist! domain_name user_should_own! user_id, domain_name @@ -365,38 +369,44 @@ class DNSManager::Storage Response::DomainDeleted.new domain_name end - # Get all removed users from `authd`, list all their domains and remove their data from `dnsmanagerd`. + # Gets all removed users from `authd`, list all their domains and remove their data from `dnsmanagerd`. def get_orphan_domains(user_id : UserDataID) : IPC::JSON user_must_be_admin! user_id -# Baguette::Log.debug "list all orphan domains (long computation)" -# orphans = [] of String -# user_data.each do |user| -# begin -# dnsmanagerd().authd.get_user? user.uid -# rescue e -# Baguette::Log.warning "no authd info on user #{user.uid}: #{e} (removing this user)" -# Baguette::Log.debug "user #{user.uid} had #{user.domains.size} domains" -# user.domains.each do |domain| -# orphans << domain -# end -# wipe_user_data user -# end -# end -# Baguette::Log.debug "total: #{orphans.size} orphans" -# -# Response::OrphanDomainList.new orphans - Response::Error.new "Not implemented." + Baguette::Log.debug "list all orphan domains (long computation)" + orphans = [] of String + domains.each do |domain| + domain.owners.each do |owner| + begin + user_must_exist! owner + rescue e + Baguette::Log.warning "no authd info on user #{owner}: #{e} (removing this user)" + if owned_domains = domains_by_owners.get? owner.to_s + Baguette::Log.debug "user #{owner} had #{owned_domains.size} domains" + owned_domains.each do |d| + orphans << d.name + end + else + Baguette::Log.warning "no domain indexed for user #{owner}, but owns domain #{domain.name}" + end + wipe_user_data owner + end + end + end + Baguette::Log.debug "total: #{orphans.size} orphans" + + Response::OrphanDomainList.new orphans end def get_zone(user_id : UserDataID, domain : String) : IPC::JSON - zone = zone_must_exist! domain + zone = zone_must_exist! domain user_should_own! user_id, domain Response::Zone.new zone end - def wipe_user_data(user_id : UserDataID) + # Removes user data. + def wipe_user_data(user_id : UserDataID) : Nil domains_by_owners.get(user_id.to_s).each do |domain| domain_cloned = domain.clone domain_cloned.owners.delete user_id @@ -415,6 +425,7 @@ class DNSManager::Storage Baguette::Log.error "while removing a domain: #{e}" end + # Removes user data. def delete_user_data(user_id : UserDataID, user_to_delete : UserDataID?) : IPC::JSON user_id_to_delete = if u = user_to_delete user_must_be_admin! user_id @@ -430,6 +441,7 @@ class DNSManager::Storage Response::Success.new end + # Gets a list of user domains. def user_domains(user_id : UserDataID) : Array(Domain) if doms = domains_by_owners.get? user_id.to_s doms @@ -440,13 +452,21 @@ class DNSManager::Storage # TODO: is the user known from authd? def user_must_exist!(user_id : UserDataID) - dnsmanagerd().authd.get_user? user_id + response = dnsmanagerd().authd.get_user? user_id + case response + when AuthD::Response::User + response.user + else + raise UnknownUserException.new "user #{user_id} doesn't exist" + end end + # Asks `authd` for the user's permissions and verifies the `dnsmanager` permissions are "Admin"-level. def user_must_be_admin!(user_id : UserDataID) : Nil dnsmanagerd().assert_permissions! user_id, "*", AuthD::User::PermissionLevel::Admin end + # Verifies the existence of a zone. def zone_must_exist!(domain : String) : Zone zone = zones_by_domain.get? domain raise DomainNotFoundException.new unless zone @@ -456,15 +476,15 @@ class DNSManager::Storage # Owning a domain means to be in the owners' list of the domain. # TODO: accept admin users to override this test. def user_should_own!(user_id : UserDataID, domain : String) : Nil - if d = domains_by_name.get? domain - unless d.owners.includes? user_id - raise NoOwnershipException.new - end - else - raise DomainNotFoundException.new + d = domains_by_name.get? domain + raise DomainNotFoundException.new if d.nil? + + unless d.owners.includes? user_id || user_must_be_admin! user_id + raise NoOwnershipException.new end end + # Asks a new token for a resource record. def new_token(user_id : UserDataID, domain : String, rrid : UInt32) : IPC::JSON zone = zone_must_exist! domain user_should_own! user_id, domain @@ -495,12 +515,14 @@ class DNSManager::Storage Response::RRUpdated.new domain, rr end + # Verifies the existence and retrieves a token for automatic updates. def token_must_exist!(token_uuid : String) : Token token = tokens_by_uuid.get? token_uuid raise TokenNotFoundException.new unless token token end + # Uses a token to automatically update a resource record. def use_token(token_uuid : String, address : String) : IPC::JSON token = token_must_exist! token_uuid zone = zone_must_exist! token.domain