• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

sleede / fab-manager / #106

pending completion
#106

push

coveralls-ruby

sylvainbx
Merge branch 'dev' for release 6.0.0

704 of 704 new or added lines in 168 files covered. (100.0%)

7919 of 13474 relevant lines covered (58.77%)

15.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/app/controllers/users/omniauth_callbacks_controller.rb
1
# frozen_string_literal: true
2

3
# Handle authentication actions via OmniAuth (used by SSO providers)
4
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
×
5
  require 'sso_logger'
×
6
  logger = SsoLogger.new
×
7

8
  active_provider = Rails.configuration.auth_provider
×
9
  define_method active_provider.strategy_name do
×
10
    logger.info "[Users::OmniauthCallbacksController##{active_provider.strategy_name}] initiated"
×
11
    if request.env['omniauth.params'].blank?
×
12
      logger.debug 'the user has not provided any authentication token'
×
13
      @user = User.from_omniauth(request.env['omniauth.auth'])
×
14

15
      # Here we create the new user or update the existing one with values retrieved from the SSO.
16

17
      if @user.id.nil? # => new user (ie. not updating existing)
×
18
        logger.debug 'trying to create a new user'
×
19
        # If the username is mapped, we just check its uniqueness as it would break the postgresql
20
        # unique constraint otherwise. If the name is not unique, another unique is generated
21
        if active_provider.db.sso_fields.include?('user.username')
×
22
          logger.debug 'the username was already in use, generating a new one'
×
23
          @user.username = generate_unique_username(@user.username)
×
24
        end
×
25
        # If the email is mapped, we check its uniqueness. If the email is already in use, we mark it as duplicate with an
26
        # unique random string, because:
27
        # - if it is the same user, his email will be filled from the SSO when he merge his accounts
28
        # - if it is not the same user, this will prevent the raise of PG::UniqueViolation
29
        if active_provider.db.sso_fields.include?('user.email') && email_exists?(@user.email)
×
30
          logger.debug 'the email was already in use, marking it as duplicate'
×
31
          old_mail = @user.email
×
32
          @user.email = "<#{old_mail}>#{Devise.friendly_token}-duplicate"
×
33
          flash[:alert] = t('omniauth.email_already_linked_to_another_account_please_input_your_authentication_code', OLD_MAIL: old_mail)
×
34
        end
×
35
      else # => update of an existing user
×
36
        logger.debug "an existing user was found (id=#{@user.id})"
×
37
        if username_exists?(@user.username, @user.id)
×
38
          logger.debug 'the username was already in use, alerting user'
×
39
          flash[:alert] = t('omniauth.your_username_is_already_linked_to_another_account_unable_to_update_it', USERNAME: @user.username)
×
40
          @user.username = User.find(@user.id).username
×
41
        end
×
42

43
        if email_exists?(@user.email, @user.id)
×
44
          logger.debug 'the email was already in use, alerting user'
×
45
          flash[:alert] = t('omniauth.your_email_address_is_already_linked_to_another_account_unable_to_update_it', EMAIL: @user.email)
×
46
          @user.email = User.find(@user.id).email
×
47
        end
×
48
      end
×
49
      # For users imported from the SSO, we consider the SSO as a source of trust so the email is automatically validated
50
      @user.confirmed_at = Time.current if active_provider.db.sso_fields.include?('user.email') && !email_exists?(@user.email)
×
51

52
      # We BYPASS THE VALIDATION because, in case of a new user, we want to save him anyway,
53
      # we'll ask him later to complete his profile (on first login).
54
      # In case of an existing user, we trust the SSO validation as we want the SSO to have authority on users management and policy.
55
      logger.debug 'saving the user'
×
56
      logger.error "unable to save the user, an error occurred : #{@user.errors.full_messages.join(', ')}" unless @user.save(validate: false)
×
57

58
      logger.debug 'signing-in the user and redirecting'
×
59
      sign_in_and_redirect @user, event: :authentication # this will throw if @user is not activated
×
60
    else
×
61
      logger.debug 'the user has provided an authentication token'
×
62
      @user = User.find_by(auth_token: request.env['omniauth.params']['auth_token'])
×
63

64
      # Here the user already exists in the database and request to be linked with the SSO
65
      # so let's update its sso attributes and log him on
66
      logger.debug "found user id=#{@user.id}"
×
67

68
      begin
×
69
        logger.debug 'linking with the omniauth provider'
×
70
        @user.link_with_omniauth_provider(request.env['omniauth.auth'])
×
71
        logger.debug 'signing-in the user and redirecting'
×
72
        sign_in_and_redirect @user, event: :authentication
×
73
      rescue DuplicateIndexError
×
74
        logger.error 'user already linked'
×
75
        redirect_to root_url, alert: t('omniauth.this_account_is_already_linked_to_an_user_of_the_platform', NAME: active_provider.name)
×
76
      rescue StandardError => e
×
77
        logger.error "an expected error occurred: #{e}"
×
78
        raise e
×
79
      end
×
80
    end
×
81
  end
×
82

83
  private
×
84

85
  def username_exists?(username, exclude_id = nil)
×
86
    if exclude_id.nil?
×
87
      User.where('lower(username) = ?', username&.downcase).size.positive?
×
88
    else
×
89
      User.where('lower(username) = ?', username&.downcase).where.not(id: exclude_id).size.positive?
×
90
    end
×
91
  end
×
92

93
  def email_exists?(email, exclude_id = nil)
×
94
    if exclude_id.nil?
×
95
      User.where('lower(email) = ?', email&.downcase).size.positive?
×
96
    else
×
97
      User.where('lower(email) = ?', email&.downcase).where.not(id: exclude_id).size.positive?
×
98
    end
×
99
  end
×
100

101
  def generate_unique_username(username)
×
102
    generated = username
×
103
    i = 1000
×
104
    while username_exists?(generated)
×
105
      generated = username + rand(1..i).to_s
×
106
      i += 10
×
107
    end
×
108
    generated
×
109
  end
×
110
end
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc