# frozen_string_literal: true require 'spec_helper' RSpec.describe Gitlab::Auth::OAuth::User do include LdapHelpers include TermsHelper let(:oauth_user) { described_class.new(auth_hash) } let(:oauth_user_2) { described_class.new(auth_hash_2) } let(:gl_user) { oauth_user.gl_user } let(:gl_user_2) { oauth_user_2.gl_user } let(:uid) { 'my-uid' } let(:uid_2) { 'my-uid-2' } let(:dn) { 'uid=user1,ou=people,dc=example' } let(:provider) { 'my-provider' } let(:auth_hash) { OmniAuth::AuthHash.new(uid: uid, provider: provider, info: info_hash) } let(:auth_hash_2) { OmniAuth::AuthHash.new(uid: uid_2, provider: provider, info: info_hash) } let(:info_hash) do { nickname: '-john+gitlab-ETC%.git@gmail.com', name: 'John', email: 'john@mail.com', address: { locality: 'locality', country: 'country' } } end let(:ldap_user) { Gitlab::Auth::Ldap::Person.new(Net::LDAP::Entry.new, 'ldapmain') } let(:ldap_user_2) { Gitlab::Auth::Ldap::Person.new(Net::LDAP::Entry.new, 'ldapmain') } describe '.find_by_uid_and_provider' do let(:dn) { 'CN=John Åström, CN=Users, DC=Example, DC=com' } it 'retrieves the correct user' do special_info = { name: 'John Åström', email: 'john@example.com', nickname: 'jastrom' } special_hash = OmniAuth::AuthHash.new(uid: dn, provider: 'ldapmain', info: special_info) special_chars_user = described_class.new(special_hash) user = special_chars_user.save expect(described_class.find_by_uid_and_provider(dn, 'ldapmain')).to eq user end end describe '#persisted?' do let!(:existing_user) { create(:omniauth_user, extern_uid: 'my-uid', provider: 'my-provider') } it "finds an existing user based on uid and provider (facebook)" do expect(oauth_user.persisted?).to be_truthy end it 'returns false if user is not found in database' do allow(auth_hash).to receive(:uid).and_return('non-existing') expect(oauth_user.persisted?).to be_falsey end end def stub_omniauth_config(messages) allow(Gitlab.config.omniauth).to receive_messages(messages) end describe '#save' do let(:provider) { 'twitter' } describe 'when account exists on server' do it 'does not mark the user as external' do create(:omniauth_user, extern_uid: 'my-uid', provider: provider) stub_omniauth_config(allow_single_sign_on: [provider], external_providers: [provider]) oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.external).to be_falsey end end describe 'signup' do context 'when signup is disabled' do before do stub_application_setting signup_enabled: false stub_omniauth_config(allow_single_sign_on: [provider]) end it 'creates the user' do oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_persisted end it 'does not repeat the default user password' do oauth_user.save # rubocop:disable Rails/SaveBang oauth_user_2.save # rubocop:disable Rails/SaveBang expect(gl_user.password).not_to eq(gl_user_2.password) end it 'has the password length within specified range' do oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user.password.length).to be_between(Devise.password_length.min, Devise.password_length.max) end end context 'when user confirmation email is enabled' do before do stub_application_setting send_user_confirmation_email: true end it 'creates and confirms the user anyway' do stub_omniauth_config(allow_single_sign_on: [provider]) oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_persisted expect(gl_user).to be_confirmed end end context 'when the current minimum password length is different from the default minimum password length' do before do stub_application_setting minimum_password_length: 21 end it 'creates the user' do stub_omniauth_config(allow_single_sign_on: [provider]) oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_persisted end end it 'marks user as having password_automatically_set' do stub_omniauth_config(allow_single_sign_on: [provider], external_providers: [provider]) oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_persisted expect(gl_user).to be_password_automatically_set end context 'terms of service' do context 'when terms are enforced' do before do enforce_terms end context 'when feature flag update_oauth_registration_flow is enabled' do before do stub_feature_flags(update_oauth_registration_flow: true) end it 'creates the user with accepted terms' do oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_persisted expect(gl_user.terms_accepted?).to be(true) end end context 'when feature flag update_oauth_registration_flow is disabled' do before do stub_feature_flags(update_oauth_registration_flow: false) end it 'creates the user without accepted terms' do oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_persisted expect(gl_user.terms_accepted?).to be(false) end end end context 'when terms are not enforced' do it 'creates the user without accepted terms' do oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_persisted expect(gl_user.terms_accepted?).to be(false) end end end shared_examples 'to verify compliance with allow_single_sign_on' do context 'provider is marked as external' do it 'marks user as external' do stub_omniauth_config(allow_single_sign_on: [provider], external_providers: [provider]) oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.external).to be_truthy end end context 'provider was external, now has been removed' do it 'does not mark external user as internal' do create(:omniauth_user, extern_uid: 'my-uid', provider: provider, external: true) stub_omniauth_config(allow_single_sign_on: [provider], external_providers: ['facebook']) oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.external).to be_truthy end end context 'provider is not external' do context 'when adding a new OAuth identity' do it 'does not promote an external user to internal' do user = create(:user, email: 'john@mail.com', external: true) user.identities.create!(provider: provider, extern_uid: uid) oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.external).to be_truthy end end end context 'with new allow_single_sign_on enabled syntax' do before do stub_omniauth_config(allow_single_sign_on: [provider]) end it "creates a user from Omniauth" do oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid identity = gl_user.identities.first expect(identity.extern_uid).to eql uid expect(identity.provider).to eql provider end end context "with old allow_single_sign_on enabled syntax" do before do stub_omniauth_config(allow_single_sign_on: true) end it "creates a user from Omniauth" do oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid identity = gl_user.identities.first expect(identity.extern_uid).to eql uid expect(identity.provider).to eql provider end end context 'with new allow_single_sign_on disabled syntax' do before do stub_omniauth_config(allow_single_sign_on: []) end it 'throws an error' do expect { oauth_user.save }.to raise_error StandardError # rubocop:disable Rails/SaveBang end end context 'with old allow_single_sign_on disabled (Default)' do before do stub_omniauth_config(allow_single_sign_on: false) end it 'throws an error' do expect { oauth_user.save }.to raise_error StandardError # rubocop:disable Rails/SaveBang end end end context "with auto_link_user disabled (default)" do before do stub_omniauth_config(auto_link_user: false) end include_examples "to verify compliance with allow_single_sign_on" end context "with auto_link_user enabled for a different provider" do before do stub_omniauth_config(auto_link_user: ['saml']) end context "and a current GitLab user with a matching email" do let!(:existing_user) { create(:user, email: 'john@mail.com', username: 'john') } it "adds the OmniAuth identity to the GitLab user account" do oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).not_to be_valid end end context "and no current GitLab user with a matching email" do include_examples "to verify compliance with allow_single_sign_on" end end context "with auto_link_user enabled for the correct provider" do before do stub_omniauth_config(auto_link_user: ['twitter']) end context "and a current GitLab user with a matching email" do let!(:existing_user) { create(:user, email: 'john@mail.com', username: 'john') } it "adds the OmniAuth identity to the GitLab user account" do oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.username).to eql 'john' expect(gl_user.email).to eql 'john@mail.com' expect(gl_user.identities.length).to be 1 identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to match_array( [ { provider: 'twitter', extern_uid: uid } ] ) end end context "and no current GitLab user with a matching email" do include_examples "to verify compliance with allow_single_sign_on" end end context "with auto_link_user enabled for all providers" do before do stub_omniauth_config(auto_link_user: true) end context "and a current GitLab user with a matching email" do let!(:existing_user) { create(:user, email: 'john@mail.com', username: 'john') } it "adds the OmniAuth identity to the GitLab user account" do oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.username).to eql 'john' expect(gl_user.email).to eql 'john@mail.com' expect(gl_user.identities.length).to be 1 identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to match_array( [ { provider: 'twitter', extern_uid: uid } ] ) end end context "and no current GitLab user with a matching email" do include_examples "to verify compliance with allow_single_sign_on" end end context "with auto_link_ldap_user disabled (default)" do before do stub_omniauth_config(auto_link_ldap_user: false) end include_examples "to verify compliance with allow_single_sign_on" end context "with auto_link_ldap_user enabled" do before do stub_omniauth_config(auto_link_ldap_user: true) end context "and no LDAP provider defined" do before do stub_ldap_config(providers: []) end include_examples "to verify compliance with allow_single_sign_on" end context "and at least one LDAP provider is defined" do before do stub_ldap_config(providers: %w(ldapmain)) end context "and a corresponding LDAP person" do before do allow(ldap_user).to receive(:uid) { uid } allow(ldap_user).to receive(:username) { uid } allow(ldap_user).to receive(:name) { 'John Doe' } allow(ldap_user).to receive(:email) { ['johndoe@example.com', 'john2@example.com'] } allow(ldap_user).to receive(:dn) { dn } allow(ldap_user_2).to receive(:uid) { uid_2 } allow(ldap_user_2).to receive(:username) { uid_2 } allow(ldap_user_2).to receive(:name) { 'Beck Potter' } allow(ldap_user_2).to receive(:email) { ['beckpotter@example.com', 'beck2@example.com'] } allow(ldap_user_2).to receive(:dn) { dn } end context "and no account for the LDAP user" do context 'when the LDAP user is found by UID' do before do allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(ldap_user) oauth_user.save # rubocop:disable Rails/SaveBang end it 'does not repeat the default user password' do allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(ldap_user_2) oauth_user_2.save # rubocop:disable Rails/SaveBang expect(gl_user.password).not_to eq(gl_user_2.password) end it "creates a user with dual LDAP and omniauth identities" do expect(gl_user).to be_valid expect(gl_user.username).to eql uid expect(gl_user.name).to eql 'John Doe' expect(gl_user.email).to eql 'johndoe@example.com' expect(gl_user.identities.length).to be 2 identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to match_array( [ { provider: 'ldapmain', extern_uid: dn }, { provider: 'twitter', extern_uid: uid } ] ) end it "has name and email set as synced" do expect(gl_user.user_synced_attributes_metadata.name_synced).to be_truthy expect(gl_user.user_synced_attributes_metadata.email_synced).to be_truthy end it "has name and email set as read-only" do expect(gl_user.read_only_attribute?(:name)).to be_truthy expect(gl_user.read_only_attribute?(:email)).to be_truthy end it "has synced attributes provider set to ldapmain" do expect(gl_user.user_synced_attributes_metadata.provider).to eql 'ldapmain' end end context 'when the LDAP user is found by email address' do before do allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(nil) allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_email).with(uid, any_args).and_return(nil) allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_email).with(info_hash[:email], any_args).and_return(ldap_user) oauth_user.save # rubocop:disable Rails/SaveBang end it 'creates the LDAP identity' do identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to include({ provider: 'ldapmain', extern_uid: dn }) end end end context "and LDAP user has an account already" do let!(:existing_user) { create(:omniauth_user, name: 'John Doe', email: 'john@example.com', extern_uid: dn, provider: 'ldapmain', username: 'john') } it "adds the omniauth identity to the LDAP account" do allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(ldap_user) oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.username).to eql 'john' expect(gl_user.name).to eql 'John Doe' expect(gl_user.email).to eql 'john@example.com' expect(gl_user.identities.length).to be 2 identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to match_array( [ { provider: 'ldapmain', extern_uid: dn }, { provider: 'twitter', extern_uid: uid } ] ) end end context 'when an LDAP person is not found by uid' do it 'tries to find an LDAP person by email and adds the omniauth identity to the user' do allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(nil) allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_email).and_return(ldap_user) oauth_user.save # rubocop:disable Rails/SaveBang identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to match_array(result_identities(dn, uid)) end context 'when also not found by email' do it 'tries to find an LDAP person by DN and adds the omniauth identity to the user' do allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(nil) allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_email).and_return(nil) allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_dn).and_return(ldap_user) oauth_user.save # rubocop:disable Rails/SaveBang identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to match_array(result_identities(dn, uid)) end end end def result_identities(dn, uid) [ { provider: 'ldapmain', extern_uid: dn }, { provider: 'twitter', extern_uid: uid } ] end context 'when there is an LDAP connection error' do before do raise_ldap_connection_error end it 'does not save the identity' do oauth_user.save # rubocop:disable Rails/SaveBang identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to match_array([{ provider: 'twitter', extern_uid: uid }]) end end end context 'and a corresponding LDAP person with a non-default username' do before do allow(ldap_user).to receive(:uid) { uid } allow(ldap_user).to receive(:username) { 'johndoe@example.com' } allow(ldap_user).to receive(:email) { %w(johndoe@example.com john2@example.com) } allow(ldap_user).to receive(:dn) { dn } end context 'and no account for the LDAP user' do it 'creates a user favoring the LDAP username and strips email domain' do allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(ldap_user) oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.username).to eql 'johndoe' end end end context "and no corresponding LDAP person" do before do allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(nil) end include_examples "to verify compliance with allow_single_sign_on" end end end context "with both auto_link_user and auto_link_ldap_user enabled" do before do stub_omniauth_config(auto_link_user: ['twitter'], auto_link_ldap_user: true) end context "and at least one LDAP provider is defined" do before do stub_ldap_config(providers: %w(ldapmain)) end context "and a corresponding LDAP person" do before do allow(ldap_user).to receive_messages( uid: uid, username: uid, name: 'John Doe', email: ['john@mail.com'], dn: dn ) end context "and no account for the LDAP user" do before do allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(ldap_user) oauth_user.save # rubocop:disable Rails/SaveBang end it "creates a user with dual LDAP and omniauth identities" do expect(gl_user).to be_valid expect(gl_user.username).to eql uid expect(gl_user.name).to eql 'John Doe' expect(gl_user.email).to eql 'john@mail.com' expect(gl_user.identities.length).to be 2 identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to match_array( [ { provider: 'ldapmain', extern_uid: dn }, { provider: 'twitter', extern_uid: uid } ] ) end it "has name and email set as synced" do expect(gl_user.user_synced_attributes_metadata.name_synced).to be_truthy expect(gl_user.user_synced_attributes_metadata.email_synced).to be_truthy end it "has name and email set as read-only" do expect(gl_user.read_only_attribute?(:name)).to be_truthy expect(gl_user.read_only_attribute?(:email)).to be_truthy end it "has synced attributes provider set to ldapmain" do expect(gl_user.user_synced_attributes_metadata.provider).to eql 'ldapmain' end end context "and LDAP user has an account already" do let!(:existing_user) { create(:omniauth_user, name: 'John Doe', email: 'john@mail.com', extern_uid: dn, provider: 'ldapmain', username: 'john') } it "adds the omniauth identity to the LDAP account" do allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(ldap_user) oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.username).to eql 'john' expect(gl_user.name).to eql 'John Doe' expect(gl_user.email).to eql 'john@mail.com' expect(gl_user.identities.length).to be 2 identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to match_array( [ { provider: 'ldapmain', extern_uid: dn }, { provider: 'twitter', extern_uid: uid } ] ) end end end end end end describe 'blocking' do let(:provider) { 'twitter' } before do stub_omniauth_config(allow_single_sign_on: ['twitter']) end shared_examples 'being blocked on creation' do context 'when blocking on creation' do it 'creates a blocked user' do oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).to be_blocked end context 'when a sign up user cap has been set up but has not been reached yet' do it 'still creates a blocked user' do stub_application_setting(new_user_signups_cap: 999) oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).to be_blocked end end end end shared_examples 'not being blocked on creation' do context 'when not blocking on creation' do it 'creates a non-blocked user' do oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).not_to be_blocked end end end context 'signup with SAML' do let(:provider) { 'saml' } let(:block_auto_created_users) { false } before do stub_omniauth_config({ allow_single_sign_on: ['saml'], auto_link_saml_user: true, block_auto_created_users: block_auto_created_users }) end it_behaves_like 'being blocked on creation' do let(:block_auto_created_users) { true } end it_behaves_like 'not being blocked on creation' do let(:block_auto_created_users) { false } end it 'does not repeat the default user password' do oauth_user.save # rubocop:disable Rails/SaveBang oauth_user_2.save # rubocop:disable Rails/SaveBang expect(gl_user.password).not_to eq(gl_user_2.password) end end context 'signup with omniauth only' do it_behaves_like 'being blocked on creation' do before do stub_omniauth_config(block_auto_created_users: true) end end it_behaves_like 'not being blocked on creation' do before do stub_omniauth_config(block_auto_created_users: false) end end end context 'signup with linked omniauth and LDAP account' do before do stub_omniauth_config(auto_link_ldap_user: true) allow(ldap_user).to receive(:uid) { uid } allow(ldap_user).to receive(:username) { uid } allow(ldap_user).to receive(:email) { ['johndoe@example.com', 'john2@example.com'] } allow(ldap_user).to receive(:dn) { dn } allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(ldap_user) end context "and no account for the LDAP user" do it_behaves_like 'being blocked on creation' do before do allow_next_instance_of(Gitlab::Auth::Ldap::Config) do |instance| allow(instance).to receive_messages(block_auto_created_users: true) end end end it_behaves_like 'not being blocked on creation' do before do allow_next_instance_of(Gitlab::Auth::Ldap::Config) do |instance| allow(instance).to receive_messages(block_auto_created_users: false) end end end end context 'and LDAP user has an account already' do let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: dn, provider: 'ldapmain', username: 'john') } it_behaves_like 'not being blocked on creation' do before do allow_next_instance_of(Gitlab::Auth::Ldap::Config) do |instance| allow(instance).to receive_messages(block_auto_created_users: false) end end end it_behaves_like 'not being blocked on creation' do before do allow_next_instance_of(Gitlab::Auth::Ldap::Config) do |instance| allow(instance).to receive_messages(block_auto_created_users: true) end end end end end context 'sign-in' do before do oauth_user.save # rubocop:disable Rails/SaveBang oauth_user.gl_user.activate end it_behaves_like 'not being blocked on creation' do before do stub_omniauth_config(block_auto_created_users: false) end end it_behaves_like 'not being blocked on creation' do before do stub_omniauth_config(block_auto_created_users: true) end end it_behaves_like 'not being blocked on creation' do before do allow_next_instance_of(Gitlab::Auth::Ldap::Config) do |instance| allow(instance).to receive_messages(block_auto_created_users: false) end end end it_behaves_like 'not being blocked on creation' do before do allow_next_instance_of(Gitlab::Auth::Ldap::Config) do |instance| allow(instance).to receive_messages(block_auto_created_users: true) end end end end end end describe 'ensure backwards compatibility with sync email from provider option' do let!(:existing_user) { create(:omniauth_user, extern_uid: 'my-uid', provider: 'my-provider') } before do stub_omniauth_config(sync_email_from_provider: 'my-provider') stub_omniauth_config(sync_profile_from_provider: ['my-provider']) end context "when provider sets an email" do it "updates the user email" do expect(gl_user.email).to eq(info_hash[:email]) end it "has email set as synced" do expect(gl_user.user_synced_attributes_metadata.email_synced).to be_truthy end it "has email set as read-only" do expect(gl_user.read_only_attribute?(:email)).to be_truthy end it "has synced attributes provider set to my-provider" do expect(gl_user.user_synced_attributes_metadata.provider).to eql 'my-provider' end end context "when provider doesn't set an email" do before do info_hash.delete(:email) end it "does not update the user email" do expect(gl_user.email).not_to eq(info_hash[:email]) end it "has email set as not synced" do expect(gl_user.user_synced_attributes_metadata.email_synced).to be_falsey end it "does not have email set as read-only" do expect(gl_user.read_only_attribute?(:email)).to be_falsey end end end describe 'generating username' do context 'when no collision with existing user' do it 'generates the username with no counter' do expect(gl_user.username).to eq('johngitlab-ETC') end end context 'when collision with existing user' do it 'generates the username with a counter' do oauth_user.save # rubocop:disable Rails/SaveBang oauth_user2 = described_class.new(OmniAuth::AuthHash.new(uid: 'my-uid2', provider: provider, info: { nickname: 'johngitlab-ETC@othermail.com', email: 'john@othermail.com' })) expect(oauth_user2.gl_user.username).to eq('johngitlab-ETC1') end end context 'when username is a reserved word' do let(:info_hash) do { nickname: 'admin@othermail.com', email: 'admin@othermail.com' } end it 'generates the username with a counter' do expect(gl_user.username).to eq('admin1') end end end describe 'updating email with sync profile' do let!(:existing_user) { create(:omniauth_user, extern_uid: 'my-uid', provider: 'my-provider') } before do stub_omniauth_config(sync_profile_from_provider: ['my-provider']) stub_omniauth_config(sync_profile_attributes: true) end context "when provider sets an email" do it "updates the user email" do expect(gl_user.email).to eq(info_hash[:email]) end it "has email set as synced" do expect(gl_user.user_synced_attributes_metadata.email_synced).to be(true) end it "has email set as read-only" do expect(gl_user.read_only_attribute?(:email)).to be_truthy end it "has synced attributes provider set to my-provider" do expect(gl_user.user_synced_attributes_metadata.provider).to eql 'my-provider' end end context "when provider doesn't set an email" do before do info_hash.delete(:email) end it "does not update the user email" do expect(gl_user.email).not_to eq(info_hash[:email]) end it "has email set as not synced" do expect(gl_user.user_synced_attributes_metadata.email_synced).to be_falsey end it "does not have email set as read-only" do expect(gl_user.read_only_attribute?(:email)).to be_falsey end end end describe 'updating name' do let!(:existing_user) { create(:omniauth_user, extern_uid: 'my-uid', provider: 'my-provider') } before do stub_omniauth_setting(sync_profile_from_provider: ['my-provider']) stub_omniauth_setting(sync_profile_attributes: true) end context "when provider sets a name" do it "updates the user name" do expect(gl_user.name).to eq(info_hash[:name]) end end context "when provider doesn't set a name" do before do info_hash.delete(:name) end it "does not update the user name" do expect(gl_user.name).not_to eq(info_hash[:name]) expect(gl_user.user_synced_attributes_metadata.name_synced).to be(false) end end end describe 'updating location' do let!(:existing_user) { create(:omniauth_user, extern_uid: 'my-uid', provider: 'my-provider') } before do stub_omniauth_setting(sync_profile_from_provider: ['my-provider']) stub_omniauth_setting(sync_profile_attributes: true) end context "when provider sets a location" do it "updates the user location" do expect(gl_user.location).to eq(info_hash[:address][:locality] + ', ' + info_hash[:address][:country]) expect(gl_user.user_synced_attributes_metadata.location_synced).to be(true) end end context "when provider doesn't set a location" do before do info_hash[:address].delete(:country) info_hash[:address].delete(:locality) end it "does not update the user location" do expect(gl_user.location).to be_nil expect(gl_user.user_synced_attributes_metadata.location_synced).to be(false) end end end describe 'updating user info' do let!(:existing_user) { create(:omniauth_user, extern_uid: 'my-uid', provider: 'my-provider') } context "update all info" do before do stub_omniauth_setting(sync_profile_from_provider: ['my-provider']) stub_omniauth_setting(sync_profile_attributes: true) end it "updates the user email" do expect(gl_user.email).to eq(info_hash[:email]) expect(gl_user.user_synced_attributes_metadata.email_synced).to be(true) end it "updates the user name" do expect(gl_user.name).to eq(info_hash[:name]) expect(gl_user.user_synced_attributes_metadata.name_synced).to be(true) end it "updates the user location" do expect(gl_user.location).to eq(info_hash[:address][:locality] + ', ' + info_hash[:address][:country]) expect(gl_user.user_synced_attributes_metadata.location_synced).to be(true) end it "sets my-provider as the attributes provider" do expect(gl_user.user_synced_attributes_metadata.provider).to eql('my-provider') end end context "update only requested info" do before do stub_omniauth_setting(sync_profile_from_provider: ['my-provider']) stub_omniauth_setting(sync_profile_attributes: %w(name location)) end it "updates the user name" do expect(gl_user.name).to eq(info_hash[:name]) expect(gl_user.user_synced_attributes_metadata.name_synced).to be(true) end it "updates the user location" do expect(gl_user.location).to eq(info_hash[:address][:locality] + ', ' + info_hash[:address][:country]) expect(gl_user.user_synced_attributes_metadata.location_synced).to be(true) end it "does not update the user email" do expect(gl_user.user_synced_attributes_metadata.email_synced).to be(false) end end context "update default_scope" do before do stub_omniauth_setting(sync_profile_from_provider: ['my-provider']) end it "updates the user email" do expect(gl_user.email).to eq(info_hash[:email]) expect(gl_user.user_synced_attributes_metadata.email_synced).to be(true) end end context "update no info when profile sync is nil" do it "does not have sync_attribute" do expect(gl_user.user_synced_attributes_metadata).to be(nil) end it "does not update the user email" do expect(gl_user.email).not_to eq(info_hash[:email]) end it "does not update the user name" do expect(gl_user.name).not_to eq(info_hash[:name]) end it "does not update the user location" do expect(gl_user.location).not_to eq(info_hash[:address][:country]) end it 'does not create associated user synced attributes metadata' do expect(gl_user.user_synced_attributes_metadata).to be_nil end end end context 'when gl_user is nil' do # We can't use `allow_next_instance_of` here because the stubbed method is called inside `initialize`. # When the class calls `gl_user` during `initialize`, the `nil` value is overwritten and we do not see expected results from the spec. # So we use `allow_any_instance_of` to preserve the `nil` value to test the behavior when `gl_user` is nil. # rubocop:disable RSpec/AnyInstanceOf before do allow_any_instance_of(described_class).to receive(:gl_user) { nil } allow_any_instance_of(described_class).to receive(:sync_profile_from_provider?) { true } # to make the code flow proceed until gl_user.build_user_synced_attributes_metadata is called end # rubocop:enable RSpec/AnyInstanceOf it 'does not raise NoMethodError' do expect { oauth_user }.not_to raise_error end end describe '._uid_and_provider' do let!(:existing_user) { create(:omniauth_user, extern_uid: 'my-uid', provider: 'my-provider') } it 'normalizes extern_uid' do allow(oauth_user.auth_hash).to receive(:uid).and_return('MY-UID') expect(oauth_user.find_user).to eql gl_user end end describe '#find_ldap_person' do context 'when LDAP connection fails' do before do raise_ldap_connection_error end it 'returns nil' do adapter = Gitlab::Auth::Ldap::Adapter.new('ldapmain') hash = OmniAuth::AuthHash.new(uid: 'whatever', provider: 'ldapmain') expect(oauth_user.send(:find_ldap_person, hash, adapter)).to be_nil end end end describe "#bypass_two_factor?" do it "when with allow_bypass_two_factor disabled (Default)" do stub_omniauth_config(allow_bypass_two_factor: false) expect(oauth_user.bypass_two_factor?).to be_falsey end it "when with allow_bypass_two_factor enabled" do stub_omniauth_config(allow_bypass_two_factor: true) expect(oauth_user.bypass_two_factor?).to be_truthy end it "when provider in allow_bypass_two_factor array" do stub_omniauth_config(allow_bypass_two_factor: [provider]) expect(oauth_user.bypass_two_factor?).to be_truthy end it "when provider not in allow_bypass_two_factor array" do stub_omniauth_config(allow_bypass_two_factor: ["foo"]) expect(oauth_user.bypass_two_factor?).to be_falsey end end describe '#protocol_name' do it 'is OAuth' do expect(oauth_user.protocol_name).to eq('OAuth') end end end