diff --git a/src/authd.cr b/src/authd.cr index 23255da..776603f 100644 --- a/src/authd.cr +++ b/src/authd.cr @@ -10,6 +10,9 @@ require "ipc" require "baguette-crystal-base" +# In any message, a user can be referred by its Int32 uid or its login. +alias UserID = Int32 | String + # Allows get configuration from a provided file. # See Baguette::Configuration::Base.get class Baguette::Configuration diff --git a/src/authd/client.cr b/src/authd/client.cr index ae221fb..23a50b8 100644 --- a/src/authd/client.cr +++ b/src/authd/client.cr @@ -158,13 +158,12 @@ module AuthD end end - def mod_user(uid_or_login : Int32 | String, password : String? = nil, email : String? = nil, phone : String? = nil, avatar : String? = nil) : Bool | Exception + def mod_user(uid_or_login : Int32 | String, password : String? = nil, email : String? = nil, phone : String? = nil) : Bool | Exception request = Request::ModUser.new uid_or_login request.password = password if password request.email = email if email request.phone = phone if phone - request.avatar = avatar if avatar send_now request diff --git a/src/authd/user.cr b/src/authd/user.cr index 3802efb..121319e 100644 --- a/src/authd/user.cr +++ b/src/authd/user.cr @@ -71,5 +71,22 @@ class AuthD::User def to_public : Public Public.new @uid, @login, @admin, @profile, @date_registration end -end + def assert_permission(service : String, resource : String, level : User::PermissionLevel) + return if @admin # skip if admin + + permissions = @permissions[service]? + unless permissions + raise AdminAuthorizationException.new "unauthorized (not admin nor in #{service} group)" + end + + rights = permissions[resource]? + unless rights + raise AdminAuthorizationException.new "unauthorized (no rights on '#{service}/#{resource}')" + end + + if rights < level + raise AdminAuthorizationException.new "unauthorized (insufficient rights on '#{service}/#{resource}')" + end + end +end diff --git a/src/requests/admin.cr b/src/requests/admin.cr index bcddf56..4c4747e 100644 --- a/src/requests/admin.cr +++ b/src/requests/admin.cr @@ -49,28 +49,24 @@ class AuthD::Request end AuthD.requests << AddUser - IPC::JSON.message ModUser, 5 do - property user : Int32 | String + property user : UserID property admin : Bool = false property password : String? = nil property email : String? = nil property phone : String? = nil - property avatar : String? = nil - def initialize(@user, @admin, @password, @email, @phone, @avatar) + def initialize(@user, @admin, @password, @email, @phone) end def handle(authd : AuthD::Service, fd : Int32) logged_user = authd.get_logged_user? fd - return Response::Error.new "you must be logged" if logged_user.nil? user = authd.user? @user - return Response::Error.new "user not found" if user.nil? - # Only an admin can create an admin. + # Only an admin can uprank someone. if @admin return Response::Error.new "unauthorized (not admin)" unless logged_user.admin end diff --git a/src/requests/delete.cr b/src/requests/delete.cr index e2ab993..2e7779e 100644 --- a/src/requests/delete.cr +++ b/src/requests/delete.cr @@ -1,32 +1,28 @@ class AuthD::Request IPC::JSON.message Delete, 17 do # Deletion can be triggered by either an admin or the related user. - property user : String | Int32 + property user : UserID def initialize(@user) end def handle(authd : AuthD::Service, fd : Int32) - user_to_delete = authd.user? @user - return Response::Error.new "invalid user" if user_to_delete.nil? - - # Get currently logged user. logged_user = authd.get_logged_user? fd - if logged_user.nil? - return Response::Error.new "you must be logged" - end + return Response::Error.new "you must be logged" if logged_user.nil? + + # Get the full AuthD::User instance, not just the public view. + user_to_delete = authd.user? logged_user.uid + return Response::Error.new "unknown user" if user_to_delete.nil? unless logged_user.admin # Is the logged user the target? - if logged_user.uid != user_to_delete.uid - return Response::Error.new "invalid credentials" - end + return Response::Error.new "invalid credentials" if logged_user.uid != user_to_delete.uid end # User or admin is now verified: let's proceed with the user deletion. authd.users_per_login.delete user_to_delete.login - # TODO: if the current user is deleted, unlog! + # If the current user is deleted, unlog! if logged_user.uid == user_to_delete.uid authd.close fd authd.logged_users.delete fd diff --git a/src/requests/list.cr b/src/requests/list.cr index bb7ab7b..3b83dc0 100644 --- a/src/requests/list.cr +++ b/src/requests/list.cr @@ -1,38 +1,17 @@ class AuthD::Request IPC::JSON.message ListUsers, 8 do - property token : String? = nil - property key : String? = nil - - def initialize(@token, @key) + def initialize() end def handle(authd : AuthD::Service, fd : Int32) - # FIXME: Lines too long, repeatedly (>80c with 4c tabs). - @token.try do |token| - user = authd.get_user_from_token token + logged_user = authd.get_logged_user? fd + return Response::Error.new "you must be logged" if logged_user.nil? - return Response::Error.new "unauthorized (user not found from token)" unless user + user = authd.user? logged_user.uid + return Response::Error.new "user not found" if user.nil? - # Test if the user is a moderator. - if permissions = user.permissions["authd"]? - if rights = permissions["*"]? - if rights >= User::PermissionLevel::Read - else - raise AdminAuthorizationException.new "unauthorized (insufficient rights on '*')" - end - else - raise AdminAuthorizationException.new "unauthorized (no rights on '*')" - end - else - raise AdminAuthorizationException.new "unauthorized (user not in authd group)" - end - end - - @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 @key || @token + # Test if the user is a moderator. + user.assert_permission("authd", "*", User::PermissionLevel::Read) Response::UsersList.new authd.users.to_h.map &.[1].to_public end diff --git a/src/requests/login.cr b/src/requests/login.cr index b0e6073..3ef8945 100644 --- a/src/requests/login.cr +++ b/src/requests/login.cr @@ -13,9 +13,7 @@ class AuthD::Request return Response::Error.new "invalid credentials" end - if user.nil? - return Response::Error.new "invalid credentials" - end + return Response::Error.new "invalid credentials" if user.nil? if user.password_hash != authd.hash_password @password return Response::Error.new "invalid credentials" diff --git a/src/requests/password.cr b/src/requests/password.cr index 2034e8d..9494a75 100644 --- a/src/requests/password.cr +++ b/src/requests/password.cr @@ -1,22 +1,16 @@ class AuthD::Request IPC::JSON.message UpdatePassword, 7 do - property login : String - property old_password : String property new_password : String - def initialize(@login, @old_password, @new_password) + def initialize(@new_password) end def handle(authd : AuthD::Service, fd : Int32) - user = authd.users_per_login.get? @login + logged_user = authd.get_logged_user? fd + return Response::Error.new "you must be logged" if logged_user.nil? - unless user - return Response::Error.new "invalid credentials" - end - - if authd.hash_password(@old_password) != user.password_hash - return Response::Error.new "invalid credentials" - end + user = authd.user? logged_user.uid + return Response::Error.new "user not found" if user.nil? user.password_hash = authd.hash_password @new_password @@ -28,7 +22,7 @@ class AuthD::Request AuthD.requests << UpdatePassword IPC::JSON.message PasswordRecovery, 11 do - property user : Int32 | String + property user : UserID property password_renew_key : String property new_password : String @@ -36,16 +30,8 @@ class AuthD::Request end def handle(authd : AuthD::Service, fd : Int32) - uid_or_login = @user - user = if uid_or_login.is_a? Int32 - authd.users_per_uid.get? uid_or_login.to_s - else - authd.users_per_login.get? uid_or_login - end - - if user.nil? - return Response::Error.new "user not found" - end + user = authd.user? @user + return Response::Error.new "user not found" if user.nil? if user.password_renew_key == @password_renew_key user.password_hash = authd.hash_password @new_password diff --git a/src/requests/permissions.cr b/src/requests/permissions.cr index 490a0f7..bf211e9 100644 --- a/src/requests/permissions.cr +++ b/src/requests/permissions.cr @@ -1,6 +1,6 @@ class AuthD::Request IPC::JSON.message CheckPermission, 9 do - property user : Int32 | String + property user : UserID property service : String property resource : String @@ -13,16 +13,8 @@ class AuthD::Request return Response::Error.new "you must be logged" if logged_user.nil? return Response::Error.new "unauthorized (not admin)" unless logged_user.admin - user = case u = @user - when .is_a? Int32 - authd.users_per_uid.get? u.to_s - else - authd.users_per_login.get? u - end - - if user.nil? - return Response::Error.new "no such user" - end + user = authd.user? @user + return Response::Error.new "no such user" if user.nil? service = @service service_permissions = user.permissions[service]? @@ -43,7 +35,7 @@ class AuthD::Request AuthD.requests << CheckPermission IPC::JSON.message SetPermission, 10 do - property user : Int32 | String + property user : UserID property service : String property resource : String property permission : ::AuthD::User::PermissionLevel @@ -57,8 +49,7 @@ class AuthD::Request return Response::Error.new "you must be logged" if logged_user.nil? return Response::Error.new "unauthorized (not admin)" unless logged_user.admin - user = authd.users_per_uid.get? @user.to_s - + user = authd.user? @user return Response::Error.new "no such user" if user.nil? service = @service diff --git a/src/server.cr b/src/server.cr index 3e8561a..4895db8 100644 --- a/src/server.cr +++ b/src/server.cr @@ -83,7 +83,7 @@ class AuthD::Service < IPC @logged_users[fd]? end - def user?(uid_or_login : Int32 | String) + def user?(uid_or_login : UserID) if uid_or_login.is_a? Int32 @users_per_uid.get? uid_or_login.to_s else