sessions_controller_spec.rb 8.93 KB
Newer Older
1 2 3
require 'spec_helper'

describe SessionsController do
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
  describe '#new' do
    before do
      @request.env['devise.mapping'] = Devise.mappings[:user]
    end

    context 'when auto sign-in is enabled' do
      before do
        stub_omniauth_setting(auto_sign_in_with_provider: :saml)
        allow(controller).to receive(:omniauth_authorize_path).with(:user, :saml).
          and_return('/saml')
      end

      context 'and no auto_sign_in param is passed' do
        it 'redirects to :omniauth_authorize_path' do
          get(:new)

          expect(response).to have_http_status(302)
          expect(response).to redirect_to('/saml')
        end
      end

      context 'and auto_sign_in=false param is passed' do
        it 'responds with 200' do
          get(:new, auto_sign_in: 'false')

          expect(response).to have_http_status(200)
        end
      end
    end
  end

35 36 37 38 39
  describe '#create' do
    before do
      @request.env['devise.mapping'] = Devise.mappings[:user]
    end

40
    context 'when using standard authentications' do
41 42 43 44 45
      context 'invalid password' do
        it 'does not authenticate user' do
          post(:create, user: { login: 'invalid', password: 'invalid' })

          expect(response)
Connor Shea's avatar
Connor Shea committed
46
            .to set_flash.now[:alert].to /Invalid Login or password/
47 48 49
        end
      end

50 51 52
      context 'when using valid password', :redis do
        include UserActivitiesHelpers

53 54 55 56 57 58 59
        let(:user) { create(:user) }

        it 'authenticates user correctly' do
          post(:create, user: { login: user.username, password: user.password })

          expect(subject.current_user). to eq user
        end
60

61
        it 'creates an audit log record' do
62
          expect { post(:create, user: { login: user.username, password: user.password }) }.to change { SecurityEvent.count }.by(1)
63 64 65
          expect(SecurityEvent.last.details[:with]).to eq('standard')
        end

66 67
        include_examples 'user login request with unique ip limit', 302 do
          def request
68 69
            post(:create, user: { login: user.username, password: user.password })
            expect(subject.current_user).to eq user
70
            subject.sign_out user
71
          end
72
        end
73 74 75 76

        it 'updates the user activity' do
          expect do
            post(:create, user: { login: user.username, password: user.password })
77
          end.to change { user_activity(user) }
78
        end
79 80 81
      end
    end

82
    context 'when using two-factor authentication via OTP' do
83 84 85 86 87 88
      let(:user) { create(:user, :two_factor) }

      def authenticate_2fa(user_params)
        post(:create, { user: user_params }, { otp_user_id: user.id })
      end

89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
      context 'remember_me field' do
        it 'sets a remember_user_token cookie when enabled' do
          allow(controller).to receive(:find_user).and_return(user)
          expect(controller).
            to receive(:remember_me).with(user).and_call_original

          authenticate_2fa(remember_me: '1', otp_attempt: user.current_otp)

          expect(response.cookies['remember_user_token']).to be_present
        end

        it 'does nothing when disabled' do
          allow(controller).to receive(:find_user).and_return(user)
          expect(controller).not_to receive(:remember_me)

          authenticate_2fa(remember_me: '0', otp_attempt: user.current_otp)

          expect(response.cookies['remember_user_token']).to be_nil
        end
      end

110 111 112
      ##
      # See #14900 issue
      #
113 114 115
      context 'when authenticating with login and OTP of another user' do
        context 'when another user has 2FA enabled' do
          let(:another_user) { create(:user, :two_factor) }
116

117 118 119 120
          context 'when OTP is valid for another user' do
            it 'does not authenticate' do
              authenticate_2fa(login: another_user.username,
                               otp_attempt: another_user.current_otp)
121

122
              expect(subject.current_user).not_to eq another_user
123
            end
124 125
          end

126 127 128 129
          context 'when OTP is invalid for another user' do
            it 'does not authenticate' do
              authenticate_2fa(login: another_user.username,
                               otp_attempt: 'invalid')
130

131
              expect(subject.current_user).not_to eq another_user
132
            end
133 134
          end

135 136 137 138 139 140 141 142
          context 'when authenticating with OTP' do
            context 'when OTP is valid' do
              it 'authenticates correctly' do
                authenticate_2fa(otp_attempt: user.current_otp)

                expect(subject.current_user).to eq user
              end
            end
143

144
            context 'when OTP is invalid' do
145 146 147
              before do
                authenticate_2fa(otp_attempt: 'invalid')
              end
148

149
              it 'does not authenticate' do
150
                expect(subject.current_user).not_to eq user
151 152 153
              end

              it 'warns about invalid OTP code' do
154 155
                expect(response).to set_flash.now[:alert]
                  .to /Invalid two-factor code/
156
              end
157 158 159
            end
          end

160 161 162 163 164 165 166 167 168 169 170 171 172 173
          context 'when the user is on their last attempt' do
            before do
              user.update(failed_attempts: User.maximum_attempts.pred)
            end

            context 'when OTP is valid' do
              it 'authenticates correctly' do
                authenticate_2fa(otp_attempt: user.current_otp)

                expect(subject.current_user).to eq user
              end
            end

            context 'when OTP is invalid' do
174 175 176
              before do
                authenticate_2fa(otp_attempt: 'invalid')
              end
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199

              it 'does not authenticate' do
                expect(subject.current_user).not_to eq user
              end

              it 'warns about invalid login' do
                expect(response).to set_flash.now[:alert]
                  .to /Invalid Login or password/
              end

              it 'locks the user' do
                expect(user.reload).to be_access_locked
              end

              it 'keeps the user locked on future login attempts' do
                post(:create, user: { login: user.username, password: user.password })

                expect(response)
                  .to set_flash.now[:alert].to /Invalid Login or password/
              end
            end
          end

200 201
          context 'when another user does not have 2FA enabled' do
            let(:another_user) { create(:user) }
202

203 204 205
            it 'does not leak that 2FA is disabled for another user' do
              authenticate_2fa(login: another_user.username,
                               otp_attempt: 'invalid')
206

207 208
              expect(response).to set_flash.now[:alert]
                .to /Invalid two-factor code/
209 210 211 212
            end
          end
        end
      end
213 214 215 216 217 218 219 220 221 222 223 224 225 226

      it "creates an audit log record" do
        expect { authenticate_2fa(login: user.username, otp_attempt: user.current_otp) }.to change { SecurityEvent.count }.by(1)
        expect(SecurityEvent.last.details[:with]).to eq("two-factor")
      end
    end

    context 'when using two-factor authentication via U2F device' do
      let(:user) { create(:user, :two_factor) }

      def authenticate_2fa_u2f(user_params)
        post(:create, { user: user_params }, { otp_user_id: user.id })
      end

227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
      context 'remember_me field' do
        it 'sets a remember_user_token cookie when enabled' do
          allow(U2fRegistration).to receive(:authenticate).and_return(true)
          allow(controller).to receive(:find_user).and_return(user)
          expect(controller).
            to receive(:remember_me).with(user).and_call_original

          authenticate_2fa_u2f(remember_me: '1', login: user.username, device_response: "{}")

          expect(response.cookies['remember_user_token']).to be_present
        end

        it 'does nothing when disabled' do
          allow(U2fRegistration).to receive(:authenticate).and_return(true)
          allow(controller).to receive(:find_user).and_return(user)
          expect(controller).not_to receive(:remember_me)

          authenticate_2fa_u2f(remember_me: '0', login: user.username, device_response: "{}")

          expect(response.cookies['remember_user_token']).to be_nil
        end
      end

250 251 252 253 254
      it "creates an audit log record" do
        allow(U2fRegistration).to receive(:authenticate).and_return(true)
        expect { authenticate_2fa_u2f(login: user.username, device_response: "{}") }.to change { SecurityEvent.count }.by(1)
        expect(SecurityEvent.last.details[:with]).to eq("two-factor-via-u2f-device")
      end
255 256
    end
  end
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272

  describe '#new' do
    before do
      @request.env['devise.mapping'] = Devise.mappings[:user]
    end

    it 'redirects correctly for referer on same host with params' do
      search_path = '/search?search=seed_project'
      allow(controller.request).to receive(:referer).
        and_return('http://%{host}%{path}' % { host: Gitlab.config.gitlab.host, path: search_path })

      get(:new, redirect_to_referer: :yes)

      expect(controller.stored_location_for(:redirect)).to eq(search_path)
    end
  end
273
end