From f8d98ab1a14646cf698a4feb427d1f52792f8da9 Mon Sep 17 00:00:00 2001 From: Karchnu Date: Fri, 9 Oct 2020 18:13:58 +0200 Subject: [PATCH] authc CLI for authd --- shard.yml | 18 +- src/authd.cr | 34 +++- src/main.cr | 53 +++++- utils/authc.cr | 232 +++++++++++++++++++++++ utils/authd-user-add.cr | 95 ---------- utils/authd-user-allow.cr | 70 ------- utils/authd-user-ask-for-new-password.cr | 43 ----- utils/authd-user-get.cr | 40 ---- utils/authd-user-mod.cr | 88 --------- utils/authd-user-perms.cr | 67 ------- utils/authd-user-search.cr | 38 ---- utils/authd-user-validate.cr | 38 ---- utils/better-parser.cr | 228 ++++++++++++++++++++++ 13 files changed, 545 insertions(+), 499 deletions(-) create mode 100644 utils/authc.cr delete mode 100644 utils/authd-user-add.cr delete mode 100644 utils/authd-user-allow.cr delete mode 100644 utils/authd-user-ask-for-new-password.cr delete mode 100644 utils/authd-user-get.cr delete mode 100644 utils/authd-user-mod.cr delete mode 100644 utils/authd-user-perms.cr delete mode 100644 utils/authd-user-search.cr delete mode 100644 utils/authd-user-validate.cr create mode 100644 utils/better-parser.cr diff --git a/shard.yml b/shard.yml index 0d14f3b..2e10eef 100644 --- a/shard.yml +++ b/shard.yml @@ -11,22 +11,8 @@ description: | targets: authd: main: src/main.cr - auth-user-perms: - main: utils/authd-user-perms.cr - auth-user-add: - main: utils/authd-user-add.cr - auth-user-allow: - main: utils/authd-user-allow.cr - auth-user-ask-for-new-password: - main: utils/authd-user-ask-for-new-password.cr - auth-user-get: - main: utils/authd-user-get.cr - auth-user-mod: - main: utils/authd-user-mod.cr - auth-user-validate: - main: utils/authd-user-validate.cr - auth-user-search: - main: utils/authd-user-search.cr + authc: + main: utils/authc.cr crystal: 0.35.1 diff --git a/src/authd.cr b/src/authd.cr index 1245c54..cc03e5f 100644 --- a/src/authd.cr +++ b/src/authd.cr @@ -345,6 +345,19 @@ class AuthD::Request property phone : String? end + class Delete < Request + # Deletion can be triggered by either an admin or the user. + property shared_key : String? + + property login : String? + property password : String? + + property user : String | Int32 + + initialize :user, :login, :password + initialize :user, :shared_key + end + # This creates a Request::Type enumeration. One entry for each request type. {% begin %} enum Type @@ -484,8 +497,8 @@ module AuthD end end - def ask_password_recovery(uid_or_login : String | Int32) - send Request::AskPasswordRecovery.new uid_or_login + def ask_password_recovery(uid_or_login : String | Int32, email : String) + send Request::AskPasswordRecovery.new uid_or_login, email response = Response.from_ipc read case response @@ -609,6 +622,23 @@ module AuthD raise Exception.new "unexpected response" end end + + def delete(user : Int32 | String, key : String) + send Request::Delete.new user, key + delete_ + end + def delete(user : Int32 | String, login : String, pass : String) + send Request::Delete.new user, login, pass + delete_ + end + def delete_ + response = Response.from_ipc read + case response + when Response::Error + raise Exception.new response.reason + end + response + end end end diff --git a/src/main.cr b/src/main.cr index a147366..3600e55 100644 --- a/src/main.cr +++ b/src/main.cr @@ -62,11 +62,11 @@ class AuthD::Service return Response::Error.new "invalid credentials" end - if user.password_hash != hash_password request.password + if user.nil? return Response::Error.new "invalid credentials" end - if user.nil? + if user.password_hash != hash_password request.password return Response::Error.new "invalid credentials" end @@ -547,6 +547,55 @@ class AuthD::Service @users_per_uid.update user Response::UserEdited.new user.uid + when Request::Delete + uid_or_login = request.user + user_to_delete = 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_to_delete.nil? + return Response::Error.new "invalid user" + end + + # Either the request comes from an admin or the user. + # Shared key == admin, check the key. + if key = request.shared_key + return Response::Error.new "unauthorized (wrong shared key)" unless key == @jwt_key + else + login = request.login + pass = request.password + if login.nil? || pass.nil? + return Response::Error.new "authentication failed (no shared key, no login)" + end + + # authenticate the user + begin + user = @users_per_login.get login + rescue e : DODB::MissingEntry + return Response::Error.new "invalid credentials" + end + + if user.nil? + return Response::Error.new "invalid credentials" + end + + if user.password_hash != hash_password pass + return Response::Error.new "invalid credentials" + end + + # Is the user to delete the requesting user? + if user.uid != user_to_delete.uid + return Response::Error.new "invalid credentials" + end + end + + # User or admin is now verified: let's proceed with the user deletion. + @users_per_login.delete user_to_delete.login + + # TODO: better response + Response::User.new user_to_delete.to_public else Response::Error.new "unhandled request type" end diff --git a/utils/authc.cr b/utils/authc.cr new file mode 100644 index 0000000..5edd5a0 --- /dev/null +++ b/utils/authc.cr @@ -0,0 +1,232 @@ +require "option_parser" + +require "ipc" +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 + + +class Context + class_property simulation = false # do not perform the action + + class_property authd_login = "undef" # undef authd user + class_property authd_pass = "undef" # undef authd user password + class_property shared_key = "undef" # undef authd user password + + # # Properties to select what to display when printing a deal. + # class_property print_title = true + # class_property print_description = true + # class_property print_owner = true + # class_property print_nb_comments = true + + class_property command = "not-implemented" + + class_property user_profile : Hash(String,JSON::Any)? + class_property phone : String? + class_property email : String? + + # Will be parsed later, with a specific parser. + class_property args : Array(String)? = nil +end + +# require "./parse-me" +require "./better-parser" + +class Actions + + def self.ask_password + STDOUT << "password: " + STDOUT << `stty -echo` + STDOUT.flush + password = STDIN.gets.try &.chomp + + STDOUT << '\n' + STDOUT << `stty echo` + + password + end + + def self.ask_something(str : String) : String? + STDOUT << "#{str} " + STDOUT.flush + answer = STDIN.gets.try &.chomp + answer + end + + + property the_call = {} of String => Proc(Nil) + property authd : AuthD::Client + + def initialize(@authd) + @the_call["user-add"] = ->user_add + @the_call["user-mod"] = ->user_mod + @the_call["user-registration"] = ->user_registration # Do not require admin priviledges. + @the_call["user-delete"] = ->user_deletion # Do not require admin priviledges. + @the_call["user-get"] = ->user_get # Do not require authentication. + @the_call["user-validation"] = ->user_validation # Do not require authentication. + @the_call["user-recovery"] = ->user_recovery # Do not require authentication. + @the_call["user-search"] = ->user_search # Do not require authentication. + + @the_call["permission-set"] = ->permission_set + @the_call["permission-check"] = ->permission_check + + end + + # + # For all functions: the number of arguments is already tested. + # + + def user_add + puts "User add!!!" + args = Context.args.not_nil! + login, email, phone = args[0..2] + profile = Context.user_profile + + password = Actions.ask_password + exit 1 unless password + + pp! authd.add_user login, password.not_nil!, email, phone, profile: profile + rescue e : AuthD::Exception + puts "error: #{e.message}" + end + + def user_registration + args = Context.args.not_nil! + login, email, phone = args[0..2] + profile = Context.user_profile + + password = Actions.ask_password + exit 1 unless password + + res = authd.register login, password.not_nil!, email, phone, profile: profile + puts res + rescue e : AuthD::Exception + puts "error: #{e.message}" + end + + # TODO + def user_mod + args = Context.args.not_nil! + userid = args[0] + + password : String? = nil + + should_ask_password = Actions.ask_something "Should we change the password (Yn) ?" || "n" + case should_ask_password + when /y/i + Baguette::Log.debug "Ok let's change the password!" + password = Actions.ask_password + exit 1 unless password + else + Baguette::Log.debug "Ok no change in password." + end + + email = Context.email + phone = Context.phone + + Baguette::Log.error "This function shouldn't be used for now." + Baguette::Log.error "It is way too cumbersome." + + # res = authd.add_user login, password, email, phone, profile: profile + # puts res + end + + def user_deletion + args = Context.args.not_nil! + userid = args[0].to_i + + # Check if the request comes from an admin or the user. + res = if Context.shared_key.nil? + authd.delete userid, Context.authd_login, Context.authd_pass + else + authd.delete userid, Context.shared_key + end + + puts res + end + + def user_validation + args = Context.args.not_nil! + login, activation_key = args[0..1] + pp! authd.validate_user login, activation_key + end + def user_search + args = Context.args.not_nil! + login = args[0] + pp! authd.search_user login + end + def user_get + args = Context.args.not_nil! + login = args[0] + pp! authd.get_user? login + end + def user_recovery + args = Context.args.not_nil! + login, email = args[0..1] + pp! authd.ask_password_recovery login, email + end + + def permission_check + args = Context.args.not_nil! + user, application, resource = args[0..2] + # pp! user, application, resource + + res = @authd.check_permission user.to_i, application, resource + puts res + end + + def permission_set + args = Context.args.not_nil! + user, application, resource, permission = args[0..3] + # pp! user, application, resource, permission + + perm = AuthD::User::PermissionLevel.parse(permission) + res = @authd.set_permission user.to_i, application, resource, perm + puts res + end +end + +def main + + # Authd connection. + authd = AuthD::Client.new + authd.key = Context.shared_key if Context.shared_key != "undef" + + # Authd token. + # FIXME: not sure about getting the token, it seems not used elsewhere. + # If login == pass == "undef": do not even try. + #unless Context.authd_login == Context.authd_pass && Context.authd_login == "undef" + # login = Context.authd_login + # pass = Context.authd_pass + # token = authd.get_token? login, pass + # raise "cannot get a token" if token.nil? + # # authd.login token + #end + + actions = Actions.new authd + + # Now we did read the intent, we should proceed doing what was asked. + begin + actions.the_call[Context.command].call + rescue e + Baguette::Log.info "The command is not recognized (or implemented)." + end + + # authd disconnection + authd.close +rescue e + Baguette::Log.info "Exception: #{e}" +end + + +# Command line: +# tool [options] command [options-for-command] + +main + diff --git a/utils/authd-user-add.cr b/utils/authd-user-add.cr deleted file mode 100644 index 9ecf8b3..0000000 --- a/utils/authd-user-add.cr +++ /dev/null @@ -1,95 +0,0 @@ -require "option_parser" - -require "../src/authd.cr" - -key_file : String? = nil -cli_login : String? = nil -profile_file : String? = nil -register = false -email = nil -phone = nil -password : String? = nil - -OptionParser.parse do |parser| - parser.unknown_args do |args| - if args.size != 3 - puts "usage: #{PROGRAM_NAME} [options]" - puts parser - exit 1 - end - - cli_login, email, phone = args[0..2] - end - - parser.on "-p file", "--profile file", "Read the user profile from a file." do |file| - profile_file = file - end - - parser.on "-X user-password", "--user-password pass", "Read the new user password." do |pass| - password = pass - end - - parser.on "-K file", "--key-file file", "Read the authd shared key from a file." do |file| - key_file = file - end - - parser.on "-R", "--register", "Use a registration request instead of a add-user one." do - register = true - end - - parser.on "-h", "--help", "Prints this help message." do - puts "usage: #{PROGRAM_NAME} [options]" - puts parser - exit 0 - end -end - -if cli_login.nil? - STDERR.puts "no login provided" - exit 1 -end - -login = cli_login.not_nil! # not_nil!? O RLY? - -profile = profile_file.try do |file| - begin - JSON.parse(File.read file).as_h - rescue e - STDERR.puts e.message - exit 1 - end -end - -if password.nil? - STDOUT << "password: " - STDOUT << `stty -echo` - STDOUT.flush - password = STDIN.gets.try &.chomp - - STDOUT << '\n' - STDOUT << `stty echo` -end - -exit 1 unless password - -authd = AuthD::Client.new - -email = nil if email == "" -phone = nil if phone == "" - -begin - if register - pp! authd.register login, password.not_nil!, email, phone, profile: profile - else - key_file.try do |file| # FIXME: fail if missing? - authd.key = File.read(file).chomp - end - - pp! authd.add_user login, password.not_nil!, email, phone, profile: profile - end -rescue e : AuthD::Exception - puts "error: #{e.message}" -end - -authd.close - diff --git a/utils/authd-user-allow.cr b/utils/authd-user-allow.cr deleted file mode 100644 index 9f083bb..0000000 --- a/utils/authd-user-allow.cr +++ /dev/null @@ -1,70 +0,0 @@ -require "option_parser" - -require "../src/authd.cr" - -key_file : String? = nil -login : String? = nil -service : String? = nil -resource : String? = nil -register = false -level = AuthD::User::PermissionLevel::Read - -OptionParser.parse do |parser| - parser.unknown_args do |args| - if args.size != 3 - puts "usage: #{PROGRAM_NAME} [options]" - puts parser - exit 1 - end - - login, service, resource = args - end - - parser.on "-K file", "--key-file file", "Read the authd shared key from a file." do |file| - key_file = file - end - - parser.on "-L level", "--level level", "Sets the permission level to give the user." do |l| - begin - level = AuthD::User::PermissionLevel.parse l - rescue - STDERR.puts "Could not parse permission level '#{l}'" - exit 1 - end - end - - parser.on "-R", "--register", "Use a registration request instead of a add-user one." do - register = true - end - - parser.on "-h", "--help", "Prints this help message." do - puts "usage: #{PROGRAM_NAME} [options]" - puts parser - exit 0 - end -end - -if key_file.nil? - STDERR.puts "you need to provide the shared key" - exit 1 -end - -authd = AuthD::Client.new - -authd.key = File.read(key_file.not_nil!).chomp - -begin - user = authd.get_user? login.not_nil! - - if user.nil? - raise AuthD::Exception.new "#{login}: no such user" - end - - # FIXME: make a “disallow” variant. - authd.set_permission user.uid, service.not_nil!, resource.not_nil!, level -rescue e : AuthD::Exception - puts "error: #{e.message}" -end - -authd.close - diff --git a/utils/authd-user-ask-for-new-password.cr b/utils/authd-user-ask-for-new-password.cr deleted file mode 100644 index ebae596..0000000 --- a/utils/authd-user-ask-for-new-password.cr +++ /dev/null @@ -1,43 +0,0 @@ -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 = 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 diff --git a/utils/authd-user-get.cr b/utils/authd-user-get.cr deleted file mode 100644 index 436f378..0000000 --- a/utils/authd-user-get.cr +++ /dev/null @@ -1,40 +0,0 @@ -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 = AuthD::Client.new - authd.key = File.read(key_file.not_nil!).chomp - - login = cli_login.not_nil! - - pp! authd.get_user? login -rescue e - puts "Error: #{e}" - exit 1 -end diff --git a/utils/authd-user-mod.cr b/utils/authd-user-mod.cr deleted file mode 100644 index 8f5405d..0000000 --- a/utils/authd-user-mod.cr +++ /dev/null @@ -1,88 +0,0 @@ -require "option_parser" - -require "../src/authd.cr" - -key_file : String? = nil -cli_login : String? = nil -profile_file : String? = nil -register = false -email = nil -phone = nil - -OptionParser.parse do |parser| - parser.unknown_args do |args| - if args.size != 3 - puts "usage: #{PROGRAM_NAME} [options]" - puts parser - exit 1 - end - - cli_login, email, phone = args[0..2] - end - - parser.on "-p file", "--profile file", "Read the user profile from a file." do |file| - profile_file = file - end - - parser.on "-K file", "--key-file file", "Read the authd shared key from a file." do |file| - key_file = file - end - - parser.on "-R", "--register", "Use a registration request instead of a add-user one." do - register = true - end - - parser.on "-h", "--help", "Prints this help message." do - puts "usage: #{PROGRAM_NAME} [options]" - puts parser - exit 0 - end -end - -if cli_login.nil? - STDERR.puts "no login provided" - exit 1 -end - -login = cli_login.not_nil! # not_nil!? O RLY? - -profile = profile_file.try do |file| - begin - JSON.parse File.read file - rescue e - STDERR.puts e.message - exit 1 - end -end - -STDOUT << "password: " -STDOUT << `stty -echo` -STDOUT.flush -password = STDIN.gets.try &.chomp - -STDOUT << '\n' -STDOUT << `stty echo` - -exit 1 unless password - -authd = AuthD::Client.new - -email = nil if email == "" -phone = nil if phone == "" - -begin - if register - pp! authd.register login, password, email, phone, profile: profile - else - key_file.try do |file| # FIXME: fail if missing? - authd.key = File.read(file).chomp - end - - pp! authd.add_user login, password, email, phone, profile: profile - end -rescue e : AuthD::Exception - puts "error: #{e.message}" -end - -authd.close - diff --git a/utils/authd-user-perms.cr b/utils/authd-user-perms.cr deleted file mode 100644 index bf36565..0000000 --- a/utils/authd-user-perms.cr +++ /dev/null @@ -1,67 +0,0 @@ -require "option_parser" - -require "../src/authd.cr" - -key_file : String? = nil -cli_login : String? = nil -cli_service : String? = nil -cli_resource : String? = nil -cli_permlvl : String? = nil - -OptionParser.parse do |parser| - parser.unknown_args do |args| - if 3 < args.size > 4 - puts "usage: #{PROGRAM_NAME} [options]" - puts parser - exit 1 - end - - cli_login = args[0] - cli_service = args[1] - cli_resource = args[2] if args.size > 2 - cli_permlvl = args[3] if args.size > 3 - 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} [permission] [options]" - puts "example: #{PROGRAM_NAME} 1002 my-application chat read" - puts - puts "permission list: none read edit admin" - puts parser - exit 0 - end -end - -if cli_login.nil? - STDERR.puts "no login provided" - exit 1 -end - -login = cli_login.not_nil!.to_i # not_nil!? O RLY? -service = cli_service.not_nil! # not_nil! -resource = cli_resource.not_nil! # not_nil! - -authd = AuthD::Client.new - -begin - key_file.try do |file| # FIXME: fail if missing? - authd.key = File.read(file).chomp - end - - if cli_permlvl.nil? - pp! authd.check_permission login, service, resource - else - permlvl = cli_permlvl.not_nil! - perm = AuthD::User::PermissionLevel.parse(permlvl) - pp! authd.set_permission login, service, resource, perm - end -rescue e : AuthD::Exception - puts "error: #{e.message}" -end - -authd.close - diff --git a/utils/authd-user-search.cr b/utils/authd-user-search.cr deleted file mode 100644 index 42dc698..0000000 --- a/utils/authd-user-search.cr +++ /dev/null @@ -1,38 +0,0 @@ -require "option_parser" - -require "../src/authd.cr" - -# key_file : String? = nil -login : String? = nil -activation_key : String? = nil - -OptionParser.parse do |parser| - parser.unknown_args do |args| - if args.size != 1 - puts "usage: #{PROGRAM_NAME} login-to-search [options]" - exit 1 - end - - 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-to-search [options]" - puts parser - exit 0 - end -end - -begin - authd = AuthD::Client.new - # authd.key = File.read(key_file.not_nil!).chomp - - pp! r = authd.search_user login.not_nil! -rescue e - puts "Error: #{e}" - exit 1 -end diff --git a/utils/authd-user-validate.cr b/utils/authd-user-validate.cr deleted file mode 100644 index 039fa06..0000000 --- a/utils/authd-user-validate.cr +++ /dev/null @@ -1,38 +0,0 @@ -require "option_parser" - -require "../src/authd.cr" - -key_file : String? = nil -login : String? = nil -activation_key : String? = nil - -OptionParser.parse do |parser| - parser.unknown_args do |args| - if args.size != 2 - puts "usage: #{PROGRAM_NAME} login activation_key [options]" - exit 1 - end - - login, activation_key = args[0..1] - 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 = AuthD::Client.new - authd.key = File.read(key_file.not_nil!).chomp - - pp! r = authd.validate_user login.not_nil!, activation_key.not_nil! -rescue e - puts "Error: #{e}" - exit 1 -end diff --git a/utils/better-parser.cr b/utils/better-parser.cr new file mode 100644 index 0000000..f6263ca --- /dev/null +++ b/utils/better-parser.cr @@ -0,0 +1,228 @@ +require "option_parser" + +opt_authd_admin = -> (parser : OptionParser) { + parser.on "-k file", "--key-file file", "Read the authd shared key from a file." do |file| + Context.shared_key = File.read(file).chomp + Baguette::Log.info "Key for admin operations: #{Context.shared_key}." + end +} + +# frequently used functions +opt_authd_login = -> (parser : OptionParser) { + parser.on "-l LOGIN", "--login LOGIN", "Authd user login." do |login| + Context.authd_login = login + Baguette::Log.info "User login for authd: #{Context.authd_login}." + end + parser.on "-p PASSWORD", "--password PASSWORD", "Authd user password." do |password| + Context.authd_pass = password + Baguette::Log.info "User password for authd: #{Context.authd_pass}." + end +} + +opt_help = -> (parser : OptionParser) { + parser.on "help", "Prints this help message." do + puts parser + exit 0 + end +} + +opt_profile = -> (parser : OptionParser) { + parser.on "-P file", "--profile file", "Read the user profile from a file." do |file| + Context.user_profile = JSON.parse(File.read file).as_h + Baguette::Log.info "Reading the user profile: #{Context.user_profile}." + end +} + +opt_phone = -> (parser : OptionParser) { + parser.on "-n phone", "--phone-number num", "Phone number." do |phone| + Context.phone = phone + Baguette::Log.info "Reading the user phone number: #{Context.phone}." + end +} + +opt_email = -> (parser : OptionParser) { + parser.on "-e email", "--email address", "Email address." do |email| + Context.email = email + Baguette::Log.info "Reading the user email address: #{Context.email}." + end +} + + +# Unrecognized parameters are used to create commands with multiple arguments. +# Example: user add _login email phone_ +# Here, login, email and phone are unrecognized arguments. +# Still, the "user add" command expect them. +unrecognized_args_to_context_args = -> (parser : OptionParser, n_expected_args : Int32) { + # With the right args, these will be interpreted as serialized data. + parser.unknown_args do |args| + if args.size != n_expected_args + Baguette::Log.error "#{parser}" + exit 1 + end + args.each do |arg| + Baguette::Log.debug "Unrecognized argument: #{arg} (adding to Context.args)" + if Context.args.nil? + Context.args = Array(String).new + end + Context.args.not_nil! << arg + end + end +} + +parser = OptionParser.new do |parser| + parser.banner = "usage: #{PROGRAM_NAME} command help" + parser.on "-v verbosity", "--verbosity v", "Verbosity. From 0 to 4 (debug)." do |v| + Baguette::Context.verbosity = v.to_i + Baguette::Log.info "verbosity = #{v}" + end + parser.on "-h", "--help", "Prints this help message." do + puts "usage: #{PROGRAM_NAME} command help" + puts parser + exit 0 + end + + parser.on "user", "Operations on users." do + parser.banner = "Usage: user [add | mod | delete | validate | search | get | recover | register ]" + + parser.on "add", "Adding a user to the DB." do + parser.banner = "usage: user add login email phone [-P profile] [opt]" + Baguette::Log.info "Adding a user to the DB." + Context.command = "user-add" + opt_authd_admin.call parser + opt_profile.call parser + opt_email.call parser + opt_phone.call parser + opt_help.call parser + # login email phone + unrecognized_args_to_context_args.call parser, 3 + end + + parser.on "mod", "Modify a user account." do + parser.banner = "Usage: user mod userid [-e email|-n phone|-P profile] [opt]" + Baguette::Log.info "Modify a user account." + Context.command = "user-mod" + opt_authd_admin.call parser + opt_email.call parser + opt_phone.call parser + opt_profile.call parser + opt_help.call parser + # userid + unrecognized_args_to_context_args.call parser, 1 + end + + parser.on "delete", "Remove user." do + parser.banner = "Usage: user delete userid [opt]" + Baguette::Log.info "Remove user." + Context.command = "user-delete" + # You can either be the owner of the account, or an admin. + opt_authd_login.call parser + opt_authd_admin.call parser + opt_help.call parser + # userid + unrecognized_args_to_context_args.call parser, 1 + end + + parser.on "validate", "Validate user." do + parser.banner = "Usage: user validate login activation-key [opt]" + Baguette::Log.info "Validate user." + Context.command = "user-validate" + # No need to be authenticated. + opt_help.call parser + # login activation-key + unrecognized_args_to_context_args.call parser, 2 + end + + parser.on "get", "Get user info." do + parser.banner = "Usage: user get login [opt]" + Baguette::Log.info "Get user info." + Context.command = "user-get" + # No need to be authenticated. + opt_help.call parser + # login + unrecognized_args_to_context_args.call parser, 1 + end + + parser.on "search", "Search user." do + parser.banner = "Usage: user recover login [opt]" + Baguette::Log.info "Search user." + Context.command = "user-search" + # No need to be authenticated. + opt_help.call parser + # login + unrecognized_args_to_context_args.call parser, 1 + end + + parser.on "recover", "Recover user password." do + parser.banner = "Usage: user recover login email [opt]" + Baguette::Log.info "Recover user password." + Context.command = "user-recovery" + # No need to be authenticated. + opt_help.call parser + # login email + unrecognized_args_to_context_args.call parser, 2 + end + + + # Do not require to be admin. + parser.on "register", "Register a user (requires activation)." do + parser.banner = "Usage: user register login email phone [-P profile] [opt]" + Baguette::Log.info "Register a user (requires activation)." + Context.command = "user-registration" + # These options shouldn't be used here, + # email and phone parameters are mandatory. + # opt_email.call parser + # opt_phone.call parser + opt_profile.call parser + opt_help.call parser + # login email phone + unrecognized_args_to_context_args.call parser, 3 + end + end + + parser.on "permission", "Permissions management." do + parser.banner = "Usage: permissions [check | set]" + parser.on "set", "Set permissions." do + parser.banner = <<-END + usage: permission set user application resource permission +example: permission set 1002 my-application chat read + +permission list: none read edit admin +END + Baguette::Log.info "Set permissions." + Context.command = "permission-set" + opt_authd_admin.call parser + opt_help.call parser + # userid application resource permission + unrecognized_args_to_context_args.call parser, 4 + end + + parser.on "check", "Check permissions." do + parser.banner = <<-END + usage: permission check user application resource +example: permission check 1002 my-application chat + +permission list: none read edit admin +END + Baguette::Log.info "Check permissions." + Context.command = "permission-check" + opt_authd_admin.call parser + opt_help.call parser + # userid application resource + unrecognized_args_to_context_args.call parser, 3 + end + end + + parser.unknown_args do |args| + if args.size > 0 + Baguette::Log.warning "Unknown args: #{args}" + end + end + +# parser.on "-X user-password", "--user-password pass", "Read the new user password." do |pass| +# password = pass +# end + +end + + +parser.parse