Migration: draft.

This commit is contained in:
Philippe PITTOLI 2024-06-08 19:23:20 +02:00
parent 4ac27e2db1
commit 111adae98f
7 changed files with 103 additions and 1 deletions

View File

@ -49,6 +49,9 @@ validate:
get-user: get-user:
./bin/authc user get $(NAME) $(LOGIN_OPT) ./bin/authc user get $(NAME) $(LOGIN_OPT)
migrate-user:
./bin/authc user migrate $(NAME) $(PASSWORD_HASH) $(LOGIN_OPT)
SERVICE ?= 'auth' SERVICE ?= 'auth'
RESOURCE ?= '*' RESOURCE ?= '*'
UID ?= 1000 UID ?= 1000

View File

@ -70,6 +70,17 @@ module AuthD
], read ], read
end end
# Migration of a user from old code base (dnsmanager v1).
def migrate_user(login : String, password_hash_brkn : String)
send_now Request::MigrateUser.new login, password_hash_brkn
parse_message [
Response::UserAdded,
Response::ErrorMustBeAuthenticated,
Response::ErrorAlreadyUsedLogin,
Response::ErrorMailRequired
], read
end
def bootstrap(login : String, def bootstrap(login : String,
password : String, password : String,
email : String, email : String,

View File

@ -38,6 +38,7 @@ class AuthD::User
# Private. # Private.
property contact : Contact property contact : Contact
property password_hash_brkn : String? = nil # Old, broken algorithm.
property password_hash : String property password_hash : String
property password_renew_key : String? property password_renew_key : String?
# service => resource => permission level # service => resource => permission level

View File

@ -93,6 +93,17 @@ parser = OptionParser.new do |parser|
unrecognized_args_to_context_args.call parser, 2 unrecognized_args_to_context_args.call parser, 2
end end
parser.on "migrate", "Adding a user from old code base." do
parser.banner = "usage: user add login password-hash-brkn"
Baguette::Log.info "Adding a user to the DB."
Context.command = "user-migrate"
opt_authd_login.call parser
opt_profile.call parser
opt_help.call parser
# login password-hash-brkn
unrecognized_args_to_context_args.call parser, 2
end
parser.on "mod", "Modify a user account." do parser.on "mod", "Modify a user account." do
parser.banner = "Usage: user mod userid [-e email|-P profile] [opt]" parser.banner = "Usage: user mod userid [-e email|-P profile] [opt]"
Baguette::Log.info "Modify a user account." Baguette::Log.info "Modify a user account."

View File

@ -62,6 +62,7 @@ class Actions
# Require admin privileges. # Require admin privileges.
@the_call["user-add"] = ->user_add @the_call["user-add"] = ->user_add
@the_call["user-migrate"] = ->user_migrate
@the_call["user-mod"] = ->user_mod @the_call["user-mod"] = ->user_mod
@the_call["permission-set"] = ->permission_set @the_call["permission-set"] = ->permission_set
@ -87,6 +88,17 @@ class Actions
puts "error: #{e.message}" puts "error: #{e.message}"
end end
# Migrate a user from old code base (dnsmanager v1).
def user_migrate
args = Context.args.not_nil!
login, password_hash_brkn = args[0..1]
profile = Context.user_profile
pp! authd.migrate_user login, password_hash_brkn
rescue e : AuthD::Exception
puts "error: #{e.message}"
end
def user_registration def user_registration
args = Context.args.not_nil! args = Context.args.not_nil!
login, email = args[0..1] login, email = args[0..1]
@ -143,7 +155,6 @@ class Actions
puts "error: #{e.message}" puts "error: #{e.message}"
end end
# TODO # TODO
def user_mod def user_mod
args = Context.args.not_nil! args = Context.args.not_nil!

View File

@ -40,6 +40,25 @@ class AuthD::Request
return Response::ErrorInvalidCredentials.new return Response::ErrorInvalidCredentials.new
end end
# MIGRATION
# The migration involves old (broken) hash algorithm.
# On first connection, the user is authenticated with the old algorithm then a new hash is generated.
if brkn_hash = user.password_hash_brkn
# Authenticates the user with its old password hash algo.
if brkn_hash != authd.obsolete_hash_password @password
Baguette::Log.error "cannot authenticate the user with his old password hash"
return Response::ErrorInvalidCredentials.new
end
# FYI: there is no need to clone the user since there are no indexes on passwords.
user.password_hash = authd.hash_password @password # Adding new password hash.
user.password_hash_brkn = nil # Removing old password hash.
Baguette::Log.info "updating password hash for #{user.login} to newer algorithm"
authd.users_per_login.update user
return AuthD::Request.perform_login authd, fd, user.not_nil!
end
pwhash = Sodium::Password::Hash.new pwhash = Sodium::Password::Hash.new
hash = Base64.decode user.password_hash hash = Base64.decode user.password_hash

46
src/requests/migration.cr Normal file
View File

@ -0,0 +1,46 @@
class AuthD::Request
# Migration involves users with a broken password hash algorithm.
IPC::JSON.message MigrateUser, 16 do
property login : String
property password_hash_brkn : String # Old, broken algorithm. Will be changed on first authentication.
property admin : Bool = false
property email : String? = nil
property profile : Hash(String, JSON::Any)? = nil
def initialize(@login, @password_hash_brkn, @admin = false, @email = nil, @profile = nil)
end
def handle(authd : AuthD::Service, fd : Int32)
logged_user = authd.get_logged_user_full? fd
return Response::ErrorMustBeAuthenticated.new if logged_user.nil?
logged_user.assert_permission("authd", "*", User::PermissionLevel::Admin)
if authd.users_per_login.get? @login
return Response::ErrorAlreadyUsedLogin.new
end
# No mail verification since there were no mail stored in dnsmanager v1.
uid = authd.new_uid
user = User.new uid, @login, "" # Current password is voluntarily not set.
user.password_hash_brkn = @password_hash_brkn
user.contact.email = @email unless @email.nil?
user.admin = @admin
@profile.try do |profile|
user.profile = profile
end
# We consider adding the user as a registration.
user.date_registration = Time.local
authd.users << user
authd.new_uid_commit uid
Response::UserAdded.new user.to_public
end
end
AuthD.requests << MigrateUser
end