summaryrefslogtreecommitdiff
path: root/app/models/user.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/user.rb')
-rw-r--r--app/models/user.rb614
1 files changed, 0 insertions, 614 deletions
diff --git a/app/models/user.rb b/app/models/user.rb
deleted file mode 100644
index d6b93afe739..00000000000
--- a/app/models/user.rb
+++ /dev/null
@@ -1,614 +0,0 @@
-# == 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
-# github_access_token :string(255)
-# gitlab_access_token :string(255)
-# notification_email :string(255)
-# hide_no_password :boolean default(FALSE)
-# password_automatically_set :boolean default(FALSE)
-# bitbucket_access_token :string(255)
-# bitbucket_access_token_secret :string(255)
-# public_email :string(255) default(""), not null
-#
-
-require 'carrierwave/orm/activerecord'
-require 'file_size_validator'
-
-class User < ActiveRecord::Base
- include Sortable
- include Gitlab::ConfigHelper
- include TokenAuthenticatable
- extend Gitlab::ConfigHelper
- include Gitlab::CurrentSettings
-
- 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 :database_authenticatable, :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
-
- # 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
-
-
- #
- # Validations
- #
- validates :name, presence: true
- validates :email, presence: true, email: { strict_mode: true }, uniqueness: 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_changed? }
- validate :unique_email, if: ->(user) { user.email_changed? }
- validate :owns_notification_email, if: ->(user) { user.notification_email_changed? }
- validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
-
- before_validation :generate_password, 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? }
-
- 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
-
-
- 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)') }
-
- #
- # 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
-
- def find_for_commit(email, name)
- # Prefer email match over name match
- User.where(email: email).first ||
- User.joins(:emails).where(emails: { email: email }).first ||
- User.where(name: name).first
- end
-
- def filter(filter_name)
- case filter_name
- when "admins"; self.admins
- when "blocked"; self.blocked
- 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)
- where('lower(username) = :value OR lower(email) = :value',
- value: login.to_s.downcase).first
- 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
- end
-
- #
- # Instance methods
- #
-
- def to_param
- 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 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
- self.errors.add(:email, 'has already been taken') if Email.exists?(email: self.email)
- 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
-
- # 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
- Project.where(namespace_id: owned_groups.pluck(:id).push(namespace.id)).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
-
- # Take only latest one
- events = events.recent.limit(1).first
- 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.in_projects(self.authorized_projects.pluck(: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 bio).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 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(&:key)
- end
-
- def temp_oauth_email?
- email.start_with?('temp-email-for-oauth')
- end
-
- def public_profile?
- authorized_projects.public_only.any?
- 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)
- user_star_project = users_star_projects.
- where(project: project, user: self).take
- if user_star_project
- user_star_project.destroy
- else
- UsersStarProject.create!(project: project, user: self)
- end
- end
-
- def manageable_namespaces
- @manageable_namespaces ||=
- begin
- namespaces = []
- namespaces << namespace
- namespaces += owned_groups
- namespaces += masters_groups
- end
- 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
-end