summaryrefslogtreecommitdiff
path: root/test/models/user_otp_test.rb
blob: 81f25575407da35a9ee68c7396f1998571dab16e (plain)
1
2
3
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
require "test_helper"

class UserOtpTest < ActiveSupport::TestCase
  fixtures :users

  def setup
    @user = users(:quentin)
  end

  test "begin_otp_enrollment! stores a pending secret and yields a provisioning URI" do
    uri = @user.begin_otp_enrollment!
    assert @user.otp_pending_secret.present?
    assert_not @user.otp_enrolled?
    assert_match %r{\Aotpauth://totp/}, uri
    assert_includes uri, "issuer="
  end

  test "confirm_otp_enrollment! promotes the pending secret and witnesses it" do
    @user.begin_otp_enrollment!
    code = ROTP::TOTP.new(@user.otp_pending_secret).now

    assert @user.confirm_otp_enrollment!(code)
    assert @user.otp_enrolled?
    assert_nil @user.otp_pending_secret

    action = NodeAction.where(:action => "otp_enroll").last
    assert_equal @user, action.user
    assert_equal [["User", @user.id]],
                 action.action_participants.map { |p| [p.subject_type, p.subject_id] }
  end

  test "confirm_otp_enrollment! rejects a wrong code and stays unenrolled" do
    @user.begin_otp_enrollment!
    assert_not @user.confirm_otp_enrollment!("000000")
    assert_not @user.otp_enrolled?
    assert @user.otp_pending_secret.present?
  end

  test "verify_otp! accepts a current code exactly once" do
    @user.update!(:otp_secret => ROTP::Base32.random)
    code = ROTP::TOTP.new(@user.otp_secret).now

    assert @user.verify_otp!(code)
    assert_not @user.verify_otp!(code), "replayed code must be rejected"
  end

  test "the confirmation code cannot be replayed at login" do
    @user.begin_otp_enrollment!
    code = ROTP::TOTP.new(@user.otp_pending_secret).now
    @user.confirm_otp_enrollment!(code)
    assert_not @user.verify_otp!(code)
  end

  test "verify_otp! rejects wrong codes and unenrolled users" do
    assert_not @user.verify_otp!("123456")
    enroll!(@user)
    assert_not @user.verify_otp!("000000")
  end

  test "disable_otp! by the user themselves is witnessed as otp_disable" do
    enroll!(@user)
    assert @user.disable_otp!(:actor => @user)
    assert_not @user.otp_enrolled?
    assert_equal "otp_disable", NodeAction.last.action
  end

  test "an admin clearing another user's factor is witnessed as otp_reset" do
    enroll!(@user)
    admin = users(:aaron)
    assert @user.disable_otp!(:actor => admin)

    action = NodeAction.where(:action => "otp_reset").last
    assert_equal admin, action.user
    assert_equal @user.login, action.metadata["target_login"]
  end

  private

    def enroll!(user)
      user.begin_otp_enrollment!
      user.confirm_otp_enrollment!(ROTP::TOTP.new(user.otp_pending_secret).now)
    end
end