Password Recovery.

This commit is contained in:
Philippe PITTOLI 2020-02-23 20:37:50 +01:00
parent 3ce1d78a50
commit bd68148924
4 changed files with 157 additions and 1 deletions

View File

@ -99,6 +99,18 @@ class AuthD::Response
initialize :user, :service, :resource, :permission initialize :user, :service, :resource, :permission
end 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. # This creates a Request::Type enumeration. One entry for each request type.
{% begin %} {% begin %}
enum Type enum Type
@ -273,6 +285,21 @@ class AuthD::Request
initialize :shared_key, :user, :service, :resource, :permission initialize :shared_key, :user, :service, :resource, :permission
end 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. # This creates a Request::Type enumeration. One entry for each request type.
{% begin %} {% begin %}
enum Type enum Type
@ -309,7 +336,7 @@ class AuthD::Request
requests.find(&.type.==(type)).try &.from_json(payload) requests.find(&.type.==(type)).try &.from_json(payload)
rescue e : JSON::ParseException rescue e : JSON::ParseException
raise Exception.new "malformed request" raise Exception.new "malformed request: #{e}"
end end
end end
@ -419,6 +446,32 @@ module AuthD
end end
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, def register(login : String,
password : String, password : String,
email : String?, email : String?,

View File

@ -209,6 +209,7 @@ class AuthD::Service
user.profile = profile user.profile = profile
end end
@users << user @users << user
# Once the user is created and stored, we try to contact him # 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 @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, 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 else
Response::Error.new "unhandled request type" Response::Error.new "unhandled request type"
end end

View File

@ -39,6 +39,7 @@ class AuthD::User
# Private. # Private.
property contact : Contact property contact : Contact
property password_hash : String property password_hash : String
property password_renew_key : String?
# service => resource => permission level # service => resource => permission level
property permissions : Hash(String, Hash(String, PermissionLevel)) property permissions : Hash(String, Hash(String, PermissionLevel))
property configuration : Hash(String, Hash(String, JSON::Any)) property configuration : Hash(String, Hash(String, JSON::Any))

View File

@ -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} <login> [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} <login> [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