Using Devise, how do I customize a failed authentication? - ruby-on-rails

I have a controller that uses Devise to authenticate actions. I'd like the default behavior of responding with a 401 on every action except for one. I'd like to proceed with the request but provide a different response body.
What method do I provide or override to accomplish this?

First you will need to skip the standard authentication on the page you want to do this on..
before_filter :authenticate_user!, except: [:mydifferentcontroller]
then you will need add some logic for the controller to bring you to your alternate response
def mydifferentcontroller
unless user_signed_in?
## add redirect_to if you want to send them to an entirely different page or whatever change in logic can go in here
end
end
Or if you just want to change a certain part of the page you can use the user_signed_in? in the view as well
- if user_signed_in?
.classyclass You're signed in
- else
.classyclass You're not signed in
if that's what you meant....

Related

Accessing Doorkeeper Application Information before login redirect

Explanation
I am wanting to halt the authorization process of a client app (running OAuth2) coming to the parent app (running Doorkeeper) in order to see which client app is requesting a login. That way I can then look up the clientID and dynamically build a custom login screen for the client app. Right now, my client goes to parent, AuthorizationController is called, but before new is called and I can get the params[:client_id], authenticate_resource_owner! is called with a before_action. That then sends the user to the login page if they are not already logged in with the parent. So, before I can get the param, it is being redirected.
Question
The authenticate_resource_owner! is held in a Doorkeeper helper file. I thought that I set it up correctly to bypass the default helper and go to mine where I can try and grab the param and save in sessions before the redirect, but I guess my route is not set up correctly and I can't find any documentation on how to correctly call it. Can anyone help?
Code
Code for setting up the client:
def setup_client
#client = Application.find_by(uid: params[:client_id])
session[:client_name] = #client.name
authenticate_resource_owner!
end
I know that the first 2 lines work as I placed them in the CustomAuthorizationsController with a byebug and it triggered after the login and before redirect back to client and showed the client name stored in a session variable.
In my config/routes.rb
use_doorkeeper do
controllers :applications => 'doorkeeper/custom_applications'
controllers :authorizations => 'doorkeeper/custom_authorizations'
helpers :doorkeeper => 'doorkeeper/doorkeeper'
end
Helper file is located in app/helpers/doorkeeper/doorkeeper_helper.rb
Error
When I start up my server I get:
: from ~/ruby-2.5.0/gems/doorkeeper-5.0.2/lib/doorkeeper/rails/routes/mapper.rb:12:in `instance_eval'
~/settingsParentApp/config/routes.rb:65:in `block (2 levels) in <top (required)>': undefined method `helpers' for #<Doorkeeper::Rails::Routes::Mapper:0x00007ffd539b9c10> (NoMethodError)
Conclusion
Am I even doing this right? Is there a simpler way built into Doorkeeper that I am not seeing to get this information to customize the login screen? Or is there some error that I am not seeing in how I am calling the helper file?
After thinking through my problem in order to ask this question, a solution dawned on me. I tested it out and it worked. I forgot that in a controller, the before_action statements are called in the order they are presented. So, my solution was just to reorder my statements to call the setup_client first before the authenticate_resource_owner!. This set up the session variable before redirecting to the login screen and then allowed me to have the variable available for use.
Code
Within my config/routes.rb file:
use_doorkeeper do
controllers :applications => 'doorkeeper/custom_applications'
controllers :authorizations => 'doorkeeper/custom_authorizations'
end
This custom route bypasses the doorkeeper default authorization controller and goes to a custom one which inherits from the default controller. So, all I need within this custom one is this code:
Found: app/controllers/doorkeeper/custom_authorizations_controller.rb
module Doorkeeper
class CustomAuthorizationsController < Doorkeeper::AuthorizationsController
before_action :setup_client
before_action :authenticate_resource_owner!
def setup_client
#client = Application.find_by(uid: params[:client_id])
session[:client_name] = #client.name
end
end
end
This code is then run before it looks to the Doorkeeper's default AuthorizationsController and thus calls setup_client first. The session variable is then saved and in the login screen I can call it this way:
<%
if session[:client_name].nil?
#client_name = ''
else
#client_name = ' for ' + session[:client_name]
end
#page_name = "Login" + #client_name
%>
And then in header of the page I call this within the HTML:
<h1><%= #page_name %></h1>
I may do more fancy things later, like saving client icons/logos and color schemes to make branding specific on the login page, but for now, this basic issue has been resolved. Thank you all for acting as my sounding board and problem-solving ducks... if you know of that reference. :-) Happy Coding!

Rails - Edit data on front-end like in Linkedin's profiles pages

Is it possible, in a RoR web application, to allow users to edit elements which are in a Show page?
The target would be something like on Linkedin, when you edit your own profile page (moving your mouse over a field gives you the ability to edit it). How do they manage to do that? Is it on a Show page or an Edit page? What kind of front-end technology do we need?
I'm not a big fan of the traditional 'Edit.html' vs 'Show.html'.
Many thanks! :)
Yes you can use your show page as an edit page. You could set up your controller to something comparable to this:
your_controller.rb
class YourController < Application Controller
before_filter :show_user
def show
render :edit
end
def edit; end
private
def show_user
#user = current_user
end
end
Also don't forget your 'update' method within this controller and params that you are passing. Then you can create your edit.html view that acts as a show page, but allows edits to take place. As far as editing comparable to LinkedIn, you can use the 'best_in_place' gem for inline editing. Found here: https://github.com/bernat/best_in_place

Allow AJAX Through Devise (Rails)

I've just added and configured the devise gem. It's working great except for blocking my form's autosave AJAX calls.
At the top of my controller, I have:
before_filter :authenticate_user!
My AJAX call comes to the same controller:
def autosave
#TODO: update relative entry
#TODO: verify user logged in
#TODO: verify entry belongs to relative user
render content_type: 'text/xml', inline: "<result status='ok' />"
end
Of course I could instead declare my before filter like so:
before_filter :authenticate_user!, except: :autosave
..but this offers nothing to prevent anyone from calling this controller function at any time.
What would be the best way to allow this function to be called? I still want to ensure that only logged in users can make the call and that the given record they're editing belongs to them.
Not entirely sure what you're trying to do, but it sounds like you just need to add a bit of logic to your autosave. With this setup the function will still be called per se, but whether or not it does anything is a another mater entirely unless the conditions are right.
def autosave
#Verify that the user is signed in and he has ownership of entry
if !current_user.nil? && entry.user == current_user
render content_type: 'text/xml', inline: "<result status='ok' />"
else
#do nothing nothing or redirect with error here
end
end
Since you're making an AJAX post you have to provide some additional information to your ajax call as a security countermeasure, namely a CSRF security token. See Devise not setting current_user on Ajax post even though correct x-csrf-token is included in request header for an example AJAX request using a security token. Also, make sure you also include <%= csrf_meta_tag %> in your head tag.

How to setup routes when the controller only has edit and update?

I can't seem to figure out how to get my routes setup properly.
In my app, I have a view that lets site owners update their address information. The new and create actions are part of the signup process and are located in the signups_controller. The edit and update actions are in the settings_controller.
When the user goes into the settings area, he/she sees only the edit form. When filled out, the user is then returned to the same form with a flash message, or error message. Here is what the controller looks like:
class SettingsController < ApplicationController
def edit
#account = current_account
#account.companies.first
#account.companies.first.addresses.first
#account.companies.first.phones.first
end
def update
#account = current_account
if #account.update_attributes(params[:account])
redirect_to edit_setting_path
flash[:notice] = "Success!"
else
render :edit
end
end
end
In my routes, I simply have:
resources :settings
The link to this area of the site is a basic RESTful named linke, with the parameter options:
edit_setting_path(:id => current_account.id)
When the user arrives to this page, they see the following URL:
http://domainname.com/settings/1/edit
When they submit the form and get errors, the URL changes to:
http://domainname.com/settings/1
Why is the URL changing -- I'd rather it not? Is there a way to make it stay the same as the initial edit view? I've tried doing a redirect on a failed update, but then I don't get the error messages.
Any ideas?
To answer your "why" question: The URL is changing because it's reflecting the URL of the failed request - which in this case is a PUT request to that URL (/settings/1). You've submitted the form and the submission of that form (correctly) points to that URL. This is a result of the RESTful routes that the helper gives you. Since the logic in your action, falls through to the render :action, there is no redirect and the form simply re-renders on the page using the same data available in this action (which is why you can see the errors).
If you want to redirect back to the edit page, yes, you will lose the errors that have been set in the #account instance variable since the redirect will reset (re-query for) the account.
You could add a route that matches a PUT to /settings/1/edit and point it to your update action and change your form etc. In short, I wouldn't recommend this, but it should work.
completely untested but attemptable:
routes.rb
put "/settings/:id/edit", :to=>"settings#update", :as=>"update_setting"
resources :settings, :except=>:update
your form would also have to submit to the update_setting_path (which also means it's not reusable for a new object... ew)
First you should read up on The Rails Guides for Routing. They will help a lot to understand why its working like that.
Secondly, to accomplish what you are trying to do, you will need to add manual routes via the match call. You'll need something like this.
match '/settings/:id/edit' => "settings#edit"

How to go 'back' 2 levels?

From the list view of my app, I can view a list of records or drill down and edit/update a record. After updating, I want to go directly back to the list view, bypassing a couple of intermediate pages - but I don't simply want to link_to(:action => list) - there's pagination involved. I want to go back to the exact 'list' page I came from. What's the best way? Pass a hidden arg somewhere with the page number? Is there an elegant way to accomplish this?
I'm just going to throw this one out there with the disclaimer that there may be security considerations or existing gems.
On your edit action, you could store the previous page in a session. Then in your update action, redirect to it.
class MyController < ApplicationController
def edit
session[:prev_url] = request.referer
end
def update
redirect_to session[:prev_url]
end
end
As an alternative to use the session, you could carry the referer through the actions using a hidden form field.
class MyController < ApplicationController
def edit
#prev_url = request.referer
end
def update
redirect_to params[:prev_url]
end
end
Form using hidden_field:
f.hidden_field :prev_url, :value => #prev_url
If you do not want to carry along the whole referer url you could also do the same with the page parameter instead and append the parameter to the url in the update action. I would also expect Rails' url helpers to accept parameters.

Resources