v0.7 -- complete rework... override SessionController::create

This commit is contained in:
dsainty 2024-06-11 23:27:00 +10:00
parent 7445bd8f06
commit 4dc121eb76

125
plugin.rb
View File

@ -4,72 +4,89 @@
# name: discourse-md5_authentication # name: discourse-md5_authentication
# about: A plugin to authenticate users with MD5 passwords from legacy systems # about: A plugin to authenticate users with MD5 passwords from legacy systems
# version: 0.6 # version: 0.7
# authors: saint # authors: saint
# url: https://gitea.federated.computer/saint/discourse-md5_authentication.git # url: https://gitea.federated.computer/saint/discourse-md5_authentication.git
# This block will run after Discourse has initialized
after_initialize do after_initialize do
# Define a module to contain the MD5 authentication logic class ::SessionController < ApplicationController
module LegacyMd5Authentication def create
# Override the log_on_user method to include MD5 authentication params.require(:login)
def log_on_user(user, session, cookie_jar, opts = {}) params.require(:password)
# If the user has an MD5 password and the MD5 password option is passed
if user.custom_fields['md5_password'] && opts[:md5_password] return invalid_credentials if params[:password].length > User.max_password_length
# Check if the provided MD5 password matches the stored MD5 password
if user.custom_fields['md5_password'] == Digest::MD5.hexdigest(opts[:md5_password]) user = User.find_by_username_or_email(normalized_login_param)
# Update the user to use the new password and clear the MD5 password
user.update!(password: opts[:md5_password]) raise Discourse::ReadOnly if staff_writes_only_mode? && !user&.staff?
user.custom_fields['md5_password'] = nil
user.save_custom_fields rate_limit_second_factor!(user)
if user.present?
password = params[:password]
custom_password_md5 = user.custom_fields['custom_password_md5']
# Check for MD5 password in custom field
if custom_password_md5.present?
# MD5 password is present
if Digest::MD5.hexdigest(password) == custom_password_md5
# MD5 matches, so update the user's password to the new one and remove the custom field
user.password = password
user.custom_fields['custom_password_md5'] = nil
user.save!
Rails.logger.debug "Updated MD5 password for user: #{user.id}"
else else
# Return nil if the MD5 password does not match
return nil # MD5 doesn't match, so we have a failed login attempt.
end Rails.logger.debug "Password incorrect for user: #{user.id}"
end invalid_credentials
# Call the original log_on_user method return
super
end end
# Define a method to authenticate a user with an MD5 password # If their password is incorrect
def authenticate_with_md5(username, password) elsif !user.confirm_password?(password)
# Find the user by username or email, ignoring case
user = User.find_by_username_or_email(username.downcase.strip) # There is no MD5 password and the password was incorrect.
# Check if the user exists and the provided MD5 password matches the stored MD5 password Rails.logger.debug "Password incorrect for user: #{user.id}"
if user && user.custom_fields['md5_password'] == Digest::MD5.hexdigest(password) invalid_credentials
# Update the user to use the new password and clear the MD5 password return
user.update!(password: password) end
user.custom_fields['md5_password'] = nil
user.save_custom_fields # If the site requires user approval and the user is not approved yet
user if login_not_approved_for?(user)
render json: login_not_approved
return
end
# User signed on with username and password, so let's prevent the invite link
# from being used to log in (if one exists).
Invite.invalidate_for_email(user.email)
# User's password has expired so they need to reset it
if user.password_expired?(password)
render json: { error: "expired", reason: "expired" }
return
end
else else
nil invalid_credentials
end return
end
end end
# Extend the DefaultCurrentUserProvider class to include our MD5 authentication logic if payload = login_error_check(user)
class ::Auth::DefaultCurrentUserProvider return render json: payload
prepend LegacyMd5Authentication
# Alias the original current_user method
alias_method :original_current_user, :current_user
def current_user
# Attempt to find the current user using the standard Discourse method
user = original_current_user
return user if user
# Check for MD5 authentication if no user is found by the standard method
username = @request.params[:login]
password = @request.params[:password]
if username && password
# Authenticate the user with MD5
user = authenticate_with_md5(username, password)
@env[CURRENT_USER_KEY] = user if user
end end
user second_factor_auth_result = authenticate_second_factor(user)
return render(json: @second_factor_failure_payload) unless second_factor_auth_result.ok
if user.active && user.email_confirmed?
login(user, second_factor_auth_result)
else
not_activated(user)
end
end end
end end
end end