-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add password protection in console sessions #103
Comments
I was just thinking about this. I don't think console1984 should provide its own authentication mechanism, but it would be nice to be able to plug an existing authentication mechanism. Maybe something like this? config.console1984.user_authentication = proc do
username = Console1984.supervisor.current_username # this will use the configured username resolver or ask for a username as needed
user = User.find_by(username: username)
raise "Invalid username" if user.nil?
password = $stdin.getpass("Password: ")
raise "Invalid password" unless user.valid_password?(password)
raise "Not an admin" unless user.is_admin?
end Then console1984 would call the This is a just a rough example, but I'd be happy to submit a PR if the maintainers would be open to such a feature. @jorgemanrubia wdyt? |
I like the idea @olivier-thatch 👍 |
@olivier-thatch I'm also interested in this functionality. Has any progress been made yet? |
No, sorry, I've been very busy with an ongoing project and probably won't get a chance to work on this in the next few weeks. |
No worries! Completely understand. I have a workaround by configuring a custom |
I temporarily override the gem to handle it with Devise users module Console1984
class Supervisor
def current_username
if Console1984.config.ask_for_user_if_empty
unless @current_username.present?
email = ask_for_value "Please, enter your email"
user = ::User.find_by(email: email)
return raise Console1984::Errors::MissingUsername unless user.present?
password = ask_for_value "Password: "
return raise Console1984::Errors::MissingUsername unless user.valid_password? password
end
@current_username ||= user.email
else
@current_username ||= username_resolver.current.presence || handle_empty_username
end
end
private
def handle_empty_username
if Console1984.config.ask_for_username_if_empty
ask_for_value "Please, enter your name:"
else
raise Console1984::Errors::MissingUsername
end
end
end
class Config
remove_const(:PROPERTIES) if (defined?(PROPERTIES))
PROPERTIES = %i[
session_logger username_resolver ask_for_username_if_empty shield command_executor
protected_environments protected_urls
production_data_warning enter_unprotected_encryption_mode_warning enter_protected_mode_warning
incinerate incinerate_after incineration_queue
protections_config
base_record_class
debug test_mode
ask_for_user_if_empty
]
attr_accessor(*PROPERTIES)
private
def set_defaults
super
self.ask_for_user_if_empty = false
end
end
class Shield
private
def prevent_invoking_protected_methods
Console1984::ProtectionsConfig.new(YAML.safe_load(File.read(Console1984::Engine.root.join("config/protections.yml"))).symbolize_keys)
end
end
end
Console1984.config.ask_for_username_if_empty = false
Console1984.config.ask_for_user_if_empty = true |
A custom resolver worked for us: config.console1984.username_resolver = CustomResolver
class CustomResolver
include Console1984::Freezable
def current
# user/pass/totp auth here
end
end |
I did a similar approach to @grk, ( at first I forked #116 but the more I read the source code, the more I figured out a built-in approach ) Here is a full example implementation for email/password with # frozen_string_literal: true
# Custom username resolver for console1984 to authenticate
# administrators to production console
module CustomConsole1984
class UsernameResolver
include ::Console1984::Freezeable # Inspired by the default resolver provided by the gem.
include ::Console1984::InputOutput # to access `ask_for_value` built-in helper
def current
email = ask_for_value("Please, enter your email:")
password = ask_for_password
user = User.find_for_authentication(email: email)
raise "User with email: `#{email}` not found" unless administrator
is_valid = user.valid_for_authentication? { user.valid_password?(password) }
# it's important to return a string representing a username for console1984
# i recommend prepending with the `id` because the username value will be stored in `Console1984::User` table
# This will allow to properly distinguish user, and is a safe approach IMO
is_valid ? "#{user.id}_#{user.full_name.sub(' ', '_')}" : raise("Authentication failed for user `#{email}`")
end
private
# helper to hide password typing, similar to any `sudo ...` command in UNIX.
def ask_for_password
puts Rainbow("Please, enter your password:").green
password = $stdin.noecho(&:gets).chomp until password.present?
password
end
end
end then in # important, this require_relative must be called AFTER `Bundler.require(*Rails.groups)`
require_relative '../lib/my_path_to_resolver'
config.console1984.username_resolver = CustomConsole1984::UsernameResolver.new # Important to instantiate an instance. |
I also wanted to send a slack notification after each successful After reading the source code, I found there is a # frozen_string_literal: true
module CustomConsole1984
class SessionLogger < ::Console1984::SessionsLogger::Database
# reopen `start_session` method
def start_session(username, reason)
super
SlackMonitoring::AlertWhenAdministratorConnectsToProdConsole.call(username:, reason:)
end
end
end You can also create your own logger but the interface/contract is a bit heavy, (as well as undocumented :/ ) So I personally recommend my approach 🤷🏼♂️ Finally add the following in # important, this require_relative must be called AFTER `Bundler.require(*Rails.groups)`
require_relative '../lib/my_path_to_resolver'
# ...
config.console1984.session_logger = CustomConsole1984::SessionLogger.new # important to create an instance. You can probably create these custom resolvers/loggers with the |
It currently asks for a name to log info, but with multiple folks having access any one can use anyone's name. Would be great to have a password issued to each user so they can use to only login using their password.
The text was updated successfully, but these errors were encountered: