diff --git a/src/authd.cr b/src/authd.cr index 2114598..a68126e 100644 --- a/src/authd.cr +++ b/src/authd.cr @@ -99,6 +99,18 @@ class AuthD::Response initialize :user, :service, :resource, :permission end + class PasswordRecoverySent < Response + property user : ::AuthD::User::Public + + initialize :user + end + + class PasswordRecovered < Response + property user : ::AuthD::User::Public + + initialize :user + end + # This creates a Request::Type enumeration. One entry for each request type. {% begin %} enum Type @@ -273,6 +285,21 @@ class AuthD::Request initialize :shared_key, :user, :service, :resource, :permission end + class PasswordRecovery < Request + property shared_key : String + property user : Int32 | String + property password_renew_key : String + property new_password : String + + initialize :shared_key, :user, :password_renew_key, :new_password + end + + class AskPasswordRecovery < Request + property user : Int32 | String + + initialize :user + end + # This creates a Request::Type enumeration. One entry for each request type. {% begin %} enum Type @@ -309,7 +336,7 @@ class AuthD::Request requests.find(&.type.==(type)).try &.from_json(payload) rescue e : JSON::ParseException - raise Exception.new "malformed request" + raise Exception.new "malformed request: #{e}" end end @@ -419,6 +446,32 @@ module AuthD end end + def ask_password_recovery(uid_or_login : String | Int32) + send Request::AskPasswordRecovery.new uid_or_login + response = Response.from_ipc read + + case response + when Response::PasswordRecoverySent + when Response::Error + raise Exception.new response.reason + else + Exception.new + end + end + + def change_password(uid_or_login : String | Int32, new_pass : String, renew_key : String) + send Request::PasswordRecovery.new @key, uid_or_login, renew_key, new_pass + response = Response.from_ipc read + + case response + when Response::PasswordRecovered + when Response::Error + raise Exception.new response.reason + else + Exception.new + end + end + def register(login : String, password : String, email : String?, diff --git a/src/main.cr b/src/main.cr index 072f218..5109c85 100644 --- a/src/main.cr +++ b/src/main.cr @@ -209,6 +209,7 @@ class AuthD::Service user.profile = profile end + @users << user # Once the user is created and stored, we try to contact him @@ -309,6 +310,62 @@ class AuthD::Service @users_per_uid.update user.uid.to_s, user Response::PermissionSet.new user.uid, service, request.resource, request.permission + when Request::AskPasswordRecovery + + uid_or_login = request.user + user = if uid_or_login.is_a? Int32 + @users_per_uid.get? uid_or_login.to_s + else + @users_per_login.get? uid_or_login + end + + if user.nil? + return Response::Error.new "user not found" + end + + user.password_renew_key = UUID.random.to_s + + @users_per_uid.update user.uid.to_s, user + + # Once the user is created and stored, we try to contact him + unless Process.run("password-recovery-mailer", [ + "-l", user.login, + "-e", user.contact.email.not_nil!, + "-t", "Password recovery email", + "-f", "karchnu@localhost", + "-a", user.password_renew_key.not_nil! + ]).success? + return Response::Error.new "cannot contact the user for password recovery" + end + + Response::PasswordRecoverySent.new user.to_public + when Request::PasswordRecovery + if request.shared_key != @jwt_key + return Response::Error.new "invalid authentication key" + end + + uid_or_login = request.user + user = if uid_or_login.is_a? Int32 + @users_per_uid.get? uid_or_login.to_s + else + @users_per_login.get? uid_or_login + end + + if user.nil? + return Response::Error.new "user not found" + end + + if user.password_renew_key == request.password_renew_key + user.password_hash = hash_password request.new_password + else + return Response::Error.new "renew key not valid" + end + + user.password_renew_key = nil + + @users_per_uid.update user.uid.to_s, user + + Response::PasswordRecoverySent.new user.to_public else Response::Error.new "unhandled request type" end diff --git a/src/user.cr b/src/user.cr index e05111b..d5aaf10 100644 --- a/src/user.cr +++ b/src/user.cr @@ -39,6 +39,7 @@ class AuthD::User # Private. property contact : Contact property password_hash : String + property password_renew_key : String? # service => resource => permission level property permissions : Hash(String, Hash(String, PermissionLevel)) property configuration : Hash(String, Hash(String, JSON::Any)) diff --git a/utils/authd-user-ask-for-new-password.cr b/utils/authd-user-ask-for-new-password.cr new file mode 100644 index 0000000..8911fe5 --- /dev/null +++ b/utils/authd-user-ask-for-new-password.cr @@ -0,0 +1,45 @@ +require "option_parser" + +require "../src/authd.cr" + +key_file : String? = nil +cli_login : String? = nil + +OptionParser.parse do |parser| + parser.unknown_args do |args| + if args.size != 1 + puts "usage: #{PROGRAM_NAME} [options]" + puts parser + exit 1 + end + + cli_login = args[0] + end + + parser.on "-K file", "--key-file file", "Read the authd shared key from a file." do |file| + key_file = file + end + + parser.on "-h", "--help", "Prints this help message." do + puts "usage: #{PROGRAM_NAME} [options]" + puts parser + exit 0 + end +end + +begin + authd = IPC::Connection.new "auth" + + authd = AuthD::Client.new + authd.key = File.read(key_file.not_nil!).chomp + + login = cli_login.not_nil! + + # AskPasswordRecovery => PasswordRecoverySent + # PasswordRecovery => + + pp! authd.ask_password_recovery login +rescue e + puts "Error: #{e}" + exit 1 +end