omniauth_callbacks_controller_spec.rb 7.6 KB
Newer Older
G
gfyoung 已提交
1 2
# frozen_string_literal: true

3 4
require 'spec_helper'

5
describe OmniauthCallbacksController, type: :controller do
6 7
  include LoginHelpers

8 9
  describe 'omniauth' do
    let(:user) { create(:omniauth_user, extern_uid: extern_uid, provider: provider) }
10

T
Tiago Botelho 已提交
11
    before do
12
      @original_env_config_omniauth_auth = mock_auth_hash(provider.to_s, +extern_uid, user.email)
13
      stub_omniauth_provider(provider, context: request)
14
    end
15

16 17 18 19
    after do
      Rails.application.env_config['omniauth.auth'] = @original_env_config_omniauth_auth
    end

20 21
    context 'when the user is on the last sign in attempt' do
      let(:extern_uid) { 'my-uid' }
22

23 24 25
      before do
        user.update(failed_attempts: User.maximum_attempts.pred)
        subject.response = ActionDispatch::Response.new
26
      end
27

28 29
      context 'when using a form based provider' do
        let(:provider) { :ldap }
30

31 32 33
        it 'locks the user when sign in fails' do
          allow(subject).to receive(:params).and_return(ActionController::Parameters.new(username: user.username))
          request.env['omniauth.error.strategy'] = OmniAuth::Strategies::LDAP.new(nil)
34

35
          subject.send(:failure)
T
Tiago Botelho 已提交
36

37 38
          expect(user.reload).to be_access_locked
        end
39
      end
40

41 42
      context 'when using a button based provider' do
        let(:provider) { :github }
43

44 45
        it 'does not lock the user when sign in fails' do
          request.env['omniauth.error.strategy'] = OmniAuth::Strategies::GitHub.new(nil)
46

47
          subject.send(:failure)
48

49
          expect(user.reload).not_to be_access_locked
T
Tiago Botelho 已提交
50
        end
51
      end
52
    end
53

54 55 56 57 58 59 60 61 62 63
    context 'when sign in fails' do
      include RoutesHelpers

      let(:extern_uid) { 'my-uid' }
      let(:provider) { :saml }

      def stub_route_as(path)
        allow(@routes).to receive(:generate_extras) { [path, []] }
      end

64
      it 'calls through to the failure handler' do
65 66 67 68 69 70 71 72 73 74 75 76
        request.env['omniauth.error'] = OneLogin::RubySaml::ValidationError.new("Fingerprint mismatch")
        request.env['omniauth.error.strategy'] = OmniAuth::Strategies::SAML.new(nil)
        stub_route_as('/users/auth/saml/callback')

        ForgeryProtection.with_forgery_protection do
          post :failure
        end

        expect(flash[:alert]).to match(/Fingerprint mismatch/)
      end
    end

77 78 79 80 81 82 83 84 85 86
    context 'when a redirect fragment is provided' do
      let(:provider) { :jwt }
      let(:extern_uid) { 'my-uid' }

      before do
        request.env['omniauth.params'] = { 'redirect_fragment' => 'L101' }
      end

      context 'when a redirect url is stored' do
        it 'redirects with fragment' do
J
Jasper Maes 已提交
87
          post provider, session: { user_return_to: '/fake/url' }
88 89 90 91 92 93 94

          expect(response).to redirect_to('/fake/url#L101')
        end
      end

      context 'when a redirect url with a fragment is stored' do
        it 'redirects with the new fragment' do
J
Jasper Maes 已提交
95
          post provider, session: { user_return_to: '/fake/url#replaceme' }
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110

          expect(response).to redirect_to('/fake/url#L101')
        end
      end

      context 'when no redirect url is stored' do
        it 'does not redirect with the fragment' do
          post provider

          expect(response.redirect?).to be true
          expect(response.location).not_to include('#L101')
        end
      end
    end

111 112 113 114
    context 'strategies' do
      context 'github' do
        let(:extern_uid) { 'my-uid' }
        let(:provider) { :github }
T
Tiago Botelho 已提交
115

116
        it 'allows sign in' do
T
Tiago Botelho 已提交
117
          post provider
118

T
Tiago Botelho 已提交
119 120
          expect(request.env['warden']).to be_authenticated
        end
121

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
        context 'when user has no linked provider' do
          let(:user) { create(:user) }

          before do
            sign_in user
          end

          it 'links identity' do
            expect do
              post provider
              user.reload
            end.to change { user.identities.count }.by(1)
          end

          context 'and is not allowed to link the provider' do
            before do
              allow_any_instance_of(IdentityProviderPolicy).to receive(:can?).with(:link).and_return(false)
            end

            it 'returns 403' do
              post provider

              expect(response).to have_gitlab_http_status(403)
            end
          end
        end

149
        shared_context 'sign_up' do
G
gfyoung 已提交
150
          let(:user) { double(email: +'new@example.com') }
151

152 153 154
          before do
            stub_omniauth_setting(block_auto_created_users: false)
          end
155
        end
T
Tiago Botelho 已提交
156

157 158
        context 'sign up' do
          include_context 'sign_up'
T
Tiago Botelho 已提交
159

160 161
          it 'is allowed' do
            post provider
T
Tiago Botelho 已提交
162

163 164
            expect(request.env['warden']).to be_authenticated
          end
T
Tiago Botelho 已提交
165 166
        end

167 168 169 170 171 172
        context 'when OAuth is disabled' do
          before do
            stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
            settings = Gitlab::CurrentSettings.current_application_settings
            settings.update(disabled_oauth_sign_in_sources: [provider.to_s])
          end
T
Tiago Botelho 已提交
173

174
          it 'prevents login via POST' do
T
Tiago Botelho 已提交
175 176 177 178
            post provider

            expect(request.env['warden']).not_to be_authenticated
          end
179 180 181 182 183 184 185 186 187

          it 'shows warning when attempting login' do
            post provider

            expect(response).to redirect_to new_user_session_path
            expect(flash[:alert]).to eq('Signing in using GitHub has been disabled')
          end

          it 'allows linking the disabled provider' do
188
            user.identities.destroy_all # rubocop: disable DestroyAll
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
            sign_in(user)

            expect { post provider }.to change { user.reload.identities.count }.by(1)
          end

          context 'sign up' do
            include_context 'sign_up'

            it 'is prevented' do
              post provider

              expect(request.env['warden']).not_to be_authenticated
            end
          end
        end
      end

      context 'auth0' do
        let(:extern_uid) { '' }
        let(:provider) { :auth0 }

        it 'does not allow sign in without extern_uid' do
          post 'auth0'

          expect(request.env['warden']).not_to be_authenticated
          expect(response.status).to eq(302)
          expect(controller).to set_flash[:alert].to('Wrong extern UID provided. Make sure Auth0 is configured correctly.')
T
Tiago Botelho 已提交
216
        end
217 218
      end
    end
219 220 221 222 223 224 225 226 227 228
  end

  describe '#saml' do
    let(:user) { create(:omniauth_user, :two_factor, extern_uid: 'my-uid', provider: 'saml') }
    let(:mock_saml_response) { File.read('spec/fixtures/authentication/saml_response.xml') }
    let(:saml_config) { mock_saml_config_with_upstream_two_factor_authn_contexts }

    before do
      stub_omniauth_saml_config({ enabled: true, auto_link_saml_user: true, allow_single_sign_on: ['saml'],
                                  providers: [saml_config] })
G
gfyoung 已提交
229
      mock_auth_hash_with_saml_xml('saml', +'my-uid', user.email, mock_saml_response)
230 231 232 233
      request.env["devise.mapping"] = Devise.mappings[:user]
      request.env['omniauth.auth'] = Rails.application.env_config['omniauth.auth']
      post :saml, params: { SAMLResponse: mock_saml_response }
    end
234

235 236 237 238 239
    context 'when worth two factors' do
      let(:mock_saml_response) do
        File.read('spec/fixtures/authentication/saml_response.xml')
            .gsub('urn:oasis:names:tc:SAML:2.0:ac:classes:Password', 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorIGTOKEN')
      end
240

241 242 243 244
      it 'expects user to be signed_in' do
        expect(request.env['warden']).to be_authenticated
      end
    end
245

246 247 248
    context 'when not worth two factors' do
      it 'expects user to provide second factor' do
        expect(response).to render_template('devise/sessions/two_factor')
T
Tiago Botelho 已提交
249 250
        expect(request.env['warden']).not_to be_authenticated
      end
251 252
    end
  end
253
end