5e495d72f5
When passed arguments as `opts` prefer to avoid side-effects from method execution. This extends similar work from #12740
146 lines
4.6 KiB
Ruby
146 lines
4.6 KiB
Ruby
require 'bcrypt'
|
|
require 'securerandom'
|
|
|
|
module Msf::DBManager::User
|
|
|
|
MIN_TOKEN_LENGTH = 20
|
|
|
|
# Returns a list of all users in the database
|
|
def users(opts)
|
|
::ApplicationRecord.connection_pool.with_connection {
|
|
|
|
opts = opts.clone() # protect the original caller's opts
|
|
search_term = opts.delete(:search_term)
|
|
if search_term && !search_term.empty?
|
|
column_search_conditions = Msf::Util::DBManager.create_all_column_search_conditions(Mdm::User, search_term)
|
|
Mdm::User.where(opts).where(column_search_conditions)
|
|
else
|
|
Mdm::User.where(opts)
|
|
end
|
|
}
|
|
end
|
|
|
|
#
|
|
# Report a user's attributes.
|
|
#
|
|
# The opts parameter MUST contain:
|
|
# +:username+:: -- the username
|
|
# +:password+:: -- the users's cleartext password
|
|
#
|
|
# The opts parameter can contain:
|
|
# +:fullname+:: -- the users's fullname
|
|
# +:email+:: -- the users's email
|
|
# +:phone+:: -- the users's phone
|
|
# +:email+:: -- the users's email
|
|
# +:company+:: -- the users's company
|
|
# +:prefs+:: -- [Hash] the users's preferences
|
|
# +:admin+:: -- [Boolean] True if the user is an admin; otherwise, false.
|
|
#
|
|
# @return [Mdm::User] The reported Mdm::User object.
|
|
def report_user(opts)
|
|
return unless active
|
|
raise ArgumentError.new("Missing required option :username") if opts[:username].nil?
|
|
raise ArgumentError.new("Missing required option :password") if opts[:password].nil?
|
|
|
|
::ApplicationRecord.connection_pool.with_connection {
|
|
|
|
conditions = {username: opts[:username]}
|
|
user = Mdm::User.where(conditions).first_or_initialize
|
|
|
|
opts.each do |k,v|
|
|
if user.attribute_names.include?(k.to_s)
|
|
user[k] = v
|
|
elsif !v.blank?
|
|
dlog("Unknown attribute for ::Mdm::User: #{k}")
|
|
end
|
|
end
|
|
|
|
user.crypted_password = BCrypt::Password.create(opts[:password])
|
|
user.admin = false if opts[:admin].nil?
|
|
|
|
# Finalize
|
|
if user.changed?
|
|
msf_import_timestamps(opts, user)
|
|
user.save!
|
|
end
|
|
|
|
user
|
|
}
|
|
end
|
|
|
|
# Update the attributes of a user entry with the values in opts.
|
|
# The values in opts should match the attributes to update.
|
|
#
|
|
# @param opts [Hash] Hash containing the updated values. Key should match the attribute to update. Must contain :id of record to update.
|
|
# @return [Mdm::User] The updated Mdm::User object.
|
|
def update_user(opts)
|
|
::ApplicationRecord.connection_pool.with_connection {
|
|
opts = opts.clone() # protect the original caller's opts
|
|
id = opts.delete(:id)
|
|
user = Mdm::User.find(id)
|
|
user.update!(opts)
|
|
return user
|
|
}
|
|
end
|
|
|
|
# Deletes user entries based on the IDs passed in.
|
|
#
|
|
# @param opts[:ids] [Array] Array containing Integers corresponding to the IDs of the user entries to delete.
|
|
# @return [Array] Array containing the Mdm::User objects that were successfully deleted.
|
|
def delete_user(opts)
|
|
raise ArgumentError.new("The following options are required: :ids") if opts[:ids].nil?
|
|
|
|
::ApplicationRecord.connection_pool.with_connection {
|
|
deleted = []
|
|
opts[:ids].each do |user_id|
|
|
user = Mdm::User.find(user_id)
|
|
begin
|
|
deleted << user.destroy
|
|
rescue # refs suck
|
|
elog("Forcibly deleting #{user}")
|
|
deleted << user.delete
|
|
end
|
|
end
|
|
|
|
return deleted
|
|
}
|
|
end
|
|
|
|
# Authenticates the user.
|
|
#
|
|
# @param opts[:ids] [Integer] ID of the user to authenticate.
|
|
# @param opts[:password] [String] The user's password.
|
|
# @return [Boolean] True if the user is successfully authenticated; otherwise, false.
|
|
def authenticate_user(opts)
|
|
raise ArgumentError.new("The following options are required: :id") if opts[:id].nil?
|
|
raise ArgumentError.new("The following options are required: :password") if opts[:password].nil?
|
|
|
|
user = Mdm::User.find(opts[:id])
|
|
begin
|
|
!user.nil? && BCrypt::Password.new(user.crypted_password) == opts[:password]
|
|
rescue BCrypt::Errors::InvalidHash
|
|
false
|
|
end
|
|
end
|
|
|
|
# Creates a new API token for the user.
|
|
#
|
|
# The opts parameter MUST contain:
|
|
# @param opts[:ids] [Integer] ID for the user.
|
|
#
|
|
# The opts parameter can contain:
|
|
# @param opts[:token_length] [Integer] Token length.
|
|
#
|
|
# @return [String] The new API token.
|
|
def create_new_user_token(opts)
|
|
raise ArgumentError.new("The following options are required: :id") if opts[:id].nil?
|
|
|
|
token_length = opts[:token_length] || MIN_TOKEN_LENGTH
|
|
# NOTE: repurposing persistence_token in the database as the API token
|
|
user = Mdm::User.find(opts[:id])
|
|
user.update!({persistence_token: SecureRandom.hex(token_length)})
|
|
user.persistence_token
|
|
end
|
|
|
|
end
|