record users, handling user status, new message format

dev
Philippe PITTOLI 2019-12-20 17:20:08 +01:00
parent 5a25cfcd25
commit d6aa9f35fb
4 changed files with 145 additions and 38 deletions

View File

@ -4,13 +4,22 @@ require "json"
require "./common.cr" require "./common.cr"
# TODO
# For now, this example only upload files.
# In a near future, we should be able to download files, too.
service_name = "filestorage" service_name = "filestorage"
files_and_directories_to_transfer = Array(String).new files_and_directories_to_transfer = Array(String).new
# This is the requests we will send to the server
requets = Array(Requets).new
OptionParser.parse do |parser| OptionParser.parse do |parser|
parser.on "-s service-name", "--service-name service-name", "Service name." do |name| parser.on "-s service-name",
"--service-name service-name",
"Service name." do |name|
service_name = name service_name = name
end end
@ -24,19 +33,16 @@ OptionParser.parse do |parser|
end end
end end
client = IPC::Client.new service_name
# #
# Get informations about files to transfer # Get informations about files to transfer
# For now, we only want to upload files, so we create an UploadRequest
# #
files_info = Array(FileInfo).new files_info = Array(FileInfo).new
puts "files and directories to transfer" puts "files and directories to transfer"
files_and_directories_to_transfer.each do |f| files_and_directories_to_transfer.each do |f|
puts "- #{f}"
if File.directory? f if File.directory? f
# TODO # TODO
puts "Directories not supported, for now" puts "Directories not supported, for now"
@ -45,11 +51,11 @@ files_and_directories_to_transfer.each do |f|
files_info << FileInfo.new file files_info << FileInfo.new file
end end
else else
if File.exists? f if ! File.exists? f
puts "#{f} does not exist" puts "#{f} does not exist"
elsif File.file? f elsif ! File.file? f
puts "#{f} is neither a directory or a file" puts "#{f} is neither a directory or a file"
elsif File.readable? f elsif ! File.readable? f
puts "#{f} is not readable" puts "#{f} is not readable"
end end
end end
@ -57,10 +63,16 @@ end
pp! files_info pp! files_info
exit 0 requests << UploadRequest.new files_info
# #
# Create the authentication message, including files info # Connection to the service
#
client = IPC::Client.new service_name
#
# Sending the authentication message, including files info
# #
token = Token.new 1002, "karchnu" token = Token.new 1002, "karchnu"
@ -69,6 +81,10 @@ authentication_message = AuthenticationMessage.new token, files_info
client.send(1.to_u8, authentication_message.to_json) client.send(1.to_u8, authentication_message.to_json)
#
# Receiving a response
#
m = client.read m = client.read
# puts "message received: #{m.to_s}" # puts "message received: #{m.to_s}"
# puts "message received payload: #{String.new m.payload}" # puts "message received payload: #{String.new m.payload}"

View File

@ -1,5 +1,16 @@
require "uuid" require "uuid"
enum MessageType
Error
AuthenticationMessage
Response
Transfer
end
# For now, upload and download are sequentials.
# In a future version, we will be able to send
# arbitrary parts of each file.
class Token class Token
JSON.mapping({ JSON.mapping({
uid: Int32, uid: Int32,
@ -10,28 +21,62 @@ class Token
end end
end end
# Who knows, maybe someday we will be on UDP, too.
#class SHA256
# JSON.mapping({
# chunk: Slice(UInt8)
# })
#end
# A file has a name, a size and tags.
class FileInfo class FileInfo
JSON.mapping({ JSON.mapping({
name: String, name: String,
size: UInt32, size: UInt64,
# list of SHA256, if we are on UDP
# chunks: Array(SHA256),
tags: Array(String)? tags: Array(String)?
}) })
# debugging constructor
def initialize(@name, @size, @tags = nil) def initialize(@name, @size, @tags = nil)
# If on UDP
# @chunks = Array(SHA256).new
# arbitrary values here
end end
def initialize(file : File, @tags = nil) def initialize(file : File, @tags = nil)
@name = file.basename @name = File.basename file.path
@size = file.size @size = file.size
end end
end end
class Request
end
class UploadRequest < Request
property files_to_upload : Array(FileInfo)
def initialize(@files_to_upload)
end
end
# WIP
class DownloadRequest < Request
property names : Array(String)?,
property tags : Array(String)?
def initialize(@names = nil, @tags = nil)
end
end
class AuthenticationMessage class AuthenticationMessage
JSON.mapping({ JSON.mapping({
mid: String, mid: String,
token: Token, token: Token,
files: Array(FileInfo), requests: Array(Requests)
tags: Array(String)?
}) })
def initialize(@token, @files, @tags = nil) def initialize(@token, @files, @tags = nil)
@ -50,7 +95,7 @@ class Response
end end
end end
class Transfer class TransferMessage
JSON.mapping({ JSON.mapping({
mid: String, mid: String,
chunk: String, chunk: String,

View File

@ -2,13 +2,16 @@ require "json"
require "./common.cr" require "./common.cr"
files_info = Array(FileInfo).new
files_info << FileInfo.new "file.txt", 4123.to_u64, %w(important truc machin)
token = Token.new 1002, "karchnu" token = Token.new 1002, "karchnu"
authentication_message = AuthenticationMessage.new token authentication_message = AuthenticationMessage.new token, files_info
# TODO, TEST, DEBUG, XXX, FIXME # TODO, TEST, DEBUG, XXX, FIXME
pp! authentication_message.to_json pp! authentication_message.to_json
am_from_json = AuthenticationMessage.from_json authentication_message.to_json am_from_json = AuthenticationMessage.from_json authentication_message.to_json
pp! am_from_json pp! am_from_json

View File

@ -30,33 +30,50 @@ OptionParser.parse do |parser|
end end
# keep track of connected users # keep track of connected users and their requests
# TODO: requests should be handled concurrently
class User class User
property uid : Int32
property token : Token property token : Token
property requests : Array(Request)
def initialize(@token) def initialize(@token)
@uid = token.uid
end end
end end
# list of connected users # list of connected users
# fd => User # fd => uid
connected_users = Hash(Int32, User).new connected_users = Hash(Int32, Int32).new
users_status = Hash(Int32, User).new
service = IPC::SwitchingService.new service_name service = IPC::SwitchingService.new service_name
def receiving_files(user : User, event : IPC::Event::Message)
end
# Could be the reception of a file or a file request
def request_handling(user : User, event : IPC::Event::Message)
puts "request handling"
#
# Here we get requests from the message received
#
end
service.loop do |event| service.loop do |event|
case event case event
when IPC::Event::Timer when IPC::Event::Timer
puts "#{CORANGE}IPC::Event::Timer#{CRESET}" puts "#{CORANGE}IPC::Event::Timer#{CRESET}"
# puts "Disconnected client is: #{client_name}"
when IPC::Event::Connection when IPC::Event::Connection
puts "#{CBLUE}IPC::Event::Connection: #{event.connection.fd}#{CRESET}" puts "#{CBLUE}IPC::Event::Connection: #{event.connection.fd}#{CRESET}"
when IPC::Event::Disconnection when IPC::Event::Disconnection
puts "#{CBLUE}IPC::Event::Disconnection: #{event.connection.fd}#{CRESET}" puts "#{CBLUE}IPC::Event::Disconnection: #{event.connection.fd}#{CRESET}"
connected_users.select! do |fd, user| connected_users.select! do |fd, uid|
fd != event.connection.fd fd != event.connection.fd
end end
@ -72,31 +89,57 @@ service.loop do |event|
puts "#{CBLUE}IPC::Event::Message#{CRESET}: #{event.connection.fd}" puts "#{CBLUE}IPC::Event::Message#{CRESET}: #{event.connection.fd}"
# 1. test if the client is already authenticated # 1. test if the client is already authenticated
if user = connected_users[event.connection.fd]? if userid = connected_users[event.connection.fd]?
puts "User is connected: #{user.token.login}" puts "User is connected: #{user.token.login}"
request_handling users_status[userid], event
else else
puts "User is not currently connected" puts "User is not currently connected"
authentication_message = AuthenticationMessage.from_json(String.new event.message.payload) # The first message sent to the server has to be the AuthenticationMessage.
# Users sent their token (JWT) to authenticate themselves.
# The token contains the user id, its login and a few other parameters.
# (see the authd documentation).
authentication_message =
AuthenticationMessage.from_json(
String.new event.message.payload
)
authentication_message.files.each do |file| # Is the user already recorded in users_status?
puts "uploading #{file.name} - #{file.size} bytes" if users_status[authentication_message.token.uid]?
end puts "We already knew the user #{authentication_message.token.uid}"
pp! users_status[authentication_message.token.uid]
else
# AuthenticationMessage includes requests.
new_user =
User.new authentication_message.token,
authentication_message.requests
connected_users[event.connection.fd] = new_user.uid
# record the new user in users_status
users_status[new_user.uid] = new_user
new_user = User.new authentication_message.token
connected_users[event.connection.fd] = new_user
puts "New user is: #{new_user.token.login}" puts "New user is: #{new_user.token.login}"
response = Response.new authentication_message.mid, "Ok"
event.connection.send 2.to_u8, response.to_json
end end
# The user is now connected.
user = users_status[authentication_message.token.uid]
# puts "New connected client is: #{client_name}" # We verify the user's rights to upload files.
# TODO RIGHTS
# if user wants to upload but not allowed to: Response
# if user wants to get a file but not allowed to: Response
# The first message is the connection. # The user is authorized to upload files.
# Users sent their token (JWT) to authenticate.
# From the token, we get the user id, its login and a few other parameters (see the authd documentation). # TODO: quotas
# Quotas are not defined yet.
# Sending a response.
# The response is "Ok" when the message is well received and authorized.
response = Response.new authentication_message.mid, "Ok"
event.connection.send MessageType::Response.to_u8, response.to_json
end
else else
raise "Event type not supported." raise "Event type not supported."
end end