Compiles.

rewrite
Karchnu 2020-11-20 11:53:10 +01:00
parent 6a6ccfe37f
commit 3879e7915d
4 changed files with 107 additions and 97 deletions

View File

@ -15,6 +15,9 @@ class Baguette::Configuration
property pass : String? = nil property pass : String? = nil
property shared_key : String = "nico-nico-nii" # Default authd key, as per the specs. :eyes: property shared_key : String = "nico-nico-nii" # Default authd key, as per the specs. :eyes:
property shared_key_file : String? = nil property shared_key_file : String? = nil
def initialize
end
end end
end end
@ -22,6 +25,12 @@ module AuthD
class Exception < ::Exception class Exception < ::Exception
end end
class UserNotFound < ::Exception
end
class AuthenticationInfoLacking < ::Exception
end
# Not used for now. # Not used for now.
class NotLoggedException < ::Exception class NotLoggedException < ::Exception
end end

View File

@ -31,7 +31,8 @@ end
require "./network" require "./network"
class AuthD::Service # Provides a JWT-based authentication scheme for service-specific users.
class AuthD::Service < IPC::Server
property configuration : Baguette::Configuration::Auth property configuration : Baguette::Configuration::Auth
# DB and its indexes. # DB and its indexes.
@ -52,6 +53,8 @@ class AuthD::Service
if @configuration.recreate_indexes if @configuration.recreate_indexes
@users.reindex_everything! @users.reindex_everything!
end end
super "authd"
end end
def hash_password(password : String) : String def hash_password(password : String) : String
@ -88,6 +91,9 @@ class AuthD::Service
response = begin response = begin
request.handle self, event request.handle self, event
rescue e : UserNotFound
Baguette::Log.error "#{request_name} user not found"
AuthD::Response::Error.new "authorization error"
rescue e : AuthorizationException rescue e : AuthorizationException
Baguette::Log.error "#{request_name} authorization error" Baguette::Log.error "#{request_name} authorization error"
AuthD::Response::Error.new "authorization error" AuthD::Response::Error.new "authorization error"
@ -126,13 +132,12 @@ class AuthD::Service
end end
def run def run
## Baguette::Log.title "Starting authd"
# Provides a JWT-based authentication scheme for service-specific users.
server = IPC::Server.new "auth"
server.base_timer = @configuration.ipc_timer
server.timer = @configuration.ipc_timer
server.loop do |event| @base_timer = @configuration.ipc_timer
@timer = @configuration.ipc_timer
self.loop do |event|
case event case event
when IPC::Event::Timer when IPC::Event::Timer
Baguette::Log.debug "Timer" if @configuration.print_ipc_timer Baguette::Log.debug "Timer" if @configuration.print_ipc_timer

View File

@ -3,7 +3,7 @@ require "json"
require "ipc/json" require "ipc/json"
class IPC::JSON class IPC::JSON
def handle(service : IPC::Server, event : IPC::Event::Events) def handle(service : AuthD::Service, event : IPC::Event::Events)
raise "unimplemented" raise "unimplemented"
end end
end end

View File

@ -8,7 +8,7 @@ class AuthD::Request
def handle(authd : AuthD::Service, event : IPC::Event::Events) def handle(authd : AuthD::Service, event : IPC::Event::Events)
begin begin
user = authd.users_per_login.get request.login user = authd.users_per_login.get @login
rescue e : DODB::MissingEntry rescue e : DODB::MissingEntry
return Response::Error.new "invalid credentials" return Response::Error.new "invalid credentials"
end end
@ -17,7 +17,7 @@ class AuthD::Request
return Response::Error.new "invalid credentials" return Response::Error.new "invalid credentials"
end end
if user.password_hash != authd.hash_password request.password if user.password_hash != authd.hash_password @password
return Response::Error.new "invalid credentials" return Response::Error.new "invalid credentials"
end end
@ -49,27 +49,27 @@ class AuthD::Request
def handle(authd : AuthD::Service, event : IPC::Event::Events) def handle(authd : AuthD::Service, event : IPC::Event::Events)
# No verification of the users' informations when an admin adds it. # No verification of the users' informations when an admin adds it.
# No mail address verification. # No mail address verification.
if request.shared_key != authd.configuration.shared_key if @shared_key != authd.configuration.shared_key
return Response::Error.new "invalid authentication key" return Response::Error.new "invalid authentication key"
end end
if authd.users_per_login.get? request.login if authd.users_per_login.get? @login
return Response::Error.new "login already used" return Response::Error.new "login already used"
end end
if authd.configuration.require_email && request.email.nil? if authd.configuration.require_email && @email.nil?
return Response::Error.new "email required" return Response::Error.new "email required"
end end
password_hash = authd.hash_password request.password password_hash = authd.hash_password @password
uid = authd.new_uid uid = authd.new_uid
user = User.new uid, request.login, password_hash user = User.new uid, @login, password_hash
user.contact.email = request.email unless request.email.nil? user.contact.email = @email unless @email.nil?
user.contact.phone = request.phone unless request.phone.nil? user.contact.phone = @phone unless @phone.nil?
request.profile.try do |profile| @profile.try do |profile|
user.profile = profile user.profile = profile
end end
@ -91,7 +91,7 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, event : IPC::Event::Events) def handle(authd : AuthD::Service, event : IPC::Event::Events)
user = authd.users_per_login.get? request.login user = authd.users_per_login.get? @login
if user.nil? if user.nil?
return Response::Error.new "user not found" return Response::Error.new "user not found"
@ -102,7 +102,7 @@ class AuthD::Request
end end
# remove the user contact activation key: the email is validated # remove the user contact activation key: the email is validated
if user.contact.activation_key == request.activation_key if user.contact.activation_key == @activation_key
user.contact.activation_key = nil user.contact.activation_key = nil
else else
return Response::Error.new "wrong activation key" return Response::Error.new "wrong activation key"
@ -122,7 +122,7 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, event : IPC::Event::Events) def handle(authd : AuthD::Service, event : IPC::Event::Events)
uid_or_login = request.user uid_or_login = @user
user = if uid_or_login.is_a? Int32 user = if uid_or_login.is_a? Int32
authd.users_per_uid.get? uid_or_login.to_s authd.users_per_uid.get? uid_or_login.to_s
else else
@ -146,13 +146,13 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, event : IPC::Event::Events) def handle(authd : AuthD::Service, event : IPC::Event::Events)
user = authd.users_per_login.get? request.login user = authd.users_per_login.get? @login
unless user unless user
return Response::Error.new "invalid credentials" return Response::Error.new "invalid credentials"
end end
if authd.hash_password(request.password) != user.password_hash if authd.hash_password(@password) != user.password_hash
return Response::Error.new "invalid credentials" return Response::Error.new "invalid credentials"
end end
@ -179,11 +179,11 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, event : IPC::Event::Events) def handle(authd : AuthD::Service, event : IPC::Event::Events)
if request.shared_key != authd.configuration.shared_key if @shared_key != authd.configuration.shared_key
return Response::Error.new "invalid authentication key" return Response::Error.new "invalid authentication key"
end end
uid_or_login = request.user uid_or_login = @user
user = if uid_or_login.is_a? Int32 user = if uid_or_login.is_a? Int32
authd.users_per_uid.get? uid_or_login.to_s authd.users_per_uid.get? uid_or_login.to_s
else else
@ -194,15 +194,15 @@ class AuthD::Request
return Response::Error.new "user not found" return Response::Error.new "user not found"
end end
request.password.try do |s| @password.try do |s|
user.password_hash = authd.hash_password s user.password_hash = authd.hash_password s
end end
request.email.try do |email| @email.try do |email|
user.contact.email = email user.contact.email = email
end end
request.phone.try do |phone| @phone.try do |phone|
user.contact.phone = phone user.contact.phone = phone
end end
@ -228,11 +228,11 @@ class AuthD::Request
return Response::Error.new "registrations not allowed" return Response::Error.new "registrations not allowed"
end end
if authd.users_per_login.get? request.login if authd.users_per_login.get? @login
return Response::Error.new "login already used" return Response::Error.new "login already used"
end end
if authd.configuration.require_email && request.email.nil? if authd.configuration.require_email && @email.nil?
return Response::Error.new "email required" return Response::Error.new "email required"
end end
@ -242,10 +242,10 @@ class AuthD::Request
return Response::Error.new "No activation URL were entered. Cannot send activation mails." return Response::Error.new "No activation URL were entered. Cannot send activation mails."
end end
if ! request.email.nil? if ! @email.nil?
# Test on the email address format. # Test on the email address format.
grok = Grok.new [ "%{EMAILADDRESS:email}" ] grok = Grok.new [ "%{EMAILADDRESS:email}" ]
result = grok.parse request.email.not_nil! result = grok.parse @email.not_nil!
email = result["email"]? email = result["email"]?
if email.nil? if email.nil?
@ -254,18 +254,18 @@ class AuthD::Request
end end
# In this case we should not accept its registration. # In this case we should not accept its registration.
if request.password.size < 4 if @password.size < 4
return Response::Error.new "password too short" return Response::Error.new "password too short"
end end
uid = authd.new_uid uid = authd.new_uid
password = authd.hash_password request.password password = authd.hash_password @password
user = User.new uid, request.login, password user = User.new uid, @login, password
user.contact.email = request.email unless request.email.nil? user.contact.email = @email unless @email.nil?
user.contact.phone = request.phone unless request.phone.nil? user.contact.phone = @phone unless @phone.nil?
request.profile.try do |profile| @profile.try do |profile|
user.profile = profile user.profile = profile
end end
@ -313,17 +313,17 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, event : IPC::Event::Events) def handle(authd : AuthD::Service, event : IPC::Event::Events)
user = authd.users_per_login.get? request.login user = authd.users_per_login.get? @login
unless user unless user
return Response::Error.new "invalid credentials" return Response::Error.new "invalid credentials"
end end
if authd.hash_password(request.old_password) != user.password_hash if authd.hash_password(@old_password) != user.password_hash
return Response::Error.new "invalid credentials" return Response::Error.new "invalid credentials"
end end
user.password_hash = authd.hash_password request.new_password user.password_hash = authd.hash_password @new_password
authd.users_per_uid.update user.uid.to_s, user authd.users_per_uid.update user.uid.to_s, user
@ -341,7 +341,7 @@ class AuthD::Request
def handle(authd : AuthD::Service, event : IPC::Event::Events) def handle(authd : AuthD::Service, event : IPC::Event::Events)
# FIXME: Lines too long, repeatedly (>80c with 4c tabs). # FIXME: Lines too long, repeatedly (>80c with 4c tabs).
request.token.try do |token| @token.try do |token|
user = authd.get_user_from_token token user = authd.get_user_from_token token
return Response::Error.new "unauthorized (user not found from token)" return Response::Error.new "unauthorized (user not found from token)"
@ -349,11 +349,11 @@ class AuthD::Request
return Response::Error.new "unauthorized (user not in authd group)" unless user.permissions["authd"]?.try(&.["*"].>=(User::PermissionLevel::Read)) return Response::Error.new "unauthorized (user not in authd group)" unless user.permissions["authd"]?.try(&.["*"].>=(User::PermissionLevel::Read))
end end
request.key.try do |key| @key.try do |key|
return Response::Error.new "unauthorized (wrong shared key)" unless key == authd.configuration.shared_key return Response::Error.new "unauthorized (wrong shared key)" unless key == authd.configuration.shared_key
end end
return Response::Error.new "unauthorized (no key nor token)" unless request.key || request.token return Response::Error.new "unauthorized (no key nor token)" unless @key || @token
Response::UsersList.new authd.users.to_h.map &.[1].to_public Response::UsersList.new authd.users.to_h.map &.[1].to_public
end end
@ -374,7 +374,7 @@ class AuthD::Request
def handle(authd : AuthD::Service, event : IPC::Event::Events) def handle(authd : AuthD::Service, event : IPC::Event::Events)
authorized = false authorized = false
if key = request.shared_key if key = @shared_key
if key == authd.configuration.shared_key if key == authd.configuration.shared_key
authorized = true authorized = true
else else
@ -382,14 +382,14 @@ class AuthD::Request
end end
end end
if token = request.token if token = @token
user = authd.get_user_from_token token user = authd.get_user_from_token token
if user.nil? if user.nil?
return Response::Error.new "token does not match user" return Response::Error.new "token does not match user"
end end
if user.login != request.user && user.uid != request.user if user.login != @user && user.uid != @user
return Response::Error.new "token does not match user" return Response::Error.new "token does not match user"
end end
@ -400,7 +400,7 @@ class AuthD::Request
return Response::Error.new "unauthorized" return Response::Error.new "unauthorized"
end end
user = case u = request.user user = case u = @user
when .is_a? Int32 when .is_a? Int32
authd.users_per_uid.get? u.to_s authd.users_per_uid.get? u.to_s
else else
@ -411,20 +411,20 @@ class AuthD::Request
return Response::Error.new "no such user" return Response::Error.new "no such user"
end end
service = request.service service = @service
service_permissions = user.permissions[service]? service_permissions = user.permissions[service]?
if service_permissions.nil? if service_permissions.nil?
return Response::PermissionCheck.new service, request.resource, user.uid, User::PermissionLevel::None return Response::PermissionCheck.new service, @resource, user.uid, User::PermissionLevel::None
end end
resource_permissions = service_permissions[request.resource]? resource_permissions = service_permissions[@resource]?
if resource_permissions.nil? if resource_permissions.nil?
return Response::PermissionCheck.new service, request.resource, user.uid, User::PermissionLevel::None return Response::PermissionCheck.new service, @resource, user.uid, User::PermissionLevel::None
end end
return Response::PermissionCheck.new service, request.resource, user.uid, resource_permissions return Response::PermissionCheck.new service, @resource, user.uid, resource_permissions
end end
end end
AuthD.requests << CheckPermission AuthD.requests << CheckPermission
@ -441,17 +441,17 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, event : IPC::Event::Events) def handle(authd : AuthD::Service, event : IPC::Event::Events)
unless request.shared_key == authd.configuration.shared_key unless @shared_key == authd.configuration.shared_key
return Response::Error.new "unauthorized" return Response::Error.new "unauthorized"
end end
user = authd.users_per_uid.get? request.user.to_s user = authd.users_per_uid.get? @user.to_s
if user.nil? if user.nil?
return Response::Error.new "no such user" return Response::Error.new "no such user"
end end
service = request.service service = @service
service_permissions = user.permissions[service]? service_permissions = user.permissions[service]?
if service_permissions.nil? if service_permissions.nil?
@ -459,15 +459,15 @@ class AuthD::Request
user.permissions[service] = service_permissions user.permissions[service] = service_permissions
end end
if request.permission.none? if @permission.none?
service_permissions.delete request.resource service_permissions.delete @resource
else else
service_permissions[request.resource] = request.permission service_permissions[@resource] = @permission
end end
authd.users_per_uid.update user.uid.to_s, user authd.users_per_uid.update user.uid.to_s, user
Response::PermissionSet.new user.uid, service, request.resource, request.permission Response::PermissionSet.new user.uid, service, @resource, @permission
end end
end end
AuthD.requests << SetPermission AuthD.requests << SetPermission
@ -481,7 +481,7 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, event : IPC::Event::Events) def handle(authd : AuthD::Service, event : IPC::Event::Events)
uid_or_login = request.user uid_or_login = @user
user = if uid_or_login.is_a? Int32 user = if uid_or_login.is_a? Int32
authd.users_per_uid.get? uid_or_login.to_s authd.users_per_uid.get? uid_or_login.to_s
else else
@ -492,8 +492,8 @@ class AuthD::Request
return Response::Error.new "user not found" return Response::Error.new "user not found"
end end
if user.password_renew_key == request.password_renew_key if user.password_renew_key == @password_renew_key
user.password_hash = authd.hash_password request.new_password user.password_hash = authd.hash_password @new_password
else else
return Response::Error.new "renew key not valid" return Response::Error.new "renew key not valid"
end end
@ -515,7 +515,7 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, event : IPC::Event::Events) def handle(authd : AuthD::Service, event : IPC::Event::Events)
uid_or_login = request.user uid_or_login = @user
user = if uid_or_login.is_a? Int32 user = if uid_or_login.is_a? Int32
authd.users_per_uid.get? uid_or_login.to_s authd.users_per_uid.get? uid_or_login.to_s
else else
@ -526,7 +526,7 @@ class AuthD::Request
return Response::Error.new "no such user" return Response::Error.new "no such user"
end end
if user.contact.email != request.email if user.contact.email != @email
# Same error as when users are not found. # Same error as when users are not found.
return Response::Error.new "no such user" return Response::Error.new "no such user"
end end
@ -575,7 +575,7 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, event : IPC::Event::Events) def handle(authd : AuthD::Service, event : IPC::Event::Events)
pattern = Regex.new request.user, Regex::Options::IGNORE_CASE pattern = Regex.new @user, Regex::Options::IGNORE_CASE
matching_users = Array(AuthD::User::Public).new matching_users = Array(AuthD::User::Public).new
@ -609,11 +609,11 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, event : IPC::Event::Events) def handle(authd : AuthD::Service, event : IPC::Event::Events)
user = authd.get_user_from_token request.token user = authd.get_user_from_token @token
return Response::Error.new "invalid user" unless user return Response::Error.new "invalid user" unless user
new_profile = request.new_profile new_profile = @new_profile
profile = user.profile || Hash(String, JSON::Any).new profile = user.profile || Hash(String, JSON::Any).new
@ -648,43 +648,39 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, event : IPC::Event::Events) def handle(authd : AuthD::Service, event : IPC::Event::Events)
user = if token = request.token user = if token = @token
user = authd.get_user_from_token token u = authd.get_user_from_token token
raise UserNotFound.new unless u
u
elsif shared_key = @shared_key
raise AdminAuthorizationException.new if shared_key != authd.configuration.shared_key
return Response::Error.new "invalid user" unless user u = @user
raise UserNotFound.new unless u
user u = if u.is_a? Int32
elsif shared_key = request.shared_key authd.users_per_uid.get? u.to_s
return Response::Error.new "invalid shared key" if shared_key != authd.configuration.shared_key
user = request.user
return Response::Error.new "invalid user" unless user
user = if user.is_a? Int32
authd.users_per_uid.get? user.to_s
else else
authd.users_per_login.get? user authd.users_per_login.get? u
end end
raise UserNotFound.new unless u
return Response::Error.new "invalid user" unless user u
user
else else
return Response::Error.new "no token or shared_key/user pair" raise AuthenticationInfoLacking.new
end end
new_profile = user.profile || Hash(String, JSON::Any).new new_profile = user.profile || Hash(String, JSON::Any).new
unless request.shared_key unless @shared_key
authd.configuration.read_only_profile_keys.each do |key| authd.configuration.read_only_profile_keys.each do |key|
if request.new_profile.has_key? key if @new_profile.has_key? key
return Response::Error.new "tried to edit read only key" return Response::Error.new "tried to edit read only key"
end end
end end
end end
request.new_profile.each do |key, value| @new_profile.each do |key, value|
new_profile[key] = value new_profile[key] = value
end end
@ -707,11 +703,11 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, event : IPC::Event::Events) def handle(authd : AuthD::Service, event : IPC::Event::Events)
user = authd.get_user_from_token request.token user = authd.get_user_from_token @token
return Response::Error.new "invalid user" unless user return Response::Error.new "invalid user" unless user
if email = request.email if email = @email
# FIXME: This *should* require checking the new mail, with # FIXME: This *should* require checking the new mail, with
# a new activation key and everything else. # a new activation key and everything else.
user.contact.email = email user.contact.email = email
@ -739,7 +735,7 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, event : IPC::Event::Events) def handle(authd : AuthD::Service, event : IPC::Event::Events)
uid_or_login = request.user uid_or_login = @user
user_to_delete = if uid_or_login.is_a? Int32 user_to_delete = if uid_or_login.is_a? Int32
authd.users_per_uid.get? uid_or_login.to_s authd.users_per_uid.get? uid_or_login.to_s
else else
@ -752,11 +748,11 @@ class AuthD::Request
# Either the request comes from an admin or the user. # Either the request comes from an admin or the user.
# Shared key == admin, check the key. # Shared key == admin, check the key.
if key = request.shared_key if key = @shared_key
return Response::Error.new "unauthorized (wrong shared key)" unless key == authd.configuration.shared_key return Response::Error.new "unauthorized (wrong shared key)" unless key == authd.configuration.shared_key
else else
login = request.login login = @login
pass = request.password pass = @password
if login.nil? || pass.nil? if login.nil? || pass.nil?
return Response::Error.new "authentication failed (no shared key, no login)" return Response::Error.new "authentication failed (no shared key, no login)"
end end
@ -798,7 +794,7 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, event : IPC::Event::Events) def handle(authd : AuthD::Service, event : IPC::Event::Events)
user = authd.get_user_from_token request.token user = authd.get_user_from_token @token
return Response::Error.new "invalid user" unless user return Response::Error.new "invalid user" unless user