lib/rodauth/rails/feature.rb in rodauth-rails-0.5.0 vs lib/rodauth/rails/feature.rb in rodauth-rails-0.6.0

- old
+ new

@@ -7,14 +7,15 @@ :rails_render, :rails_csrf_tag, :rails_csrf_param, :rails_csrf_token, :rails_check_csrf!, - :rails_controller_instance, :rails_controller, ) + auth_cached_method :rails_controller_instance + # Renders templates with layout. First tries to render a user-defined # template, otherwise falls back to Rodauth's template. def view(page, *) rails_render(action: page.tr("-", "_"), layout: true) || rails_render(html: super.html_safe, layout: true) @@ -26,32 +27,85 @@ rails_render(partial: page.tr("-", "_"), layout: false) || rails_render(action: page.tr("-", "_"), layout: false) || super end + # Render Rails CSRF tags in Rodauth templates. + def csrf_tag(*) + rails_csrf_tag + end + # Verify Rails' authenticity token. def check_csrf rails_check_csrf! end # Have Rodauth call #check_csrf automatically. def check_csrf? true end - # Render Rails CSRF tags in Rodauth templates. - def csrf_tag(*) - rails_csrf_tag - end - # Default the flash error key to Rails' default :alert. def flash_error_key :alert end private + # Runs controller callbacks and rescue handlers around Rodauth actions. + def _around_rodauth(&block) + result = nil + + rails_controller_rescue do + rails_controller_callbacks do + result = catch(:halt) { super(&block) } + end + end + + if rails_controller_instance.performed? + rails_controller_response + else + result[1].merge!(rails_controller_instance.response.headers) + throw :halt, result + end + end + + # Runs any #(before|around|after)_action controller callbacks. + def rails_controller_callbacks + # don't verify CSRF token as part of callbacks, Rodauth will do that + rails_controller_instance.allow_forgery_protection = false + + rails_controller_instance.run_callbacks(:process_action) do + # turn the setting back to default so that form tags generate CSRF tags + rails_controller_instance.allow_forgery_protection = rails_controller.allow_forgery_protection + + yield + end + end + + # Runs any registered #rescue_from controller handlers. + def rails_controller_rescue + yield + rescue Exception => exception + rails_controller_instance.rescue_with_handler(exception) || raise + + unless rails_controller_instance.performed? + raise Rodauth::Rails::Error, "rescue_from handler didn't write any response" + end + end + + # Returns Roda response from controller response if set. + def rails_controller_response + controller_response = rails_controller_instance.response + + response.status = controller_response.status + response.headers.merge! controller_response.headers + response.write controller_response.body + + request.halt + end + # Create emails with ActionMailer which uses configured delivery method. def create_email_to(to, subject, body) Mailer.create_email(to: to, from: email_from, subject: "#{email_subject_prefix}#{subject}", body: body) end @@ -62,17 +116,20 @@ # Calls the Rails renderer, returning nil if a template is missing. def rails_render(*args) return if only_json? - begin - rails_controller_instance.render_to_string(*args) - rescue ActionView::MissingTemplate - nil - end + rails_controller_instance.render_to_string(*args) + rescue ActionView::MissingTemplate + nil end + # Calls the controller to verify the authenticity token. + def rails_check_csrf! + rails_controller_instance.send(:verify_authenticity_token) + end + # Hidden tag with Rails CSRF token inserted into Rodauth templates. def rails_csrf_tag %(<input type="hidden" name="#{rails_csrf_param}" value="#{rails_csrf_token}">) end @@ -84,16 +141,11 @@ # The Rails CSRF token value inserted into Rodauth templates. def rails_csrf_token rails_controller_instance.send(:form_authenticity_token) end - # Calls the controller to verify the authenticity token. - def rails_check_csrf! - rails_controller_instance.send(:verify_authenticity_token) - end - # Instances of the configured controller with current request's env hash. - def rails_controller_instance + def _rails_controller_instance request = ActionDispatch::Request.new(scope.env) instance = rails_controller.new if ActionPack.version >= Gem::Version.new("5.0") instance.set_request! request