lib/msf/core/db_manager/user.rb
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_assign_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