diff --git a/shard.yml b/shard.yml index 2e10eef..36d941c 100644 --- a/shard.yml +++ b/shard.yml @@ -1,12 +1,11 @@ name: authd -version: 0.2.0 - -authors: - - Karchnu - - Luka Vandervelden +version: 0.1.0 description: | - JWT-based authentication daemon. + JWT-based authentication daemon. + +authors: + - Philippe Pittoli targets: authd: @@ -14,25 +13,28 @@ targets: authc: main: utils/authc.cr -crystal: 0.35.1 +crystal: 1.7.1 dependencies: - grok: - github: spinscale/grok.cr - passwd: - git: https://git.baguette.netlib.re/Baguette/passwd.cr - branch: master - ipc: - git: https://git.baguette.netlib.re/Baguette/ipc.cr - branch: master - jwt: - github: crystal-community/jwt - branch: master - baguette-crystal-base: - git: https://git.baguette.netlib.re/Baguette/baguette-crystal-base - branch: master - dodb: - git: https://git.baguette.netlib.re/Baguette/dodb.cr - branch: master + grok: + github: spinscale/grok.cr + passwd: + git: https://git.baguette.netlib.re/Baguette/passwd.cr + branch: master + jwt: + github: crystal-community/jwt + branch: master + baguette-crystal-base: + git: https://git.baguette.netlib.re/Baguette/baguette-crystal-base + branch: master + dodb: + git: https://git.baguette.netlib.re/Baguette/dodb.cr + branch: master + cbor: + git: https://git.baguette.netlib.re/Baguette/crystal-cbor + branch: master + ipc: + git: https://git.baguette.netlib.re/Baguette/ipc.cr + branch: master -license: EUPL +license: ISC diff --git a/src/authd.cr b/src/authd.cr index adf9634..46d2ee9 100644 --- a/src/authd.cr +++ b/src/authd.cr @@ -1,34 +1,239 @@ -require "json" -require "jwt" -require "ipc" +extend AuthD -require "baguette-crystal-base" -require "./user.cr" - -# Allows get configuration from a provided file. -# See Baguette::Configuration::Base.get class Baguette::Configuration class Auth < IPC - include YAML::Serializable + property recreate_indexes : Bool = false + property storage : String = "storage" + property registrations : Bool = false + property require_email : Bool = false + property activation_template : String = "email-activation" + property recovery_template : String = "email-recovery" + property mailer_exe : String = "mailer" + property read_only_profile_keys : Array(String) = Array(String).new - property login : String? = nil - property pass : String? = nil - property shared_key : String = "nico-nico-nii" # Default authd key, as per the specs. :eyes: - property shared_key_file : String? = nil - - def initialize - end + property print_password_recovery_parameters : Bool = false end end -class AuthD::Service < IPC::Server +# Provides a JWT-based authentication scheme for service-specific users. +class AuthD::Service < IPC + property configuration : Baguette::Configuration::Auth + + # DB and its indexes. + property users : DODB::DataBase(User) + property users_per_uid : DODB::Index(User) + property users_per_login : DODB::Index(User) + + # #{@configuration.storage}/last_used_uid + property last_uid_file : String + + def initialize(@configuration) + super() + + @users = DODB::DataBase(User).new @configuration.storage + @users_per_uid = @users.new_index "uid", &.uid.to_s + @users_per_login = @users.new_index "login", &.login + + @last_uid_file = "#{@configuration.storage}/last_used_uid" + + if @configuration.recreate_indexes + @users.reindex_everything! + end + + self.timer @configuration.ipc_timer + self.service_init "auth" + end + + def hash_password(password : String) : String + digest = OpenSSL::Digest.new "sha256" + digest << password + digest.hexfinal + end + + # new_uid reads the last given UID and returns it incremented. + # Splitting the retrieval and record of new user ids allows to + # only increment when an user fully registers, thus avoiding a + # Denial of Service attack. + # + # WARNING: to record this new UID, new_uid_commit must be called. + # WARNING: new_uid isn't thread safe. + def new_uid + begin + uid = File.read(@last_uid_file).to_i + rescue + uid = 999 + end + + uid += 1 + end + + # new_uid_commit records the new UID. + # WARNING: new_uid_commit isn't thread safe. + def new_uid_commit(uid : Int) + File.write @last_uid_file, uid.to_s + end + + def handle_request(event : IPC::Event) + request_start = Time.utc + + array = event.message.not_nil! + slice = Slice.new array.to_unsafe, array.size + message = IPCMessage::TypedMessage.deserialize slice + request = AuthD.requests.parse_ipc_json message.not_nil! + + if request.nil? + raise "unknown request type" + end + + request_name = request.class.name.sub /^AuthD::Request::/, "" + Baguette::Log.debug "<< #{request_name}" + + response = begin + request.handle self + rescue e : UserNotFound + Baguette::Log.error "#{request_name} user not found" + AuthD::Response::Error.new "authorization error" + rescue e : AuthenticationInfoLacking + Baguette::Log.error "#{request_name} lacking authentication info" + AuthD::Response::Error.new "authorization error" + rescue e : AdminAuthorizationException + Baguette::Log.error "#{request_name} admin authentication failed" + AuthD::Response::Error.new "authorization error" + rescue e + Baguette::Log.error "#{request_name} generic error #{e}" + AuthD::Response::Error.new "unknown error" + end + + # If clients sent requests with an “id” field, it is copied + # in the responses. Allows identifying responses easily. + response.id = request.id + + schedule event.fd, response + + duration = Time.utc - request_start + + response_name = response.class.name.sub /^AuthD::Response::/, "" + + if response.is_a? AuthD::Response::Error + Baguette::Log.warning ">> #{response_name} (#{response.reason})" + else + Baguette::Log.debug ">> #{response_name} (Total duration: #{duration})" + end + end + + def get_user_from_token(token : String) + token_payload = Token.from_s(@configuration.shared_key, token) + + @users_per_uid.get? token_payload.uid.to_s + end + + def run + Baguette::Log.title "Starting authd" + + Baguette::Log.info "(mailer) Email activation template: #{@configuration.activation_template}" + Baguette::Log.info "(mailer) Email recovery template: #{@configuration.recovery_template}" + + self.loop do |event| + case event.type + when LibIPC::EventType::Timer + Baguette::Log.debug "Timer" if @configuration.print_ipc_timer + + when LibIPC::EventType::MessageRx + Baguette::Log.debug "Received message from #{event.fd}" if @configuration.print_ipc_message_received + begin + handle_request event + rescue e + Baguette::Log.error "#{e.message}" + # send event.fd, Response::Error.new e.message + end + + when LibIPC::EventType::MessageTx + Baguette::Log.debug "Message sent to #{event.fd}" if @configuration.print_ipc_message_sent + + when LibIPC::EventType::Connection + Baguette::Log.debug "Connection from #{event.fd}" if @configuration.print_ipc_connection + when LibIPC::EventType::Disconnection + Baguette::Log.debug "Disconnection from #{event.fd}" if @configuration.print_ipc_disconnection + else + Baguette::Log.error "Not implemented behavior for event: #{event}" + end + end + + end end -# Requests and responses. -require "./exceptions" -# Requests and responses. -require "./network" +begin + simulation, no_configuration, configuration_file = Baguette::Configuration.option_parser -# Functions to request the authd server. -require "./libclient.cr" + configuration = if no_configuration + Baguette::Log.info "do not load a configuration file." + Baguette::Configuration::Auth.new + else + Baguette::Configuration::Auth.get(configuration_file) || + Baguette::Configuration::Auth.new + end + + Baguette::Context.verbosity = configuration.verbosity + + if key_file = configuration.shared_key_file + configuration.shared_key = File.read(key_file).chomp + end + + OptionParser.parse do |parser| + parser.banner = "usage: authd [options]" + + parser.on "--storage directory", "Directory in which to store users." do |directory| + configuration.storage = directory + end + + parser.on "-K file", "--key-file file", "JWT key file" do |file_name| + configuration.shared_key = File.read(file_name).chomp + end + + parser.on "-R", "--allow-registrations", "Allow user registration." do + configuration.registrations = true + end + + parser.on "-E", "--require-email", "Require an email." do + configuration.require_email = true + end + + parser.on "-t activation-template-name", "--activation-template name", "Email activation template." do |opt| + configuration.activation_template = opt + end + + parser.on "-r recovery-template-name", "--recovery-template name", "Email recovery template." do |opt| + configuration.recovery_template = opt + end + + parser.on "-m mailer-exe", "--mailer mailer-exe", "Application to send registration emails." do |opt| + configuration.mailer_exe = opt + end + + + parser.on "-x key", "--read-only-profile-key key", "Marks a user profile key as being read-only." do |key| + configuration.read_only_profile_keys.push key + end + + parser.on "-h", "--help", "Show this help" do + puts parser + exit 0 + end + end + + if simulation + pp! configuration + exit 0 + end + + AuthD::Service.new(configuration).run + +rescue e : OptionParser::Exception + Baguette::Log.error e.message +rescue e + Baguette::Log.error "exception raised: #{e.message}" + e.backtrace.try &.each do |line| + STDERR << " - " << line << '\n' + end +end diff --git a/src/libclient.cr b/src/authd/client.cr similarity index 89% rename from src/libclient.cr rename to src/authd/client.cr index 21941da..d75ee23 100644 --- a/src/libclient.cr +++ b/src/authd/client.cr @@ -1,13 +1,25 @@ require "ipc/json" +require "json" module AuthD - class Client < IPC::Client + class Client < IPC property key : String + property server_fd : Int32 = -1 def initialize + super @key = "" + fd = self.connect "auth" + if fd.nil? + raise "couldn't connect to 'auth' IPC service" + end + @server_fd = fd + end - initialize "auth" + def read + slice = self.read @server_fd + m = IPCMessage::TypedMessage.deserialize slice + m.not_nil! end def get_token?(login : String, password : String) : String? @@ -46,8 +58,14 @@ module AuthD end end + def send_now(msg : IPC::JSON) + m = IPCMessage::TypedMessage.new msg.type.to_u8, msg.to_json + write @server_fd, m + end + def send_now(type : Request::Type, payload) - send_now @server_fd, type.value.to_u8, payload + m = IPCMessage::TypedMessage.new type.value.to_u8, payload + write @server_fd, m end def decode_token(token) @@ -62,7 +80,7 @@ module AuthD def add_user(login : String, password : String, email : String?, phone : String?, - profile : Hash(String, JSON::Any)?) : ::AuthD::User::Public | Exception + profile : Hash(String, ::JSON::Any)?) : ::AuthD::User::Public | Exception send_now Request::AddUser.new @key, login, password, email, phone, profile @@ -127,7 +145,7 @@ module AuthD password : String, email : String?, phone : String?, - profile : Hash(String, JSON::Any)?) : ::AuthD::User::Public? + profile : Hash(String, ::JSON::Any)?) : ::AuthD::User::Public? send_now Request::Register.new login, password, email, phone, profile response = AuthD.responses.parse_ipc_json read diff --git a/src/exceptions.cr b/src/authd/exceptions.cr similarity index 100% rename from src/exceptions.cr rename to src/authd/exceptions.cr diff --git a/src/token.cr b/src/authd/token.cr similarity index 100% rename from src/token.cr rename to src/authd/token.cr diff --git a/src/user.cr b/src/authd/user.cr similarity index 98% rename from src/user.cr rename to src/authd/user.cr index 00246e9..8bfd4cf 100644 --- a/src/user.cr +++ b/src/authd/user.cr @@ -1,9 +1,6 @@ require "json" - require "uuid" -require "./token.cr" - class AuthD::User include JSON::Serializable diff --git a/src/libauth.cr b/src/libauth.cr new file mode 100644 index 0000000..181dc3d --- /dev/null +++ b/src/libauth.cr @@ -0,0 +1,34 @@ +require "json" +require "jwt" +require "ipc" + +require "baguette-crystal-base" + +# Allows get configuration from a provided file. +# See Baguette::Configuration::Base.get +class Baguette::Configuration + class Auth < IPC + include YAML::Serializable + + property login : String? = nil + property pass : String? = nil + property shared_key : String = "nico-nico-nii" # Default authd key, as per the specs. :eyes: + property shared_key_file : String? = nil + + def initialize + end + end +end + +# Token and user classes. +require "./authd/token.cr" +require "./authd/user.cr" + +# Requests and responses. +require "./authd/exceptions" + +# Requests and responses. +require "./network" + +# Functions to request the authd server. +require "./authd/client.cr" diff --git a/src/main.cr b/src/main.cr index a1fddcc..a2d7941 100644 --- a/src/main.cr +++ b/src/main.cr @@ -2,240 +2,12 @@ require "uuid" require "option_parser" require "openssl" require "colorize" - require "jwt" -require "ipc" -require "dodb" - -require "baguette-crystal-base" require "grok" +require "dodb" +require "baguette-crystal-base" + +require "ipc" +require "./libauth.cr" require "./authd.cr" - -extend AuthD - -class Baguette::Configuration - class Auth < IPC - property recreate_indexes : Bool = false - property storage : String = "storage" - property registrations : Bool = false - property require_email : Bool = false - property activation_url : String? = nil - property field_subject : String? = nil - property field_from : String? = nil - property read_only_profile_keys : Array(String) = Array(String).new - - property print_password_recovery_parameters : Bool = false - end -end - -# Provides a JWT-based authentication scheme for service-specific users. -class AuthD::Service < IPC::Server - property configuration : Baguette::Configuration::Auth - - # DB and its indexes. - property users : DODB::DataBase(User) - property users_per_uid : DODB::Index(User) - property users_per_login : DODB::Index(User) - - # #{@configuration.storage}/last_used_uid - property last_uid_file : String - - def initialize(@configuration) - @users = DODB::DataBase(User).new @configuration.storage - @users_per_uid = @users.new_index "uid", &.uid.to_s - @users_per_login = @users.new_index "login", &.login - - @last_uid_file = "#{@configuration.storage}/last_used_uid" - - if @configuration.recreate_indexes - @users.reindex_everything! - end - - super "auth" - end - - def hash_password(password : String) : String - digest = OpenSSL::Digest.new "sha256" - digest << password - digest.hexfinal - end - - def new_uid - begin - uid = File.read(@last_uid_file).to_i - rescue - uid = 999 - end - - uid += 1 - - File.write @last_uid_file, uid.to_s - - uid - end - - def handle_request(event : IPC::Event::MessageReceived) - request_start = Time.utc - - request = AuthD.requests.parse_ipc_json event.message - - if request.nil? - raise "unknown request type" - end - - request_name = request.class.name.sub /^AuthD::Request::/, "" - Baguette::Log.debug "<< #{request_name}" - - response = begin - request.handle self, event - rescue e : UserNotFound - Baguette::Log.error "#{request_name} user not found" - AuthD::Response::Error.new "authorization error" - rescue e : AuthenticationInfoLacking - Baguette::Log.error "#{request_name} lacking authentication info" - AuthD::Response::Error.new "authorization error" - rescue e : AdminAuthorizationException - Baguette::Log.error "#{request_name} admin authentication failed" - AuthD::Response::Error.new "authorization error" - rescue e - Baguette::Log.error "#{request_name} generic error #{e}" - AuthD::Response::Error.new "unknown error" - end - - # If clients sent requests with an “id” field, it is copied - # in the responses. Allows identifying responses easily. - response.id = request.id - - send event.fd, response - - duration = Time.utc - request_start - - response_name = response.class.name.sub /^AuthD::Response::/, "" - - if response.is_a? AuthD::Response::Error - Baguette::Log.warning ">> #{response_name} (#{response.reason})" - else - Baguette::Log.debug ">> #{response_name} (Total duration: #{duration})" - end - end - - def get_user_from_token(token : String) - token_payload = Token.from_s(@configuration.shared_key, token) - - @users_per_uid.get? token_payload.uid.to_s - end - - def run - Baguette::Log.title "Starting authd" - - @base_timer = @configuration.ipc_timer - @timer = @configuration.ipc_timer - - self.loop do |event| - case event - when IPC::Event::Timer - Baguette::Log.debug "Timer" if @configuration.print_ipc_timer - - when IPC::Event::MessageReceived - Baguette::Log.debug "Received message from #{event.fd}" if @configuration.print_ipc_message_received - begin - handle_request event - rescue e - Baguette::Log.error "#{e.message}" - # send event.fd, Response::Error.new e.message - end - - when IPC::Event::MessageSent - Baguette::Log.debug "Message sent to #{event.fd}" if @configuration.print_ipc_message_sent - - when IPC::Exception - Baguette::Log.error "IPC::Exception" - pp! event - when IPC::Event::Connection - Baguette::Log.debug "Connection from #{event.fd}" if @configuration.print_ipc_connection - when IPC::Event::Disconnection - Baguette::Log.debug "Disconnection from #{event.fd}" if @configuration.print_ipc_disconnection - else - Baguette::Log.error "Not implemented behavior for event: #{event}" - end - end - - end -end - - -begin - simulation, no_configuration, configuration_file = Baguette::Configuration.option_parser - - configuration = if no_configuration - Baguette::Log.info "do not load a configuration file." - Baguette::Configuration::Auth.new - else - Baguette::Configuration::Auth.get(configuration_file) || - Baguette::Configuration::Auth.new - end - - Baguette::Context.verbosity = configuration.verbosity - - if key_file = configuration.shared_key_file - configuration.shared_key = File.read(key_file).chomp - end - - OptionParser.parse do |parser| - parser.banner = "usage: authd [options]" - - parser.on "--storage directory", "Directory in which to store users." do |directory| - configuration.storage = directory - end - - parser.on "-K file", "--key-file file", "JWT key file" do |file_name| - configuration.shared_key = File.read(file_name).chomp - end - - parser.on "-R", "--allow-registrations" do - configuration.registrations = true - end - - parser.on "-E", "--require-email" do - configuration.require_email = true - end - - parser.on "-t subject", "--subject title", "Subject of the email." do |s| - configuration.field_subject = s - end - - parser.on "-f from-email", "--from email", "'From:' field to use in activation email." do |f| - configuration.field_from = f - end - - parser.on "-u", "--activation-url url", "Activation URL." do |opt| - configuration.activation_url = opt - end - - parser.on "-x key", "--read-only-profile-key key", "Marks a user profile key as being read-only." do |key| - configuration.read_only_profile_keys.push key - end - - parser.on "-h", "--help", "Show this help" do - puts parser - exit 0 - end - end - - if simulation - pp! configuration - exit 0 - end - - AuthD::Service.new(configuration).run - -rescue e : OptionParser::Exception - Baguette::Log.error e.message -rescue e - Baguette::Log.error "exception raised: #{e.message}" - e.backtrace.try &.each do |line| - STDERR << " - " << line << '\n' - end -end - diff --git a/src/network.cr b/src/network.cr index d74c23b..56edeb7 100644 --- a/src/network.cr +++ b/src/network.cr @@ -1,9 +1,8 @@ require "ipc" -require "json" require "ipc/json" class IPC::JSON - def handle(service : AuthD::Service, event : IPC::Event::Events) + def handle(service : AuthD::Service) raise "unimplemented" end end @@ -13,22 +12,11 @@ module AuthD class_getter responses = [] of IPC::JSON.class end -class IPC::Context - def send(fd, response : AuthD::Response) - send fd, response.type.to_u8, response.to_json +class IPC + def schedule(fd, m : (AuthD::Request | AuthD::Response)) + schedule fd, m.type.to_u8, m.to_json end end -class IPC::Client - def send(request : AuthD::Request) - unless (fd = @server_fd).nil? - send_now fd, request.type.to_u8, request.to_json - else - raise "Client not connected to the server" - end - end -end - - require "./requests/*" require "./responses/*" diff --git a/src/requests/admin.cr b/src/requests/admin.cr index 8ff4e54..e32f331 100644 --- a/src/requests/admin.cr +++ b/src/requests/admin.cr @@ -13,7 +13,7 @@ class AuthD::Request def initialize(@shared_key, @login, @password, @email, @phone, @profile) end - def handle(authd : AuthD::Service, event : IPC::Event::Events) + def handle(authd : AuthD::Service) # No verification of the users' informations when an admin adds it. # No mail address verification. if @shared_key != authd.configuration.shared_key @@ -44,7 +44,7 @@ class AuthD::Request user.date_registration = Time.local authd.users << user - + authd.new_uid_commit uid Response::UserAdded.new user.to_public end end @@ -63,7 +63,7 @@ class AuthD::Request def initialize(@shared_key, @user) end - def handle(authd : AuthD::Service, event : IPC::Event::Events) + def handle(authd : AuthD::Service) if @shared_key != authd.configuration.shared_key return Response::Error.new "invalid authentication key" end diff --git a/src/requests/contact.cr b/src/requests/contact.cr index 5149ebc..4ab83fb 100644 --- a/src/requests/contact.cr +++ b/src/requests/contact.cr @@ -8,7 +8,7 @@ class AuthD::Request def initialize(@token) end - def handle(authd : AuthD::Service, event : IPC::Event::Events) + def handle(authd : AuthD::Service) user = authd.get_user_from_token @token return Response::Error.new "invalid user" unless user @@ -32,7 +32,7 @@ class AuthD::Request def initialize(@token) end - def handle(authd : AuthD::Service, event : IPC::Event::Events) + def handle(authd : AuthD::Service) user = authd.get_user_from_token @token return Response::Error.new "invalid user" unless user diff --git a/src/requests/delete.cr b/src/requests/delete.cr index e2e30ad..a1e58c6 100644 --- a/src/requests/delete.cr +++ b/src/requests/delete.cr @@ -13,7 +13,7 @@ class AuthD::Request def initialize(@user, @shared_key) end - def handle(authd : AuthD::Service, event : IPC::Event::Events) + def handle(authd : AuthD::Service) uid_or_login = @user user_to_delete = if uid_or_login.is_a? Int32 authd.users_per_uid.get? uid_or_login.to_s diff --git a/src/requests/list.cr b/src/requests/list.cr index edd05d1..6f52d92 100644 --- a/src/requests/list.cr +++ b/src/requests/list.cr @@ -6,7 +6,7 @@ class AuthD::Request def initialize(@token, @key) end - def handle(authd : AuthD::Service, event : IPC::Event::Events) + def handle(authd : AuthD::Service) # FIXME: Lines too long, repeatedly (>80c with 4c tabs). @token.try do |token| user = authd.get_user_from_token token diff --git a/src/requests/password.cr b/src/requests/password.cr index 646f469..19602b8 100644 --- a/src/requests/password.cr +++ b/src/requests/password.cr @@ -7,7 +7,7 @@ class AuthD::Request def initialize(@login, @old_password, @new_password) end - def handle(authd : AuthD::Service, event : IPC::Event::Events) + def handle(authd : AuthD::Service) user = authd.users_per_login.get? @login unless user @@ -35,7 +35,7 @@ class AuthD::Request def initialize(@user, @password_renew_key, @new_password) end - def handle(authd : AuthD::Service, event : IPC::Event::Events) + def handle(authd : AuthD::Service) uid_or_login = @user user = if uid_or_login.is_a? Int32 authd.users_per_uid.get? uid_or_login.to_s @@ -69,7 +69,7 @@ class AuthD::Request def initialize(@user, @email) end - def handle(authd : AuthD::Service, event : IPC::Event::Events) + def handle(authd : AuthD::Service) uid_or_login = @user user = if uid_or_login.is_a? Int32 authd.users_per_uid.get? uid_or_login.to_s @@ -90,32 +90,29 @@ class AuthD::Request authd.users_per_uid.update user.uid.to_s, user - unless (activation_url = authd.configuration.activation_url).nil? + # Once the user is created and stored, we try to contact him + if authd.configuration.print_password_recovery_parameters + pp! user.login, + user.contact.email.not_nil!, + user.password_renew_key.not_nil! + end - field_from = authd.configuration.field_from.not_nil! - activation_url = authd.configuration.activation_url.not_nil! + mailer_exe = authd.configuration.mailer_exe + template_name = authd.configuration.recovery_template - # Once the user is created and stored, we try to contact him + u_login = user.login + u_email = user.contact.email.not_nil! + u_token = user.password_renew_key.not_nil! - if authd.configuration.print_password_recovery_parameters - pp! user.login, - user.contact.email.not_nil!, - field_from, - activation_url, - user.password_renew_key.not_nil! - end - - unless Process.run("password-recovery-mailer", [ - "-l", user.login, - "-e", user.contact.email.not_nil!, - "-t", "Password recovery email", - "-f", field_from, - "-u", activation_url, - "-a", user.password_renew_key.not_nil! - ]).success? - - return Response::Error.new "cannot contact the user for password recovery" - end + # Once the user is created and stored, we try to contact him. + unless Process.run(mailer_exe, + # PARAMETERS + [ "send", template_name, u_email ], + # ENV + { "LOGIN" => u_login, "TOKEN" => u_token }, + true # clear environment + ).success? + raise "cannot contact user #{u_login} address #{u_email}" end Response::PasswordRecoverySent.new user.to_public diff --git a/src/requests/permissions.cr b/src/requests/permissions.cr index fdc2b6c..48cf7b5 100644 --- a/src/requests/permissions.cr +++ b/src/requests/permissions.cr @@ -10,7 +10,7 @@ class AuthD::Request def initialize(@shared_key, @user, @service, @resource) end - def handle(authd : AuthD::Service, event : IPC::Event::Events) + def handle(authd : AuthD::Service) authorized = false if key = @shared_key @@ -79,7 +79,7 @@ class AuthD::Request def initialize(@shared_key, @user, @service, @resource, @permission) end - def handle(authd : AuthD::Service, event : IPC::Event::Events) + def handle(authd : AuthD::Service) unless @shared_key == authd.configuration.shared_key return Response::Error.new "unauthorized" end diff --git a/src/requests/profile.cr b/src/requests/profile.cr index daa2a16..306987a 100644 --- a/src/requests/profile.cr +++ b/src/requests/profile.cr @@ -6,7 +6,7 @@ class AuthD::Request def initialize(@token, @new_profile) end - def handle(authd : AuthD::Service, event : IPC::Event::Events) + def handle(authd : AuthD::Service) user = authd.get_user_from_token @token return Response::Error.new "invalid user" unless user @@ -45,7 +45,7 @@ class AuthD::Request def initialize(@token, @new_profile) end - def handle(authd : AuthD::Service, event : IPC::Event::Events) + def handle(authd : AuthD::Service) user = if token = @token u = authd.get_user_from_token token raise UserNotFound.new unless u diff --git a/src/requests/register.cr b/src/requests/register.cr index 8407174..2855e30 100644 --- a/src/requests/register.cr +++ b/src/requests/register.cr @@ -9,7 +9,7 @@ class AuthD::Request def initialize(@login, @password, @email, @phone, @profile) end - def handle(authd : AuthD::Service, event : IPC::Event::Events) + def handle(authd : AuthD::Service) if ! authd.configuration.registrations return Response::Error.new "registrations not allowed" end @@ -22,12 +22,6 @@ class AuthD::Request return Response::Error.new "email required" end - activation_url = authd.configuration.activation_url - if activation_url.nil? - # In this case we should not accept its registration. - return Response::Error.new "No activation URL were entered. Cannot send activation mails." - end - if ! @email.nil? # Test on the email address format. grok = Grok.new [ "%{EMAILADDRESS:email}" ] @@ -58,33 +52,31 @@ class AuthD::Request user.date_registration = Time.local begin - field_subject = authd.configuration.field_subject.not_nil! - field_from = authd.configuration.field_from.not_nil! - activation_url = authd.configuration.activation_url.not_nil! + mailer_exe = authd.configuration.mailer_exe + template_name = authd.configuration.activation_template u_login = user.login u_email = user.contact.email.not_nil! u_activation_key = user.contact.activation_key.not_nil! - # Once the user is created and stored, we try to contact him - unless Process.run("activation-mailer", [ - "-l", u_login, - "-e", u_email, - "-t", field_subject, - "-f", field_from, - "-u", activation_url, - "-a", u_activation_key - ]).success? - raise "cannot contact user #{user.login} address #{user.contact.email}" + # Once the user is created and stored, we try to contact him. + unless Process.run(mailer_exe, + # PARAMETERS + [ "send", template_name, u_email ], + # ENV + { "LOGIN" => u_login, "TOKEN" => u_activation_key }, + true # clear environment + ).success? + raise "cannot contact user #{u_login} address #{u_email}" end rescue e - Baguette::Log.error "activation-mailer: #{e}" + Baguette::Log.error "mailer: #{e}" return Response::Error.new "cannot contact the user (not registered)" end # add the user only if we were able to send the confirmation mail authd.users << user - + authd.new_uid_commit uid Response::UserAdded.new user.to_public end end diff --git a/src/requests/search.cr b/src/requests/search.cr index b42f3b6..239ab47 100644 --- a/src/requests/search.cr +++ b/src/requests/search.cr @@ -5,7 +5,7 @@ class AuthD::Request def initialize(@user) end - def handle(authd : AuthD::Service, event : IPC::Event::Events) + def handle(authd : AuthD::Service) pattern = Regex.new @user, Regex::Options::IGNORE_CASE matching_users = Array(AuthD::User::Public).new diff --git a/src/requests/token.cr b/src/requests/token.cr index 6749285..94f8462 100644 --- a/src/requests/token.cr +++ b/src/requests/token.cr @@ -6,7 +6,7 @@ class AuthD::Request def initialize(@login, @password) end - def handle(authd : AuthD::Service, event : IPC::Event::Events) + def handle(authd : AuthD::Service) begin user = authd.users_per_login.get @login rescue e : DODB::MissingEntry diff --git a/src/requests/users.cr b/src/requests/users.cr index 0bf573b..edf6b91 100644 --- a/src/requests/users.cr +++ b/src/requests/users.cr @@ -6,7 +6,7 @@ class AuthD::Request def initialize(@login, @activation_key) end - def handle(authd : AuthD::Service, event : IPC::Event::Events) + def handle(authd : AuthD::Service) user = authd.users_per_login.get? @login if user.nil? @@ -37,7 +37,7 @@ class AuthD::Request def initialize(@user) end - def handle(authd : AuthD::Service, event : IPC::Event::Events) + def handle(authd : AuthD::Service) uid_or_login = @user user = if uid_or_login.is_a? Int32 authd.users_per_uid.get? uid_or_login.to_s @@ -61,7 +61,7 @@ class AuthD::Request def initialize(@login, @password) end - def handle(authd : AuthD::Service, event : IPC::Event::Events) + def handle(authd : AuthD::Service) user = authd.users_per_login.get? @login unless user diff --git a/utils/authc.cr b/utils/authc.cr index 5edd5a0..a212827 100644 --- a/utils/authc.cr +++ b/utils/authc.cr @@ -5,12 +5,7 @@ require "yaml" require "baguette-crystal-base" -require "../src/authd.cr" - -# require "./altideal-client.cr" -# require "./yaml_uuid.cr" # YAML UUID parser -# require "./authd_api.cr" # Authd interface functions - +require "../src/libauth.cr" class Context class_property simulation = false # do not perform the action @@ -35,7 +30,6 @@ class Context class_property args : Array(String)? = nil end -# require "./parse-me" require "./better-parser" class Actions @@ -106,7 +100,7 @@ class Actions res = authd.register login, password.not_nil!, email, phone, profile: profile puts res - rescue e : AuthD::Exception + rescue e puts "error: #{e.message}" end @@ -229,4 +223,3 @@ end # tool [options] command [options-for-command] main -