<commit_before>require 'encoding'
require 'digest' # required for {set,reset}_api_token
require 'base64' # required for {set,reset}_api_token
# required for repository actions
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'repo', 'repository')

# We always assume the following fields exists:
# => :user_name, :last_name, :first_name
# If there are added columns, add the default values to default_values
class User < ActiveRecord::Base
  before_validation :strip_name

  # Group relationships
  has_many :memberships, dependent: :delete_all
  has_many :grade_entry_students
  has_many :groupings, through: :memberships
  has_many :notes, as: :noteable, dependent: :destroy
  has_many :accepted_memberships,
           -> { where membership_status: [StudentMembership::STATUSES[:accepted], StudentMembership::STATUSES[:inviter]] },
           class_name: 'Membership'
  has_many :annotations, as: :creator

  validates_presence_of     :user_name, :last_name, :first_name
  validates_uniqueness_of   :user_name

  validates_format_of       :type,          with: /Student|Admin|Ta/
  # role constants
  STUDENT = 'Student'
  ADMIN = 'Admin'
  TA = 'Ta'

  # Authentication constants to be used as return values
  # see self.authenticated? and main_controller for details
  AUTHENTICATE_SUCCESS =      0   # valid username/password combination
  AUTHENTICATE_NO_SUCH_USER = 1   # user does not exist
  AUTHENTICATE_BAD_PASSWORD = 2   # wrong password
  AUTHENTICATE_ERROR =        3   # generic/unknown error
  AUTHENTICATE_BAD_CHAR =     4   # invalid character in username/password
  AUTHENTICATE_BAD_PLATFORM = 5   # external authentication works for *NIX platforms only
  AUTHENTICATE_CUSTOM_MESSAGE = 6 # custom validate code for custom message

  # Verifies if user is allowed to enter MarkUs
  # Returns user object representing the user with the given login.
  def self.authorize(login)
    # fetch login in database to see if it is registered.
    where(user_name: login).first
  end

  # Authenticates login against its password
  # through a script specified by config VALIDATE_FILE
  def self.authenticate(login, password)
    # Do not allow the following characters in usernames/passwords
    # Right now, this is \n and \0 only, since username and password
    # are delimited by \n and C programs use \0 to terminate strings
    not_allowed_regexp = Regexp.new(/[\n\0]+/)
    if not_allowed_regexp.match(login) || not_allowed_regexp.match(password)
      m_logger = MarkusLogger.instance
      m_logger.log("User '#{login}' failed to log in. Username/password contained " +
                       'illegal characters', MarkusLogger::ERROR)
      AUTHENTICATE_BAD_CHAR
    else
      # Open a pipe and write to stdin of the program specified by config VALIDATE_FILE.
      # We could read something from the programs stdout, but there is no need
      # for that at the moment (you would do it by e.g. pipe.readlines)

      # External validation is supported on *NIX only
      if RUBY_PLATFORM =~ /(:?mswin|mingw)/ # should match for Windows only
        return AUTHENTICATE_BAD_PLATFORM
      end

      # In general, the external password validation program will return the
      # following codes (other than 0):
      #  1 means no such user
      #  2 means bad password
      #  3 is used for other error exits
      pipe = IO.popen(MarkusConfigurator.markus_config_validate_file, 'w+')
      pipe.puts("#{login}\n#{password}") # write to stdin of markus_config_validate
      pipe.close
      m_logger = MarkusLogger.instance
      if (defined? VALIDATE_CUSTOM_EXIT_STATUS) && $?.exitstatus == VALIDATE_CUSTOM_EXIT_STATUS
        m_logger.log("Login failed. Reason: Custom exit status.", MarkusLogger::ERROR)
        return AUTHENTICATE_CUSTOM_MESSAGE
      end
      case $?.exitstatus
        when 0
          m_logger.log("User '#{login}' logged in.", MarkusLogger::INFO)
          return AUTHENTICATE_SUCCESS
        when 1
          m_logger.log("Login failed. Reason: No such user '#{login}'.", MarkusLogger::ERROR)
          return AUTHENTICATE_NO_SUCH_USER
        when 2
          m_logger.log("Wrong username/password: User '#{login}'.", MarkusLogger::ERROR)
          return AUTHENTICATE_BAD_PASSWORD
        else
          m_logger.log("User '#{login}' failed to log in.", MarkusLogger::ERROR)
          return AUTHENTICATE_ERROR
      end
    end
  end


  #TODO: make these proper associations. They work fine for now but
  # they'll be slow in production
  def active_groupings
    groupings.where('memberships.membership_status != ?',<commit_msg>Fix login when validate file path has spaces<commit_after>require 'encoding'
require 'digest' # required for {set,reset}_api_token
require 'base64' # required for {set,reset}_api_token
# required for repository actions
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'repo', 'repository')

# We always assume the following fields exists:
# => :user_name, :last_name, :first_name
# If there are added columns, add the default values to default_values
class User < ActiveRecord::Base
  before_validation :strip_name

  # Group relationships
  has_many :memberships, dependent: :delete_all
  has_many :grade_entry_students
  has_many :groupings, through: :memberships
  has_many :notes, as: :noteable, dependent: :destroy
  has_many :accepted_memberships,
           -> { where membership_status: [StudentMembership::STATUSES[:accepted], StudentMembership::STATUSES[:inviter]] },
           class_name: 'Membership'
  has_many :annotations, as: :creator

  validates_presence_of     :user_name, :last_name, :first_name
  validates_uniqueness_of   :user_name

  validates_format_of       :type,          with: /Student|Admin|Ta/
  # role constants
  STUDENT = 'Student'
  ADMIN = 'Admin'
  TA = 'Ta'

  # Authentication constants to be used as return values
  # see self.authenticated? and main_controller for details
  AUTHENTICATE_SUCCESS =      0   # valid username/password combination
  AUTHENTICATE_NO_SUCH_USER = 1   # user does not exist
  AUTHENTICATE_BAD_PASSWORD = 2   # wrong password
  AUTHENTICATE_ERROR =        3   # generic/unknown error
  AUTHENTICATE_BAD_CHAR =     4   # invalid character in username/password
  AUTHENTICATE_BAD_PLATFORM = 5   # external authentication works for *NIX platforms only
  AUTHENTICATE_CUSTOM_MESSAGE = 6 # custom validate code for custom message

  # Verifies if user is allowed to enter MarkUs
  # Returns user object representing the user with the given login.
  def self.authorize(login)
    # fetch login in database to see if it is registered.
    where(user_name: login).first
  end

  # Authenticates login against its password
  # through a script specified by config VALIDATE_FILE
  def self.authenticate(login, password)
    # Do not allow the following characters in usernames/passwords
    # Right now, this is \n and \0 only, since username and password
    # are delimited by \n and C programs use \0 to terminate strings
    not_allowed_regexp = Regexp.new(/[\n\0]+/)
    if not_allowed_regexp.match(login) || not_allowed_regexp.match(password)
      m_logger = MarkusLogger.instance
      m_logger.log("User '#{login}' failed to log in. Username/password contained " +
                       'illegal characters', MarkusLogger::ERROR)
      AUTHENTICATE_BAD_CHAR
    else
      # Open a pipe and write to stdin of the program specified by config VALIDATE_FILE.
      # We could read something from the programs stdout, but there is no need
      # for that at the moment (you would do it by e.g. pipe.readlines)

      # External validation is supported on *NIX only
      if RUBY_PLATFORM =~ /(:?mswin|mingw)/ # should match for Windows only
        return AUTHENTICATE_BAD_PLATFORM
      end

      # In general, the external password validation program will return the
      # following codes (other than 0):
      #  1 means no such user
      #  2 means bad password
      #  3 is used for other error exits
      pipe = IO.popen("'#{MarkusConfigurator.markus_config_validate_file}'", 'w+') # quotes to avoid choking on spaces
      pipe.puts("#{login}\n#{password}") # write to stdin of markus_config_validate
      pipe.close
      m_logger = MarkusLogger.instance
      if (defined? VALIDATE_CUSTOM_EXIT_STATUS) && $?.exitstatus == VALIDATE_CUSTOM_EXIT_STATUS
        m_logger.log("Login failed. Reason: Custom exit status.", MarkusLogger::ERROR)
        return AUTHENTICATE_CUSTOM_MESSAGE
      end
      case $?.exitstatus
        when 0
          m_logger.log("User '#{login}' logged in.", MarkusLogger::INFO)
          return AUTHENTICATE_SUCCESS
        when 1
          m_logger.log("Login failed. Reason: No such user '#{login}'.", MarkusLogger::ERROR)
          return AUTHENTICATE_NO_SUCH_USER
        when 2
          m_logger.log("Wrong username/password: User '#{login}'.", MarkusLogger::ERROR)
          return AUTHENTICATE_BAD_PASSWORD
        else
          m_logger.log("User '#{login}' failed to log in.", MarkusLogger::ERROR)
          return AUTHENTICATE_ERROR
      end
    end
  end


  #TODO: make these proper associations. They work fine for now but
  # they'll be slow in production
  def active_groupings
    groupings.where('memberships.membership_status != ?',
{'fraction_non_alphanumeric': 0.0807078118256366, 'fraction_numerical': 0.004100129477772983, 'mean_word_length': 3.4033254156769597, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7275316', 'n_tokens_mistral': 2759, 'n_tokens_neox': 2683, 'n_words': 873}