authc CLI for authd
This commit is contained in:
		
							parent
							
								
									b90facdb82
								
							
						
					
					
						commit
						f8d98ab1a1
					
				
					 13 changed files with 545 additions and 499 deletions
				
			
		
							
								
								
									
										18
									
								
								shard.yml
									
										
									
									
									
								
							
							
						
						
									
										18
									
								
								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 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										34
									
								
								src/authd.cr
									
										
									
									
									
								
							
							
						
						
									
										34
									
								
								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 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										53
									
								
								src/main.cr
									
										
									
									
									
								
							
							
						
						
									
										53
									
								
								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 | ||||
|  |  | |||
							
								
								
									
										232
									
								
								utils/authc.cr
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										232
									
								
								utils/authc.cr
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||
| 
 | ||||
|  | @ -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} <login> <email> <phone> [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} <login> <email> <phone> [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 | ||||
| 
 | ||||
|  | @ -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} <user> <service> <resource> [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} <user> <service> <resource> [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 | ||||
| 
 | ||||
|  | @ -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} <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 = 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 | ||||
|  | @ -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} <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> <email> <phone> [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 | ||||
|  | @ -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} <login> <email> <phone> [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} <login> <email> <phone> [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 | ||||
| 
 | ||||
|  | @ -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} <uid> <service> <resource> <permlevel> [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} <uid> <service>      <resource> [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 | ||||
| 
 | ||||
|  | @ -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 | ||||
|  | @ -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} <login> <email> <phone> [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 | ||||
							
								
								
									
										228
									
								
								utils/better-parser.cr
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										228
									
								
								utils/better-parser.cr
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||
		Loading…
	
	Add table
		
		Reference in a new issue