diff --git a/src/requests/user.cr b/src/requests/user.cr index 68a986e..d08b388 100644 --- a/src/requests/user.cr +++ b/src/requests/user.cr @@ -15,15 +15,14 @@ class DNSManager::Request # "dnsmanager-last-connection" => JSON::Any.new Time.utc.to_s #} user_id = response.user.uid - dnsmanagerd.storage.user_must_exist! user_id accepted_domains = dnsmanagerd.configuration.accepted_domains.not_nil! # Limit the number of domains in this message. # Pagination will be required beyond a hundred domains. user_domains = dnsmanagerd.storage.user_domains(user_id).[0..100] - perms = dnsmanagerd.check_permissions user_id, "*" + Response::Logged.new (perms == AuthD::User::PermissionLevel::Admin), accepted_domains, user_domains else Response::ErrorInvalidToken.new diff --git a/src/storage.cr b/src/storage.cr index 1b46d47..4885e38 100644 --- a/src/storage.cr +++ b/src/storage.cr @@ -8,16 +8,16 @@ require "dodb" class DNSManager::Storage getter domains : DODB::CachedDataBase(Domain) - getter domains_by_name : DODB::Index(Domain) - getter domains_by_share_key : DODB::Index(Domain) - getter domains_by_transfer_key : DODB::Index(Domain) + getter domains_by_name : DODB::CachedIndex(Domain) + getter domains_by_share_key : DODB::CachedIndex(Domain) + getter domains_by_transfer_key : DODB::CachedIndex(Domain) getter domains_by_owners : DODB::Tags(Domain) getter zones : DODB::CachedDataBase(Zone) - getter zones_by_domain : DODB::Index(Zone) + getter zones_by_domain : DODB::CachedIndex(Zone) getter tokens : DODB::CachedDataBase(Token) - getter tokens_by_uuid : DODB::Index(Token) + getter tokens_by_uuid : DODB::CachedIndex(Token) getter tokens_by_domain : DODB::Partition(Token) getter root : String @@ -103,8 +103,6 @@ class DNSManager::Storage end def get_generated_zonefile(user_id : UserDataID, domain : String) : IPC::JSON - - user_must_exist! user_id zone = zone_must_exist! domain user_should_own! user_id, zone.domain @@ -118,8 +116,6 @@ class DNSManager::Storage user_id : UserDataID, domain : String) : IPC::JSON - user_must_exist! user_id - # Prevent future very confusing errors. domain = domain.downcase @@ -164,7 +160,6 @@ class DNSManager::Storage end def ask_share_token(user_id : UserDataID, domain_name : String) - user_must_exist! user_id user_should_own! user_id, domain_name domain = @domains_by_name.get domain_name @@ -178,7 +173,6 @@ class DNSManager::Storage end def ask_transfer_token(user_id : UserDataID, domain_name : String) - user_must_exist! user_id user_should_own! user_id, domain_name domain = @domains_by_name.get domain_name @@ -194,13 +188,12 @@ 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) - user_must_exist! user_id user_should_own! user_id, domain_name 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_or_create domain_name, domain + @domains_by_name.update domain Response::DomainChanged.new domain else Response::Error.new "You are not the only owner." @@ -208,14 +201,12 @@ class DNSManager::Storage end def gain_ownership(user_id : UserDataID, uuid : String) - user_must_exist! user_id - if domain = @domains_by_share_key.get? uuid if domain.owners.includes? user_id return Response::Error.new "You already own this domain." end domain.owners << user_id - @domains_by_name.update_or_create domain.name, domain + @domains_by_name.update domain Response::DomainChanged.new domain elsif domain = @domains_by_transfer_key.get? uuid if domain.owners.includes? user_id @@ -223,7 +214,7 @@ class DNSManager::Storage end domain.transfer_key = nil domain.owners = [user_id] - @domains_by_name.update_or_create domain.name, domain + @domains_by_name.update domain Response::DomainChanged.new domain else Response::Error.new "There is no key with this UUID." @@ -231,8 +222,6 @@ class DNSManager::Storage end def add_or_update_zone(user_id : UserDataID, zone : Zone) : IPC::JSON - user_must_exist! user_id - # Test zone validity. if errors = zone.get_errors? Baguette::Log.warning "zone #{zone.domain} update with errors: #{errors}" @@ -254,7 +243,6 @@ class DNSManager::Storage end def add_rr(user_id : UserDataID, domain : String, rr : Zone::ResourceRecord) : IPC::JSON - user_must_exist! user_id zone = zone_must_exist! domain user_should_own! user_id, domain @@ -281,7 +269,6 @@ class DNSManager::Storage end def update_rr(user_id : UserDataID, domain : String, rr : Zone::ResourceRecord) : IPC::JSON - user_must_exist! user_id zone = zone_must_exist! domain user_should_own! user_id, domain @@ -303,8 +290,7 @@ class DNSManager::Storage end def delete_rr(user_id : UserDataID, domain : String, rrid : UInt32) : IPC::JSON - user_must_exist! user_id - zone = zone_must_exist! domain + zone = zone_must_exist! domain user_should_own! user_id, domain rr = zone.rr_not_readonly! rrid @@ -321,7 +307,6 @@ class DNSManager::Storage end def delete_domain(user_id : UserDataID, domain_name : String) : IPC::JSON - user_must_exist! user_id zone_must_exist! domain_name user_should_own! user_id, domain_name @@ -370,7 +355,6 @@ class DNSManager::Storage end def get_zone(user_id : UserDataID, domain : String) : IPC::JSON - user_must_exist! user_id zone = zone_must_exist! domain user_should_own! user_id, domain @@ -395,11 +379,9 @@ class DNSManager::Storage end def delete_user_data(user_id : UserDataID, user_to_delete : UserDataID?) : IPC::JSON - user_must_exist! user_id user_id_to_delete = if u = user_to_delete user_must_be_admin! user_id Baguette::Log.info "Admin #{user_id} removes data of user #{u}." - user_must_exist! u u else Baguette::Log.info "User #{user_id} terminates his account." @@ -412,8 +394,11 @@ class DNSManager::Storage end def user_domains(user_id : UserDataID) : Array(Domain) - user_must_exist! user_id - domains_by_owners.get(user_id.to_s) + if doms = domains_by_owners.get? user_id.to_s + doms + else + Array(Domain).new + end end # TODO: is the user known from authd? @@ -434,14 +419,16 @@ 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 - d = domains_by_name.get domain - unless d.owners.includes? user_id - raise NoOwnershipException.new + if d = domains_by_name.get? domain + unless d.owners.includes? user_id + raise NoOwnershipException.new + end + else + raise DomainNotFoundException.new end end def new_token(user_id : UserDataID, domain : String, rrid : UInt32) : IPC::JSON - user_must_exist! user_id zone = zone_must_exist! domain user_should_own! user_id, domain diff --git a/src/storage/domain.cr b/src/storage/domain.cr index 63f6039..291a688 100644 --- a/src/storage/domain.cr +++ b/src/storage/domain.cr @@ -14,4 +14,6 @@ class DNSManager::Storage::Domain def initialize(@name, share_key = nil, transfer_key = nil, owners = [] of UserDataID) end + + def_clone end diff --git a/src/storage/token.cr b/src/storage/token.cr index 22d7329..1daa5c8 100644 --- a/src/storage/token.cr +++ b/src/storage/token.cr @@ -7,6 +7,8 @@ class DNSManager::Storage::Token property domain : String property rrid : UInt32 + def_clone + def initialize(@domain, @rrid) @uuid = UUID.random.to_s end diff --git a/src/storage/zone.cr b/src/storage/zone.cr index 4bec0c2..7af19f7 100644 --- a/src/storage/zone.cr +++ b/src/storage/zone.cr @@ -35,6 +35,8 @@ class DNSManager::Storage::Zone def initialize(@domain) end + def_clone + alias Error = String # Store a Resource Record: A, AAAA, TXT, PTR, CNAME… @@ -96,6 +98,8 @@ class DNSManager::Storage::Zone end class SOA < ResourceRecord + def_clone + # Start of Authority property mname : String # Master Name Server for the zone. property rname : String # admin email address john.doe@example.com => john\.doe.example.com @@ -159,6 +163,8 @@ class DNSManager::Storage::Zone end class A < ResourceRecord + def_clone + def get_errors : Array(Error) errors = [] of Error @@ -179,6 +185,8 @@ class DNSManager::Storage::Zone end class AAAA < ResourceRecord + def_clone + def get_errors : Array(Error) errors = [] of Error @@ -199,6 +207,8 @@ class DNSManager::Storage::Zone end class TXT < ResourceRecord + def_clone + def get_errors : Array(Error) errors = [] of Error @@ -224,6 +234,8 @@ class DNSManager::Storage::Zone end class PTR < ResourceRecord + def_clone + def get_errors : Array(Error) errors = [] of Error @@ -252,6 +264,8 @@ class DNSManager::Storage::Zone end class NS < ResourceRecord + def_clone + def get_errors : Array(Error) errors = [] of Error @@ -268,6 +282,8 @@ class DNSManager::Storage::Zone end class CNAME < ResourceRecord + def_clone + def get_errors : Array(Error) errors = [] of Error @@ -289,7 +305,7 @@ class DNSManager::Storage::Zone # TODO: verifications + print. # baguette.netlib.re. 3600 IN TXT "v=spf1 a mx ip4: a:mail.baguette.netlib.re ~all" class SPF < ResourceRecord - + def_clone # SPF mechanisms are about policy, which comes with the form of a single character: # - '?' means no policy (neutral), @@ -332,6 +348,8 @@ class DNSManager::Storage::Zone property t : Type # type of modifier property v : String # value + def_clone + def initialize(@t, @v) end @@ -362,6 +380,8 @@ class DNSManager::Storage::Zone INCLUDE # include foreign SPF policy end + def_clone + property q : Qualifier = Qualifier::Pass property t : Type # type of mechanism property v : String # value @@ -484,6 +504,7 @@ class DNSManager::Storage::Zone # TODO class DKIM < ResourceRecord + def_clone enum Version DKIM1 end @@ -508,6 +529,8 @@ class DNSManager::Storage::Zone io << "v=#{v};h=#{h.to_s.downcase};k=#{k.to_s.downcase};p=#{p}" io << ";n=#{n}" unless n == "" end + + def_clone end property dkim : DKIMProperties @@ -542,6 +565,7 @@ class DNSManager::Storage::Zone # TODO class DMARC < ResourceRecord + def_clone enum Version DMARC1 end @@ -551,6 +575,8 @@ class DNSManager::Storage::Zone property mail : String property limit : UInt32? = nil + def_clone + def to_s(io : IO) io << "#{mail}" if l = @limit @@ -596,6 +622,8 @@ class DNSManager::Storage::Zone property rf : Array(ReportFormat)? = nil property ri : UInt32 + def_clone + def initialize(@p, @sp, @v, @adkim, @aspf, @pct, @fo, @rua, @ruf, @rf, @ri) end @@ -694,6 +722,7 @@ class DNSManager::Storage::Zone end class MX < ResourceRecord + def_clone property priority : UInt32 = 10 def initialize(@name, @ttl, @target, @priority = 10) @rrtype = "MX" @@ -729,6 +758,7 @@ class DNSManager::Storage::Zone end class SRV < ResourceRecord + def_clone property port : UInt16 property protocol : String = "tcp" property priority : UInt32 = 10