diff --git a/src/main.cr b/src/main.cr index 7870f6a..c82404b 100644 --- a/src/main.cr +++ b/src/main.cr @@ -38,7 +38,7 @@ class DNSManager::Service < IPC::Server getter storage : DNSManager::Storage getter logged_users : Hash(Int32, AuthD::User::Public) - @authd : AuthD::Client + property authd : AuthD::Client def initialize(@configuration, @authd : AuthD::Client) @storage = DNSManager::Storage.new @configuration.storage_directory @@ -55,22 +55,7 @@ class DNSManager::Service < IPC::Server end def decode_token(token : String) - @auth.decode_token token - end - - def get_user_data(uid : Int32) - @storage.user_data_per_user.get uid.to_s - rescue e : DODB::MissingEntry - entry = UserData.new uid - entry - end - - def get_user_data(user : ::AuthD::User::Public) - get_user_data user.uid - end - - def update_user_data(user_data : UserData) - @storage.user_data_per_user.update_or_create user_data.uid.to_s, user_data + @authd.decode_token token end def handle_request(event : IPC::Event::MessageReceived) diff --git a/src/storage.cr b/src/storage.cr index c66405e..84f1262 100644 --- a/src/storage.cr +++ b/src/storage.cr @@ -1,12 +1,56 @@ require "json" require "uuid" require "uuid/json" +require "baguette-crystal-base" require "dodb" -# require "./storage/*" - class DNSManager::Storage + getter user_data : DODB::CachedDataBase(UserData) + getter user_data_by_uid : DODB::Index(UserData) + + getter zones : DODB::CachedDataBase(Zone) + getter zones_by_domain : DODB::Index(Zone) + def initialize(@root : String) + @user_data = DODB::CachedDataBase(UserData).new "#{@root}/user-data" + @user_data_by_uid = @user_data.new_index "uid", &.uid.to_s + @zones = DODB::CachedDataBase(Zone).new "#{@root}/zones" + @zones_by_domain = @zones.new_index "domain", &.domain + + Baguette::Log.info "storage initialized" + end + + def get_user_data(uid : Int32) + user_data_by_uid.get uid.to_s + rescue e : DODB::MissingEntry + entry = UserData.new uid + entry + end + + def get_user_data(user : ::AuthD::User::Public) + get_user_data user.uid + end + + def update_user_data(user_data : UserData) + user_data_by_uid.update_or_create user_data.uid.to_s, user_data + end + + def new_domain(user_id : Int32, zone : Zone) + user_data = user_data_by_uid.get? user_id.to_s + if user_data + # store the new zone + @zones << zone + + # update user data only after ensuring this zone isn't already existing + user_data.domains << zone.domain + update_user_data user_data + else + Baguette::Log.error "trying to add zone #{zone.domain} to unknown user #{user_id}" + end + rescue e + Baguette::Log.error "trying to add zone #{zone.domain} #{e}" end end + +require "./storage/*" diff --git a/src/storage/user_data.cr b/src/storage/user_data.cr new file mode 100644 index 0000000..a3de880 --- /dev/null +++ b/src/storage/user_data.cr @@ -0,0 +1,15 @@ +require "json" +require "uuid" +require "uuid/json" + +class DNSManager::Storage::UserData + include JSON::Serializable + + property uid : Int32 + + # Users may have many domains, and a domain can have many owners. + property domains = [] of String + + def initialize(@uid) + end +end diff --git a/src/storage/zone.cr b/src/storage/zone.cr new file mode 100644 index 0000000..e5f66a0 --- /dev/null +++ b/src/storage/zone.cr @@ -0,0 +1,91 @@ + +# Store a DNS zone. +class DNSManager::Storage::Zone + include JSON::Serializable + + property domain : String + property resources = [] of DNSManager::Storage::Zone::ResourceRecord + + def initialize(@domain) + end + + + # Store a Resource Record: A, AAAA, TXT, PTR, CNAME… + abstract class ResourceRecord + include JSON::Serializable + + use_json_discriminator "type", { + a: A, + aaaa: AAAA, + soa: SOA, + txt: TXT, + ptr: PTR, + ns: NS, + cname: CNAME, + mx: MX, + srv: SRV + } + + # Used to discriminate between classes. + property type : String = "" + + property name : String + property ttl : UInt32 + property target : String + + # zone class is omited, it always will be IN in our case. + def initialize(@name, @ttl, @target) + @type = self.name.downcase.gsub /dnsmanager::storage::zone::/, "" + end + end + + class SOA < ResourceRecord + # 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 + property serial : UInt64 = 0 # Number for tracking new versions of the zone (master-slaves). + property refresh : UInt64 = 86400 # #seconds before requesting new zone version (master-slave). + property retry : UInt64 = 7200 # #seconds before retry accessing new data from the master. + property expire : UInt64 = 3600000# #seconds slaves should consider master dead. + + def initialize(@name, @ttl, @target, + @mname, @rname, + @serial = 0, @refresh = 86400, @retry = 7200, @expire = 3600000) + @type = "soa" + end + end + + class A < ResourceRecord + end + class AAAA < ResourceRecord + end + class TXT < ResourceRecord + end + class PTR < ResourceRecord + end + class NS < ResourceRecord + end + class CNAME < ResourceRecord + end + + class MX < ResourceRecord + property priority : UInt32 = 10 + def initialize(@name, @ttl, @target, @priority = 10) + @type = "mx" + end + end + + class SRV < ResourceRecord + property port : UInt16 + property protocol : String = "tcp" + property priority : UInt32 = 10 + property weight : UInt32 = 10 + def initialize(@name, @ttl, @target, @port, @protocol = "tcp", @priority = 10, @weight = 10) + @type = "srv" + end + end + + def to_s(io : IO) + io << "TEST" + end +end