v0.10.1 -- restored optimal design, resolved multiple issues, notable progress
This commit is contained in:
		
							
								
								
									
										307
									
								
								plugin.rb
									
									
									
									
									
								
							
							
						
						
									
										307
									
								
								plugin.rb
									
									
									
									
									
								
							@@ -4,32 +4,132 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# 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.9.6
 | 
					# version: 0.10.1
 | 
				
			||||||
# authors: saint
 | 
					# authors: saint
 | 
				
			||||||
# url: https://gitea.federated.computer/saint/discourse-md5_authentication.git
 | 
					# url: https://gitea.federated.computer/saint/discourse-md5_authentication.git
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# require 'digest'
 | 
					# require 'digest'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
after_initialize do
 | 
					after_initialize do
 | 
				
			||||||
  class ::SessionController < ApplicationController
 | 
					  # Define a module to contain the MD5 authentication logic
 | 
				
			||||||
 | 
					  module LegacyMd5Authentication
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Constants
 | 
					    # Constants
 | 
				
			||||||
    ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
 | 
					    ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to64(value, length)
 | 
				
			||||||
 | 
					      result = String.new
 | 
				
			||||||
 | 
					      length.times do
 | 
				
			||||||
 | 
					        result << ITOA64[value & 0x3f]
 | 
				
			||||||
 | 
					        value >>= 6
 | 
				
			||||||
 | 
					        Rails.logger.debug "to64 result: #{result}"
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					      result
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def gossamer_md5_crypt(password, legacy_hash)
 | 
				
			||||||
 | 
					      # Extract the salt from the legacy hash
 | 
				
			||||||
 | 
					      parts = legacy_hash.split('$')
 | 
				
			||||||
 | 
					      salt = parts[2]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      # Limit the salt to 8 characters
 | 
				
			||||||
 | 
					      salt = salt[0, 8]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      magic = "$GT$"
 | 
				
			||||||
 | 
					      Rails.logger.debug "MD5 magic: #{magic}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      ctx = Digest::MD5.new
 | 
				
			||||||
 | 
					      ctx.update(password)
 | 
				
			||||||
 | 
					      ctx.update(magic)
 | 
				
			||||||
 | 
					      ctx.update(salt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      final = Digest::MD5.new
 | 
				
			||||||
 | 
					      final.update(password)
 | 
				
			||||||
 | 
					      final.update(salt)
 | 
				
			||||||
 | 
					      final.update(password)
 | 
				
			||||||
 | 
					      final_digest = final.digest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      password_length = password.length
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      while password_length > 0
 | 
				
			||||||
 | 
					        ctx.update(final_digest[0, [password_length, 16].min])
 | 
				
			||||||
 | 
					        password_length -= 16
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      password_length = password.length
 | 
				
			||||||
 | 
					      while password_length > 0
 | 
				
			||||||
 | 
					        if password_length & 1 != 0
 | 
				
			||||||
 | 
					          ctx.update("\x00")
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					          ctx.update(password[0, 1])
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        password_length >>= 1
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      final_digest = ctx.digest
 | 
				
			||||||
 | 
					      Rails.logger.debug "MD5 final_digest: #{final_digest}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      1000.times do |i|
 | 
				
			||||||
 | 
					        ctx1 = Digest::MD5.new
 | 
				
			||||||
 | 
					        if i & 1 != 0
 | 
				
			||||||
 | 
					          Rails.logger.debug "AAA"
 | 
				
			||||||
 | 
					          ctx1.update(password)
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					          Rails.logger.debug "BBB"
 | 
				
			||||||
 | 
					          ctx1.update(final_digest)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        ctx1.update(salt) if i % 3 != 0
 | 
				
			||||||
 | 
					        ctx1.update(password) if i % 7 != 0
 | 
				
			||||||
 | 
					        if i & 1 != 0
 | 
				
			||||||
 | 
					          Rails.logger.debug "CCC"
 | 
				
			||||||
 | 
					          ctx1.update(final_digest)
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					          Rails.logger.debug "DDD"
 | 
				
			||||||
 | 
					          ctx1.update(password)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        final_digest = ctx1.digest
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					      Rails.logger.debug "MD6 final_digest: #{final_digest}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      result = String.new
 | 
				
			||||||
 | 
					      Rails.logger.debug "A result: #{result}"
 | 
				
			||||||
 | 
					      result << to64((final_digest[0].ord << 16) | (final_digest[6].ord << 8) | final_digest[12].ord, 4)
 | 
				
			||||||
 | 
					      Rails.logger.debug "B result: #{result}"
 | 
				
			||||||
 | 
					      result << to64((final_digest[1].ord << 16) | (final_digest[7].ord << 8) | final_digest[13].ord, 4)
 | 
				
			||||||
 | 
					      Rails.logger.debug "C result: #{result}"
 | 
				
			||||||
 | 
					      result << to64((final_digest[2].ord << 16) | (final_digest[8].ord << 8) | final_digest[14].ord, 4)
 | 
				
			||||||
 | 
					      Rails.logger.debug "D result: #{result}"
 | 
				
			||||||
 | 
					      result << to64((final_digest[3].ord << 16) | (final_digest[9].ord << 8) | final_digest[15].ord, 4)
 | 
				
			||||||
 | 
					      Rails.logger.debug "E result: #{result}"
 | 
				
			||||||
 | 
					      result << to64((final_digest[4].ord << 16) | (final_digest[10].ord << 8) | final_digest[5].ord, 4)
 | 
				
			||||||
 | 
					      Rails.logger.debug "F result: #{result}"
 | 
				
			||||||
 | 
					      result << to64(final_digest[11].ord, 2)
 | 
				
			||||||
 | 
					      Rails.logger.debug "G result: #{result}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      Rails.logger.debug "magic salt result   #{magic}#{salt}$#{result}"
 | 
				
			||||||
 | 
					      "#{magic}#{salt}$#{result}"
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def verify_gossamer_password(password, legacy_hash)
 | 
				
			||||||
 | 
					      generated_hash = gossamer_md5_crypt(password, legacy_hash)
 | 
				
			||||||
 | 
					      generated_hash == legacy_hash
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Extend the SessionController create method to include our MD5 authentication logic
 | 
				
			||||||
 | 
					  class ::SessionController < ApplicationController
 | 
				
			||||||
 | 
					    prepend LegacyMd5Authentication
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create
 | 
					    def create
 | 
				
			||||||
      # Require the necessary parameters
 | 
					 | 
				
			||||||
      params.require(:login)
 | 
					      params.require(:login)
 | 
				
			||||||
      params.require(:password)
 | 
					      params.require(:password)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      # Validate password length
 | 
					 | 
				
			||||||
      return invalid_credentials if params[:password].length > User.max_password_length
 | 
					      return invalid_credentials if params[:password].length > User.max_password_length
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      # Find user by username or email
 | 
					 | 
				
			||||||
      user = User.find_by_username_or_email(normalized_login_param)
 | 
					      user = User.find_by_username_or_email(normalized_login_param)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      # Check for read-only mode for non-staff users
 | 
					 | 
				
			||||||
      raise Discourse::ReadOnly if staff_writes_only_mode? && !user&.staff?
 | 
					      raise Discourse::ReadOnly if staff_writes_only_mode? && !user&.staff?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      # Apply rate limit for second factor authentication
 | 
					 | 
				
			||||||
      rate_limit_second_factor!(user)
 | 
					      rate_limit_second_factor!(user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if user.present?
 | 
					      if user.present?
 | 
				
			||||||
@@ -37,162 +137,107 @@ after_initialize do
 | 
				
			|||||||
        custom_password_md5 = user.custom_fields['custom_password_md5']
 | 
					        custom_password_md5 = user.custom_fields['custom_password_md5']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Check for MD5 password in custom field
 | 
					        # Check for MD5 password in custom field
 | 
				
			||||||
 | 
					        Rails.logger.debug "Check for MD5 password in custom field"
 | 
				
			||||||
        if custom_password_md5.present?
 | 
					        if custom_password_md5.present?
 | 
				
			||||||
          Rails.logger.debug "MD5 password is present. custom_password_md5: #{custom_password_md5}, password: #{password}"
 | 
					          # MD5 password is present
 | 
				
			||||||
 | 
					          Rails.logger.debug "MD5 password is present custom_password_md5: #{custom_password_md5} password: #{password}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          # Extract the salt from the legacy hash
 | 
					          if verify_gossamer_password(password, custom_password_md5)
 | 
				
			||||||
          parts = custom_password_md5.split('$')
 | 
					            # MD5 matches, so update the user's password to the new one, remove the custom field and ensure user is set to active and approved
 | 
				
			||||||
          Rails.logger.debug "Split parts: #{parts.inspect}"
 | 
					            Rails.logger.debug "MD5 matches"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if parts.length >= 3
 | 
					            # Set password using Discourse's current standards, ensuring correct hashing.
 | 
				
			||||||
            salt = parts[2][0, 8]
 | 
					 | 
				
			||||||
          else
 | 
					 | 
				
			||||||
            Rails.logger.debug "Invalid MD5 format for custom_password_md5: #{custom_password_md5}"
 | 
					 | 
				
			||||||
            return invalid_credentials
 | 
					 | 
				
			||||||
          end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          magic = "$GT$"
 | 
					 | 
				
			||||||
          Rails.logger.debug "MD5 magic: #{magic}, salt: #{salt}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          # Create initial MD5 context
 | 
					 | 
				
			||||||
          ctx = Digest::MD5.new
 | 
					 | 
				
			||||||
          ctx.update(password)
 | 
					 | 
				
			||||||
          ctx.update(magic)
 | 
					 | 
				
			||||||
          ctx.update(salt)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          # Create final MD5 digest
 | 
					 | 
				
			||||||
          final = Digest::MD5.new
 | 
					 | 
				
			||||||
          final.update(password)
 | 
					 | 
				
			||||||
          final.update(salt)
 | 
					 | 
				
			||||||
          final.update(password)
 | 
					 | 
				
			||||||
          final_digest = final.digest.dup  # Ensure final_digest is not frozen
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          # Perform password length operations
 | 
					 | 
				
			||||||
          password_length = password.length
 | 
					 | 
				
			||||||
          while password_length > 0
 | 
					 | 
				
			||||||
            ctx.update(final_digest[0, [password_length, 16].min])
 | 
					 | 
				
			||||||
            password_length -= 16
 | 
					 | 
				
			||||||
          end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          password_length = password.length
 | 
					 | 
				
			||||||
          while password_length > 0
 | 
					 | 
				
			||||||
            if password_length & 1 != 0
 | 
					 | 
				
			||||||
              ctx.update("\x00")
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
              ctx.update(password[0, 1])
 | 
					 | 
				
			||||||
            end
 | 
					 | 
				
			||||||
            password_length >>= 1
 | 
					 | 
				
			||||||
          end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          final_digest = ctx.digest.dup  # Ensure final_digest is not frozen
 | 
					 | 
				
			||||||
          Rails.logger.debug "MD5 final_digest after initial operations: #{final_digest}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          # Perform 1000 iterations of MD5 hashing
 | 
					 | 
				
			||||||
          1000.times do |i|
 | 
					 | 
				
			||||||
            ctx1 = Digest::MD5.new
 | 
					 | 
				
			||||||
            if i & 1 != 0
 | 
					 | 
				
			||||||
              ctx1.update(password)
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
              ctx1.update(final_digest.dup)  # Ensure final_digest is not frozen
 | 
					 | 
				
			||||||
            end
 | 
					 | 
				
			||||||
            ctx1.update(salt) if i % 3 != 0
 | 
					 | 
				
			||||||
            ctx1.update(password) if i % 7 != 0
 | 
					 | 
				
			||||||
            if i & 1 != 0
 | 
					 | 
				
			||||||
              ctx1.update(final_digest.dup)  # Ensure final_digest is not frozen
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
              ctx1.update(password)
 | 
					 | 
				
			||||||
            end
 | 
					 | 
				
			||||||
            final_digest = ctx1.digest.dup  # Ensure final_digest is not frozen
 | 
					 | 
				
			||||||
          end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          # Convert final digest to the hashed password format
 | 
					 | 
				
			||||||
          result = ''
 | 
					 | 
				
			||||||
          length = 4
 | 
					 | 
				
			||||||
          value = (final_digest[0].ord << 16) | (final_digest[6].ord << 8) | final_digest[12].ord
 | 
					 | 
				
			||||||
          length.times do
 | 
					 | 
				
			||||||
            result << ITOA64[value & 0x3f]
 | 
					 | 
				
			||||||
            value >>= 6
 | 
					 | 
				
			||||||
          end
 | 
					 | 
				
			||||||
          value = (final_digest[1].ord << 16) | (final_digest[7].ord << 8) | final_digest[13].ord
 | 
					 | 
				
			||||||
          length.times do
 | 
					 | 
				
			||||||
            result << ITOA64[value & 0x3f]
 | 
					 | 
				
			||||||
            value >>= 6
 | 
					 | 
				
			||||||
          end
 | 
					 | 
				
			||||||
          value = (final_digest[2].ord << 16) | (final_digest[8].ord << 8) | final_digest[14].ord
 | 
					 | 
				
			||||||
          length.times do
 | 
					 | 
				
			||||||
            result << ITOA64[value & 0x3f]
 | 
					 | 
				
			||||||
            value >>= 6
 | 
					 | 
				
			||||||
          end
 | 
					 | 
				
			||||||
          value = (final_digest[3].ord << 16) | (final_digest[9].ord << 8) | final_digest[15].ord
 | 
					 | 
				
			||||||
          length.times do
 | 
					 | 
				
			||||||
            result << ITOA64[value & 0x3f]
 | 
					 | 
				
			||||||
            value >>= 6
 | 
					 | 
				
			||||||
          end
 | 
					 | 
				
			||||||
          value = (final_digest[4].ord << 16) | (final_digest[10].ord << 8) | final_digest[5].ord
 | 
					 | 
				
			||||||
          length.times do
 | 
					 | 
				
			||||||
            result << ITOA64[value & 0x3f]
 | 
					 | 
				
			||||||
            value >>= 6
 | 
					 | 
				
			||||||
          end
 | 
					 | 
				
			||||||
          length = 2
 | 
					 | 
				
			||||||
          value = final_digest[11].ord
 | 
					 | 
				
			||||||
          length.times do
 | 
					 | 
				
			||||||
            result << ITOA64[value & 0x3f]
 | 
					 | 
				
			||||||
            value >>= 6
 | 
					 | 
				
			||||||
          end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          entered_password_md5 = "#{magic}#{salt}$#{result}"
 | 
					 | 
				
			||||||
          Rails.logger.debug "Generated MD5 hash: #{entered_password_md5}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          # Verify the entered password MD5 hash against the stored hash
 | 
					 | 
				
			||||||
          if entered_password_md5 == custom_password_md5
 | 
					 | 
				
			||||||
            Rails.logger.debug "MD5 hash matches for user: #{user.id}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # MD5 matches, update the user's password to the new one and remove the custom field
 | 
					 | 
				
			||||||
            user.password = password
 | 
					            user.password = password
 | 
				
			||||||
            user.custom_fields['custom_password_md5'] = nil
 | 
					
 | 
				
			||||||
 | 
					            # Set other attributes
 | 
				
			||||||
 | 
					            user.active = true
 | 
				
			||||||
 | 
					            user.approved = true
 | 
				
			||||||
 | 
					            # user.email_confirmed = true
 | 
				
			||||||
 | 
					            user.approved_at = Time.now
 | 
				
			||||||
 | 
					            user.approved_by_id = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            #
 | 
				
			||||||
 | 
					            # hashed_password = UserAuthenticator.new(nil).password_digest(password)
 | 
				
			||||||
 | 
					            # user.update_columns(
 | 
				
			||||||
 | 
					            #   hashed_password: hashed_password
 | 
				
			||||||
 | 
					            # )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # user.custom_fields['custom_password_md5'] = nil
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            # user.active = true
 | 
				
			||||||
 | 
					            # user.approved = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # # Initialize UserAuthenticator with user and session
 | 
				
			||||||
 | 
					            # authenticator = UserAuthenticator.new(user, session)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # # Generate a salted password hash for the new password
 | 
				
			||||||
 | 
					            # hashed_password = authenticator.password_digest(password)
 | 
				
			||||||
 | 
					            # Rails.logger.debug "NEW hashed_password  #{hashed_password}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # # Update the user object with all changes
 | 
				
			||||||
 | 
					            # user.assign_attributes(
 | 
				
			||||||
 | 
					            #   password_hash: hashed_password,
 | 
				
			||||||
 | 
					            #   # salt: authenticator.salt,
 | 
				
			||||||
 | 
					            #   # password_algorithm: authenticator.algorithm_name,
 | 
				
			||||||
 | 
					            #   active: true,
 | 
				
			||||||
 | 
					            #   approved: true,
 | 
				
			||||||
 | 
					            #   custom_fields: { 'custom_password_md5' => nil }
 | 
				
			||||||
 | 
					            # )
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            # Save the changes
 | 
				
			||||||
            user.save!
 | 
					            user.save!
 | 
				
			||||||
            Rails.logger.debug "Updated MD5 password for user: #{user.id}"
 | 
					
 | 
				
			||||||
 | 
					            # if user.save
 | 
				
			||||||
 | 
					            #   Rails.logger.debug "User changes saved:  #{user.username}"
 | 
				
			||||||
 | 
					            # else
 | 
				
			||||||
 | 
					            #   Rails.logger.debug "User changes FAILED:  #{user.errors.full_messages}"
 | 
				
			||||||
 | 
					            #   invalid_credentials
 | 
				
			||||||
 | 
					            #   return
 | 
				
			||||||
 | 
					            # end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Rails.logger.debug "Updated user: #{user.id}"
 | 
				
			||||||
          else
 | 
					          else
 | 
				
			||||||
            Rails.logger.debug "MD5 password incorrect for user: #{user.id}"
 | 
					            # MD5 doesn't match, so we have a failed login attempt.
 | 
				
			||||||
            return invalid_credentials
 | 
					            Rails.logger.debug "MD5 Password incorrect for user: #{user.id}"
 | 
				
			||||||
 | 
					            invalid_credentials
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
          end
 | 
					          end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        elsif !user.confirm_password?(password)
 | 
					        elsif !user.confirm_password?(password)
 | 
				
			||||||
 | 
					          # There is no MD5 password and the password was incorrect.
 | 
				
			||||||
          Rails.logger.debug "Password incorrect for user: #{user.id}"
 | 
					          Rails.logger.debug "Password incorrect for user: #{user.id}"
 | 
				
			||||||
          return invalid_credentials
 | 
					          invalid_credentials
 | 
				
			||||||
 | 
					          return
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Handle user approval requirements
 | 
					        # If the site requires user approval and the user is not approved yet
 | 
				
			||||||
        if login_not_approved_for?(user)
 | 
					        if login_not_approved_for?(user)
 | 
				
			||||||
          render json: login_not_approved
 | 
					          render json: login_not_approved
 | 
				
			||||||
          return
 | 
					          return
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Invalidate invite link if user signed on with username and password
 | 
					        # 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)
 | 
					        Invite.invalidate_for_email(user.email)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Handle password expiration
 | 
					        # User's password has expired so they need to reset it
 | 
				
			||||||
        if user.password_expired?(password)
 | 
					        if user.password_expired?(password)
 | 
				
			||||||
          render json: { error: "expired", reason: "expired" }
 | 
					          render json: { error: "expired", reason: "expired" }
 | 
				
			||||||
          return
 | 
					          return
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      else
 | 
					      else
 | 
				
			||||||
        Rails.logger.debug "User not found with login: #{params[:login]}"
 | 
					        invalid_credentials
 | 
				
			||||||
        return invalid_credentials
 | 
					        return
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if payload = login_error_check(user)
 | 
					      if payload = login_error_check(user)
 | 
				
			||||||
        return render json: payload
 | 
					        return render json: payload
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      # Perform second factor authentication
 | 
					 | 
				
			||||||
      second_factor_auth_result = authenticate_second_factor(user)
 | 
					      second_factor_auth_result = authenticate_second_factor(user)
 | 
				
			||||||
      unless second_factor_auth_result.ok
 | 
					      return render(json: @second_factor_failure_payload) unless second_factor_auth_result.ok
 | 
				
			||||||
        Rails.logger.debug "Second factor authentication failed for user: #{user.id}"
 | 
					 | 
				
			||||||
        return render(json: @second_factor_failure_payload)
 | 
					 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      # Handle active and email confirmed users
 | 
					 | 
				
			||||||
      if user.active && user.email_confirmed?
 | 
					      if user.active && user.email_confirmed?
 | 
				
			||||||
        login(user, second_factor_auth_result)
 | 
					        login(user, second_factor_auth_result)
 | 
				
			||||||
      else
 | 
					      else
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user