Adding a client (draft) and domain test.
This commit is contained in:
parent
c1ef4f6235
commit
7f5d162e91
13
src/client/lib/authd_api.cr
Normal file
13
src/client/lib/authd_api.cr
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
def authd_get_token(key_file : String? = nil, login : String? = nil, pass : String? = nil)
|
||||||
|
authd = AuthD::Client.new
|
||||||
|
key_file.try do |file| # FIXME: fail if missing?
|
||||||
|
authd.key = File.read(file).chomp
|
||||||
|
end
|
||||||
|
|
||||||
|
token = authd.get_token? login, pass
|
||||||
|
raise "cannot get a token" if token.nil?
|
||||||
|
authd.close
|
||||||
|
|
||||||
|
token
|
||||||
|
end
|
46
src/client/lib/dnsmanager-client.cr
Normal file
46
src/client/lib/dnsmanager-client.cr
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
require "../../requests/*"
|
||||||
|
|
||||||
|
class DNSManager::Client < IPC::Client
|
||||||
|
def initialize
|
||||||
|
initialize "dnsmanager"
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: parse_message should raise exception if response not anticipated
|
||||||
|
def parse_message(expected_messages, message)
|
||||||
|
em = Array(IPC::JSON.class).new
|
||||||
|
expected_messages.each do |e|
|
||||||
|
em << e
|
||||||
|
end
|
||||||
|
em << DNSManager::Response::Error
|
||||||
|
em.parse_ipc_json message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Simple users.
|
||||||
|
class DNSManager::Client < IPC::Client
|
||||||
|
def login(token : String)
|
||||||
|
request = DNSManager::Request::Login.new token
|
||||||
|
send_now @server_fd.not_nil!, request
|
||||||
|
parse_message [ DNSManager::Response::Success ], read
|
||||||
|
end
|
||||||
|
|
||||||
|
# Adding a full zone.
|
||||||
|
def user_zone_add(zone : DNSManager::Storage::Zone)
|
||||||
|
request = DNSManager::Request::AddZone.new zone
|
||||||
|
send_now @server_fd.not_nil!, request
|
||||||
|
parse_message [ DNSManager::Response::Success ], read
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Admin stuff.
|
||||||
|
class DNSManager::Client < IPC::Client
|
||||||
|
def admin_maintainance(key : String, subject : DNSManager::Request::Maintainance::Subject, value : Int32? = nil)
|
||||||
|
request = DNSManager::Request::Maintainance.new(key,subject)
|
||||||
|
if value
|
||||||
|
request.value = value
|
||||||
|
end
|
||||||
|
send_now @server_fd.not_nil!, request
|
||||||
|
parse_message [ DNSManager::Response::Success ], read
|
||||||
|
end
|
||||||
|
end
|
15
src/client/lib/yaml_uuid.cr
Normal file
15
src/client/lib/yaml_uuid.cr
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
# YAML UUID parser
|
||||||
|
def UUID.new(ctx : YAML::ParseContext, node : YAML::Nodes::Node)
|
||||||
|
ctx.read_alias(node, UUID) do |obj|
|
||||||
|
return UUID.new obj
|
||||||
|
end
|
||||||
|
|
||||||
|
if node.is_a?(YAML::Nodes::Scalar)
|
||||||
|
value = node.value
|
||||||
|
ctx.record_anchor(node, value)
|
||||||
|
UUID.new value
|
||||||
|
else
|
||||||
|
node.raise "Expected UUID, not #{node.class.name}"
|
||||||
|
end
|
||||||
|
end
|
@ -17,9 +17,9 @@ end
|
|||||||
|
|
||||||
require "./parser.cr"
|
require "./parser.cr"
|
||||||
|
|
||||||
#def read_searches
|
def read_zones
|
||||||
# Array(DNSManager::Request::BLAH).from_json_files Context.args.not_nil!
|
Array(DNSManager::Storage::Zone).from_json_files Context.args.not_nil!
|
||||||
#end
|
end
|
||||||
|
|
||||||
class Actions
|
class Actions
|
||||||
property the_call = {} of String => Proc(Nil)
|
property the_call = {} of String => Proc(Nil)
|
||||||
@ -34,7 +34,8 @@ class Actions
|
|||||||
#
|
#
|
||||||
|
|
||||||
# Maintainance
|
# Maintainance
|
||||||
@the_call["admin-maintainance"] = ->admin_maintainance
|
@the_call["admin-maintainance"] = ->admin_maintainance
|
||||||
|
@the_call["user-zone-add"] = ->user_zone_add
|
||||||
end
|
end
|
||||||
|
|
||||||
def admin_maintainance
|
def admin_maintainance
|
||||||
@ -66,6 +67,18 @@ class Actions
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def user_zone_add
|
||||||
|
zones = read_zones
|
||||||
|
zones.each do |zone|
|
||||||
|
begin
|
||||||
|
pp! zone
|
||||||
|
pp! @dnsmanagerd.user_zone_add zone
|
||||||
|
rescue e
|
||||||
|
puts "error for admin_maintainance #{subject}: #{e.message}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def main
|
def main
|
||||||
|
151
src/lib.cr
Normal file
151
src/lib.cr
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
require "uuid"
|
||||||
|
|
||||||
|
# YAML UUID parser
|
||||||
|
def UUID.new(ctx : YAML::ParseContext, node : YAML::Nodes::Node)
|
||||||
|
ctx.read_alias(node, UUID) do |obj|
|
||||||
|
return UUID.new obj
|
||||||
|
end
|
||||||
|
|
||||||
|
if node.is_a?(YAML::Nodes::Scalar)
|
||||||
|
value = node.value
|
||||||
|
ctx.record_anchor(node, value)
|
||||||
|
UUID.new value
|
||||||
|
else
|
||||||
|
node.raise "Expected UUID, not #{node.class.name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module YAML
|
||||||
|
# change dates in YAML formated content
|
||||||
|
def self.human_dates(content : String)
|
||||||
|
new_lines = Array(String).new
|
||||||
|
content.each_line do |line|
|
||||||
|
case line
|
||||||
|
when /(?<date>.*date):[ \t]+NOW[ \t]*(?<op>[-+])[ \t]*(?<rand>rand)?[ \t]*(?<delta>[0-9]+) *(?<scale>[a-z]+)?/
|
||||||
|
date = $~["date"]
|
||||||
|
op = $~["op"]
|
||||||
|
delta = $~["delta"].to_i
|
||||||
|
rand = $~["rand"] rescue nil
|
||||||
|
scale = $~["scale"] rescue nil # days, hours
|
||||||
|
|
||||||
|
unless rand.nil?
|
||||||
|
old = delta
|
||||||
|
delta = Random.rand(delta)
|
||||||
|
end
|
||||||
|
|
||||||
|
vdelta = delta.days
|
||||||
|
case scale
|
||||||
|
when /day/
|
||||||
|
# default one
|
||||||
|
when /hour/
|
||||||
|
vdelta = delta.hours
|
||||||
|
else
|
||||||
|
# puts "scale infered: days"
|
||||||
|
end
|
||||||
|
|
||||||
|
yaml_date = Time::Format::YAML_DATE.format(Time.local + vdelta)
|
||||||
|
case op
|
||||||
|
when /-/
|
||||||
|
yaml_date = Time::Format::YAML_DATE.format(Time.local - vdelta)
|
||||||
|
# puts "-"
|
||||||
|
when /\+/
|
||||||
|
# default one
|
||||||
|
# puts "+"
|
||||||
|
else
|
||||||
|
# puts "date operation not understood: #{op}, + infered"
|
||||||
|
end
|
||||||
|
|
||||||
|
new_lines << "#{date}: #{yaml_date}"
|
||||||
|
next
|
||||||
|
when /(?<date>.+date):[ \t]+NOW[ \t]*$/
|
||||||
|
date = $~["date"]
|
||||||
|
yaml_date = Time::Format::YAML_DATE.format(Time.local)
|
||||||
|
|
||||||
|
new_lines << "#{date}: #{yaml_date}"
|
||||||
|
next
|
||||||
|
when /(?<date>[a-z]+_date):[ \t]+NOW[ \t]*$/
|
||||||
|
date = $~["date"]
|
||||||
|
yaml_date = Time::Format::YAML_DATE.format(Time.local)
|
||||||
|
|
||||||
|
new_lines << "#{date}: #{yaml_date}"
|
||||||
|
next
|
||||||
|
# when /(?<date>[a-z]+_date):/
|
||||||
|
# puts "date that does not compute: #{line}"
|
||||||
|
end
|
||||||
|
new_lines << line
|
||||||
|
end
|
||||||
|
|
||||||
|
new_lines.join "\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Array(T)
|
||||||
|
def self.from_yaml_files(files)
|
||||||
|
values = Array(T).new
|
||||||
|
files.each do |file|
|
||||||
|
raise "File doesn't exist #{file}" unless File.exists? file
|
||||||
|
from_yaml_file(file).each do |v|
|
||||||
|
values << v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
values
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_yaml_file(file)
|
||||||
|
from_yaml_content File.read file
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_yaml_content(input_content)
|
||||||
|
content = YAML.human_dates input_content
|
||||||
|
|
||||||
|
values = Array(T).new
|
||||||
|
|
||||||
|
begin
|
||||||
|
values << T.from_yaml content
|
||||||
|
rescue e
|
||||||
|
Baguette::Log.warning "reading the input #{e}"
|
||||||
|
begin
|
||||||
|
Array(T).from_yaml(content).each do |b|
|
||||||
|
values << b
|
||||||
|
end
|
||||||
|
rescue e
|
||||||
|
raise "wrong YAML content: #{e}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
values
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_json_files(files)
|
||||||
|
values = Array(T).new
|
||||||
|
files.each do |file|
|
||||||
|
raise "File doesn't exist #{file}" unless File.exists? file
|
||||||
|
from_json_file(file).each do |v|
|
||||||
|
values << v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
values
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_json_file(file)
|
||||||
|
from_json_content File.read file
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_json_content(content)
|
||||||
|
values = Array(T).new
|
||||||
|
|
||||||
|
begin
|
||||||
|
values << T.from_json content
|
||||||
|
rescue e
|
||||||
|
begin
|
||||||
|
Array(T).from_json(content).each do |b|
|
||||||
|
values << b
|
||||||
|
end
|
||||||
|
rescue e
|
||||||
|
raise "wrong JSON content: #{e}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
values
|
||||||
|
end
|
||||||
|
end
|
@ -40,7 +40,7 @@ class DNSManager::Service < IPC::Server
|
|||||||
end
|
end
|
||||||
|
|
||||||
def get_logged_user(event : IPC::Event::Events)
|
def get_logged_user(event : IPC::Event::Events)
|
||||||
fd = event.connection.fd
|
fd = event.fd
|
||||||
|
|
||||||
@logged_users[fd]?
|
@logged_users[fd]?
|
||||||
end
|
end
|
||||||
|
31
src/requests/zone.cr
Normal file
31
src/requests/zone.cr
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
require "grok"
|
||||||
|
|
||||||
|
class DNSManager::Request
|
||||||
|
|
||||||
|
IPC::JSON.message AddOrUpdateZone, 10 do
|
||||||
|
property zone : DNSManager::Storage::Zone
|
||||||
|
|
||||||
|
def initialize(@zone)
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event::Events)
|
||||||
|
user = dnsmanagerd.get_logged_user event
|
||||||
|
raise NotLoggedException.new if user.nil?
|
||||||
|
|
||||||
|
# TODO: test for zone validity.
|
||||||
|
if errors = zone.get_errors?
|
||||||
|
return DNSManager::Response::InvalidZone.new errors
|
||||||
|
end
|
||||||
|
|
||||||
|
# In case there is no error, retrieve the zone in the DB.
|
||||||
|
#z = dnsmanagerd.storage.zones_by_domain.get? zone.domain
|
||||||
|
#if z
|
||||||
|
#else
|
||||||
|
# dnsmanagerd.storage.zones << @zone
|
||||||
|
#end
|
||||||
|
|
||||||
|
Response::Success.new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
DNSManager.requests << AddOrUpdateZone
|
||||||
|
end
|
11
src/responses/zone.cr
Normal file
11
src/responses/zone.cr
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
class DNSManager::Response
|
||||||
|
IPC::JSON.message InvalidZone, 10 do
|
||||||
|
# For now, Error is just an alias on String.
|
||||||
|
property errors : Array(DNSManager::Storage::Zone::Error)
|
||||||
|
def initialize(@errors)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
DNSManager.responses << InvalidZone
|
||||||
|
end
|
||||||
|
|
@ -9,6 +9,7 @@ class DNSManager::Storage::Zone
|
|||||||
def initialize(@domain)
|
def initialize(@domain)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
alias Error = String
|
||||||
|
|
||||||
# Store a Resource Record: A, AAAA, TXT, PTR, CNAME…
|
# Store a Resource Record: A, AAAA, TXT, PTR, CNAME…
|
||||||
abstract class ResourceRecord
|
abstract class ResourceRecord
|
||||||
@ -27,7 +28,7 @@ class DNSManager::Storage::Zone
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Used to discriminate between classes.
|
# Used to discriminate between classes.
|
||||||
property rrtype : String = ""
|
property rrtype : String = ""
|
||||||
|
|
||||||
property name : String
|
property name : String
|
||||||
property ttl : UInt32
|
property ttl : UInt32
|
||||||
@ -37,6 +38,10 @@ class DNSManager::Storage::Zone
|
|||||||
def initialize(@name, @ttl, @target)
|
def initialize(@name, @ttl, @target)
|
||||||
@rrtype = self.class.name.downcase.gsub /dnsmanager::storage::zone::/, ""
|
@rrtype = self.class.name.downcase.gsub /dnsmanager::storage::zone::/, ""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_error : Error?
|
||||||
|
nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class SOA < ResourceRecord
|
class SOA < ResourceRecord
|
||||||
@ -88,4 +93,33 @@ class DNSManager::Storage::Zone
|
|||||||
def to_s(io : IO)
|
def to_s(io : IO)
|
||||||
io << "TEST"
|
io << "TEST"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_errors? : Array(Error)
|
||||||
|
errors = [] of Error
|
||||||
|
unless Zone.is_domain_valid? @domain
|
||||||
|
errors << "invalid domain"
|
||||||
|
end
|
||||||
|
|
||||||
|
@resources.each do |r|
|
||||||
|
if error = r.get_error
|
||||||
|
errors << error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
errors
|
||||||
|
end
|
||||||
|
|
||||||
|
# This regex only is "good enough for now".
|
||||||
|
def self.is_domain_valid?(domain) : Bool
|
||||||
|
if domain =~ /^(((?!-))(xn--|_{1,1})?[a-z0-9-]{0,61}[a-z0-9]{1,1}\.)*((xn--)?[a-z0-9][a-z0-9\-]{0,60}[a-z0-9]|(xn--)?[a-z0-9]{1,60})\.[a-z]{2,}$/
|
||||||
|
true
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
rescue e
|
||||||
|
Baguette::Log.error "invalid zone domain #{domain}: #{e}"
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user