|
@ -6,17 +6,27 @@ require "exception" |
|
|
require "crypto/bcrypt/password" |
|
|
require "crypto/bcrypt/password" |
|
|
require "uuid" |
|
|
require "uuid" |
|
|
require "uuid/json" |
|
|
require "uuid/json" |
|
|
|
|
|
require "socket/udp_socket" |
|
|
require "../../config" |
|
|
require "../../config" |
|
|
|
|
|
|
|
|
|
|
|
def fread(file) : String |
|
|
|
|
|
slc = Bytes.new file.size |
|
|
|
|
|
count = file.read slc |
|
|
|
|
|
String.new slc[0, count] |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
def authenticate(user : String, token : UUID) : (User | Nil) |
|
|
def authenticate(user : String, token : UUID) : (User | Nil) |
|
|
user_file = User.from_json File.read(Statics.data_path+"user/"+user) |
|
|
|
|
|
if nil == user_file.tokens.not_nil!.find{ |tok| token == tok} |
|
|
|
|
|
nil |
|
|
|
|
|
else |
|
|
|
|
|
user_file.password_hash = "" |
|
|
|
|
|
user_file |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
user_file : User | Nil = nil |
|
|
|
|
|
user_file = KVStore.access.fetch!("user/"+user) |
|
|
|
|
|
if user_file.nil? |
|
|
|
|
|
return nil |
|
|
|
|
|
end |
|
|
|
|
|
if nil == user_file.not_nil!.tokens.not_nil!.find{ |tok| token == tok} |
|
|
|
|
|
nil |
|
|
|
|
|
else |
|
|
|
|
|
user_file.not_nil!.password_hash = "" |
|
|
|
|
|
user_file |
|
|
|
|
|
end |
|
|
end |
|
|
end |
|
|
|
|
|
|
|
|
def authenticate!(user : String, token : UUID) : User |
|
|
def authenticate!(user : String, token : UUID) : User |
|
@ -31,39 +41,81 @@ def authenticate_admin!(user : String, token : UUID) : User |
|
|
raise "Administrator only" |
|
|
raise "Administrator only" |
|
|
end |
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
ws "/socket/user/authenticate" do |socket| |
|
|
|
|
|
socket.on_message do |message| |
|
|
|
|
|
json = JSON.parse(message) |
|
|
|
|
|
if(authenticate(json["user"].to_s, UUID.new(json["api_token"].to_s)).nil?) |
|
|
|
|
|
json.as_h.["valid"]=JSON::Any.new false |
|
|
|
|
|
else |
|
|
|
|
|
json.as_h.["valid"]=JSON::Any.new true |
|
|
|
|
|
end |
|
|
|
|
|
socket.send json.to_json |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
udp_listener = UDPSocket.new |
|
|
|
|
|
udp_listener.bind "0.0.0.0", 3000 |
|
|
|
|
|
udp_mutex = Mutex.new |
|
|
|
|
|
(1..Statics.nb_udp_listeners).each do |
|
|
|
|
|
spawn do |
|
|
|
|
|
while true |
|
|
|
|
|
message : String | Nil = nil |
|
|
|
|
|
address : Socket::IPAddress | Nil = nil |
|
|
|
|
|
udp_mutex.synchronize do |
|
|
|
|
|
message, address = udp_listener.receive |
|
|
|
|
|
end |
|
|
|
|
|
json = JSON.parse(message.not_nil!) |
|
|
|
|
|
if(authenticate(json["user"].to_s, UUID.new(json["api_token"].to_s)).nil?) |
|
|
|
|
|
json.as_h.["valid"]=JSON::Any.new false |
|
|
|
|
|
else |
|
|
|
|
|
json.as_h.["valid"]=JSON::Any.new true |
|
|
|
|
|
end |
|
|
|
|
|
udp_mutex.synchronize do |
|
|
|
|
|
udp_listener.send json.to_json, address.not_nil! |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
post "/login" do |context| |
|
|
post "/login" do |context| |
|
|
user : User |
|
|
|
|
|
user_file : User |
|
|
|
|
|
begin |
|
|
|
|
|
user = User.from_json context.request.body.not_nil! |
|
|
|
|
|
user_file = User.from_json File.read(Statics.data_path+"user/"+user.email) |
|
|
|
|
|
rescue ex |
|
|
|
|
|
halt context, status_code: 403, response: ex.to_s |
|
|
|
|
|
end |
|
|
|
|
|
if Crypto::Bcrypt::Password.new(user_file.password_hash.not_nil!) == user.password_hash.not_nil! |
|
|
|
|
|
else |
|
|
|
|
|
halt context, status_code: 403, response: "Invalid password" |
|
|
|
|
|
end |
|
|
|
|
|
token = UUID.random() |
|
|
|
|
|
if user_file.tokens.nil? |
|
|
|
|
|
user_file.tokens = Array(UUID).new |
|
|
|
|
|
user_file.tokens.not_nil!<<token |
|
|
|
|
|
else |
|
|
|
|
|
user_file.tokens.not_nil!<<token |
|
|
|
|
|
end |
|
|
|
|
|
if user_file.tokens.not_nil!.size>5 |
|
|
|
|
|
user_file.tokens = user_file.tokens.not_nil!.last(5) |
|
|
|
|
|
end |
|
|
|
|
|
File.write(Statics.data_path+"user/"+user_file.email,user_file.to_json) |
|
|
|
|
|
|
|
|
user o">= User.from_json context.request.body.not_nil! |
|
|
|
|
|
user_file : User |
|
|
|
|
|
token : UUID | Nil = nil |
|
|
|
|
|
KVStore.access.transaction do |store| |
|
|
|
|
|
begin |
|
|
|
|
|
user_file = store.fetch! "user/"+user.email |
|
|
|
|
|
rescue ex |
|
|
|
|
|
halt context, status_code: 403, response: ex.to_s |
|
|
|
|
|
end |
|
|
|
|
|
if Crypto::Bcrypt::Password.new(user_file.password_hash.not_nil!) == user.password_hash.not_nil! |
|
|
|
|
|
else |
|
|
|
|
|
halt context, status_code: 403, response: "Invalid password" |
|
|
|
|
|
end |
|
|
|
|
|
if user_file.tokens.nil? |
|
|
|
|
|
user_file.tokens = Array(UUID).new |
|
|
|
|
|
end |
|
|
|
|
|
token = UUID.random() |
|
|
|
|
|
user_file.tokens.not_nil!<<token |
|
|
|
|
|
if user_file.tokens.not_nil!.size>5 |
|
|
|
|
|
user_file.tokens = user_file.tokens.not_nil!.last(5) |
|
|
|
|
|
end |
|
|
|
|
|
store.push "user/"+user.email, user_file.to_json |
|
|
|
|
|
end |
|
|
context.response.content_type = "application/json" |
|
|
context.response.content_type = "application/json" |
|
|
token.to_json |
|
|
|
|
|
|
|
|
token.not_nil!.to_json |
|
|
end |
|
|
end |
|
|
|
|
|
|
|
|
post "/logout" do |context| |
|
|
|
|
|
user = User.from_json context.request.body.not_nil! |
|
|
|
|
|
user_file = User.from_json File.read(Statics.data_path+"user/"+user.email) |
|
|
|
|
|
user_file.tokens=user_file.tokens.not_nil!-user.tokens.not_nil! |
|
|
|
|
|
File.write(Statics.data_path+"user/"+user_file.email,user_file.to_json) |
|
|
|
|
|
|
|
|
get "/logout" do |context| |
|
|
|
|
|
user = context.request.headers["user"] |
|
|
|
|
|
|
|
|
|
|
|
KVStore.access.transaction do |store| |
|
|
|
|
|
user_file = store.fetch!("user/"+user) |
|
|
|
|
|
user_file.tokens.not_nil!.delete(UUID.new(context.request.headers["api_token"])) |
|
|
|
|
|
store.push "user/"+user, user_file.to_json |
|
|
|
|
|
end |
|
|
context.response.content_type = "application/json" |
|
|
context.response.content_type = "application/json" |
|
|
"OK".to_json |
|
|
"OK".to_json |
|
|
end |
|
|
end |
|
@ -74,10 +126,12 @@ post "/logout-all" do |context| |
|
|
user = authenticate!(context.request.headers["user"],UUID.new(context.request.headers["api_token"])) |
|
|
user = authenticate!(context.request.headers["user"],UUID.new(context.request.headers["api_token"])) |
|
|
rescue ex |
|
|
rescue ex |
|
|
halt context, status_code: 403, response: ex.to_s |
|
|
halt context, status_code: 403, response: ex.to_s |
|
|
end |
|
|
|
|
|
user_file = User.from_json File.read(Statics.data_path+"user/"+user.email) |
|
|
|
|
|
user_file.tokens=Array(UUID).new |
|
|
|
|
|
File.write(Statics.data_path+"user/"+user_file.email,user_file.to_json) |
|
|
|
|
|
|
|
|
end |
|
|
|
|
|
KVStore.access.transaction do |store| |
|
|
|
|
|
user_file = store.fetch!("user/"+user.email) |
|
|
|
|
|
user_file.tokens=Array(UUID).new |
|
|
|
|
|
store.push "user/"+user_file.email, user_file.to_json |
|
|
|
|
|
end |
|
|
context.response.content_type = "application/json" |
|
|
context.response.content_type = "application/json" |
|
|
"OK".to_json |
|
|
"OK".to_json |
|
|
end |
|
|
end |
|
@ -87,18 +141,24 @@ post "/user" do |context| |
|
|
ph = user.password_hash |
|
|
ph = user.password_hash |
|
|
user.tokens = Array(UUID).new |
|
|
user.tokens = Array(UUID).new |
|
|
user.invoices = Array(Invoice).new |
|
|
user.invoices = Array(Invoice).new |
|
|
if ph.nil? |
|
|
|
|
|
raise Exception.new("No password provided") |
|
|
|
|
|
|
|
|
if ph.nil? |
|
|
|
|
|
halt context, status_code: 401, response: "No password provided" |
|
|
else |
|
|
else |
|
|
user.password_hash=Crypto::Bcrypt::Password.create(ph,cost: 12).to_s |
|
|
user.password_hash=Crypto::Bcrypt::Password.create(ph,cost: 12).to_s |
|
|
end |
|
|
end |
|
|
if Statics.email_regex.match(user.email)==nil |
|
|
if Statics.email_regex.match(user.email)==nil |
|
|
raise Exception.new("Bad email address") |
|
|
|
|
|
end |
|
|
|
|
|
if File.exists?(Statics.data_path+"user/"+user.email) |
|
|
|
|
|
raise Exception.new("Email address already in use") |
|
|
|
|
|
|
|
|
halt context, status_code: 401, response: "Bad email address provided" |
|
|
end |
|
|
end |
|
|
File.write(Statics.data_path+"user/"+user.email,user.to_json) |
|
|
|
|
|
|
|
|
begin |
|
|
|
|
|
KVStore.access.transaction do |store| |
|
|
|
|
|
if store.fetch("user/"+user.email)!=nil |
|
|
|
|
|
raise IndexError.new |
|
|
|
|
|
end |
|
|
|
|
|
store.push "user/"+user.email, user.to_json |
|
|
|
|
|
end |
|
|
|
|
|
rescue ex |
|
|
|
|
|
halt context, status_code: 401, response: "Email address already in use" |
|
|
|
|
|
end |
|
|
context.response.content_type = "application/json" |
|
|
context.response.content_type = "application/json" |
|
|
"OK".to_json |
|
|
"OK".to_json |
|
|
end |
|
|
end |
|
@ -114,6 +174,17 @@ get "/user/tokens" do |context| |
|
|
user.tokens.to_json |
|
|
user.tokens.to_json |
|
|
end |
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
get "/user/authenticate" do |context| |
|
|
|
|
|
user : User |
|
|
|
|
|
begin |
|
|
|
|
|
user = authenticate!(context.request.headers["user"],UUID.new(context.request.headers["api_token"])) |
|
|
|
|
|
rescue ex |
|
|
|
|
|
halt context, status_code: 403, response: ex.to_s |
|
|
|
|
|
end |
|
|
|
|
|
context.response.content_type = "application/json" |
|
|
|
|
|
"OK".to_json |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
get "/user/address" do |context| |
|
|
get "/user/address" do |context| |
|
|
user : User |
|
|
user : User |
|
|
begin |
|
|
begin |
|
@ -132,37 +203,43 @@ post "/user/address" do |context| |
|
|
rescue ex |
|
|
rescue ex |
|
|
halt context, status_code: 403, response: ex.to_s |
|
|
halt context, status_code: 403, response: ex.to_s |
|
|
end |
|
|
end |
|
|
addresses = Array(Address).from_json(context.request.body.not_nil!).not_nil! |
|
|
|
|
|
user_file = User.from_json File.read(Statics.data_path+"user/"+user.email) |
|
|
|
|
|
old_list=user_file.addresses |
|
|
|
|
|
if old_list.nil? |
|
|
|
|
|
else |
|
|
|
|
|
addresses=old_list+addresses |
|
|
|
|
|
end |
|
|
|
|
|
user_file.addresses=addresses |
|
|
|
|
|
File.write(Statics.data_path+"user/"+user.email,user_file.to_json) |
|
|
|
|
|
|
|
|
addresses = Array(Address).from_json(context.request.body.not_nil!).not_nil! |
|
|
|
|
|
|
|
|
|
|
|
KVStore.access.transaction do |store| |
|
|
|
|
|
user_file = store.fetch!("user/"+user.email) |
|
|
|
|
|
old_list=user_file.addresses |
|
|
|
|
|
if old_list.nil? |
|
|
|
|
|
else |
|
|
|
|
|
addresses=old_list+addresses |
|
|
|
|
|
end |
|
|
|
|
|
user_file.addresses=addresses |
|
|
|
|
|
store.push "user/"+user_file.email, user_file.to_json |
|
|
|
|
|
end |
|
|
context.response.content_type = "application/json" |
|
|
context.response.content_type = "application/json" |
|
|
"OK".to_json |
|
|
"OK".to_json |
|
|
end |
|
|
end |
|
|
|
|
|
|
|
|
delete "/user/address" do |context| |
|
|
delete "/user/address" do |context| |
|
|
user = authenticate!(context.request.headers["user"],UUID.new(context.request.headers["api_token"])) |
|
|
user = authenticate!(context.request.headers["user"],UUID.new(context.request.headers["api_token"])) |
|
|
addresses = Array(Address).from_json(context.request.body.not_nil!).not_nil! |
|
|
|
|
|
user_file = User.from_json File.read(Statics.data_path+"user/"+user.email) |
|
|
|
|
|
old_list=user_file.addresses |
|
|
|
|
|
if old_list.nil? |
|
|
|
|
|
addresses=Array(Address).new |
|
|
|
|
|
else |
|
|
|
|
|
addresses=old_list.select do |v| |
|
|
|
|
|
isin=false |
|
|
|
|
|
addresses.each do |va| |
|
|
|
|
|
isin |= v==va |
|
|
|
|
|
end |
|
|
|
|
|
!isin |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
user_file.addresses=addresses |
|
|
|
|
|
File.write(Statics.data_path+"user/"+user.email,user_file.to_json) |
|
|
|
|
|
|
|
|
addresses = Array(Address).from_json(context.request.body.not_nil!).not_nil! |
|
|
|
|
|
|
|
|
|
|
|
KVStore.access.transaction do |store| |
|
|
|
|
|
user_file = store.fetch!("user/"+user.email) |
|
|
|
|
|
old_list=user_file.addresses |
|
|
|
|
|
if old_list.nil? |
|
|
|
|
|
addresses=Array(Address).new |
|
|
|
|
|
else |
|
|
|
|
|
addresses=old_list.select do |v| |
|
|
|
|
|
isin=false |
|
|
|
|
|
addresses.each do |va| |
|
|
|
|
|
isin |= v==va |
|
|
|
|
|
end |
|
|
|
|
|
!isin |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
user_file.addresses=addresses |
|
|
|
|
|
store.push "user/"+user_file.email, user_file.to_json |
|
|
|
|
|
end |
|
|
context.response.content_type = "application/json" |
|
|
context.response.content_type = "application/json" |
|
|
"OK".to_json |
|
|
"OK".to_json |
|
|
end |
|
|
end |
|
@ -171,9 +248,17 @@ get "/user" do |context| |
|
|
context.response.content_type = "application/json" |
|
|
context.response.content_type = "application/json" |
|
|
user : User | Nil |
|
|
user : User | Nil |
|
|
begin |
|
|
begin |
|
|
user = authenticate!(context.request.headers["user"],UUID.new(context.request.headers["api_token"])) |
|
|
|
|
|
rescue ex |
|
|
|
|
|
halt context, status_code: 403, response: ex.to_s |
|
|
|
|
|
|
|
|
user = authenticate!(context.request.headers["user"],UUID.new(context.request.headers["api_token"])) |
|
|
|
|
|
rescue ex |
|
|
|
|
|
resp = String::Builder.build do |builder| |
|
|
|
|
|
if ENV["KEMAL_ENV"] == "test" |
|
|
|
|
|
ex.inspect_with_backtrace builder |
|
|
|
|
|
else |
|
|
|
|
|
ex.to_s builder |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
halt context, status_code: 403, response: resp |
|
|
end |
|
|
end |
|
|
user.not_nil! |
|
|
user.not_nil! |
|
|
end |
|
|
end |
|
|
|
|
|
|