user_spec.rb 13.5 KB
Newer Older
1 2
require 'spec_helper'

3
describe Gitlab::Saml::User do
4
  include LdapHelpers
5
  include LoginHelpers
6

7 8 9
  let(:saml_user) { described_class.new(auth_hash) }
  let(:gl_user) { saml_user.gl_user }
  let(:uid) { 'my-uid' }
10
  let(:dn) { 'uid=user1,ou=People,dc=example' }
11
  let(:provider) { 'saml' }
12 13
  let(:raw_info_attr) { { 'groups' => %w(Developers Freelancers Designers) } }
  let(:auth_hash) { OmniAuth::AuthHash.new(uid: uid, provider: provider, info: info_hash, extra: { raw_info: OneLogin::RubySaml::Attributes.new(raw_info_attr) }) }
14 15 16 17 18 19 20 21 22
  let(:info_hash) do
    {
      name: 'John',
      email: 'john@mail.com'
    }
  end
  let(:ldap_user) { Gitlab::LDAP::Person.new(Net::LDAP::Entry.new, 'ldapmain') }

  describe '#save' do
23 24 25
    before do
      stub_basic_saml_config
    end
26

27
    describe 'account exists on server' do
28 29 30 31
      before do
        stub_omniauth_config({ allow_single_sign_on: ['saml'], auto_link_saml_user: true })
      end

32
      let!(:existing_user) { create(:user, email: 'john@mail.com', username: 'john') }
33

34 35 36 37 38 39 40 41 42 43
      context 'and should bind with SAML' do
        it 'adds the SAML identity to the existing user' do
          saml_user.save
          expect(gl_user).to be_valid
          expect(gl_user).to eq existing_user
          identity = gl_user.identities.first
          expect(identity.extern_uid).to eql uid
          expect(identity.provider).to eql 'saml'
        end
      end
44 45 46 47

      context 'external groups' do
        context 'are defined' do
          it 'marks the user as external' do
P
Patricio Cano 已提交
48
            stub_saml_group_config(%w(Freelancers))
49
            saml_user.save
50
            expect(gl_user).to be_valid
51 52 53 54
            expect(gl_user.external).to be_truthy
          end
        end

55 56 57 58
        before do
          stub_saml_group_config(%w(Interns))
        end

59 60 61
        context 'are defined but the user does not belong there' do
          it 'does not mark the user as external' do
            saml_user.save
62
            expect(gl_user).to be_valid
63 64 65 66 67
            expect(gl_user.external).to be_falsey
          end
        end

        context 'user was external, now should not be' do
68
          it 'makes user internal' do
69 70
            existing_user.update_attribute('external', true)
            saml_user.save
71
            expect(gl_user).to be_valid
72 73 74 75
            expect(gl_user.external).to be_falsey
          end
        end
      end
76 77 78 79 80
    end

    describe 'no account exists on server' do
      shared_examples 'to verify compliance with allow_single_sign_on' do
        context 'with allow_single_sign_on enabled' do
81 82 83
          before do
            stub_omniauth_config(allow_single_sign_on: ['saml'])
          end
84 85 86 87 88 89 90 91 92 93 94 95

          it 'creates a user from SAML' do
            saml_user.save

            expect(gl_user).to be_valid
            identity = gl_user.identities.first
            expect(identity.extern_uid).to eql uid
            expect(identity.provider).to eql 'saml'
          end
        end

        context 'with allow_single_sign_on default (["saml"])' do
96 97 98 99
          before do
            stub_omniauth_config(allow_single_sign_on: ['saml'])
          end

100
          it 'does not throw an error' do
101
            expect { saml_user.save }.not_to raise_error
102 103 104 105
          end
        end

        context 'with allow_single_sign_on disabled' do
106 107 108 109
          before do
            stub_omniauth_config(allow_single_sign_on: false)
          end

110
          it 'throws an error' do
111
            expect { saml_user.save }.to raise_error StandardError
112 113 114 115
          end
        end
      end

116 117 118
      context 'external groups' do
        context 'are defined' do
          it 'marks the user as external' do
P
Patricio Cano 已提交
119
            stub_saml_group_config(%w(Freelancers))
120
            saml_user.save
121
            expect(gl_user).to be_valid
122 123 124 125 126 127
            expect(gl_user.external).to be_truthy
          end
        end

        context 'are defined but the user does not belong there' do
          it 'does not mark the user as external' do
P
Patricio Cano 已提交
128
            stub_saml_group_config(%w(Interns))
129
            saml_user.save
130
            expect(gl_user).to be_valid
131 132 133 134 135
            expect(gl_user.external).to be_falsey
          end
        end
      end

136
      context 'with auto_link_ldap_user disabled (default)' do
137 138 139 140
        before do
          stub_omniauth_config({ auto_link_ldap_user: false, auto_link_saml_user: false, allow_single_sign_on: ['saml'] })
        end

141 142 143 144
        include_examples 'to verify compliance with allow_single_sign_on'
      end

      context 'with auto_link_ldap_user enabled' do
145 146 147
        before do
          stub_omniauth_config({ auto_link_ldap_user: true, auto_link_saml_user: false })
        end
148 149

        context 'and at least one LDAP provider is defined' do
150 151 152
          before do
            stub_ldap_config(providers: %w(ldapmain))
          end
153 154

          context 'and a corresponding LDAP person' do
155 156
            let(:adapter) { ldap_adapter('ldapmain') }

157 158 159
            before do
              allow(ldap_user).to receive(:uid) { uid }
              allow(ldap_user).to receive(:username) { uid }
P
Patricio Cano 已提交
160
              allow(ldap_user).to receive(:email) { %w(john@mail.com john2@example.com) }
161 162 163 164 165
              allow(ldap_user).to receive(:dn) { dn }
              allow(Gitlab::LDAP::Adapter).to receive(:new).and_return(adapter)
              allow(Gitlab::LDAP::Person).to receive(:find_by_uid).with(uid, adapter).and_return(ldap_user)
              allow(Gitlab::LDAP::Person).to receive(:find_by_dn).with(dn, adapter).and_return(ldap_user)
              allow(Gitlab::LDAP::Person).to receive(:find_by_email).with('john@mail.com', adapter).and_return(ldap_user)
166 167 168 169 170 171 172 173
            end

            context 'and no account for the LDAP user' do
              it 'creates a user with dual LDAP and SAML identities' do
                saml_user.save

                expect(gl_user).to be_valid
                expect(gl_user.username).to eql uid
P
Patricio Cano 已提交
174
                expect(gl_user.email).to eql 'john@mail.com'
R
Rémy Coutable 已提交
175
                expect(gl_user.identities.length).to be 2
176
                identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } }
177
                expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: dn },
178
                                                           { provider: 'saml', extern_uid: uid }])
179 180 181 182
              end
            end

            context 'and LDAP user has an account already' do
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
              let(:auth_hash_base_attributes) do
                {
                  uid: uid,
                  provider: provider,
                  info: info_hash,
                  extra: {
                    raw_info: OneLogin::RubySaml::Attributes.new(
                      { 'groups' => %w(Developers Freelancers Designers) }
                    )
                  }
                }
              end
              let(:auth_hash) { OmniAuth::AuthHash.new(auth_hash_base_attributes) }
              let(:uid_types) { %w(uid dn email) }

D
Drew Blessing 已提交
198 199 200
              before do
                create(:omniauth_user,
                       email: 'john@mail.com',
201
                       extern_uid: dn,
D
Drew Blessing 已提交
202 203 204 205
                       provider: 'ldapmain',
                       username: 'john')
              end

206 207 208
              shared_examples 'find LDAP person' do |uid_type, uid|
                let(:auth_hash) { OmniAuth::AuthHash.new(auth_hash_base_attributes.merge(uid: extern_uid)) }

209
                before do
210 211 212 213 214 215
                  nil_types = uid_types - [uid_type]

                  nil_types.each do |type|
                    allow(Gitlab::LDAP::Person).to receive(:"find_by_#{type}").and_return(nil)
                  end

216 217 218 219 220
                  allow(Gitlab::LDAP::Person).to receive(:"find_by_#{uid_type}").and_return(ldap_user)
                end

                it 'adds the omniauth identity to the LDAP account' do
                  identities = [
221
                    { provider: 'ldapmain', extern_uid: dn },
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
                    { provider: 'saml', extern_uid: extern_uid }
                  ]

                  identities_as_hash = gl_user.identities.map do |id|
                    { provider: id.provider, extern_uid: id.extern_uid }
                  end

                  saml_user.save

                  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 2
                  expect(identities_as_hash).to match_array(identities)
                end
              end

              context 'when uid is an uid' do
240
                it_behaves_like 'find LDAP person', 'uid' do
241 242 243 244 245
                  let(:extern_uid) { uid }
                end
              end

              context 'when uid is a dn' do
246 247
                it_behaves_like 'find LDAP person', 'dn' do
                  let(:extern_uid) { dn }
248 249 250 251
                end
              end

              context 'when uid is an email' do
252
                it_behaves_like 'find LDAP person', 'email' do
253 254 255 256
                  let(:extern_uid) { 'john@mail.com' }
                end
              end

P
Patricio Cano 已提交
257
              it 'adds the omniauth identity to the LDAP account' do
258 259 260 261
                saml_user.save

                expect(gl_user).to be_valid
                expect(gl_user.username).to eql 'john'
P
Patricio Cano 已提交
262
                expect(gl_user.email).to eql 'john@mail.com'
R
Rémy Coutable 已提交
263
                expect(gl_user.identities.length).to be 2
264
                identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } }
265
                expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: dn },
266
                                                           { provider: 'saml', extern_uid: uid }])
267
              end
D
Drew Blessing 已提交
268 269 270 271 272 273 274 275 276

              it 'saves successfully on subsequent tries, when both identities are present' do
                saml_user.save
                local_saml_user = described_class.new(auth_hash)
                local_saml_user.save

                expect(local_saml_user.gl_user).to be_valid
                expect(local_saml_user.gl_user).to be_persisted
              end
277
            end
278 279 280

            context 'user has SAML user, and wants to add their LDAP identity' do
              it 'adds the LDAP identity to the existing SAML user' do
281 282 283 284 285
                create(:omniauth_user, email: 'john@mail.com', extern_uid: dn, provider: 'saml', username: 'john')

                allow(Gitlab::LDAP::Person).to receive(:find_by_uid).with(dn, adapter).and_return(ldap_user)

                local_hash = OmniAuth::AuthHash.new(uid: dn, provider: provider, info: info_hash)
286
                local_saml_user = described_class.new(local_hash)
287

288 289
                local_saml_user.save
                local_gl_user = local_saml_user.gl_user
290

291
                expect(local_gl_user).to be_valid
R
Rémy Coutable 已提交
292
                expect(local_gl_user.identities.length).to be 2
293
                identities_as_hash = local_gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } }
294 295
                expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: dn },
                                                           { provider: 'saml', extern_uid: dn }])
296 297
              end
            end
298 299 300
          end
        end
      end
301 302 303 304 305 306 307 308 309 310 311 312

      context 'when signup is disabled' do
        before do
          stub_application_setting signup_enabled: false
        end

        it 'creates the user' do
          saml_user.save

          expect(gl_user).to be_persisted
        end
      end
313 314 315 316 317 318 319 320 321 322 323 324 325

      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
          saml_user.save

          expect(gl_user).to be_persisted
          expect(gl_user).to be_confirmed
        end
      end
326 327 328
    end

    describe 'blocking' do
329 330 331
      before do
        stub_omniauth_config({ allow_single_sign_on: ['saml'], auto_link_saml_user: true })
      end
332 333 334

      context 'signup with SAML only' do
        context 'dont block on create' do
335 336 337
          before do
            stub_omniauth_config(block_auto_created_users: false)
          end
338

339
          it 'does not block the user' do
340 341 342 343 344 345 346
            saml_user.save
            expect(gl_user).to be_valid
            expect(gl_user).not_to be_blocked
          end
        end

        context 'block on create' do
347 348 349
          before do
            stub_omniauth_config(block_auto_created_users: true)
          end
350

351
          it 'blocks user' do
352 353 354 355 356 357 358 359 360 361 362 363 364 365
            saml_user.save
            expect(gl_user).to be_valid
            expect(gl_user).to be_blocked
          end
        end
      end

      context 'sign-in' do
        before do
          saml_user.save
          saml_user.gl_user.activate
        end

        context 'dont block on create' do
366 367 368
          before do
            stub_omniauth_config(block_auto_created_users: false)
          end
369 370 371 372 373 374 375 376 377

          it do
            saml_user.save
            expect(gl_user).to be_valid
            expect(gl_user).not_to be_blocked
          end
        end

        context 'block on create' do
378 379 380
          before do
            stub_omniauth_config(block_auto_created_users: true)
          end
381 382 383 384 385 386 387 388 389 390

          it do
            saml_user.save
            expect(gl_user).to be_valid
            expect(gl_user).not_to be_blocked
          end
        end
      end
    end
  end
391 392 393 394 395 396 397 398 399 400 401 402

  describe '#find_user' do
    context 'raw info hash attributes empty' do
      let(:raw_info_attr) { {} }

      it 'does not mark user as external' do
        stub_saml_group_config(%w(Freelancers))

        expect(saml_user.find_user.external).to be_falsy
      end
    end
  end
403
end