779 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			779 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			Ruby
		
	
	
	
| # == Schema Information
 | |
| #
 | |
| # Table name: users
 | |
| #
 | |
| #  id                         :integer          not null, primary key
 | |
| #  email                      :string(255)      default(""), not null
 | |
| #  encrypted_password         :string(255)      default(""), not null
 | |
| #  reset_password_token       :string(255)
 | |
| #  reset_password_sent_at     :datetime
 | |
| #  remember_created_at        :datetime
 | |
| #  sign_in_count              :integer          default(0)
 | |
| #  current_sign_in_at         :datetime
 | |
| #  last_sign_in_at            :datetime
 | |
| #  current_sign_in_ip         :string(255)
 | |
| #  last_sign_in_ip            :string(255)
 | |
| #  created_at                 :datetime
 | |
| #  updated_at                 :datetime
 | |
| #  name                       :string(255)
 | |
| #  admin                      :boolean          default(FALSE), not null
 | |
| #  projects_limit             :integer          default(10)
 | |
| #  skype                      :string(255)      default(""), not null
 | |
| #  linkedin                   :string(255)      default(""), not null
 | |
| #  twitter                    :string(255)      default(""), not null
 | |
| #  authentication_token       :string(255)
 | |
| #  theme_id                   :integer          default(1), not null
 | |
| #  bio                        :string(255)
 | |
| #  failed_attempts            :integer          default(0)
 | |
| #  locked_at                  :datetime
 | |
| #  username                   :string(255)
 | |
| #  can_create_group           :boolean          default(TRUE), not null
 | |
| #  can_create_team            :boolean          default(TRUE), not null
 | |
| #  state                      :string(255)
 | |
| #  color_scheme_id            :integer          default(1), not null
 | |
| #  notification_level         :integer          default(1), not null
 | |
| #  password_expires_at        :datetime
 | |
| #  created_by_id              :integer
 | |
| #  last_credential_check_at   :datetime
 | |
| #  avatar                     :string(255)
 | |
| #  confirmation_token         :string(255)
 | |
| #  confirmed_at               :datetime
 | |
| #  confirmation_sent_at       :datetime
 | |
| #  unconfirmed_email          :string(255)
 | |
| #  hide_no_ssh_key            :boolean          default(FALSE)
 | |
| #  website_url                :string(255)      default(""), not null
 | |
| #  notification_email         :string(255)
 | |
| #  hide_no_password           :boolean          default(FALSE)
 | |
| #  password_automatically_set :boolean          default(FALSE)
 | |
| #  location                   :string(255)
 | |
| #  encrypted_otp_secret       :string(255)
 | |
| #  encrypted_otp_secret_iv    :string(255)
 | |
| #  encrypted_otp_secret_salt  :string(255)
 | |
| #  otp_required_for_login     :boolean          default(FALSE), not null
 | |
| #  otp_backup_codes           :text
 | |
| #  public_email               :string(255)      default(""), not null
 | |
| #  dashboard                  :integer          default(0)
 | |
| #  project_view               :integer          default(0)
 | |
| #  layout                     :integer          default(0)
 | |
| #
 | |
| 
 | |
| require 'carrierwave/orm/activerecord'
 | |
| require 'file_size_validator'
 | |
| 
 | |
| class User < ActiveRecord::Base
 | |
|   extend Gitlab::ConfigHelper
 | |
| 
 | |
|   include Gitlab::ConfigHelper
 | |
|   include Gitlab::CurrentSettings
 | |
|   include Referable
 | |
|   include Sortable
 | |
|   include TokenAuthenticatable
 | |
|   include CaseSensitivity
 | |
| 
 | |
|   default_value_for :admin, false
 | |
|   default_value_for :can_create_group, gitlab_config.default_can_create_group
 | |
|   default_value_for :can_create_team, false
 | |
|   default_value_for :hide_no_ssh_key, false
 | |
|   default_value_for :hide_no_password, false
 | |
|   default_value_for :theme_id, gitlab_config.default_theme
 | |
| 
 | |
|   devise :two_factor_authenticatable,
 | |
|          otp_secret_encryption_key: File.read(Rails.root.join('.secret')).chomp
 | |
|   alias_attribute :two_factor_enabled, :otp_required_for_login
 | |
| 
 | |
|   devise :two_factor_backupable, otp_number_of_backup_codes: 10
 | |
|   serialize :otp_backup_codes, JSON
 | |
| 
 | |
|   devise :lockable, :async, :recoverable, :rememberable, :trackable,
 | |
|     :validatable, :omniauthable, :confirmable, :registerable
 | |
| 
 | |
|   attr_accessor :force_random_password
 | |
| 
 | |
|   # Virtual attribute for authenticating by either username or email
 | |
|   attr_accessor :login
 | |
| 
 | |
|   #
 | |
|   # Relations
 | |
|   #
 | |
| 
 | |
|   # Namespace for personal projects
 | |
|   has_one :namespace, -> { where type: nil }, dependent: :destroy, foreign_key: :owner_id, class_name: "Namespace"
 | |
| 
 | |
|   # Profile
 | |
|   has_many :keys, dependent: :destroy
 | |
|   has_many :emails, dependent: :destroy
 | |
|   has_many :identities, dependent: :destroy, autosave: true
 | |
| 
 | |
|   # Groups
 | |
|   has_many :members, dependent: :destroy
 | |
|   has_many :project_members, source: 'ProjectMember'
 | |
|   has_many :group_members, source: 'GroupMember'
 | |
|   has_many :groups, through: :group_members
 | |
|   has_many :owned_groups, -> { where members: { access_level: Gitlab::Access::OWNER } }, through: :group_members, source: :group
 | |
|   has_many :masters_groups, -> { where members: { access_level: Gitlab::Access::MASTER } }, through: :group_members, source: :group
 | |
| 
 | |
|   # Projects
 | |
|   has_many :groups_projects,          through: :groups, source: :projects
 | |
|   has_many :personal_projects,        through: :namespace, source: :projects
 | |
|   has_many :projects,                 through: :project_members
 | |
|   has_many :created_projects,         foreign_key: :creator_id, class_name: 'Project'
 | |
|   has_many :users_star_projects, dependent: :destroy
 | |
|   has_many :starred_projects, through: :users_star_projects, source: :project
 | |
| 
 | |
|   has_many :snippets,                 dependent: :destroy, foreign_key: :author_id, class_name: "Snippet"
 | |
|   has_many :project_members,          dependent: :destroy, class_name: 'ProjectMember'
 | |
|   has_many :issues,                   dependent: :destroy, foreign_key: :author_id
 | |
|   has_many :notes,                    dependent: :destroy, foreign_key: :author_id
 | |
|   has_many :merge_requests,           dependent: :destroy, foreign_key: :author_id
 | |
|   has_many :events,                   dependent: :destroy, foreign_key: :author_id,   class_name: "Event"
 | |
|   has_many :subscriptions,            dependent: :destroy
 | |
|   has_many :recent_events, -> { order "id DESC" }, foreign_key: :author_id,   class_name: "Event"
 | |
|   has_many :assigned_issues,          dependent: :destroy, foreign_key: :assignee_id, class_name: "Issue"
 | |
|   has_many :assigned_merge_requests,  dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest"
 | |
|   has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy
 | |
|   has_one  :abuse_report,             dependent: :destroy
 | |
|   has_many :ci_builds,                dependent: :nullify, class_name: 'Ci::Build'
 | |
| 
 | |
| 
 | |
|   #
 | |
|   # Validations
 | |
|   #
 | |
|   validates :name, presence: true
 | |
|   # Note that a 'uniqueness' and presence check is provided by devise :validatable for email. We do not need to
 | |
|   # duplicate that here as the validation framework will have duplicate errors in the event of a failure.
 | |
|   validates :email, presence: true, email: { strict_mode: true }
 | |
|   validates :notification_email, presence: true, email: { strict_mode: true }
 | |
|   validates :public_email, presence: true, email: { strict_mode: true }, allow_blank: true, uniqueness: true
 | |
|   validates :bio, length: { maximum: 255 }, allow_blank: true
 | |
|   validates :projects_limit, presence: true, numericality: { greater_than_or_equal_to: 0 }
 | |
|   validates :username,
 | |
|     presence: true,
 | |
|     uniqueness: { case_sensitive: false },
 | |
|     exclusion: { in: Gitlab::Blacklist.path },
 | |
|     format: { with: Gitlab::Regex.namespace_regex,
 | |
|               message: Gitlab::Regex.namespace_regex_message }
 | |
| 
 | |
|   validates :notification_level, inclusion: { in: Notification.notification_levels }, presence: true
 | |
|   validate :namespace_uniq, if: ->(user) { user.username_changed? }
 | |
|   validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
 | |
|   validate :unique_email, if: ->(user) { user.email_changed? }
 | |
|   validate :owns_notification_email, if: ->(user) { user.notification_email_changed? }
 | |
|   validate :owns_public_email, if: ->(user) { user.public_email_changed? }
 | |
|   validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
 | |
| 
 | |
|   before_validation :generate_password, on: :create
 | |
|   before_validation :restricted_signup_domains, on: :create
 | |
|   before_validation :sanitize_attrs
 | |
|   before_validation :set_notification_email, if: ->(user) { user.email_changed? }
 | |
|   before_validation :set_public_email, if: ->(user) { user.public_email_changed? }
 | |
| 
 | |
|   after_update :update_emails_with_primary_email, if: ->(user) { user.email_changed? }
 | |
|   before_save :ensure_authentication_token
 | |
|   after_save :ensure_namespace_correct
 | |
|   after_initialize :set_projects_limit
 | |
|   after_create :post_create_hook
 | |
|   after_destroy :post_destroy_hook
 | |
| 
 | |
|   # User's Layout preference
 | |
|   enum layout: [:fixed, :fluid]
 | |
| 
 | |
|   # User's Dashboard preference
 | |
|   # Note: When adding an option, it MUST go on the end of the array.
 | |
|   enum dashboard: [:projects, :stars, :project_activity, :starred_project_activity]
 | |
| 
 | |
|   # User's Project preference
 | |
|   # Note: When adding an option, it MUST go on the end of the array.
 | |
|   enum project_view: [:readme, :activity, :files]
 | |
| 
 | |
|   alias_attribute :private_token, :authentication_token
 | |
| 
 | |
|   delegate :path, to: :namespace, allow_nil: true, prefix: true
 | |
| 
 | |
|   state_machine :state, initial: :active do
 | |
|     event :block do
 | |
|       transition active: :blocked
 | |
|     end
 | |
| 
 | |
|     event :activate do
 | |
|       transition blocked: :active
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   mount_uploader :avatar, AvatarUploader
 | |
| 
 | |
|   # Scopes
 | |
|   scope :admins, -> { where(admin: true) }
 | |
|   scope :blocked, -> { with_state(:blocked) }
 | |
|   scope :active, -> { with_state(:active) }
 | |
|   scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all }
 | |
|   scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') }
 | |
|   scope :with_two_factor,    -> { where(two_factor_enabled: true) }
 | |
|   scope :without_two_factor, -> { where(two_factor_enabled: false) }
 | |
| 
 | |
|   #
 | |
|   # Class methods
 | |
|   #
 | |
|   class << self
 | |
|     # Devise method overridden to allow sign in with email or username
 | |
|     def find_for_database_authentication(warden_conditions)
 | |
|       conditions = warden_conditions.dup
 | |
|       if login = conditions.delete(:login)
 | |
|         where(conditions).where(["lower(username) = :value OR lower(email) = :value", { value: login.downcase }]).first
 | |
|       else
 | |
|         where(conditions).first
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     def sort(method)
 | |
|       case method.to_s
 | |
|       when 'recent_sign_in' then reorder(last_sign_in_at: :desc)
 | |
|       when 'oldest_sign_in' then reorder(last_sign_in_at: :asc)
 | |
|       else
 | |
|         order_by(method)
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     # Find a User by their primary email or any associated secondary email
 | |
|     def find_by_any_email(email)
 | |
|       user_table = arel_table
 | |
|       email_table = Email.arel_table
 | |
| 
 | |
|       # Use ARel to build a query:
 | |
|       query = user_table.
 | |
|         # SELECT "users".* FROM "users"
 | |
|         project(user_table[Arel.star]).
 | |
|         # LEFT OUTER JOIN "emails"
 | |
|         join(email_table, Arel::Nodes::OuterJoin).
 | |
|         # ON "users"."id" = "emails"."user_id"
 | |
|         on(user_table[:id].eq(email_table[:user_id])).
 | |
|         # WHERE ("user"."email" = '<email>' OR "emails"."email" = '<email>')
 | |
|         where(user_table[:email].eq(email).or(email_table[:email].eq(email)))
 | |
| 
 | |
|       find_by_sql(query.to_sql).first
 | |
|     end
 | |
| 
 | |
|     def filter(filter_name)
 | |
|       case filter_name
 | |
|       when 'admins'
 | |
|         self.admins
 | |
|       when 'blocked'
 | |
|         self.blocked
 | |
|       when 'two_factor_disabled'
 | |
|         self.without_two_factor
 | |
|       when 'two_factor_enabled'
 | |
|         self.with_two_factor
 | |
|       when 'wop'
 | |
|         self.without_projects
 | |
|       else
 | |
|         self.active
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     def search(query)
 | |
|       where("lower(name) LIKE :query OR lower(email) LIKE :query OR lower(username) LIKE :query", query: "%#{query.downcase}%")
 | |
|     end
 | |
| 
 | |
|     def by_login(login)
 | |
|       return nil unless login
 | |
| 
 | |
|       if login.include?('@'.freeze)
 | |
|         unscoped.iwhere(email: login).take
 | |
|       else
 | |
|         unscoped.iwhere(username: login).take
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     def find_by_username!(username)
 | |
|       find_by!('lower(username) = ?', username.downcase)
 | |
|     end
 | |
| 
 | |
|     def by_username_or_id(name_or_id)
 | |
|       where('users.username = ? OR users.id = ?', name_or_id.to_s, name_or_id.to_i).first
 | |
|     end
 | |
| 
 | |
|     def build_user(attrs = {})
 | |
|       User.new(attrs)
 | |
|     end
 | |
| 
 | |
|     def reference_prefix
 | |
|       '@'
 | |
|     end
 | |
| 
 | |
|     # Pattern used to extract `@user` user references from text
 | |
|     def reference_pattern
 | |
|       %r{
 | |
|         #{Regexp.escape(reference_prefix)}
 | |
|         (?<user>#{Gitlab::Regex::NAMESPACE_REGEX_STR})
 | |
|       }x
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   #
 | |
|   # Instance methods
 | |
|   #
 | |
| 
 | |
|   def to_param
 | |
|     username
 | |
|   end
 | |
| 
 | |
|   def to_reference(_from_project = nil)
 | |
|     "#{self.class.reference_prefix}#{username}"
 | |
|   end
 | |
| 
 | |
|   def notification
 | |
|     @notification ||= Notification.new(self)
 | |
|   end
 | |
| 
 | |
|   def generate_password
 | |
|     if self.force_random_password
 | |
|       self.password = self.password_confirmation = Devise.friendly_token.first(8)
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def generate_reset_token
 | |
|     @reset_token, enc = Devise.token_generator.generate(self.class, :reset_password_token)
 | |
| 
 | |
|     self.reset_password_token   = enc
 | |
|     self.reset_password_sent_at = Time.now.utc
 | |
| 
 | |
|     @reset_token
 | |
|   end
 | |
| 
 | |
|   def recently_sent_password_reset?
 | |
|     reset_password_sent_at.present? && reset_password_sent_at >= 1.minute.ago
 | |
|   end
 | |
| 
 | |
|   def disable_two_factor!
 | |
|     update_attributes(
 | |
|       two_factor_enabled:        false,
 | |
|       encrypted_otp_secret:      nil,
 | |
|       encrypted_otp_secret_iv:   nil,
 | |
|       encrypted_otp_secret_salt: nil,
 | |
|       otp_backup_codes:          nil
 | |
|     )
 | |
|   end
 | |
| 
 | |
|   def namespace_uniq
 | |
|     namespace_name = self.username
 | |
|     existing_namespace = Namespace.by_path(namespace_name)
 | |
|     if existing_namespace && existing_namespace != self.namespace
 | |
|       self.errors.add :username, "already exists"
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def avatar_type
 | |
|     unless self.avatar.image?
 | |
|       self.errors.add :avatar, "only images allowed"
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def unique_email
 | |
|     if !self.emails.exists?(email: self.email) && Email.exists?(email: self.email)
 | |
|       self.errors.add(:email, 'has already been taken')
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def owns_notification_email
 | |
|     self.errors.add(:notification_email, "is not an email you own") unless self.all_emails.include?(self.notification_email)
 | |
|   end
 | |
| 
 | |
|   def owns_public_email
 | |
|     return if self.public_email.blank?
 | |
| 
 | |
|     self.errors.add(:public_email, "is not an email you own") unless self.all_emails.include?(self.public_email)
 | |
|   end
 | |
| 
 | |
|   def update_emails_with_primary_email
 | |
|     primary_email_record = self.emails.find_by(email: self.email)
 | |
|     if primary_email_record
 | |
|       primary_email_record.destroy
 | |
|       self.emails.create(email: self.email_was)
 | |
| 
 | |
|       self.update_secondary_emails!
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   # Groups user has access to
 | |
|   def authorized_groups
 | |
|     @authorized_groups ||= begin
 | |
|                              group_ids = (groups.pluck(:id) + authorized_projects.pluck(:namespace_id))
 | |
|                              Group.where(id: group_ids)
 | |
|                            end
 | |
|   end
 | |
| 
 | |
| 
 | |
|   # Projects user has access to
 | |
|   def authorized_projects
 | |
|     @authorized_projects ||= begin
 | |
|                                project_ids = personal_projects.pluck(:id)
 | |
|                                project_ids.push(*groups_projects.pluck(:id))
 | |
|                                project_ids.push(*projects.pluck(:id).uniq)
 | |
|                                Project.where(id: project_ids)
 | |
|                              end
 | |
|   end
 | |
| 
 | |
|   def owned_projects
 | |
|     @owned_projects ||=
 | |
|       begin
 | |
|         namespace_ids = owned_groups.pluck(:id).push(namespace.id)
 | |
|         Project.in_namespace(namespace_ids).joins(:namespace)
 | |
|       end
 | |
|   end
 | |
| 
 | |
|   # Team membership in authorized projects
 | |
|   def tm_in_authorized_projects
 | |
|     ProjectMember.where(source_id: authorized_projects.map(&:id), user_id: self.id)
 | |
|   end
 | |
| 
 | |
|   def is_admin?
 | |
|     admin
 | |
|   end
 | |
| 
 | |
|   def require_ssh_key?
 | |
|     keys.count == 0
 | |
|   end
 | |
| 
 | |
|   def require_password?
 | |
|     password_automatically_set? && !ldap_user?
 | |
|   end
 | |
| 
 | |
|   def can_change_username?
 | |
|     gitlab_config.username_changing_enabled
 | |
|   end
 | |
| 
 | |
|   def can_create_project?
 | |
|     projects_limit_left > 0
 | |
|   end
 | |
| 
 | |
|   def can_create_group?
 | |
|     can?(:create_group, nil)
 | |
|   end
 | |
| 
 | |
|   def abilities
 | |
|     Ability.abilities
 | |
|   end
 | |
| 
 | |
|   def can_select_namespace?
 | |
|     several_namespaces? || admin
 | |
|   end
 | |
| 
 | |
|   def can?(action, subject)
 | |
|     abilities.allowed?(self, action, subject)
 | |
|   end
 | |
| 
 | |
|   def first_name
 | |
|     name.split.first unless name.blank?
 | |
|   end
 | |
| 
 | |
|   def cared_merge_requests
 | |
|     MergeRequest.cared(self)
 | |
|   end
 | |
| 
 | |
|   def projects_limit_left
 | |
|     projects_limit - personal_projects.count
 | |
|   end
 | |
| 
 | |
|   def projects_limit_percent
 | |
|     return 100 if projects_limit.zero?
 | |
|     (personal_projects.count.to_f / projects_limit) * 100
 | |
|   end
 | |
| 
 | |
|   def recent_push(project_id = nil)
 | |
|     # Get push events not earlier than 2 hours ago
 | |
|     events = recent_events.code_push.where("created_at > ?", Time.now - 2.hours)
 | |
|     events = events.where(project_id: project_id) if project_id
 | |
| 
 | |
|     # Use the latest event that has not been pushed or merged recently
 | |
|     events.recent.find do |event|
 | |
|       project = Project.find_by_id(event.project_id)
 | |
|       next unless project
 | |
|       repo = project.repository
 | |
| 
 | |
|       if repo.branch_names.include?(event.branch_name)
 | |
|         merge_requests = MergeRequest.where("created_at >= ?", event.created_at).
 | |
|             where(source_project_id: project.id,
 | |
|                   source_branch: event.branch_name)
 | |
|         merge_requests.empty?
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def projects_sorted_by_activity
 | |
|     authorized_projects.sorted_by_activity
 | |
|   end
 | |
| 
 | |
|   def several_namespaces?
 | |
|     owned_groups.any? || masters_groups.any?
 | |
|   end
 | |
| 
 | |
|   def namespace_id
 | |
|     namespace.try :id
 | |
|   end
 | |
| 
 | |
|   def name_with_username
 | |
|     "#{name} (#{username})"
 | |
|   end
 | |
| 
 | |
|   def tm_of(project)
 | |
|     project.project_member_by_id(self.id)
 | |
|   end
 | |
| 
 | |
|   def already_forked?(project)
 | |
|     !!fork_of(project)
 | |
|   end
 | |
| 
 | |
|   def fork_of(project)
 | |
|     links = ForkedProjectLink.where(forked_from_project_id: project, forked_to_project_id: personal_projects)
 | |
| 
 | |
|     if links.any?
 | |
|       links.first.forked_to_project
 | |
|     else
 | |
|       nil
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def ldap_user?
 | |
|     identities.exists?(["provider LIKE ? AND extern_uid IS NOT NULL", "ldap%"])
 | |
|   end
 | |
| 
 | |
|   def ldap_identity
 | |
|     @ldap_identity ||= identities.find_by(["provider LIKE ?", "ldap%"])
 | |
|   end
 | |
| 
 | |
|   def project_deploy_keys
 | |
|     DeployKey.unscoped.in_projects(self.authorized_projects.pluck(:id)).distinct(:id)
 | |
|   end
 | |
| 
 | |
|   def accessible_deploy_keys
 | |
|     @accessible_deploy_keys ||= begin
 | |
|       key_ids = project_deploy_keys.pluck(:id)
 | |
|       key_ids.push(*DeployKey.are_public.pluck(:id))
 | |
|       DeployKey.where(id: key_ids)
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def created_by
 | |
|     User.find_by(id: created_by_id) if created_by_id
 | |
|   end
 | |
| 
 | |
|   def sanitize_attrs
 | |
|     %w(name username skype linkedin twitter).each do |attr|
 | |
|       value = self.send(attr)
 | |
|       self.send("#{attr}=", Sanitize.clean(value)) if value.present?
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def set_notification_email
 | |
|     if self.notification_email.blank? || !self.all_emails.include?(self.notification_email)
 | |
|       self.notification_email = self.email
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def set_public_email
 | |
|     if self.public_email.blank? || !self.all_emails.include?(self.public_email)
 | |
|       self.public_email = ''
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def update_secondary_emails!
 | |
|     self.set_notification_email
 | |
|     self.set_public_email
 | |
|     self.save if self.notification_email_changed? || self.public_email_changed?
 | |
|   end
 | |
| 
 | |
|   def set_projects_limit
 | |
|     connection_default_value_defined = new_record? && !projects_limit_changed?
 | |
|     return unless self.projects_limit.nil? || connection_default_value_defined
 | |
| 
 | |
|     self.projects_limit = current_application_settings.default_projects_limit
 | |
|   end
 | |
| 
 | |
|   def requires_ldap_check?
 | |
|     if !Gitlab.config.ldap.enabled
 | |
|       false
 | |
|     elsif ldap_user?
 | |
|       !last_credential_check_at || (last_credential_check_at + 1.hour) < Time.now
 | |
|     else
 | |
|       false
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def solo_owned_groups
 | |
|     @solo_owned_groups ||= owned_groups.select do |group|
 | |
|       group.owners == [self]
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def with_defaults
 | |
|     User.defaults.each do |k, v|
 | |
|       self.send("#{k}=", v)
 | |
|     end
 | |
| 
 | |
|     self
 | |
|   end
 | |
| 
 | |
|   def can_leave_project?(project)
 | |
|     project.namespace != namespace &&
 | |
|       project.project_member(self)
 | |
|   end
 | |
| 
 | |
|   # Reset project events cache related to this user
 | |
|   #
 | |
|   # Since we do cache @event we need to reset cache in special cases:
 | |
|   # * when the user changes their avatar
 | |
|   # Events cache stored like  events/23-20130109142513.
 | |
|   # The cache key includes updated_at timestamp.
 | |
|   # Thus it will automatically generate a new fragment
 | |
|   # when the event is updated because the key changes.
 | |
|   def reset_events_cache
 | |
|     Event.where(author_id: self.id).
 | |
|       order('id DESC').limit(1000).
 | |
|       update_all(updated_at: Time.now)
 | |
|   end
 | |
| 
 | |
|   def full_website_url
 | |
|     return "http://#{website_url}" if website_url !~ /\Ahttps?:\/\//
 | |
| 
 | |
|     website_url
 | |
|   end
 | |
| 
 | |
|   def short_website_url
 | |
|     website_url.sub(/\Ahttps?:\/\//, '')
 | |
|   end
 | |
| 
 | |
|   def all_ssh_keys
 | |
|     keys.map(&:publishable_key)
 | |
|   end
 | |
| 
 | |
|   def temp_oauth_email?
 | |
|     email.start_with?('temp-email-for-oauth')
 | |
|   end
 | |
| 
 | |
|   def avatar_url(size = nil)
 | |
|     if avatar.present?
 | |
|       [gitlab_config.url, avatar.url].join
 | |
|     else
 | |
|       GravatarService.new.execute(email, size)
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def all_emails
 | |
|     [self.email, *self.emails.map(&:email)]
 | |
|   end
 | |
| 
 | |
|   def hook_attrs
 | |
|     {
 | |
|       name: name,
 | |
|       username: username,
 | |
|       avatar_url: avatar_url
 | |
|     }
 | |
|   end
 | |
| 
 | |
|   def ensure_namespace_correct
 | |
|     # Ensure user has namespace
 | |
|     self.create_namespace!(path: self.username, name: self.username) unless self.namespace
 | |
| 
 | |
|     if self.username_changed?
 | |
|       self.namespace.update_attributes(path: self.username, name: self.username)
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def post_create_hook
 | |
|     log_info("User \"#{self.name}\" (#{self.email}) was created")
 | |
|     notification_service.new_user(self, @reset_token) if self.created_by_id
 | |
|     system_hook_service.execute_hooks_for(self, :create)
 | |
|   end
 | |
| 
 | |
|   def post_destroy_hook
 | |
|     log_info("User \"#{self.name}\" (#{self.email})  was removed")
 | |
|     system_hook_service.execute_hooks_for(self, :destroy)
 | |
|   end
 | |
| 
 | |
|   def notification_service
 | |
|     NotificationService.new
 | |
|   end
 | |
| 
 | |
|   def log_info(message)
 | |
|     Gitlab::AppLogger.info message
 | |
|   end
 | |
| 
 | |
|   def system_hook_service
 | |
|     SystemHooksService.new
 | |
|   end
 | |
| 
 | |
|   def starred?(project)
 | |
|     starred_projects.exists?(project)
 | |
|   end
 | |
| 
 | |
|   def toggle_star(project)
 | |
|     UsersStarProject.transaction do
 | |
|       user_star_project = users_star_projects.
 | |
|           where(project: project, user: self).lock(true).first
 | |
| 
 | |
|       if user_star_project
 | |
|         user_star_project.destroy
 | |
|       else
 | |
|         UsersStarProject.create!(project: project, user: self)
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def manageable_namespaces
 | |
|     @manageable_namespaces ||= [namespace] + owned_groups + masters_groups
 | |
|   end
 | |
| 
 | |
|   def namespaces
 | |
|     namespace_ids = groups.pluck(:id)
 | |
|     namespace_ids.push(namespace.id)
 | |
|     Namespace.where(id: namespace_ids)
 | |
|   end
 | |
| 
 | |
|   def oauth_authorized_tokens
 | |
|     Doorkeeper::AccessToken.where(resource_owner_id: self.id, revoked_at: nil)
 | |
|   end
 | |
| 
 | |
|   def contributed_projects_ids
 | |
|     Event.contributions.where(author_id: self).
 | |
|       where("created_at > ?", Time.now - 1.year).
 | |
|       reorder(project_id: :desc).
 | |
|       select(:project_id).
 | |
|       uniq.map(&:project_id)
 | |
|   end
 | |
| 
 | |
|   def restricted_signup_domains
 | |
|     email_domains = current_application_settings.restricted_signup_domains
 | |
| 
 | |
|     unless email_domains.blank?
 | |
|       match_found = email_domains.any? do |domain|
 | |
|         escaped = Regexp.escape(domain).gsub('\*','.*?')
 | |
|         regexp = Regexp.new "^#{escaped}$", Regexp::IGNORECASE
 | |
|         email_domain = Mail::Address.new(self.email).domain
 | |
|         email_domain =~ regexp
 | |
|       end
 | |
| 
 | |
|       unless match_found
 | |
|         self.errors.add :email,
 | |
|                         'is not whitelisted. ' +
 | |
|                         'Email domains valid for registration are: ' +
 | |
|                         email_domains.join(', ')
 | |
|         return false
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     true
 | |
|   end
 | |
| 
 | |
|   def can_be_removed?
 | |
|     !solo_owned_groups.present?
 | |
|   end
 | |
| 
 | |
|   def ci_authorized_projects
 | |
|     @ci_authorized_projects ||= Ci::Project.where(gitlab_id: authorized_projects)
 | |
|   end
 | |
| 
 | |
|   def ci_authorized_runners
 | |
|     Ci::Runner.specific.includes(:runner_projects).
 | |
|       where(ci_runner_projects: { project_id: ci_authorized_projects } )
 | |
|   end
 | |
| end
 |