Class: Redmine::Twofa::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/redmine/twofa/base.rb

Direct Known Subclasses

Totp

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(user) ⇒ Base

Returns a new instance of Base.



36
37
38
# File 'lib/redmine/twofa/base.rb', line 36

def initialize(user)
  @user = user
end

Class Method Details

.inherited(child) ⇒ Object



23
24
25
26
# File 'lib/redmine/twofa/base.rb', line 23

def self.inherited(child)
  # require-ing a Base subclass will register it as a 2FA scheme
  Redmine::Twofa.register_scheme(scheme_name(child), child)
end

.scheme_name(klass = self) ⇒ Object



28
29
30
# File 'lib/redmine/twofa/base.rb', line 28

def self.scheme_name(klass = self)
  klass.name.demodulize.underscore
end

Instance Method Details

#backup_codesObject



151
152
153
# File 'lib/redmine/twofa/base.rb', line 151

def backup_codes
  Token.where(user_id: @user.id, action: 'twofa_backup_code')
end

#confirm_pairing!(code) ⇒ Object



44
45
46
47
48
49
50
51
52
53
# File 'lib/redmine/twofa/base.rb', line 44

def confirm_pairing!(code)
  # make sure an otp and not a backup code is used
  if verify_otp!(code)
    @user.update!(twofa_scheme: scheme_name)
    deliver_twofa_paired
    return true
  else
    return false
  end
end

#deliver_twofa_pairedObject



55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/redmine/twofa/base.rb', line 55

def deliver_twofa_paired
  ::Mailer.deliver_security_notification(
    @user,
    User.current,
    {
      title: :label_my_account,
      message: 'twofa_mail_body_security_notification_paired',
      # (mis-)use field here as value wouldn't get localized
      field: "twofa__#{scheme_name}__name",
      url: {controller: 'my', action: 'account'}
    }
  )
end

#deliver_twofa_unpairedObject



84
85
86
87
88
89
90
91
92
93
94
# File 'lib/redmine/twofa/base.rb', line 84

def deliver_twofa_unpaired
  ::Mailer.deliver_security_notification(
    @user,
    User.current,
    {
      title: :label_my_account,
      message: 'twofa_mail_body_security_notification_unpaired',
      url: {controller: 'my', action: 'account'}
    }
  )
end

#destroy_pairing!(code) ⇒ Object



69
70
71
72
73
74
75
76
# File 'lib/redmine/twofa/base.rb', line 69

def destroy_pairing!(code)
  if verify!(code)
    destroy_pairing_without_verify!
    return true
  else
    return false
  end
end

#destroy_pairing_without_verify!Object



78
79
80
81
82
# File 'lib/redmine/twofa/base.rb', line 78

def destroy_pairing_without_verify!
  @user.update!(twofa_scheme: nil)
  backup_codes.delete_all
  deliver_twofa_unpaired
end

#init_backup_codes!Object



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/redmine/twofa/base.rb', line 131

def init_backup_codes!
  backup_codes.delete_all
  tokens = []
  10.times do
    token = Token.create(user_id: @user.id, action: 'twofa_backup_code')
    token.update_columns value: Redmine::Utils.random_hex(6)
    tokens << token
  end
  ::Mailer.deliver_security_notification(
    @user,
    User.current,
    {
      title: :label_my_account,
      message: 'twofa_mail_body_backup_codes_generated',
      url: {controller: 'my', action: 'account'}
    }
  )
  tokens
end

#init_pairing!Object



40
41
42
# File 'lib/redmine/twofa/base.rb', line 40

def init_pairing!
  @user
end

#init_pairing_view_variablesObject

this will only be used on pairing initialization



156
157
158
# File 'lib/redmine/twofa/base.rb', line 156

def init_pairing_view_variables
  otp_confirm_view_variables
end

#otp_confirm_view_variablesObject



160
161
162
163
164
165
# File 'lib/redmine/twofa/base.rb', line 160

def otp_confirm_view_variables
  {
    scheme_name: scheme_name,
    resendable: false
  }
end

#scheme_nameObject



32
33
34
# File 'lib/redmine/twofa/base.rb', line 32

def scheme_name
  self.class.scheme_name
end

#send_code(controller: nil, action: nil) ⇒ Object



96
97
98
99
# File 'lib/redmine/twofa/base.rb', line 96

def send_code(controller: nil, action: nil)
  # return true only if the scheme sends a code to the user
  false
end

#verify!(code) ⇒ Object



101
102
103
# File 'lib/redmine/twofa/base.rb', line 101

def verify!(code)
  verify_otp!(code) || verify_backup_code!(code)
end

#verify_backup_code!(code) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/redmine/twofa/base.rb', line 109

def verify_backup_code!(code)
  # backup codes are case-insensitive and white-space-insensitive
  code = code.to_s.remove(/[[:space:]]/).downcase
  user_from_code = Token.find_active_user('twofa_backup_code', code)
  # invalidate backup code after usage
  Token.where(user_id: @user.id).find_token('twofa_backup_code', code).try(:delete)
  # make sure the user using the backup code is the same it's been issued to
  return false unless @user.present? && @user == user_from_code

  ::Mailer.deliver_security_notification(
    @user,
    User.current,
    {
      originator: @user,
      title: :label_my_account,
      message: 'twofa_mail_body_backup_code_used',
      url: {controller: 'my', action: 'account'}
    }
  )
  return true
end

#verify_otp!(code) ⇒ Object



105
106
107
# File 'lib/redmine/twofa/base.rb', line 105

def verify_otp!(code)
  raise 'not implemented'
end