From 4dc121eb76baaf4dff8a43fc1cfb3835eca50953 Mon Sep 17 00:00:00 2001 From: dsainty Date: Tue, 11 Jun 2024 23:27:00 +1000 Subject: [PATCH] v0.7 -- complete rework... override SessionController::create --- plugin.rb | 131 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 74 insertions(+), 57 deletions(-) diff --git a/plugin.rb b/plugin.rb index 912163b..9574d88 100644 --- a/plugin.rb +++ b/plugin.rb @@ -4,72 +4,89 @@ # name: discourse-md5_authentication # about: A plugin to authenticate users with MD5 passwords from legacy systems -# version: 0.6 +# version: 0.7 # authors: saint # url: https://gitea.federated.computer/saint/discourse-md5_authentication.git -# This block will run after Discourse has initialized after_initialize do - # Define a module to contain the MD5 authentication logic - module LegacyMd5Authentication - # Override the log_on_user method to include MD5 authentication - def log_on_user(user, session, cookie_jar, opts = {}) - # If the user has an MD5 password and the MD5 password option is passed - if user.custom_fields['md5_password'] && opts[:md5_password] - # Check if the provided MD5 password matches the stored MD5 password - if user.custom_fields['md5_password'] == Digest::MD5.hexdigest(opts[:md5_password]) - # Update the user to use the new password and clear the MD5 password - user.update!(password: opts[:md5_password]) - user.custom_fields['md5_password'] = nil - user.save_custom_fields - else - # Return nil if the MD5 password does not match - return nil + class ::SessionController < ApplicationController + def create + params.require(:login) + params.require(:password) + + return invalid_credentials if params[:password].length > User.max_password_length + + user = User.find_by_username_or_email(normalized_login_param) + + raise Discourse::ReadOnly if staff_writes_only_mode? && !user&.staff? + + 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 + + # MD5 doesn't match, so we have a failed login attempt. + Rails.logger.debug "Password incorrect for user: #{user.id}" + invalid_credentials + return + end + + # If their password is incorrect + elsif !user.confirm_password?(password) + + # There is no MD5 password and the password was incorrect. + Rails.logger.debug "Password incorrect for user: #{user.id}" + invalid_credentials + return end - end - # Call the original log_on_user method - super - end - # Define a method to authenticate a user with an MD5 password - def authenticate_with_md5(username, password) - # Find the user by username or email, ignoring case - user = User.find_by_username_or_email(username.downcase.strip) - # Check if the user exists and the provided MD5 password matches the stored MD5 password - if user && user.custom_fields['md5_password'] == Digest::MD5.hexdigest(password) - # Update the user to use the new password and clear the MD5 password - user.update!(password: password) - user.custom_fields['md5_password'] = nil - user.save_custom_fields - user + # If the site requires user approval and the user is not approved yet + 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 - nil - end - end - end - - # Extend the DefaultCurrentUserProvider class to include our MD5 authentication logic - class ::Auth::DefaultCurrentUserProvider - 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 + invalid_credentials + return end - user + if payload = login_error_check(user) + return render json: payload + end + + 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