require 'spec_helper'

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

    context 'standard authentications' do
      context 'invalid password' do
        it 'does not authenticate user' do
          post(:create, user: { login: 'invalid', password: 'invalid' })

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

      context 'valid password' do
        let(:user) { create(:user) }

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

          expect(response).to set_flash.to /Signed in successfully/
          expect(subject.current_user). to eq user
        end
      end
    end

    context 'two-factor authentication' do
      let(:user) { create(:user, :two_factor) }

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

      ##
      # See #14900 issue
      #
      context 'authenticating with login and OTP belonging to another user' do
        let(:another_user) { create(:user, :two_factor) }


        context 'OTP valid for another user' do
          it 'does not authenticate' do
            authenticate_2fa(login: another_user.username,
                             otp_attempt: another_user.current_otp)

            expect(subject.current_user).to_not eq another_user
          end
        end

        context 'OTP invalid for another user' do
          before do
            authenticate_2fa(login: another_user.username,
                             otp_attempt: 'invalid')
          end

          it 'does not authenticate' do
            expect(subject.current_user).to_not eq another_user
          end

          it 'does not leak information about 2FA enabled' do
            expect(response).to_not set_flash.now[:alert].to /Invalid two-factor code/
          end
        end

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

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

          context 'invalid OTP' do
            before { authenticate_2fa(otp_attempt: 'invalid') }

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

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

Add specs for sessions controller including 2FA
Add specs for sessions controller  including 2FA

This also contains specs for a bug described in #14900
