Use exceptions extensively.
parent
8824835975
commit
8534dcb246
29
src/main.cr
29
src/main.cr
|
@ -11,10 +11,24 @@ require "./config"
|
||||||
module DNSManager
|
module DNSManager
|
||||||
class Exception < ::Exception
|
class Exception < ::Exception
|
||||||
end
|
end
|
||||||
|
class DomainNotFoundException < ::Exception
|
||||||
|
end
|
||||||
|
class UnknownUserException < ::Exception
|
||||||
|
end
|
||||||
|
class RRReadOnlyException < ::Exception
|
||||||
|
property domain : String
|
||||||
|
property rr : DNSManager::Storage::Zone::ResourceRecord
|
||||||
|
def initialize(@domain, @rr)
|
||||||
|
end
|
||||||
|
end
|
||||||
class AuthorizationException < ::Exception
|
class AuthorizationException < ::Exception
|
||||||
end
|
end
|
||||||
|
class NoOwnershipException < ::Exception
|
||||||
|
end
|
||||||
class NotLoggedException < ::Exception
|
class NotLoggedException < ::Exception
|
||||||
end
|
end
|
||||||
|
class RRNotFoundException < ::Exception
|
||||||
|
end
|
||||||
class AdminAuthorizationException < ::Exception
|
class AdminAuthorizationException < ::Exception
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -85,12 +99,27 @@ class DNSManager::Service < IPC
|
||||||
rescue e : AuthorizationException
|
rescue e : AuthorizationException
|
||||||
Baguette::Log.error "#{reqname} authorization error"
|
Baguette::Log.error "#{reqname} authorization error"
|
||||||
Response::Error.new "authorization error"
|
Response::Error.new "authorization error"
|
||||||
|
rescue e : DomainNotFoundException
|
||||||
|
Baguette::Log.error "#{reqname} domain not found"
|
||||||
|
Response::DomainNotFound.new
|
||||||
|
rescue e : UnknownUserException
|
||||||
|
Baguette::Log.error "#{reqname} unknown user"
|
||||||
|
Response::UnknownUser.new
|
||||||
|
rescue e : NoOwnershipException
|
||||||
|
Baguette::Log.error "#{reqname} no ownership error"
|
||||||
|
Response::NoOwnership.new
|
||||||
rescue e : AdminAuthorizationException
|
rescue e : AdminAuthorizationException
|
||||||
Baguette::Log.error "#{reqname} no admin authorization"
|
Baguette::Log.error "#{reqname} no admin authorization"
|
||||||
Response::Error.new "admin authorization error"
|
Response::Error.new "admin authorization error"
|
||||||
rescue e : NotLoggedException
|
rescue e : NotLoggedException
|
||||||
Baguette::Log.error "#{reqname} user not logged"
|
Baguette::Log.error "#{reqname} user not logged"
|
||||||
Response::Error.new "user not logged"
|
Response::Error.new "user not logged"
|
||||||
|
rescue e : RRNotFoundException
|
||||||
|
Baguette::Log.error "#{reqname} RR not found"
|
||||||
|
Response::RRNotFound.new
|
||||||
|
rescue e : RRReadOnlyException
|
||||||
|
Baguette::Log.error "#{reqname} RR is read only"
|
||||||
|
Response::RRReadOnly.new e.domain, e.rr
|
||||||
rescue e # Generic case
|
rescue e # Generic case
|
||||||
Baguette::Log.error "#{reqname} generic error #{e}"
|
Baguette::Log.error "#{reqname} generic error #{e}"
|
||||||
DNSManager::Response::Error.new "generic error"
|
DNSManager::Response::Error.new "generic error"
|
||||||
|
|
218
src/storage.cr
218
src/storage.cr
|
@ -97,20 +97,10 @@ class DNSManager::Storage
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_generated_zonefile(user_id : Int32, domain : String) : IPC::JSON
|
def get_generated_zonefile(user_id : Int32, domain : String) : IPC::JSON
|
||||||
zone = zones_by_domain.get? domain
|
|
||||||
return Response::DomainNotFound.new unless zone
|
|
||||||
|
|
||||||
# User must own the zone.
|
user_data = user_must_exist! user_id
|
||||||
user_data = user_data_by_uid.get? user_id.to_s
|
zone = zone_must_exist! domain
|
||||||
unless user_data
|
user_should_own! user_data, zone.domain
|
||||||
Baguette::Log.warning "unknown user #{user_id} asked a zonefile for domain #{domain}"
|
|
||||||
return Response::UnknownUser.new
|
|
||||||
end
|
|
||||||
|
|
||||||
unless user_data.domains.includes?(zone.domain) || user_data.admin
|
|
||||||
Baguette::Log.warning "user #{user_id} doesn't own domain #{zone.domain}"
|
|
||||||
return Response::NoOwnership.new
|
|
||||||
end
|
|
||||||
|
|
||||||
io = IO::Memory.new
|
io = IO::Memory.new
|
||||||
zone.to_bind9 io
|
zone.to_bind9 io
|
||||||
|
@ -122,12 +112,7 @@ class DNSManager::Storage
|
||||||
user_id : Int32,
|
user_id : Int32,
|
||||||
domain : String) : IPC::JSON
|
domain : String) : IPC::JSON
|
||||||
|
|
||||||
# User must exist.
|
user_data = user_must_exist! user_id
|
||||||
user_data = user_data_by_uid.get? user_id.to_s
|
|
||||||
unless user_data
|
|
||||||
Baguette::Log.warning "unknown user #{user_id} tries to add domain #{domain}"
|
|
||||||
return Response::UnknownUser.new
|
|
||||||
end
|
|
||||||
|
|
||||||
return Response::DomainAlreadyExists.new if zones_by_domain.get? domain
|
return Response::DomainAlreadyExists.new if zones_by_domain.get? domain
|
||||||
|
|
||||||
|
@ -167,26 +152,17 @@ class DNSManager::Storage
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_or_update_zone(user_id : Int32, zone : Zone) : IPC::JSON
|
def add_or_update_zone(user_id : Int32, zone : Zone) : IPC::JSON
|
||||||
|
user_data = user_must_exist! user_id
|
||||||
|
|
||||||
# Test zone validity.
|
# Test zone validity.
|
||||||
if errors = zone.get_errors?
|
if errors = zone.get_errors?
|
||||||
Baguette::Log.warning "zone #{zone.domain} update with errors: #{errors}"
|
Baguette::Log.warning "zone #{zone.domain} update with errors: #{errors}"
|
||||||
return Response::InvalidZone.new errors
|
return Response::InvalidZone.new errors
|
||||||
end
|
end
|
||||||
|
|
||||||
# User must exist.
|
|
||||||
user_data = user_data_by_uid.get? user_id.to_s
|
|
||||||
unless user_data
|
|
||||||
Baguette::Log.warning "unknown user #{user_id} tries to add -or update- zone #{zone.domain}"
|
|
||||||
return Response::UnknownUser.new
|
|
||||||
end
|
|
||||||
|
|
||||||
# Does the zone already exist?
|
# Does the zone already exist?
|
||||||
if z = zones_by_domain.get? zone.domain
|
if z = zones_by_domain.get? zone.domain
|
||||||
# User must own the zone.
|
user_should_own! user_data, z.domain
|
||||||
unless user_data.domains.includes?(zone.domain) || user_data.admin
|
|
||||||
Baguette::Log.warning "user #{user_id} doesn't own domain #{zone.domain}"
|
|
||||||
return Response::NoOwnership.new
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
# Add the domain to the user's domain.
|
# Add the domain to the user's domain.
|
||||||
user_data.domains << zone.domain
|
user_data.domains << zone.domain
|
||||||
|
@ -199,28 +175,12 @@ class DNSManager::Storage
|
||||||
zones_by_domain.update_or_create zone.domain, zone
|
zones_by_domain.update_or_create zone.domain, zone
|
||||||
|
|
||||||
Response::Success.new
|
Response::Success.new
|
||||||
rescue e
|
|
||||||
Baguette::Log.error "trying to add -or update- zone #{zone.domain}: #{e}"
|
|
||||||
Response::Error.new "error while adding or updating the domain #{zone.domain}"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_rr(user_id : Int32, domain : String, rr : Zone::ResourceRecord) : IPC::JSON
|
def add_rr(user_id : Int32, domain : String, rr : Zone::ResourceRecord) : IPC::JSON
|
||||||
# User must exist.
|
user_data = user_must_exist! user_id
|
||||||
user_data = user_data_by_uid.get? user_id.to_s
|
zone = zone_must_exist! domain
|
||||||
unless user_data
|
user_should_own! user_data, domain
|
||||||
Baguette::Log.warning "unknown user #{user_id} tries to add a RR in domain #{domain}"
|
|
||||||
return Response::UnknownUser.new
|
|
||||||
end
|
|
||||||
|
|
||||||
# Zone must exist.
|
|
||||||
zone = zones_by_domain.get? domain
|
|
||||||
return Response::DomainNotFound.new unless zone
|
|
||||||
|
|
||||||
# User must own the zone.
|
|
||||||
unless user_data.domains.includes?(domain) || user_data.admin
|
|
||||||
Baguette::Log.warning "user #{user_id} doesn't own domain #{domain}"
|
|
||||||
return Response::NoOwnership.new
|
|
||||||
end
|
|
||||||
|
|
||||||
# Test RR validity.
|
# Test RR validity.
|
||||||
rr.get_errors.tap do |errors|
|
rr.get_errors.tap do |errors|
|
||||||
|
@ -235,9 +195,6 @@ class DNSManager::Storage
|
||||||
update_zone zone
|
update_zone zone
|
||||||
|
|
||||||
Response::RRAdded.new zone.domain, rr
|
Response::RRAdded.new zone.domain, rr
|
||||||
rescue e
|
|
||||||
Baguette::Log.error "trying to add a resource record in domain #{domain}: #{e}"
|
|
||||||
Response::Error.new "error while adding a resource record in domain #{domain}"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Any modification of the zone must be performed here.
|
# Any modification of the zone must be performed here.
|
||||||
|
@ -248,29 +205,11 @@ class DNSManager::Storage
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_rr(user_id : Int32, domain : String, rr : Zone::ResourceRecord) : IPC::JSON
|
def update_rr(user_id : Int32, domain : String, rr : Zone::ResourceRecord) : IPC::JSON
|
||||||
# User must exist.
|
user_data = user_must_exist! user_id
|
||||||
user_data = user_data_by_uid.get? user_id.to_s
|
zone = zone_must_exist! domain
|
||||||
unless user_data
|
user_should_own! user_data, domain
|
||||||
Baguette::Log.warning "unknown user #{user_id} tries to update a RR in domain #{domain}"
|
|
||||||
return Response::UnknownUser.new
|
|
||||||
end
|
|
||||||
|
|
||||||
# Zone must exist.
|
zone.rr_not_readonly! rr.rrid
|
||||||
zone = zones_by_domain.get? domain
|
|
||||||
return Response::DomainNotFound.new unless zone
|
|
||||||
|
|
||||||
# User must own the zone.
|
|
||||||
unless user_data.domains.includes?(domain) || user_data.admin
|
|
||||||
Baguette::Log.warning "user #{user_id} doesn't own domain #{domain}"
|
|
||||||
return Response::NoOwnership.new
|
|
||||||
end
|
|
||||||
|
|
||||||
# Verify the RR exists.
|
|
||||||
stored_rr = zone.get_rr rr.rrid
|
|
||||||
return Response::RRNotFound.new unless stored_rr
|
|
||||||
|
|
||||||
# Verify the RR isn't read only.
|
|
||||||
return Response::RRReadOnly.new domain, stored_rr if stored_rr.readonly
|
|
||||||
|
|
||||||
# Test RR validity.
|
# Test RR validity.
|
||||||
rr.get_errors.tap do |errors|
|
rr.get_errors.tap do |errors|
|
||||||
|
@ -282,68 +221,33 @@ class DNSManager::Storage
|
||||||
|
|
||||||
zone.update_rr rr
|
zone.update_rr rr
|
||||||
|
|
||||||
zone.resources = zone.resources.map { |x| x.rrid == rr.rrid ? rr : x }
|
|
||||||
|
|
||||||
update_zone zone
|
update_zone zone
|
||||||
|
|
||||||
Response::RRUpdated.new domain, rr
|
Response::RRUpdated.new domain, rr
|
||||||
rescue e
|
|
||||||
Baguette::Log.error "trying to replace a resource record in domain #{domain}: #{e}"
|
|
||||||
Response::Error.new "error while replacing a resource record in domain #{domain}"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_rr(user_id : Int32, domain : String, rrid : UInt32) : IPC::JSON
|
def delete_rr(user_id : Int32, domain : String, rrid : UInt32) : IPC::JSON
|
||||||
# User must exist.
|
user_data = user_must_exist! user_id
|
||||||
user_data = user_data_by_uid.get? user_id.to_s
|
zone = zone_must_exist! domain
|
||||||
unless user_data
|
user_should_own! user_data, domain
|
||||||
Baguette::Log.warning "unknown user #{user_id} tries to delete a RR in domain #{domain}"
|
|
||||||
return Response::UnknownUser.new
|
|
||||||
end
|
|
||||||
|
|
||||||
# Zone must exist.
|
rr = zone.rr_not_readonly! rrid
|
||||||
zone = zones_by_domain.get? domain
|
|
||||||
return Response::DomainNotFound.new unless zone
|
|
||||||
|
|
||||||
# User must own the zone.
|
|
||||||
unless user_data.domains.includes?(domain) || user_data.admin
|
|
||||||
Baguette::Log.warning "user #{user_id} doesn't own domain #{domain}"
|
|
||||||
return Response::NoOwnership.new
|
|
||||||
end
|
|
||||||
|
|
||||||
# Verify the RR exists.
|
|
||||||
rr = zone.get_rr rrid
|
|
||||||
return Response::RRNotFound.new unless rr
|
|
||||||
|
|
||||||
# Verify the RR isn't read only.
|
|
||||||
return Response::RRReadOnly.new domain, rr if rr.readonly
|
|
||||||
|
|
||||||
# Remove token from the db.
|
# Remove token from the db.
|
||||||
if token_uuid = rr.token
|
if token_uuid = rr.token
|
||||||
tokens_by_uuid.delete token_uuid
|
tokens_by_uuid.delete token_uuid
|
||||||
end
|
end
|
||||||
zone.resources.select! { |x| x.rrid != rrid }
|
|
||||||
|
|
||||||
|
zone.delete_rr rrid
|
||||||
update_zone zone
|
update_zone zone
|
||||||
|
|
||||||
Response::RRDeleted.new rrid
|
Response::RRDeleted.new rrid
|
||||||
rescue e
|
|
||||||
Baguette::Log.error "trying to remove a resource record in domain #{domain}: #{e}"
|
|
||||||
Response::Error.new "error while removing a resource record in domain #{domain}"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_domain(user_id : Int32, domain : String) : IPC::JSON
|
def delete_domain(user_id : Int32, domain : String) : IPC::JSON
|
||||||
# User must exist.
|
user_data = user_must_exist! user_id
|
||||||
user_data = user_data_by_uid.get? user_id.to_s
|
zone_must_exist! domain
|
||||||
unless user_data
|
user_should_own! user_data, domain
|
||||||
Baguette::Log.warning "unknown user #{user_id} tries to delete domain #{domain}"
|
|
||||||
return Response::UnknownUser.new
|
|
||||||
end
|
|
||||||
|
|
||||||
# User must own the domain.
|
|
||||||
unless user_data.domains.includes?(domain) || user_data.admin
|
|
||||||
Baguette::Log.warning "user #{user_id} tries to delete domain #{domain} but doesn't own it"
|
|
||||||
return Response::NoOwnership.new
|
|
||||||
end
|
|
||||||
|
|
||||||
# Remove this domain from the list of user's domains.
|
# Remove this domain from the list of user's domains.
|
||||||
user_data.domains.delete domain
|
user_data.domains.delete domain
|
||||||
|
@ -356,73 +260,45 @@ class DNSManager::Storage
|
||||||
tokens_by_domain.delete domain
|
tokens_by_domain.delete domain
|
||||||
|
|
||||||
Response::DomainDeleted.new domain
|
Response::DomainDeleted.new domain
|
||||||
rescue e
|
|
||||||
Baguette::Log.error "trying to delete a domain #{domain}: #{e}"
|
|
||||||
Response::Error.new "error while deleting the domain #{domain}"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_zone(user_id : Int32, domain : String) : IPC::JSON
|
def get_zone(user_id : Int32, domain : String) : IPC::JSON
|
||||||
# User must exist.
|
user_data = user_must_exist! user_id
|
||||||
user_data = user_data_by_uid.get? user_id.to_s
|
zone = zone_must_exist! domain
|
||||||
unless user_data
|
user_should_own! user_data, domain
|
||||||
Baguette::Log.warning "unknown user #{user_id} tries to get zone #{domain}"
|
|
||||||
return Response::UnknownUser.new
|
|
||||||
end
|
|
||||||
|
|
||||||
# User must own the domain.
|
|
||||||
unless user_data.domains.includes?(domain) || user_data.admin
|
|
||||||
Baguette::Log.warning "user #{user_id} tries to get zone #{domain} but doesn't own it"
|
|
||||||
return Response::NoOwnership.new
|
|
||||||
end
|
|
||||||
|
|
||||||
zone = zones_by_domain.get? domain
|
|
||||||
unless zone
|
|
||||||
return Response::UnknownZone.new
|
|
||||||
end
|
|
||||||
|
|
||||||
Response::Zone.new zone
|
Response::Zone.new zone
|
||||||
rescue e
|
|
||||||
Baguette::Log.error "trying to get a zone #{domain}: #{e}"
|
|
||||||
Response::Error.new "error while accessing a zone #{domain}"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_domains(user_id : Int32) : IPC::JSON
|
def user_domains(user_id : Int32) : IPC::JSON
|
||||||
# User must exist.
|
user_data = user_must_exist! user_id
|
||||||
user_data = user_data_by_uid.get? user_id.to_s
|
Response::DomainList.new user_data.domains
|
||||||
unless user_data
|
|
||||||
Baguette::Log.warning "unknown user #{user_id} tries to list its domains"
|
|
||||||
return Response::UnknownUser.new
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Response::DomainList.new user_data.domains
|
def user_must_exist!(user_id : Int32) : UserData
|
||||||
rescue e
|
user_data = user_data_by_uid.get? user_id.to_s
|
||||||
Baguette::Log.error "trying to list all user #{user_id} domains: #{e}"
|
raise UnknownUserException.new unless user_data
|
||||||
Response::Error.new "error while listing domains"
|
user_data
|
||||||
|
end
|
||||||
|
|
||||||
|
def zone_must_exist!(domain : String) : Zone
|
||||||
|
zone = zones_by_domain.get? domain
|
||||||
|
raise DomainNotFoundException.new unless zone
|
||||||
|
zone
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_should_own!(user_data : UserData, domain : String)
|
||||||
|
unless user_data.domains.includes?(domain) || user_data.admin
|
||||||
|
raise NoOwnershipException.new
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def new_token(user_id : Int32, domain : String, rrid : UInt32) : IPC::JSON
|
def new_token(user_id : Int32, domain : String, rrid : UInt32) : IPC::JSON
|
||||||
# 0. verifications: must exist: user, zone, RR. Zone must be owned by user.
|
user_data = user_must_exist! user_id
|
||||||
|
zone = zone_must_exist! domain
|
||||||
|
user_should_own! user_data, domain
|
||||||
|
|
||||||
# User must exist.
|
rr = zone.rr_must_exist! rrid
|
||||||
user_data = user_data_by_uid.get? user_id.to_s
|
|
||||||
unless user_data
|
|
||||||
Baguette::Log.warning "unknown user #{user_id} tries get a new token for a RR in domain #{domain}"
|
|
||||||
return Response::UnknownUser.new
|
|
||||||
end
|
|
||||||
|
|
||||||
# Zone must exist.
|
|
||||||
zone = zones_by_domain.get? domain
|
|
||||||
return Response::DomainNotFound.new unless zone
|
|
||||||
|
|
||||||
# User should own it.
|
|
||||||
unless user_data.domains.includes?(domain) || user_data.admin
|
|
||||||
Baguette::Log.warning "user #{user_id} doesn't own domain #{domain}"
|
|
||||||
return Response::NoOwnership.new
|
|
||||||
end
|
|
||||||
|
|
||||||
# RR must exist.
|
|
||||||
rr = zone.get_rr rrid
|
|
||||||
return Response::RRNotFound.new unless rr
|
|
||||||
|
|
||||||
old_token = rr.token
|
old_token = rr.token
|
||||||
|
|
||||||
|
|
|
@ -630,10 +630,26 @@ class DNSManager::Storage::Zone
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def rr_must_exist!(rrid : UInt32) : ResourceRecord
|
||||||
|
rr = get_rr rrid
|
||||||
|
raise RRNotFoundException.new unless rr
|
||||||
|
rr
|
||||||
|
end
|
||||||
|
|
||||||
|
def rr_not_readonly!(rrid : UInt32) : ResourceRecord
|
||||||
|
rr = rr_must_exist! rrid
|
||||||
|
raise RRReadOnlyException.new @domain, rr if rr.readonly
|
||||||
|
rr
|
||||||
|
end
|
||||||
|
|
||||||
def update_rr(rr : ResourceRecord)
|
def update_rr(rr : ResourceRecord)
|
||||||
@resources.map! { |x| x.rrid != rr.rrid ? x : rr }
|
@resources.map! { |x| x.rrid != rr.rrid ? x : rr }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def delete_rr(rrid : UInt32)
|
||||||
|
@resources.select! { |x| x.rrid != rrid }
|
||||||
|
end
|
||||||
|
|
||||||
def to_s(io : IO)
|
def to_s(io : IO)
|
||||||
io << "DOMAIN #{@domain}.\n"
|
io << "DOMAIN #{@domain}.\n"
|
||||||
@resources.each do |rr|
|
@resources.each do |rr|
|
||||||
|
|
Loading…
Reference in New Issue