authd/src/main.cr

205 lines
4.9 KiB
Crystal
Raw Normal View History

2018-09-22 19:08:28 +02:00
require "uuid"
require "option_parser"
2019-02-19 20:45:19 +01:00
require "openssl"
2018-09-22 19:08:28 +02:00
require "jwt"
2019-11-17 15:56:35 +01:00
require "passwd"
require "ipc"
require "fs"
require "./authd.cr"
extend AuthD
2019-11-23 01:08:05 +01:00
class AuthD::Service
property registrations_allowed = false
def initialize(@passwd : Passwd, @jwt_key : String, @extras_root : String)
2018-12-19 13:54:19 +01:00
end
2019-11-03 13:17:24 +01:00
2019-11-23 01:08:05 +01:00
def handle_request(request : AuthD::Request?, connection : IPC::Connection)
case request
2019-11-22 18:14:52 +01:00
when Request::GetToken
2019-11-23 01:08:05 +01:00
user = @passwd.get_user request.login, request.password
if user.nil?
2019-11-23 01:08:05 +01:00
return Response::Error.new "invalid credentials"
end
2019-11-23 01:08:05 +01:00
token = JWT.encode user.to_h, @jwt_key, JWT::Algorithm::HS256
2019-11-22 22:55:12 +01:00
2019-11-22 23:13:34 +01:00
Response::Token.new token
2019-11-22 18:14:52 +01:00
when Request::AddUser
2019-11-23 01:08:05 +01:00
if request.shared_key != @jwt_key
return Response::Error.new "invalid authentication key"
end
2019-11-23 01:08:05 +01:00
if @passwd.user_exists? request.login
return Response::Error.new "login already used"
2018-12-19 13:54:19 +01:00
end
2019-11-23 01:08:05 +01:00
user = @passwd.add_user request.login, request.password
2018-12-19 13:54:19 +01:00
2019-11-22 23:13:34 +01:00
Response::UserAdded.new user
2019-11-22 18:14:52 +01:00
when Request::GetUserByCredentials
2019-11-23 01:08:05 +01:00
user = @passwd.get_user request.login, request.password
if user
2019-11-22 23:13:34 +01:00
Response::User.new user
else
2019-11-22 23:13:34 +01:00
Response::Error.new "user not found"
end
2019-11-22 18:14:52 +01:00
when Request::GetUser
2019-11-23 01:08:05 +01:00
user = @passwd.get_user request.uid
2019-01-07 17:04:20 +01:00
if user
2019-11-22 23:13:34 +01:00
Response::User.new user
2019-01-07 17:04:20 +01:00
else
2019-11-22 23:13:34 +01:00
Response::Error.new "user not found"
2019-01-07 17:04:20 +01:00
end
2019-11-22 18:14:52 +01:00
when Request::ModUser
2019-11-23 01:08:05 +01:00
if request.shared_key != @jwt_key
return Response::Error.new "invalid authentication key"
end
2019-05-29 16:06:11 +02:00
password_hash = request.password.try do |s|
Passwd.hash_password s
end
2019-11-23 01:08:05 +01:00
@passwd.mod_user request.uid, password_hash: password_hash
2019-05-29 16:06:11 +02:00
2019-11-22 23:13:34 +01:00
Response::UserEdited.new request.uid
when Request::Register
if ! @registrations_allowed
return Response::Error.new "registrations not allowed"
end
if @passwd.user_exists? request.login
return Response::Error.new "login already used"
end
user = @passwd.add_user request.login, request.password
Response::UserAdded.new user
when Request::GetExtra
user = get_user_from_token request.token
return Response::Error.new "invalid token" unless user
storage = FS::Hash(String, JSON::Any).new "#{@extras_root}/#{user.uid}"
Response::Extra.new user.uid, request.name, storage[request.name]?
when Request::SetExtra
user = get_user_from_token request.token
return Response::Error.new "invalid token" unless user
storage = FS::Hash(String, JSON::Any).new "#{@extras_root}/#{user.uid}"
storage[request.name] = request.extra
Response::ExtraUpdated.new user.uid, request.name, request.extra
when Request::UpdatePassword
user = @passwd.get_user request.login, request.old_password
return Response::Error.new "invalid credentials" unless user
password_hash = Passwd.hash_password request.new_password
@passwd.mod_user user.uid, password_hash: password_hash
Response::UserEdited.new user.uid
2019-12-09 21:57:38 +01:00
when Request::ListUsers
request.token.try do |token|
user = get_user_from_token token
return Response::Error.new "unauthorized" unless user
return Response::Error.new "unauthorized" unless user.groups.any? &.==("authd")
end
request.key.try do |key|
return Response::Error.new "unauthorized" unless key == @jwt_key
end
return Response::Error.new "unauthorized" unless request.key || request.token
Response::UsersList.new @passwd.get_all_users
2019-11-22 23:13:34 +01:00
else
Response::Error.new "unhandled request type"
end
2019-11-23 01:08:05 +01:00
end
def get_user_from_token(token)
user, meta = JWT.decode token, @jwt_key, JWT::Algorithm::HS256
Passwd::User.from_json user.to_json
end
2019-11-23 01:08:05 +01:00
def run
##
# Provides a JWT-based authentication scheme for service-specific users.
IPC::Service.new "auth" do |event|
if event.is_a? IPC::Exception
puts "oh no"
pp! event
next
end
case event
when IPC::Event::Message
begin
request = Request.from_ipc event.message
2019-11-23 01:08:05 +01:00
response = handle_request request, event.connection
2019-11-23 01:08:05 +01:00
event.connection.send response
rescue e
STDERR.puts "error: #{e.message}"
end
2019-11-23 01:08:05 +01:00
end
end
end
end
authd_passwd_file = "passwd"
authd_group_file = "group"
authd_jwt_key = "nico-nico-nii"
authd_registrations = false
authd_extra_storage = "storage"
2019-11-23 01:08:05 +01:00
OptionParser.parse do |parser|
parser.on "-u file", "--passwd-file file", "passwd file." do |name|
authd_passwd_file = name
end
parser.on "-g file", "--group-file file", "group file." do |name|
authd_group_file = name
end
parser.on "-K file", "--key-file file", "JWT key file" do |file_name|
authd_jwt_key = File.read(file_name).chomp
end
parser.on "-S dir", "--extra-storage dir", "Storage for extra user-data." do |directory|
authd_extra_storage = directory
end
parser.on "-R", "--allow-registrations" do
authd_registrations = true
end
2019-11-23 01:08:05 +01:00
parser.on "-h", "--help", "Show this help" do
puts parser
2019-11-22 23:13:34 +01:00
2019-11-23 01:08:05 +01:00
exit 0
end
2018-09-22 19:08:28 +02:00
end
2019-11-23 01:08:05 +01:00
passwd = Passwd.new authd_passwd_file, authd_group_file
AuthD::Service.new(passwd, authd_jwt_key, authd_extra_storage).tap do |authd|
authd.registrations_allowed = authd_registrations
end.run
2019-11-23 01:08:05 +01:00