File: json.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 (242 lines) | stat: -rw-r--r-- 7,592 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
# frozen-string-literal: true

module Rodauth
  Feature.define(:json, :Json) do
    translatable_method :json_not_accepted_error_message, 'Unsupported Accept header. Must accept "application/json" or compatible content type'
    translatable_method :json_non_post_error_message, 'non-POST method used in JSON API'
    auth_value_method :json_accept_regexp, /(?:(?:\*|\bapplication)\/\*|\bapplication\/(?:vnd\.api\+)?json\b)/i
    auth_value_method :json_check_accept?, true
    auth_value_method :json_request_content_type_regexp, /\bapplication\/(?:vnd\.api\+)?json\b/i
    auth_value_method :json_response_content_type, 'application/json'
    auth_value_method :json_response_custom_error_status?, true
    auth_value_method :json_response_error_status, 400
    auth_value_method :json_response_error_key, "error"
    auth_value_method :json_response_field_error_key, "field-error"
    auth_value_method :json_response_success_key, "success"
    translatable_method :non_json_request_error_message, 'Only JSON format requests are allowed'

    auth_value_methods(
      :only_json?,
      :use_json?,
    )

    auth_methods(
      :json_request?,
      :json_response_error?
    )

    auth_private_methods :json_response_body

    def set_field_error(field, message)
      return super unless use_json?
      json_response[json_response_field_error_key] = [field, message]
    end

    def set_error_flash(message)
      return super unless use_json?
      json_response[json_response_error_key] = message
    end

    def set_redirect_error_flash(message)
      return super unless use_json?
      json_response[json_response_error_key] = message
    end

    def set_notice_flash(message)
      return super unless use_json?
      json_response[json_response_success_key] = message if include_success_messages?
    end

    def set_notice_now_flash(message)
      return super unless use_json?
      json_response[json_response_success_key] = message if include_success_messages?
    end

    def json_request?
      return @json_request if defined?(@json_request)
      @json_request = request.content_type =~ json_request_content_type_regexp
    end

    def use_json?
      json_request? || only_json?
    end

    def view(page, title)
      return super unless use_json?
      return_json_response
    end

    def json_response_error?
      !!json_response[json_response_error_key]
    end

    private

    def check_csrf?
      return false if use_json?
      super
    end

    def _set_otp_unlock_info
      if use_json?
        json_response[:num_successes] = otp_unlock_num_successes
        json_response[:required_successes] = otp_unlock_auths_required
        json_response[:next_attempt_after] = otp_unlock_next_auth_attempt_after.to_i
      end
    end

    def after_otp_unlock_auth_success
      super if defined?(super)
      if otp_locked_out?
        _set_otp_unlock_info
        json_response[:deadline] = otp_unlock_deadline.to_i
      end
    end

    def after_otp_unlock_auth_failure
      super if defined?(super)
      _set_otp_unlock_info
    end

    def after_otp_unlock_not_yet_available
      super if defined?(super)
      _set_otp_unlock_info
    end

    def before_two_factor_manage_route
      super if defined?(super)
      if use_json?
        json_response[:setup_links] = two_factor_setup_links.sort.map{|_,link| link}
        json_response[:remove_links] = two_factor_remove_links.sort.map{|_,link| link}
        json_response[json_response_success_key] ||= "" if include_success_messages?
        return_json_response
      end
    end

    def before_two_factor_auth_route
      super if defined?(super)
      if use_json?
        json_response[:auth_links] = two_factor_auth_links.sort.map{|_,link| link}
        json_response[json_response_success_key] ||= "" if include_success_messages?
        return_json_response
      end
    end

    def before_view_recovery_codes
      super if defined?(super)
      if use_json?
        json_response[:codes] = recovery_codes
        json_response[json_response_success_key] ||= "" if include_success_messages?
      end
    end

    def before_webauthn_setup_route
      super if defined?(super)
      if use_json? && !param_or_nil(webauthn_setup_param)
        cred = new_webauthn_credential
        json_response[webauthn_setup_param] = cred.as_json
        json_response[webauthn_setup_challenge_param] = cred.challenge
        json_response[webauthn_setup_challenge_hmac_param] = compute_hmac(cred.challenge)
      end
    end

    def before_webauthn_auth_route
      super if defined?(super)
      if use_json? && !param_or_nil(webauthn_auth_param)
        cred = webauthn_credential_options_for_get
        json_response[webauthn_auth_param] = cred.as_json
        json_response[webauthn_auth_challenge_param] = cred.challenge
        json_response[webauthn_auth_challenge_hmac_param] = compute_hmac(cred.challenge)
      end
    end

    def before_webauthn_login_route
      super if defined?(super)
      if use_json? && !param_or_nil(webauthn_auth_param) && webauthn_login_options?
        cred = webauthn_credential_options_for_get
        json_response[webauthn_auth_param] = cred.as_json
        json_response[webauthn_auth_challenge_param] = cred.challenge
        json_response[webauthn_auth_challenge_hmac_param] = compute_hmac(cred.challenge)
      end
    end

    def before_webauthn_remove_route
      super if defined?(super)
      if use_json? && !param_or_nil(webauthn_remove_param)
        json_response[webauthn_remove_param] = account_webauthn_usage
      end
    end

    def before_otp_setup_route
      super if defined?(super)
      if use_json? && otp_keys_use_hmac? && !param_or_nil(otp_setup_raw_param)
        _otp_tmp_key(otp_new_secret)
        json_response[otp_setup_param] = otp_user_key
        json_response[otp_setup_raw_param] = otp_key
      end
    end

    def before_rodauth
      if json_request?
        if json_check_accept? && (accept = request.env['HTTP_ACCEPT']) && accept !~ json_accept_regexp
          response.status = 406
          json_response[json_response_error_key] = json_not_accepted_error_message
          _return_json_response
        end

        unless request.post?
          response.status = 405
          set_response_header('allow', 'POST')
          json_response[json_response_error_key] = json_non_post_error_message
          return_json_response
        end
      elsif only_json?
        response.status = json_response_error_status
        return_response non_json_request_error_message
      end

      super
    end

    def redirect(_)
      return super unless use_json?
      return_json_response
    end

    def return_json_response
      _return_json_response
    end

    def _return_json_response
      response.status ||= json_response_error_status if json_response_error?
      response.headers[convert_response_header_key('content-type')] ||= json_response_content_type
      return_response _json_response_body(json_response)
    end

    def include_success_messages?
      !json_response_success_key.nil?
    end

    def _json_response_body(hash)
      request.send(:convert_to_json, hash)
    end

    def json_response
      @json_response ||= {}
    end

    def set_redirect_error_status(status)
      if use_json? && json_response_custom_error_status?
        response.status = status
      end
    end

    def set_response_error_status(status)
      if use_json? && !json_response_custom_error_status?
        status = json_response_error_status
      end

      super
    end
  end
end