File: verify_account.rb

package info (click to toggle)
ruby-rodauth 2.42.0-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 812 kB
  • sloc: ruby: 7,524; javascript: 100; makefile: 4
file content (311 lines) | stat: -rw-r--r-- 10,568 bytes parent folder | download | duplicates (2)
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# frozen-string-literal: true

module Rodauth
  Feature.define(:verify_account, :VerifyAccount) do
    depends :login, :create_account, :email_base

    error_flash "Unable to verify account"
    error_flash "Unable to resend verify account email", 'verify_account_resend'
    error_flash "An email has recently been sent to you with a link to verify your account", 'verify_account_email_recently_sent'
    error_flash "There was an error verifying your account: invalid verify account key", 'no_matching_verify_account_key'
    error_flash "The account you tried to create is currently awaiting verification", 'attempt_to_create_unverified_account'
    error_flash "The account you tried to login with is currently awaiting verification", 'attempt_to_login_to_unverified_account'
    notice_flash "Your account has been verified"
    notice_flash "An email has been sent to you with a link to verify your account", 'verify_account_email_sent'
    loaded_templates %w'verify-account verify-account-resend verify-account-email'
    view 'verify-account', 'Verify Account'
    view 'verify-account-resend', 'Resend Verification Email', 'resend_verify_account'
    additional_form_tags
    additional_form_tags 'verify_account_resend'
    after
    after 'verify_account_email_resend'
    before
    before 'verify_account_email_resend'
    button 'Verify Account'
    button 'Send Verification Email Again', 'verify_account_resend'
    redirect
    response
    response :verify_account_email_sent
    redirect(:verify_account_email_sent){default_post_email_redirect}
    redirect(:verify_account_email_recently_sent){default_post_email_redirect}
    email :verify_account, 'Verify Account'

    auth_value_method :verify_account_key_param, 'key'
    auth_value_method :verify_account_autologin?, true
    auth_value_method :verify_account_table, :account_verification_keys
    auth_value_method :verify_account_id_column, :id
    auth_value_method :verify_account_email_last_sent_column, :email_last_sent
    auth_value_method :verify_account_skip_resend_email_within, 300
    auth_value_method :verify_account_key_column, :key
    translatable_method :verify_account_resend_explanatory_text, "<p>If you no longer have the email to verify the account, you can request that it be resent to you:</p>"
    translatable_method :verify_account_resend_link_text, "Resend Verify Account Information"
    session_key :verify_account_session_key, :verify_account_key
    auth_value_method :verify_account_set_password?, true

    auth_methods(
      :allow_resending_verify_account_email?,
      :create_verify_account_key,
      :get_verify_account_key,
      :get_verify_account_email_last_sent,
      :remove_verify_account_key,
      :set_verify_account_email_last_sent,
      :verify_account,
      :verify_account_email_link,
      :verify_account_key_insert_hash,
      :verify_account_key_value
    )

    auth_private_methods(
      :account_from_verify_account_key
    )

    internal_request_method(:verify_account_resend)
    internal_request_method

    route(:verify_account_resend) do |r|
      verify_account_check_already_logged_in
      before_verify_account_resend_route

      r.get do
        resend_verify_account_view
      end

      r.post do
        if account_from_login(login_param_value) && allow_resending_verify_account_email?
          if verify_account_email_recently_sent?
            set_redirect_error_flash verify_account_email_recently_sent_error_flash
            redirect verify_account_email_recently_sent_redirect
          end

          before_verify_account_email_resend
          if verify_account_email_resend
            after_verify_account_email_resend
            verify_account_email_sent_response
          end
        end

        set_redirect_error_status(no_matching_login_error_status)
        set_error_reason :no_matching_login
        set_redirect_error_flash verify_account_resend_error_flash
        redirect verify_account_email_sent_redirect
      end
    end

    route do |r|
      verify_account_check_already_logged_in
      before_verify_account_route
      @password_field_autocomplete_value = 'new-password'

      r.get do
        if key = param_or_nil(verify_account_key_param)
          set_session_value(verify_account_session_key, key)
          redirect(r.path)
        end

        if (key = session[verify_account_session_key]) && account_from_verify_account_key(key)
          verify_account_view
        else
          remove_session_value(verify_account_session_key)
          set_redirect_error_flash no_matching_verify_account_key_error_flash
          redirect require_login_redirect
        end
      end

      r.post do
        key = session[verify_account_session_key] || param(verify_account_key_param)
        unless account_from_verify_account_key(key)
          set_redirect_error_status(invalid_key_error_status)
          set_error_reason :invalid_verify_account_key
          set_redirect_error_flash verify_account_error_flash
          redirect verify_account_redirect
        end

        catch_error do
          if verify_account_set_password?
            password = param(password_param)

            if require_password_confirmation? && password != param(password_confirm_param)
              throw_error_reason(:passwords_do_not_match, unmatched_field_error_status, password_param, passwords_do_not_match_message)
            end

            unless password_meets_requirements?(password)
              throw_error_status(invalid_field_error_status, password_param, password_does_not_meet_requirements_message)
            end
          end

          transaction do
            before_verify_account
            verify_account
            if verify_account_set_password?
              set_password(password)
            end
            clear_tokens(:verify_account)
            after_verify_account
          end

          if verify_account_autologin?
            autologin_session('verify_account')
          end

          remove_session_value(verify_account_session_key)
          verify_account_response
        end

        set_error_flash verify_account_error_flash
        verify_account_view
      end
    end

    def require_login_confirmation?
      false
    end

    def allow_resending_verify_account_email?
      account[account_status_column] == account_unverified_status_value
    end

    def remove_verify_account_key
      verify_account_ds.delete
    end

    def verify_account
      update_account(account_status_column=>account_open_status_value) == 1
    end

    def verify_account_email_resend
      if @verify_account_key_value = get_verify_account_key(account_id)
        set_verify_account_email_last_sent
        send_verify_account_email
        true
      end
    end

    def create_account_notice_flash
      verify_account_email_sent_notice_flash
    end

    def new_account(login)
      if account_from_login(login) && allow_resending_verify_account_email?
        set_response_error_reason_status(:already_an_unverified_account_with_this_login, unopen_account_error_status)
        set_error_flash attempt_to_create_unverified_account_error_flash
        return_response resend_verify_account_view
      end
      super
    end

    def account_from_verify_account_key(key)
      @account = _account_from_verify_account_key(key)
    end

    def account_initial_status_value
      account_unverified_status_value
    end

    def verify_account_email_link
      token_link(verify_account_route, verify_account_key_param, verify_account_key_value)
    end

    def get_verify_account_key(id)
      verify_account_ds(id).get(verify_account_key_column)
    end

    def skip_status_checks?
      false
    end

    def create_account_autologin?
      false
    end

    def create_account_set_password?
      return false if verify_account_set_password?
      super
    end

    def set_verify_account_email_last_sent
       verify_account_ds.update(verify_account_email_last_sent_column=>Sequel::CURRENT_TIMESTAMP) if verify_account_email_last_sent_column
    end

    def get_verify_account_email_last_sent
      if column = verify_account_email_last_sent_column
        if ts = verify_account_ds.get(column)
          convert_timestamp(ts)
        end
      end
    end

    def setup_account_verification
      generate_verify_account_key_value
      create_verify_account_key
      send_verify_account_email
    end

    def verify_account_email_recently_sent?
      account && (email_last_sent = get_verify_account_email_last_sent) && (Time.now - email_last_sent < verify_account_skip_resend_email_within)
    end

    def clear_tokens(reason)
      super
      remove_verify_account_key
    end

    private

    def _login_form_footer_links
      links = super
      if !param_or_nil(login_param) || ((account || account_from_login(login_param_value)) && allow_resending_verify_account_email?)
        links << [30, verify_account_resend_path, verify_account_resend_link_text]
      end
      links
    end

    attr_reader :verify_account_key_value

    def before_login_attempt
      unless open_account?
        set_response_error_reason_status(:unverified_account, unopen_account_error_status)
        set_error_flash attempt_to_login_to_unverified_account_error_flash
        return_response resend_verify_account_view
      end
      super
    end

    def after_create_account
      setup_account_verification
      super
    end

    def verify_account_check_already_logged_in
      check_already_logged_in
    end

    def generate_verify_account_key_value
      @verify_account_key_value = random_key
    end

    def create_verify_account_key
      ds = verify_account_ds
      transaction do
        if ds.empty?
          if e = raised_uniqueness_violation{ds.insert(verify_account_key_insert_hash)}
            # If inserting into the verify account table causes a violation, we can pull the 
            # key from the verify account table, or reraise.
            raise e unless @verify_account_key_value = get_verify_account_key(account_id)
          end
        end
      end
    end

    def verify_account_key_insert_hash
      {verify_account_id_column=>account_id, verify_account_key_column=>verify_account_key_value}
    end

    def verify_account_ds(id=account_id)
      db[verify_account_table].where(verify_account_id_column=>id)
    end

    def _account_from_verify_account_key(token)
      account_from_key(token, account_unverified_status_value){|id| get_verify_account_key(id)}
    end
  end
end