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.



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

def initialize(user)
  @user = user
end

Class Method Details

.inherited(child) ⇒ Object



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

def self.inherited(child)
  super
  # 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



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

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

Instance Method Details

#backup_codesObject



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

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

#confirm_pairing!(code) ⇒ Object



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

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



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

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



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

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



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

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

#destroy_pairing_without_verify!Object



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

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

#init_backup_codes!Object



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

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



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

def init_pairing!
  @user
end

#init_pairing_view_variablesObject

this will only be used on pairing initialization



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

def init_pairing_view_variables
  otp_confirm_view_variables
end

#otp_confirm_view_variablesObject



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

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

#scheme_nameObject



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

def scheme_name
  self.class.scheme_name
end

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



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

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

#verify!(code) ⇒ Object



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

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

#verify_backup_code!(code) ⇒ Object



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

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



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

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