Read about our upcoming Code of Conduct on this issue

user.rb 20.5 KB
Newer Older
1
2
3
4
# == Schema Information
#
# Table name: users
#
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#  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)
Stan Hu's avatar
Stan Hu committed
52
#  location                      :string(255)
Robert Speicher's avatar
Robert Speicher committed
53
54
55
56
57
#  encrypted_otp_secret          :string(255)
#  encrypted_otp_secret_iv       :string(255)
#  encrypted_otp_secret_salt     :string(255)
#  otp_required_for_login        :boolean
#  otp_backup_codes              :text
58
#  public_email                  :string(255)      default(""), not null
59
60
#

Steven Thonus's avatar
Steven Thonus committed
61
62
63
require 'carrierwave/orm/activerecord'
require 'file_size_validator'

gitlabhq's avatar
gitlabhq committed
64
class User < ActiveRecord::Base
65
  extend Gitlab::ConfigHelper
66
67

  include Gitlab::ConfigHelper
68
  include Gitlab::CurrentSettings
69
70
71
  include Referable
  include Sortable
  include TokenAuthenticatable
72

73
  default_value_for :admin, false
74
  default_value_for :can_create_group, gitlab_config.default_can_create_group
75
76
  default_value_for :can_create_team, false
  default_value_for :hide_no_ssh_key, false
77
  default_value_for :hide_no_password, false
78
  default_value_for :theme_id, gitlab_config.default_theme
79

80
81
  devise :two_factor_authenticatable,
         otp_secret_encryption_key: File.read(Rails.root.join('.secret')).chomp
Robert Speicher's avatar
Robert Speicher committed
82

83
  devise :two_factor_backupable, otp_number_of_backup_codes: 10
Robert Speicher's avatar
Robert Speicher committed
84
85
  serialize :otp_backup_codes, JSON

86
87
  devise :lockable, :async, :recoverable, :rememberable, :trackable,
    :validatable, :omniauthable, :confirmable, :registerable
gitlabhq's avatar
gitlabhq committed
88

89
  attr_accessor :force_random_password
gitlabhq's avatar
gitlabhq committed
90

91
92
93
  # Virtual attribute for authenticating by either username or email
  attr_accessor :login

94
95
96
97
  #
  # Relations
  #

98
  # Namespace for personal projects
99
  has_one :namespace, -> { where type: nil }, dependent: :destroy, foreign_key: :owner_id, class_name: "Namespace"
100
101
102

  # Profile
  has_many :keys, dependent: :destroy
103
  has_many :emails, dependent: :destroy
104
  has_many :identities, dependent: :destroy
105
106

  # Groups
107
108
109
110
  has_many :members, dependent: :destroy
  has_many :project_members, source: 'ProjectMember'
  has_many :group_members, source: 'GroupMember'
  has_many :groups, through: :group_members
111
112
  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
113

114
  # Projects
115
116
  has_many :groups_projects,          through: :groups, source: :projects
  has_many :personal_projects,        through: :namespace, source: :projects
117
  has_many :projects,                 through: :project_members
118
  has_many :created_projects,         foreign_key: :creator_id, class_name: 'Project'
Ciro Santilli's avatar
Ciro Santilli committed
119
120
  has_many :users_star_projects, dependent: :destroy
  has_many :starred_projects, through: :users_star_projects, source: :project
121

122
  has_many :snippets,                 dependent: :destroy, foreign_key: :author_id, class_name: "Snippet"
123
  has_many :project_members,          dependent: :destroy, class_name: 'ProjectMember'
124
125
126
127
  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"
128
  has_many :subscriptions,            dependent: :destroy
129
  has_many :recent_events, -> { order "id DESC" }, foreign_key: :author_id,   class_name: "Event"
130
131
  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"
Valery Sizov's avatar
Valery Sizov committed
132
  has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy
133

134

135
136
137
  #
  # Validations
  #
Cyril Jouve's avatar
Cyril Jouve committed
138
  validates :name, presence: true
139
  validates :email, presence: true, email: { strict_mode: true }, uniqueness: true
140
  validates :notification_email, presence: true, email: { strict_mode: true }
141
  validates :public_email, presence: true, email: { strict_mode: true }, allow_blank: true, uniqueness: true
142
  validates :bio, length: { maximum: 255 }, allow_blank: true
143
  validates :projects_limit, presence: true, numericality: { greater_than_or_equal_to: 0 }
144
145
146
147
  validates :username,
    presence: true,
    uniqueness: { case_sensitive: false },
    exclusion: { in: Gitlab::Blacklist.path },
Douwe Maan's avatar
Douwe Maan committed
148
149
    format: { with: Gitlab::Regex.namespace_regex,
              message: Gitlab::Regex.namespace_regex_message }
150

Andrey Kumanyaev's avatar
Andrey Kumanyaev committed
151
  validates :notification_level, inclusion: { in: Notification.notification_levels }, presence: true
152
  validate :namespace_uniq, if: ->(user) { user.username_changed? }
153
  validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
154
  validate :unique_email, if: ->(user) { user.email_changed? }
155
  validate :owns_notification_email, if: ->(user) { user.notification_email_changed? }
156
  validate :owns_public_email, if: ->(user) { user.public_email_changed? }
157
  validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
158

159
  before_validation :generate_password, on: :create
160
  before_validation :restricted_signup_domains, on: :create
161
  before_validation :sanitize_attrs
162
  before_validation :set_notification_email, if: ->(user) { user.email_changed? }
163
  before_validation :set_public_email, if: ->(user) { user.public_email_changed? }
164

165
  after_update :update_emails_with_primary_email, if: ->(user) { user.email_changed? }
Nihad Abbasov's avatar
Nihad Abbasov committed
166
  before_save :ensure_authentication_token
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
167
  after_save :ensure_namespace_correct
168
  after_initialize :set_projects_limit
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
169
170
171
  after_create :post_create_hook
  after_destroy :post_destroy_hook

172

Nihad Abbasov's avatar
Nihad Abbasov committed
173
  alias_attribute :private_token, :authentication_token
174

175
  delegate :path, to: :namespace, allow_nil: true, prefix: true
176

177
178
179
180
181
182
183
184
185
186
  state_machine :state, initial: :active do
    event :block do
      transition active: :blocked
    end

    event :activate do
      transition blocked: :active
    end
  end

187
  mount_uploader :avatar, AvatarUploader
Steven Thonus's avatar
Steven Thonus committed
188

Andrey Kumanyaev's avatar
Andrey Kumanyaev committed
189
  # Scopes
Andrew8xx8's avatar
Andrew8xx8 committed
190
  scope :admins, -> { where(admin:  true) }
191
192
  scope :blocked, -> { with_state(:blocked) }
  scope :active, -> { with_state(:active) }
skv's avatar
skv committed
193
  scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all }
194
  scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') }
Andrey Kumanyaev's avatar
Andrey Kumanyaev committed
195

196
197
198
  #
  # Class methods
  #
Andrey Kumanyaev's avatar
Andrey Kumanyaev committed
199
  class << self
200
    # Devise method overridden to allow sign in with email or username
201
202
203
204
205
206
207
208
    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
209

Valery Sizov's avatar
Valery Sizov committed
210
211
    def sort(method)
      case method.to_s
212
213
214
215
      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)
Valery Sizov's avatar
Valery Sizov committed
216
217
218
      end
    end

219
220
221
222
223
224
    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
225

226
    def filter(filter_name)
Andrey Kumanyaev's avatar
Andrey Kumanyaev committed
227
228
229
230
231
232
233
      case filter_name
      when "admins"; self.admins
      when "blocked"; self.blocked
      when "wop"; self.without_projects
      else
        self.active
      end
234
235
    end

236
    def search(query)
237
      where("lower(name) LIKE :query OR lower(email) LIKE :query OR lower(username) LIKE :query", query: "%#{query.downcase}%")
Andrey Kumanyaev's avatar
Andrey Kumanyaev committed
238
    end
239

240
241
242
243
244
    def by_login(login)
      where('lower(username) = :value OR lower(email) = :value',
            value: login.to_s.downcase).first
    end

245
    def by_username_or_id(name_or_id)
246
      where('users.username = ? OR users.id = ?', name_or_id.to_s, name_or_id.to_i).first
247
    end
248

249
250
    def build_user(attrs = {})
      User.new(attrs)
251
    end
252
253
254
255

    def reference_prefix
      '@'
    end
vsizov's avatar
vsizov committed
256
  end
randx's avatar
randx committed
257

258
259
260
  #
  # Instance methods
  #
261
262
263
264
265

  def to_param
    username
  end

266
267
268
269
  def to_reference(_from_project = nil)
    "#{self.class.reference_prefix}#{username}"
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
270
271
272
273
  def notification
    @notification ||= Notification.new(self)
  end

Andrey Kumanyaev's avatar
Andrey Kumanyaev committed
274
275
276
277
  def generate_password
    if self.force_random_password
      self.password = self.password_confirmation = Devise.friendly_token.first(8)
    end
randx's avatar
randx committed
278
  end
279

280
  def generate_reset_token
Marin Jankovski's avatar
Marin Jankovski committed
281
    @reset_token, enc = Devise.token_generator.generate(self.class, :reset_password_token)
282
283
284
285

    self.reset_password_token   = enc
    self.reset_password_sent_at = Time.now.utc

Marin Jankovski's avatar
Marin Jankovski committed
286
    @reset_token
287
288
  end

289
290
  def namespace_uniq
    namespace_name = self.username
291
292
    existing_namespace = Namespace.by_path(namespace_name)
    if existing_namespace && existing_namespace != self.namespace
lol768's avatar
lol768 committed
293
      self.errors.add :username, "already exists"
294
295
    end
  end
296

297
298
299
300
301
302
  def avatar_type
    unless self.avatar.image?
      self.errors.add :avatar, "only images allowed"
    end
  end

303
  def unique_email
304
305
306
    if !self.emails.exists?(email: self.email) && Email.exists?(email: self.email)
      self.errors.add(:email, 'has already been taken')
    end
307
308
  end

309
310
311
312
  def owns_notification_email
    self.errors.add(:notification_email, "is not an email you own") unless self.all_emails.include?(self.notification_email)
  end

313
314
315
316
317
318
319
320
321
  def owns_public_email
    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)
322

323
324
325
326
      self.update_secondary_emails!
    end
  end

327
328
  # Groups user has access to
  def authorized_groups
329
    @authorized_groups ||= begin
330
                             group_ids = (groups.pluck(:id) + authorized_projects.pluck(:namespace_id))
331
                             Group.where(id: group_ids)
332
                           end
333
334
335
336
337
  end


  # Projects user has access to
  def authorized_projects
338
    @authorized_projects ||= begin
339
                               project_ids = personal_projects.pluck(:id)
340
341
                               project_ids.push(*groups_projects.pluck(:id))
                               project_ids.push(*projects.pluck(:id).uniq)
342
                               Project.where(id: project_ids)
343
                             end
344
345
  end

346
347
348
349
350
351
  def owned_projects
    @owned_projects ||= begin
                          Project.where(namespace_id: owned_groups.pluck(:id).push(namespace.id)).joins(:namespace)
                        end
  end

352
353
  # Team membership in authorized projects
  def tm_in_authorized_projects
354
    ProjectMember.where(source_id: authorized_projects.map(&:id), user_id: self.id)
355
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
356
357
358
359
360
361
362
363
364

  def is_admin?
    admin
  end

  def require_ssh_key?
    keys.count == 0
  end

365
366
367
368
  def require_password?
    password_automatically_set? && !ldap_user?
  end

369
  def can_change_username?
370
    gitlab_config.username_changing_enabled
371
372
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
373
  def can_create_project?
374
    projects_limit_left > 0
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
375
376
377
  end

  def can_create_group?
378
    can?(:create_group, nil)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
379
380
381
  end

  def abilities
Ciro Santilli's avatar
Ciro Santilli committed
382
    Ability.abilities
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
383
384
  end

385
386
387
388
  def can_select_namespace?
    several_namespaces? || admin
  end

389
  def can?(action, subject)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
390
391
392
393
394
395
396
397
    abilities.allowed?(self, action, subject)
  end

  def first_name
    name.split.first unless name.blank?
  end

  def cared_merge_requests
398
    MergeRequest.cared(self)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
399
400
  end

401
  def projects_limit_left
402
    projects_limit - personal_projects.count
403
404
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
405
406
  def projects_limit_percent
    return 100 if projects_limit.zero?
407
    (personal_projects.count.to_f / projects_limit) * 100
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
408
409
  end

410
  def recent_push(project_id = nil)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
411
412
413
414
415
416
417
418
419
420
421
422
423
    # 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?
424
    owned_groups.any? || masters_groups.any?
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
425
426
427
428
429
  end

  def namespace_id
    namespace.try :id
  end
430

431
432
433
  def name_with_username
    "#{name} (#{username})"
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
434
435

  def tm_of(project)
436
    project.project_member_by_id(self.id)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
437
  end
438

439
  def already_forked?(project)
440
441
442
    !!fork_of(project)
  end

443
  def fork_of(project)
444
445
446
447
448
449
450
451
    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
452
453

  def ldap_user?
454
455
456
457
458
    identities.exists?(["provider LIKE ? AND extern_uid IS NOT NULL", "ldap%"])
  end

  def ldap_identity
    @ldap_identity ||= identities.find_by(["provider LIKE ?", "ldap%"])
459
  end
460

461
  def project_deploy_keys
462
    DeployKey.unscoped.in_projects(self.authorized_projects.pluck(:id)).distinct(:id)
463
464
  end

465
  def accessible_deploy_keys
466
467
468
469
470
    @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
471
  end
472
473

  def created_by
skv's avatar
skv committed
474
    User.find_by(id: created_by_id) if created_by_id
475
  end
476
477
478
479
480
481
482

  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
483

484
485
  def set_notification_email
    if self.notification_email.blank? || !self.all_emails.include?(self.notification_email)
486
      self.notification_email = self.email
487
488
489
    end
  end

490
491
  def set_public_email
    if self.public_email.blank? || !self.all_emails.include?(self.public_email)
492
      self.public_email = nil
493
494
495
    end
  end

496
497
498
499
500
501
  def update_secondary_emails!
    self.set_notification_email
    self.set_public_email
    self.save if self.notification_email_changed? || self.public_email_changed?
  end

502
503
504
505
506
507
508
  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

509
  def requires_ldap_check?
510
511
512
    if !Gitlab.config.ldap.enabled
      false
    elsif ldap_user?
513
514
515
516
517
518
      !last_credential_check_at || (last_credential_check_at + 1.hour) < Time.now
    else
      false
    end
  end

519
520
521
522
523
  def solo_owned_groups
    @solo_owned_groups ||= owned_groups.select do |group|
      group.owners == [self]
    end
  end
524
525

  def with_defaults
526
527
    User.defaults.each do |k, v|
      self.send("#{k}=", v)
528
    end
529
530

    self
531
  end
532

533
534
535
536
  def can_leave_project?(project)
    project.namespace != namespace &&
      project.project_member(self)
  end
537
538
539
540
541
542
543
544
545
546
547
548
549
550

  # 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
Jerome Dalbert's avatar
Jerome Dalbert committed
551
552

  def full_website_url
553
    return "http://#{website_url}" if website_url !~ /\Ahttps?:\/\//
Jerome Dalbert's avatar
Jerome Dalbert committed
554
555
556
557
558

    website_url
  end

  def short_website_url
559
    website_url.sub(/\Ahttps?:\/\//, '')
Jerome Dalbert's avatar
Jerome Dalbert committed
560
  end
GitLab's avatar
GitLab committed
561

562
  def all_ssh_keys
GitLab's avatar
GitLab committed
563
    keys.map(&:key)
564
  end
565
566

  def temp_oauth_email?
567
    email.start_with?('temp-email-for-oauth')
568
569
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
570
571
572
  def public_profile?
    authorized_projects.public_only.any?
  end
573
574
575

  def avatar_url(size = nil)
    if avatar.present?
576
      [gitlab_config.url, avatar.url].join
577
    else
578
      GravatarService.new.execute(email, size)
579
580
    end
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
581

582
583
584
585
  def all_emails
    [self.email, *self.emails.map(&:email)]
  end

Kirill Zaitsev's avatar
Kirill Zaitsev committed
586
587
588
589
590
591
592
593
  def hook_attrs
    {
      name: name,
      username: username,
      avatar_url: avatar_url
    }
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
594
595
596
597
598
599
600
601
602
603
604
  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")
605
    notification_service.new_user(self, @reset_token) if self.created_by_id
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
606
607
608
609
610
611
612
613
    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

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
614
  def notification_service
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
615
616
617
    NotificationService.new
  end

618
  def log_info(message)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
619
620
621
622
623
624
    Gitlab::AppLogger.info message
  end

  def system_hook_service
    SystemHooksService.new
  end
Ciro Santilli's avatar
Ciro Santilli committed
625
626
627
628
629
630

  def starred?(project)
    starred_projects.exists?(project)
  end

  def toggle_star(project)
631
632
    user_star_project = users_star_projects.
      where(project: project, user: self).take
Ciro Santilli's avatar
Ciro Santilli committed
633
634
635
636
637
638
    if user_star_project
      user_star_project.destroy
    else
      UsersStarProject.create!(project: project, user: self)
    end
  end
639
640
641
642
643
644
645
646
647
648

  def manageable_namespaces
    @manageable_namespaces ||=
      begin
        namespaces = []
        namespaces << namespace
        namespaces += owned_groups
        namespaces += masters_groups
      end
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
649
650
651
652

  def oauth_authorized_tokens
    Doorkeeper::AccessToken.where(resource_owner_id: self.id, revoked_at: nil)
  end
653
654

  def contributed_projects_ids
655
    Event.contributions.where(author_id: self).
656
      where("created_at > ?", Time.now - 1.year).
657
      reorder(project_id: :desc).
658
      select(:project_id).
659
      uniq.map(&:project_id)
660
  end
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683

  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
gitlabhq's avatar
gitlabhq committed
684
end