From 3879e7915d5e6fb9cbe93709a94bd19434ebb4e3 Mon Sep 17 00:00:00 2001 From: Karchnu Date: Fri, 20 Nov 2020 11:53:10 +0100 Subject: [PATCH] Compiles. --- src/authd.cr | 11 ++- src/main.cr | 19 +++-- src/network.cr | 2 +- src/requests/requests.cr | 172 +++++++++++++++++++-------------------- 4 files changed, 107 insertions(+), 97 deletions(-) diff --git a/src/authd.cr b/src/authd.cr index 47a3ff8..e01350e 100644 --- a/src/authd.cr +++ b/src/authd.cr @@ -13,8 +13,11 @@ class Baguette::Configuration property login : 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 + + def initialize + end end end @@ -22,6 +25,12 @@ module AuthD class Exception < ::Exception end + class UserNotFound < ::Exception + end + + class AuthenticationInfoLacking < ::Exception + end + # Not used for now. class NotLoggedException < ::Exception end diff --git a/src/main.cr b/src/main.cr index 4607e7e..c3983c7 100644 --- a/src/main.cr +++ b/src/main.cr @@ -31,7 +31,8 @@ end 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 # DB and its indexes. @@ -52,6 +53,8 @@ class AuthD::Service if @configuration.recreate_indexes @users.reindex_everything! end + + super "authd" end def hash_password(password : String) : String @@ -88,6 +91,9 @@ class AuthD::Service response = begin request.handle self, event + rescue e : UserNotFound + Baguette::Log.error "#{request_name} user not found" + AuthD::Response::Error.new "authorization error" rescue e : AuthorizationException Baguette::Log.error "#{request_name} authorization error" AuthD::Response::Error.new "authorization error" @@ -126,13 +132,12 @@ class AuthD::Service end def run - ## - # 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 + Baguette::Log.title "Starting authd" - server.loop do |event| + @base_timer = @configuration.ipc_timer + @timer = @configuration.ipc_timer + + self.loop do |event| case event when IPC::Event::Timer Baguette::Log.debug "Timer" if @configuration.print_ipc_timer diff --git a/src/network.cr b/src/network.cr index 0cc9c69..cdadf28 100644 --- a/src/network.cr +++ b/src/network.cr @@ -3,7 +3,7 @@ require "json" require "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" end end diff --git a/src/requests/requests.cr b/src/requests/requests.cr index a04324b..7c505a8 100644 --- a/src/requests/requests.cr +++ b/src/requests/requests.cr @@ -8,7 +8,7 @@ class AuthD::Request def handle(authd : AuthD::Service, event : IPC::Event::Events) begin - user = authd.users_per_login.get request.login + user = authd.users_per_login.get @login rescue e : DODB::MissingEntry return Response::Error.new "invalid credentials" end @@ -17,7 +17,7 @@ class AuthD::Request return Response::Error.new "invalid credentials" end - if user.password_hash != authd.hash_password request.password + if user.password_hash != authd.hash_password @password return Response::Error.new "invalid credentials" end @@ -49,27 +49,27 @@ class AuthD::Request def handle(authd : AuthD::Service, event : IPC::Event::Events) # No verification of the users' informations when an admin adds it. # 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" end - if authd.users_per_login.get? request.login + if authd.users_per_login.get? @login return Response::Error.new "login already used" end - if authd.configuration.require_email && request.email.nil? + if authd.configuration.require_email && @email.nil? return Response::Error.new "email required" end - password_hash = authd.hash_password request.password + password_hash = authd.hash_password @password uid = authd.new_uid - user = User.new uid, request.login, password_hash - user.contact.email = request.email unless request.email.nil? - user.contact.phone = request.phone unless request.phone.nil? + user = User.new uid, @login, password_hash + user.contact.email = @email unless @email.nil? + user.contact.phone = @phone unless @phone.nil? - request.profile.try do |profile| + @profile.try do |profile| user.profile = profile end @@ -91,7 +91,7 @@ class AuthD::Request end 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? return Response::Error.new "user not found" @@ -102,7 +102,7 @@ class AuthD::Request end # 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 else return Response::Error.new "wrong activation key" @@ -122,7 +122,7 @@ class AuthD::Request end 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 authd.users_per_uid.get? uid_or_login.to_s else @@ -146,13 +146,13 @@ class AuthD::Request end 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 return Response::Error.new "invalid credentials" end - if authd.hash_password(request.password) != user.password_hash + if authd.hash_password(@password) != user.password_hash return Response::Error.new "invalid credentials" end @@ -179,11 +179,11 @@ class AuthD::Request end 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" end - uid_or_login = request.user + uid_or_login = @user user = if uid_or_login.is_a? Int32 authd.users_per_uid.get? uid_or_login.to_s else @@ -194,15 +194,15 @@ class AuthD::Request return Response::Error.new "user not found" end - request.password.try do |s| + @password.try do |s| user.password_hash = authd.hash_password s end - request.email.try do |email| + @email.try do |email| user.contact.email = email end - request.phone.try do |phone| + @phone.try do |phone| user.contact.phone = phone end @@ -228,11 +228,11 @@ class AuthD::Request return Response::Error.new "registrations not allowed" end - if authd.users_per_login.get? request.login + if authd.users_per_login.get? @login return Response::Error.new "login already used" end - if authd.configuration.require_email && request.email.nil? + if authd.configuration.require_email && @email.nil? return Response::Error.new "email required" end @@ -242,10 +242,10 @@ class AuthD::Request return Response::Error.new "No activation URL were entered. Cannot send activation mails." end - if ! request.email.nil? + if ! @email.nil? # Test on the email address format. grok = Grok.new [ "%{EMAILADDRESS:email}" ] - result = grok.parse request.email.not_nil! + result = grok.parse @email.not_nil! email = result["email"]? if email.nil? @@ -254,18 +254,18 @@ class AuthD::Request end # 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" end uid = authd.new_uid - password = authd.hash_password request.password + password = authd.hash_password @password - user = User.new uid, request.login, password - user.contact.email = request.email unless request.email.nil? - user.contact.phone = request.phone unless request.phone.nil? + user = User.new uid, @login, password + user.contact.email = @email unless @email.nil? + user.contact.phone = @phone unless @phone.nil? - request.profile.try do |profile| + @profile.try do |profile| user.profile = profile end @@ -313,17 +313,17 @@ class AuthD::Request end 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 return Response::Error.new "invalid credentials" 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" 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 @@ -341,7 +341,7 @@ class AuthD::Request def handle(authd : AuthD::Service, event : IPC::Event::Events) # 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 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)) end - request.key.try do |key| + @key.try do |key| return Response::Error.new "unauthorized (wrong shared key)" unless key == authd.configuration.shared_key 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 end @@ -374,7 +374,7 @@ class AuthD::Request def handle(authd : AuthD::Service, event : IPC::Event::Events) authorized = false - if key = request.shared_key + if key = @shared_key if key == authd.configuration.shared_key authorized = true else @@ -382,14 +382,14 @@ class AuthD::Request end end - if token = request.token + if token = @token user = authd.get_user_from_token token if user.nil? return Response::Error.new "token does not match user" 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" end @@ -400,7 +400,7 @@ class AuthD::Request return Response::Error.new "unauthorized" end - user = case u = request.user + user = case u = @user when .is_a? Int32 authd.users_per_uid.get? u.to_s else @@ -411,20 +411,20 @@ class AuthD::Request return Response::Error.new "no such user" end - service = request.service + service = @service service_permissions = user.permissions[service]? 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 - resource_permissions = service_permissions[request.resource]? + resource_permissions = service_permissions[@resource]? 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 - return Response::PermissionCheck.new service, request.resource, user.uid, resource_permissions + return Response::PermissionCheck.new service, @resource, user.uid, resource_permissions end end AuthD.requests << CheckPermission @@ -441,17 +441,17 @@ class AuthD::Request end 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" end - user = authd.users_per_uid.get? request.user.to_s + user = authd.users_per_uid.get? @user.to_s if user.nil? return Response::Error.new "no such user" end - service = request.service + service = @service service_permissions = user.permissions[service]? if service_permissions.nil? @@ -459,15 +459,15 @@ class AuthD::Request user.permissions[service] = service_permissions end - if request.permission.none? - service_permissions.delete request.resource + if @permission.none? + service_permissions.delete @resource else - service_permissions[request.resource] = request.permission + service_permissions[@resource] = @permission end 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 AuthD.requests << SetPermission @@ -481,7 +481,7 @@ class AuthD::Request end 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 authd.users_per_uid.get? uid_or_login.to_s else @@ -492,8 +492,8 @@ class AuthD::Request return Response::Error.new "user not found" end - if user.password_renew_key == request.password_renew_key - user.password_hash = authd.hash_password request.new_password + if user.password_renew_key == @password_renew_key + user.password_hash = authd.hash_password @new_password else return Response::Error.new "renew key not valid" end @@ -515,7 +515,7 @@ class AuthD::Request end 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 authd.users_per_uid.get? uid_or_login.to_s else @@ -526,7 +526,7 @@ class AuthD::Request return Response::Error.new "no such user" end - if user.contact.email != request.email + if user.contact.email != @email # Same error as when users are not found. return Response::Error.new "no such user" end @@ -575,7 +575,7 @@ class AuthD::Request end 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 @@ -609,11 +609,11 @@ class AuthD::Request end 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 - new_profile = request.new_profile + new_profile = @new_profile profile = user.profile || Hash(String, JSON::Any).new @@ -648,43 +648,39 @@ class AuthD::Request end def handle(authd : AuthD::Service, event : IPC::Event::Events) - user = if token = request.token - user = authd.get_user_from_token token + user = if 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 - elsif shared_key = request.shared_key - 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 + u = if u.is_a? Int32 + authd.users_per_uid.get? u.to_s else - authd.users_per_login.get? user + authd.users_per_login.get? u end + raise UserNotFound.new unless u - return Response::Error.new "invalid user" unless user - - user + u else - return Response::Error.new "no token or shared_key/user pair" + raise AuthenticationInfoLacking.new end 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| - if request.new_profile.has_key? key + if @new_profile.has_key? key return Response::Error.new "tried to edit read only key" end end end - request.new_profile.each do |key, value| + @new_profile.each do |key, value| new_profile[key] = value end @@ -707,11 +703,11 @@ class AuthD::Request end 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 - if email = request.email + if email = @email # FIXME: This *should* require checking the new mail, with # a new activation key and everything else. user.contact.email = email @@ -739,7 +735,7 @@ class AuthD::Request end 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 authd.users_per_uid.get? uid_or_login.to_s else @@ -752,11 +748,11 @@ class AuthD::Request # Either the request comes from an admin or the user. # 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 else - login = request.login - pass = request.password + login = @login + pass = @password if login.nil? || pass.nil? return Response::Error.new "authentication failed (no shared key, no login)" end @@ -798,7 +794,7 @@ class AuthD::Request end 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