Delegation: new Edit and Reset mechanisms.
This commit is contained in:
parent
7750052aed
commit
99f94a30e7
3 changed files with 150 additions and 31 deletions
|
|
@ -18,4 +18,44 @@ class DNSManager::Request
|
|||
end
|
||||
end
|
||||
DNSManager.requests << DelegateDomain
|
||||
|
||||
IPC::JSON.message EditDelegation, 26 do
|
||||
property domain : String
|
||||
property nameserver1 : String
|
||||
property nameserver2 : String
|
||||
def initialize(@domain, @nameserver1, @nameserver2)
|
||||
end
|
||||
|
||||
def to_s(io : IO)
|
||||
super io
|
||||
io << " (domain: #{@domain}, ns1: #{@nameserver1}, ns2: #{@nameserver2}"
|
||||
end
|
||||
|
||||
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) : IPC::JSON
|
||||
user = dnsmanagerd.get_logged_user event
|
||||
return Response::ErrorUserNotLogged.new unless user
|
||||
dnsmanagerd.storage.edit_delegation user.uid, @domain, @nameserver1, @nameserver2
|
||||
end
|
||||
end
|
||||
DNSManager.requests << EditDelegation
|
||||
|
||||
IPC::JSON.message ResetDelegation, 27 do
|
||||
property domain : String
|
||||
property nameserver1 : String
|
||||
property nameserver2 : String
|
||||
def initialize(@domain, @nameserver1, @nameserver2)
|
||||
end
|
||||
|
||||
def to_s(io : IO)
|
||||
super io
|
||||
io << " (domain: #{@domain}, ns1: #{@nameserver1}, ns2: #{@nameserver2}"
|
||||
end
|
||||
|
||||
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) : IPC::JSON
|
||||
user = dnsmanagerd.get_logged_user event
|
||||
return Response::ErrorUserNotLogged.new unless user
|
||||
dnsmanagerd.storage.reset_delegation user.uid, @domain
|
||||
end
|
||||
end
|
||||
DNSManager.requests << ResetDelegation
|
||||
end
|
||||
|
|
|
|||
140
src/storage.cr
140
src/storage.cr
|
|
@ -24,7 +24,7 @@ class DNSManager::Storage
|
|||
|
||||
getter root : String
|
||||
getter zonefiledir : String
|
||||
getter delegationdir : String
|
||||
getter delegationdir : String
|
||||
|
||||
property dnsmanagerd : DNSManager::Service? = nil
|
||||
|
||||
|
|
@ -165,34 +165,24 @@ class DNSManager::Storage
|
|||
Response::GeneratedZone.new domain, (String.new io.buffer, io.pos)
|
||||
end
|
||||
|
||||
# Adds a new domain.
|
||||
def new_domain(user_id : UserDataID, domain : String) : IPC::JSON
|
||||
def remove_first_label (domain : String) : String
|
||||
domain.gsub /^[^.]+./, ""
|
||||
end
|
||||
|
||||
# `has_valid_tld?` takes a domain and returns its TLD if it matches one of the available TLD.
|
||||
def has_valid_tld? (domain : String) : String?
|
||||
accepted_domains = dnsmanagerd.configuration.accepted_domains.not_nil!
|
||||
|
||||
new_domain_tld = remove_first_label domain
|
||||
|
||||
tld = accepted_domains & [ new_domain_tld ]
|
||||
|
||||
return tld.pop unless tld.empty?
|
||||
end
|
||||
|
||||
# Provides a zone based on a template (selected based on the tld).
|
||||
def get_new_zone(tld : String, domain : String) : Zone
|
||||
template_directory = dnsmanagerd.configuration.template_directory
|
||||
|
||||
# Prevent future very confusing errors.
|
||||
domain = domain.downcase
|
||||
|
||||
return Response::DomainAlreadyExists.new if zones_by_domain.get? domain
|
||||
|
||||
# Verify if the domain is acceptable.
|
||||
matching_domains = accepted_domains.select { |adomain| domain.ends_with? adomain }
|
||||
unless matching_domains.size > 0
|
||||
Baguette::Log.warning "trying to add an unacceptable domain: '#{domain}'"
|
||||
return Response::UnacceptableDomain.new
|
||||
end
|
||||
|
||||
matching_domains.each do |md|
|
||||
# Prevent empty domains (from crafted requests) to be accepted.
|
||||
return Response::InvalidDomainName.new unless (domain.chomp md).size > 1
|
||||
#Baguette::Log.info "Add new domain #{domain} (matching domain #{md})"
|
||||
end
|
||||
|
||||
# Verify the domain name validity.
|
||||
return Response::InvalidDomainName.new unless Zone.is_domain_valid? domain
|
||||
|
||||
# Fill a template zone.
|
||||
tld = matching_domains.pop
|
||||
default_zone = Zone.from_json File.read "#{template_directory}/#{tld}.json"
|
||||
|
||||
# Replace domain by the real domain.
|
||||
|
|
@ -204,17 +194,43 @@ class DNSManager::Storage
|
|||
# configuration.
|
||||
default_zone.reset_serial
|
||||
|
||||
default_zone
|
||||
end
|
||||
|
||||
# Adds a new domain.
|
||||
def new_domain(user_id : UserDataID, domain : String) : IPC::JSON
|
||||
|
||||
# Prevent future very confusing errors.
|
||||
domain = domain.downcase
|
||||
|
||||
# First test: verify the domain name validity.
|
||||
return Response::InvalidDomainName.new unless Zone.is_domain_valid? domain
|
||||
|
||||
# Second test: check on the TLD.
|
||||
# This also rejects empty domains and a few other invalid names as a side effect.
|
||||
new_domain_tld = has_valid_tld? domain
|
||||
if new_domain_tld.nil?
|
||||
Baguette::Log.warning "trying to add an unacceptable domain: '#{domain}'"
|
||||
return Response::UnacceptableDomain.new
|
||||
end
|
||||
|
||||
# Last check: verify the availability of the domain name.
|
||||
return Response::DomainAlreadyExists.new if zones_by_domain.get? domain
|
||||
|
||||
#
|
||||
# Actually write data on-disk.
|
||||
#
|
||||
|
||||
Baguette::Log.debug "Add new domain #{domain} (matching domain #{new_domain_tld})"
|
||||
|
||||
# Add the new domain.
|
||||
the_new_domain = Domain.new domain
|
||||
the_new_domain.owners = [user_id]
|
||||
@domains << the_new_domain
|
||||
|
||||
# Add the new zone in the database.
|
||||
update_zone default_zone
|
||||
# Fill a template zone then add it to the database.
|
||||
the_new_zone = get_new_zone new_domain_tld, domain
|
||||
update_zone the_new_zone
|
||||
|
||||
Response::DomainAdded.new domain
|
||||
end
|
||||
|
|
@ -340,12 +356,13 @@ 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.
|
||||
# Also, this function generates the Bind9 file unless the zone is delegated.
|
||||
def update_zone(zone : Zone) : Nil
|
||||
zone.update_serial
|
||||
zones_by_domain.update_or_create zone.domain, zone
|
||||
|
||||
generate_bind9_zonefile zone.domain
|
||||
# Do not generate a bind9 file if the zone is delegated.
|
||||
generate_bind9_zonefile zone.domain unless zone.delegation
|
||||
end
|
||||
|
||||
def update_rr(user_id : UserDataID, domain : String, rr : Zone::ResourceRecord) : IPC::JSON
|
||||
|
|
@ -449,6 +466,67 @@ class DNSManager::Storage
|
|||
Response::Zone.new zone
|
||||
end
|
||||
|
||||
# `edit_delegation` enables users to update the delegation name servers.
|
||||
def edit_delegation(user_id : UserDataID, domain_name : String,
|
||||
nameserver1 : String, nameserver2 : String) : IPC::JSON
|
||||
|
||||
# Verifies the provided name server domains.
|
||||
return Response::InvalidDomainName.new unless Zone.is_domain_valid? nameserver1
|
||||
return Response::InvalidDomainName.new unless Zone.is_domain_valid? nameserver2
|
||||
|
||||
zone = zone_must_exist! domain_name
|
||||
user_should_own! user_id, domain_name
|
||||
|
||||
# Make sure both name servers are "absolute" domain names.
|
||||
ns1 = if nameserver1.ends_with? '.'
|
||||
nameserver1
|
||||
else
|
||||
"#{nameserver1}."
|
||||
end
|
||||
ns2 = if nameserver2.ends_with? '.'
|
||||
nameserver2
|
||||
else
|
||||
"#{nameserver2}."
|
||||
end
|
||||
|
||||
# Clone the zone because the update mechanism needs the old values in order to remove their files.
|
||||
zone_clone = zone.clone
|
||||
|
||||
# Edit the name servers used for delegation.
|
||||
zone_clone.delegation = Zone::Delegation.new ns1, ns2
|
||||
|
||||
# On-disk update of the name servers used for delegation.
|
||||
zone_clone.update_delegation @delegationdir
|
||||
|
||||
# Once the new delegation file has been written, the script generating the (root) zone file must
|
||||
# be informed by touching a file (named "delegation token file" in the source code).
|
||||
update_delegation_token_file
|
||||
|
||||
update_zone zone_clone
|
||||
|
||||
Response::Success.new
|
||||
end
|
||||
|
||||
# `reset_delegation` removes the delegation of a domain, auto-fill the zone with a template.
|
||||
def reset_delegation(user_id : UserDataID, domain_name : String) : IPC::JSON
|
||||
user_should_own! user_id, domain_name
|
||||
|
||||
# Removes all traces of the zone (including the delegation file).
|
||||
wipe_zone user_id, domain_name
|
||||
|
||||
domain_tld = has_valid_tld? domain_name
|
||||
if domain_tld.nil?
|
||||
Baguette::Log.warning "trying to add an unacceptable domain: '#{domain_name}'"
|
||||
return Response::UnacceptableDomain.new
|
||||
end
|
||||
|
||||
# Creates and auto-fill a new zone from a template then add it to the database.
|
||||
the_new_zone = get_new_zone domain_tld, domain_name
|
||||
update_zone the_new_zone
|
||||
|
||||
Response::Success.new
|
||||
end
|
||||
|
||||
# Deletes the zone and its tokens to wipe the zone data then delegates the domain.
|
||||
def delegate_domain(user_id : UserDataID, domain_name : String,
|
||||
nameserver1 : String, nameserver2 : String) : IPC::JSON
|
||||
|
|
|
|||
|
|
@ -1029,6 +1029,7 @@ class DNSManager::Storage::Zone
|
|||
false
|
||||
end
|
||||
|
||||
# TODO: this verification is superficial. To be replaced.
|
||||
# Valid names: valid subdomains, wildcards alone or followed by a subdomain.
|
||||
def self.is_valid_name?(subdomain) : Bool
|
||||
if subdomain =~ subdomain_pattern
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue