omniauth_callbacks_controller_spec.rb 7.4 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
G
gfyoung 已提交
12
      mock_auth_hash(provider.to_s, +extern_uid, user.email)
13
      stub_omniauth_provider(provider, context: request)
14
    end
15

16 17
    context 'when the user is on the last sign in attempt' do
      let(:extern_uid) { 'my-uid' }
18

19 20 21
      before do
        user.update(failed_attempts: User.maximum_attempts.pred)
        subject.response = ActionDispatch::Response.new
22
      end
23

24 25
      context 'when using a form based provider' do
        let(:provider) { :ldap }
26

27 28 29
        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)
30

31
          subject.send(:failure)
T
Tiago Botelho 已提交
32

33 34
          expect(user.reload).to be_access_locked
        end
35
      end
36

37 38
      context 'when using a button based provider' do
        let(:provider) { :github }
39

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

43
          subject.send(:failure)
44

45
          expect(user.reload).not_to be_access_locked
T
Tiago Botelho 已提交
46
        end
47
      end
48
    end
49

50 51 52 53 54 55 56 57 58 59
    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

60
      it 'calls through to the failure handler' do
61 62 63 64 65 66 67 68 69 70 71 72
        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

73 74 75 76 77 78 79 80 81 82
    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 已提交
83
          post provider, session: { user_return_to: '/fake/url' }
84 85 86 87 88 89 90

          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 已提交
91
          post provider, session: { user_return_to: '/fake/url#replaceme' }
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106

          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

107 108 109 110
    context 'strategies' do
      context 'github' do
        let(:extern_uid) { 'my-uid' }
        let(:provider) { :github }
T
Tiago Botelho 已提交
111

112
        it 'allows sign in' do
T
Tiago Botelho 已提交
113
          post provider
114

T
Tiago Botelho 已提交
115 116
          expect(request.env['warden']).to be_authenticated
        end
117

118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
        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

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

148 149 150
          before do
            stub_omniauth_setting(block_auto_created_users: false)
          end
151
        end
T
Tiago Botelho 已提交
152

153 154
        context 'sign up' do
          include_context 'sign_up'
T
Tiago Botelho 已提交
155

156 157
          it 'is allowed' do
            post provider
T
Tiago Botelho 已提交
158

159 160
            expect(request.env['warden']).to be_authenticated
          end
T
Tiago Botelho 已提交
161 162
        end

163 164 165 166 167 168
        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 已提交
169

170
          it 'prevents login via POST' do
T
Tiago Botelho 已提交
171 172 173 174
            post provider

            expect(request.env['warden']).not_to be_authenticated
          end
175 176 177 178 179 180 181 182 183

          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
184
            user.identities.destroy_all # rubocop: disable DestroyAll
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
            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 已提交
212
        end
213 214
      end
    end
215 216 217 218 219 220 221 222 223 224
  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 已提交
225
      mock_auth_hash_with_saml_xml('saml', +'my-uid', user.email, mock_saml_response)
226 227 228 229
      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
230

231 232 233 234 235
    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
236

237 238 239 240
      it 'expects user to be signed_in' do
        expect(request.env['warden']).to be_authenticated
      end
    end
241

242 243 244
    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 已提交
245 246
        expect(request.env['warden']).not_to be_authenticated
      end
247 248
    end
  end
249
end