More fine-grained authorizations and remove useless message GetUserByCredentials.

This commit is contained in:
Philippe Pittoli 2023-06-12 14:40:03 +02:00
parent 3e40a4ce3d
commit cb358ef1de
11 changed files with 68 additions and 88 deletions

View File

@ -11,10 +11,10 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, fd : Int32) def handle(authd : AuthD::Service, fd : Int32)
logged_user = authd.get_logged_user? fd logged_user = authd.get_logged_user_full? fd
return Response::Error.new "you must be logged" if logged_user.nil? return Response::Error.new "you must be logged" if logged_user.nil?
return Response::Error.new "unauthorized (not admin)" unless logged_user.admin
logged_user.assert_permission("authd", "*", User::PermissionLevel::Admin)
if authd.users_per_login.get? @login if authd.users_per_login.get? @login
return Response::Error.new "login already used" return Response::Error.new "login already used"
@ -60,7 +60,7 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, fd : Int32) def handle(authd : AuthD::Service, fd : Int32)
logged_user = authd.get_logged_user? fd logged_user = authd.get_logged_user_full? fd
return Response::Error.new "you must be logged" if logged_user.nil? return Response::Error.new "you must be logged" if logged_user.nil?
user = authd.user? @user user = authd.user? @user
@ -68,7 +68,9 @@ class AuthD::Request
# Only an admin can uprank someone. # Only an admin can uprank someone.
if @admin if @admin
return Response::Error.new "unauthorized (not admin)" unless logged_user.admin logged_user.assert_permission("authd", "*", User::PermissionLevel::Admin)
else
logged_user.assert_permission("authd", "*", User::PermissionLevel::Edit)
end end
@password.try do |s| @password.try do |s|

View File

@ -7,26 +7,22 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, fd : Int32) def handle(authd : AuthD::Service, fd : Int32)
logged_user = authd.get_logged_user? fd logged_user = authd.get_logged_user_full? fd
return Response::Error.new "you must be logged" if logged_user.nil? return Response::Error.new "you must be logged" if logged_user.nil?
# Get the full AuthD::User instance, not just the public view.
user = authd.user? logged_user.uid
return Response::Error.new "unknown user" if user.nil?
if email = @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 logged_user.contact.email = email
end end
if phone = @phone if phone = @phone
user.contact.phone = phone logged_user.contact.phone = phone
end end
authd.users_per_uid.update user authd.users_per_uid.update logged_user
Response::UserEdited.new user.uid Response::UserEdited.new logged_user.uid
end end
end end
AuthD.requests << EditContacts AuthD.requests << EditContacts
@ -36,15 +32,11 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, fd : Int32) def handle(authd : AuthD::Service, fd : Int32)
logged_user = authd.get_logged_user? fd logged_user = authd.get_logged_user_full? fd
return Response::Error.new "you must be logged" if logged_user.nil? return Response::Error.new "you must be logged" if logged_user.nil?
# Get the full AuthD::User instance, not just the public view. _c = logged_user.contact
user = authd.user? logged_user.uid Response::Contacts.new logged_user.uid, _c.email, _c.phone
return Response::Error.new "unknown user" if user.nil?
_c = user.contact
Response::Contacts.new user.uid, _c.email, _c.phone
end end
end end
AuthD.requests << GetContacts AuthD.requests << GetContacts

View File

@ -1,23 +1,22 @@
class AuthD::Request class AuthD::Request
IPC::JSON.message Delete, 17 do IPC::JSON.message Delete, 17 do
# Deletion can be triggered by either an admin or the related user. # Deletion can be triggered by either an admin or the related user.
property user : UserID property user : UserID | Nil = nil
def initialize(@user) def initialize(@user = nil)
end end
def handle(authd : AuthD::Service, fd : Int32) def handle(authd : AuthD::Service, fd : Int32)
logged_user = authd.get_logged_user? fd logged_user = authd.get_logged_user_full? fd
return Response::Error.new "you must be logged" if logged_user.nil? 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 = if u = @user
user_to_delete = authd.user? logged_user.uid logged_user.assert_permission("authd", "*", User::PermissionLevel::Admin)
return Response::Error.new "unknown user" if user_to_delete.nil? authd.user? u
else
unless logged_user.admin logged_user
# Is the logged user the target?
return Response::Error.new "invalid credentials" if logged_user.uid != user_to_delete.uid
end end
return Response::Error.new "unknown user" if user_to_delete.nil?
# User or admin is now verified: let's proceed with the user deletion. # User or admin is now verified: let's proceed with the user deletion.
authd.users_per_login.delete user_to_delete.login authd.users_per_login.delete user_to_delete.login

View File

@ -4,14 +4,11 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, fd : Int32) def handle(authd : AuthD::Service, fd : Int32)
logged_user = authd.get_logged_user? fd logged_user = authd.get_logged_user_full? fd
return Response::Error.new "you must be logged" if logged_user.nil? return Response::Error.new "you must be logged" if logged_user.nil?
user = authd.user? logged_user.uid
return Response::Error.new "user not found" if user.nil?
# Test if the user is a moderator. # Test if the user is a moderator.
user.assert_permission("authd", "*", User::PermissionLevel::Read) logged_user.assert_permission("authd", "*", User::PermissionLevel::Read)
Response::UsersList.new authd.users.to_h.map &.[1].to_public Response::UsersList.new authd.users.to_h.map &.[1].to_public
end end

View File

@ -10,9 +10,13 @@ class AuthD::Request
begin begin
user = authd.users_per_login.get @login user = authd.users_per_login.get @login
rescue e : DODB::MissingEntry rescue e : DODB::MissingEntry
# This lack of proper error message is intentional.
# Let attackers try to authenticate themselves with a wrong login.
return Response::Error.new "invalid credentials" return Response::Error.new "invalid credentials"
end end
# This line is basically just to please the Crystal's type system.
# No user means DODB::MissingEntry, so it's already covered.
return Response::Error.new "invalid credentials" if user.nil? return Response::Error.new "invalid credentials" if user.nil?
if user.password_hash != authd.hash_password @password if user.password_hash != authd.hash_password @password

View File

@ -6,17 +6,13 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, fd : Int32) def handle(authd : AuthD::Service, fd : Int32)
logged_user = authd.get_logged_user? fd logged_user = authd.get_logged_user_full? fd
return Response::Error.new "you must be logged" if logged_user.nil? return Response::Error.new "you must be logged" if logged_user.nil?
user = authd.user? logged_user.uid logged_user.password_hash = authd.hash_password @new_password
return Response::Error.new "user not found" if user.nil? authd.users_per_uid.update logged_user.uid.to_s, logged_user
user.password_hash = authd.hash_password @new_password Response::UserEdited.new logged_user.uid
authd.users_per_uid.update user.uid.to_s, user
Response::UserEdited.new user.uid
end end
end end
AuthD.requests << UpdatePassword AuthD.requests << UpdatePassword
@ -31,6 +27,8 @@ class AuthD::Request
def handle(authd : AuthD::Service, fd : Int32) def handle(authd : AuthD::Service, fd : Int32)
user = authd.user? @user user = authd.user? @user
# This is a way for an attacker to know what are the valid logins.
# Not sure I care enough to fix this.
return Response::Error.new "user not found" if user.nil? return Response::Error.new "user not found" if user.nil?
if user.password_renew_key == @password_renew_key if user.password_renew_key == @password_renew_key
@ -56,6 +54,8 @@ class AuthD::Request
def handle(authd : AuthD::Service, fd : Int32) def handle(authd : AuthD::Service, fd : Int32)
user = authd.user? @user user = authd.user? @user
# This is a way for an attacker to know what are the valid logins.
# Not sure I care enough to fix this.
return Response::Error.new "user not found" if user.nil? return Response::Error.new "user not found" if user.nil?
# Create a new random key for password renewal. # Create a new random key for password renewal.

View File

@ -8,10 +8,9 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, fd : Int32) def handle(authd : AuthD::Service, fd : Int32)
logged_user = authd.get_logged_user? fd logged_user = authd.get_logged_user_full? fd
return Response::Error.new "you must be logged" if logged_user.nil? return Response::Error.new "you must be logged" if logged_user.nil?
return Response::Error.new "unauthorized (not admin)" unless logged_user.admin logged_user.assert_permission("authd", "*", User::PermissionLevel::Admin)
user = authd.user? @user user = authd.user? @user
return Response::Error.new "no such user" if user.nil? return Response::Error.new "no such user" if user.nil?
@ -40,10 +39,9 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, fd : Int32) def handle(authd : AuthD::Service, fd : Int32)
logged_user = authd.get_logged_user? fd logged_user = authd.get_logged_user_full? fd
return Response::Error.new "you must be logged" if logged_user.nil? return Response::Error.new "you must be logged" if logged_user.nil?
return Response::Error.new "unauthorized (not admin)" unless logged_user.admin logged_user.assert_permission("authd", "*", User::PermissionLevel::Admin)
user = authd.user? @user user = authd.user? @user
return Response::Error.new "no such user" if user.nil? return Response::Error.new "no such user" if user.nil?

View File

@ -6,14 +6,12 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, fd : Int32) def handle(authd : AuthD::Service, fd : Int32)
logged_user = authd.get_logged_user? fd logged_user = authd.get_logged_user_full? fd
return Response::Error.new "you must be logged" if logged_user.nil? return Response::Error.new "you must be logged" if logged_user.nil?
user = authd.user? logged_user.uid profile = logged_user.profile || Hash(String, JSON::Any).new
return Response::Error.new "user not found" if user.nil?
profile = user.profile || Hash(String, JSON::Any).new
# Skip this verification for authd administrators.
unless logged_user.admin unless logged_user.admin
authd.configuration.read_only_profile_keys.each do |key| authd.configuration.read_only_profile_keys.each do |key|
if @new_profile[key]? != profile[key]? if @new_profile[key]? != profile[key]?
@ -22,11 +20,11 @@ class AuthD::Request
end end
end end
user.profile = @new_profile logged_user.profile = @new_profile
authd.users_per_uid.update user.uid.to_s, user authd.users_per_uid.update logged_user.uid.to_s, logged_user
Response::User.new user.to_public Response::User.new logged_user.to_public
end end
end end
AuthD.requests << ReplaceProfile AuthD.requests << ReplaceProfile
@ -42,14 +40,14 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, fd : Int32) def handle(authd : AuthD::Service, fd : Int32)
logged_user = authd.get_logged_user? fd logged_user = authd.get_logged_user_full? fd
return Response::Error.new "you must be logged" if logged_user.nil? return Response::Error.new "you must be logged" if logged_user.nil?
user = if u = @user user = if u = @user
raise AdminAuthorizationException.new unless logged_user.admin logged_user.assert_permission("authd", "*", User::PermissionLevel::Edit)
authd.user? u authd.user? u
else else
authd.user? logged_user.uid logged_user
end end
return Response::Error.new "user not found" if user.nil? return Response::Error.new "user not found" if user.nil?

View File

@ -6,9 +6,9 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, fd : Int32) def handle(authd : AuthD::Service, fd : Int32)
logged_user = authd.get_logged_user? fd logged_user = authd.get_logged_user_full? fd
return Response::Error.new "you must be logged" if logged_user.nil? return Response::Error.new "you must be logged" if logged_user.nil?
return Response::Error.new "unauthorized (not admin)" unless logged_user.admin logged_user.assert_permission("authd", "*", User::PermissionLevel::Read)
pattern = Regex.new @user, Regex::Options::IGNORE_CASE pattern = Regex.new @user, Regex::Options::IGNORE_CASE

View File

@ -8,6 +8,8 @@ class AuthD::Request
def handle(authd : AuthD::Service, fd : Int32) def handle(authd : AuthD::Service, fd : Int32)
user = authd.user? @user user = authd.user? @user
# This is a way for an attacker to know what are the valid logins.
# Not sure I care enough to fix this.
return Response::Error.new "user not found" if user.nil? return Response::Error.new "user not found" if user.nil?
if user.contact.activation_key.nil? if user.contact.activation_key.nil?
@ -35,36 +37,16 @@ class AuthD::Request
end end
def handle(authd : AuthD::Service, fd : Int32) 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 user = authd.user? @user
# This is a way for an attacker to know what are the valid logins.
# Not sure I care enough to fix this.
return Response::Error.new "user not found" if user.nil? return Response::Error.new "user not found" if user.nil?
Response::User.new user.to_public Response::User.new user.to_public
end end
end end
AuthD.requests << GetUser AuthD.requests << GetUser
IPC::JSON.message GetUserByCredentials, 4 do
property login : String
property password : String
def initialize(@login, @password)
end
def handle(authd : AuthD::Service, fd : Int32)
user = authd.users_per_login.get? @login
return Response::Error.new "invalid credentials" unless user
if authd.hash_password(@password) != user.password_hash
return Response::Error.new "invalid credentials"
end
user.date_last_connection = Time.local
# Change the date of the last connection.
authd.users_per_uid.update user.uid.to_s, user
Response::User.new user.to_public
end
end
AuthD.requests << GetUserByCredentials
end end

View File

@ -83,6 +83,14 @@ class AuthD::Service < IPC
@logged_users[fd]? @logged_users[fd]?
end end
# Instead of just getting the public view of a logged user,
# get the actual User instance.
def get_logged_user_full?(fd : Int32)
if u = @logged_users[fd]?
user? u.uid
end
end
def user?(uid_or_login : UserID) def user?(uid_or_login : UserID)
if uid_or_login.is_a? Int32 if uid_or_login.is_a? Int32
@users_per_uid.get? uid_or_login.to_s @users_per_uid.get? uid_or_login.to_s