Edit Update actions in Rails? - ruby-on-rails

I have been through M. Hartl's book on ROR Tutorial and in there for the Edit and Update action for the user the book uses "#user = User.find(params[:id])" to find the user and then for authorization purposes adds a "correct_user" method which checks if #user is the current user. The thing that is baffling me is that why can't I just find the user to begin with using "#user = current_user" in the edit and update actions so that I don't have to worry about users passing in other User ID's through the URL. Does this approach I am thinking of leave any security loopholes? I am using Devise so I already have a current_user method handy.

One potential problem with that approach is that it might be a user with special privileges (admin, moderator, etc.) trying to edit another user that they are allowed to update. Best to stick with fetching from the database. Also, if rails has already fetched the user in the current request cycle, the object should be cached, so it shouldn't actually do a second database request anyways.

Related

Tips on Performing Rails User Authorization

I am new to Ruby on Rails development. Currently, I am creating a web app where users can log in, create, and manipulate their own "campaigns" (database objects) that are then displayed on a dashboard. I am using the devise gem, but at best it filters the database objects without actually using any permissions. I need to make sure that the database objects that appear on the dashboard are specific to only the current user that is logged in. What would be a good solution for displaying the campaigns of only the logged in user on the dashboard, and making sure that the user can't access/see anyone else's objects on the dashboard.
It sound like you need a before_filter on your controller. I don't use devise, but just google "devise before action" and you will find many links like this one that might be helpful. On another note, here is an excellent tutorial that shows how to create your own authentication system. I recommend doing it twice. The rails guides are also great.
Update:
Try this in your contoller
def index
#user = User.find(params[:id])
#campaigns = #user.campaigns.all
end

Devise: instead of creating a new user when signing up, convert an existing guest user into a registered one?

When a guest comes to my website, he is automatically logged in as a User.create guest: true. This way he can easily write comments, etc., and doesn't have to decide to sign up at the very beginning.
If he likes to, he should be able then to sign up later. And here's my question: instead of signing up as a new user, I'd like to convert the existing guest user into a registered user.
Is there an easy way to tell devise to use an existing user? Another way would be to move all the created comments, etc. to the newly created user, but this seems a bit tedious. Or I could remove the guest user and change the newly created user's ID to the deleted one's. In both cases I have to be very careful about dependent: :destroy relations, so it seems rather a bad solution.
Any idea on how to convert the existing user is highly appreciated.
Assuming this is what you mean by guest user then that's just a regular User missing a few fields and with no validations run on it.
When creating a User from an existing guest user just:
def edit
#user = User.find(params[:id])
end
def update
user = User.find(params[:id])
user.update_attributes(params[:user])
end
edit.
You're literally just dealing with a CRUDy user update action. Have your Sign Up anchor tag direct to the edit page for that specific guest_user ala: <%=link_to "Sign Up", edit_user_path(guest_user)%>
I've updated the code example. Assuming you're using a scaffoldish setup, those methods should be in users_controller. You'll want some checks to make sure you handle missing params or errors.
I think you are tricking yourself into thinking you are dealing with a super special case. Guest User is just a User missing some information. Registration just means adding that information to the existing User instance.

Keeping POST data during sign up with Devise

I might be approaching this problem the wrong way ... so if you have a more elegant solution I'm all ears.
Imagine I'm making a system like Kickstarter. I want my users to be able to specify how much they want to pledge before I ask them to sign up.
Then, if they're not registered I need them to sign up before putting them back in the flow that they would have been on had they just signed in. Devise makes this easy by redirecting a user back to the after_sign_up_path_for which ends up being after_sign_in_path_for by default.
So this will always issue a GET request. But if I have data that I received from the POST with the amount they wanted to pledge, but that's lost.
Is the only way to do this to store that posted data in the session? Or is there a clever way to start creating the pledge record without the user (without needing to run jobs to destroy orphaned pledge records)?
I found the approach described in this blog post over at highgroove.com quite interesting in this regard: 
http://highgroove.com/articles/2012/10/09/lazy-user-registration-for-rails-apps.html
The basic idea is to always have an anonymous user at hand, even if the current vistor is not registered. Like this you can create e.g. associations as usual and — once the visitor actually does sign–up — you edit the user rather than all associated objects.
If the user does not ever register, you can simply look for abandoned user accounts and delete them including their associations, rather than look for all kind of abandoned models.

Lazy registration with RESTful routing in Rails

I'm stuck figuring out the best practice...
I want to create a "following" system in a way that a user can follow a car (getting email updates when car price changes, etc). The part of implementation that's giving me headaches is when I want to introduce lazy registration by only using email.
Everything needs to work as AJAX requests.
In the interface, there will be a button to trigger the follow action, which will check if the user is registered or not. If a user is logged in, create a new CarSubscription item, otherwise display a form where he could type his email address. Once submitted, the form should create a user with no password (if email exists, ask for the password and log in) and then it should create the relationship item.
The challenge here is to use redirection after submission of the form to the CREATE action of the CarSubscriptionController. Since I can't redirect using POST I can't simulate the CREATE or DESTROY action.
The non-RESTful solution would be to create 2 actions under cars_controller: follow and unfollow and let them do the logic of creating entries and deleting them. That would enable me to just store the request path and use it after the user enters their email and logs in.
How can I achieve what I want using RESTful resources?
After trying to describe my problem here, it seems it's way too complicated and I am indeed very stuck... There are 3 different controllers and possibly 4 requests in this scenario.
Any help would be tremendously appreciated!
Please see my flow chart below:
Not an expert here, I don't know if it's the best solution, but what I have done in similar situation is :
In your controller, respond with javascript instead of redirecting the user
In your javascript file, use $.post(...) to issue a POST to your controller action
Et voilà!
You can also use ActiveResource to achieve this, but I actually never tried that solution : http://api.rubyonrails.org/classes/ActiveResource/Base.html#label-Custom+REST+methods
Person.new(:name => 'Ryan').post(:register)
Hope this helps
I had a very similar need and had trouble pulling the various bits of info on how to do this with Devise and Rails together into a working example. Here's a fully working example based on Rails 4, Ruby 2, and Devise 3.0:
https://github.com/mwlang/lazy_registration_demos

How would one implement this sort of gradual engagement/lazy registration in Rails?

A long time ago I ran into a website (I unfortunately lost the address, it was some kind of newspaper site) that allowed you to make use of everything as if you were a registered user. You could rate, favorite and comment articles, and when you did it would display a discreet, embedded message saying you had to register to the website for your contributions to be saved. Then it had the link for you to see how your profile would look like if you did, and I was surprised to see it had all my activity there; the articles I read and saved, comments, etc. I left the site and when I came back to it later just out of curiosity, it still had my activity saved.
I thought it was the greatest thing ever, and now that I am in the process of building a website with social features, I would like to take that approach as well. But I am still pretty much a noob and so I don't have much clue as to how to go about it. How would you do it?
I would create a Profile model which is automatically created for any user that visits your site and adds the first favourite, rates the first item, etc. The Profile should be saved to your database including a suitably random and unique string. This string can be stored as a cookie on the client side, and will be used later to retrieve your profile. It should be random and long enough so that you cannot easily tamper with your cookie and get other anonymous people's profiles, but this is not entirely avoidable (so beware you store no sensitive data in anonymous profiles!).
Once a user registers, you can associate their Profile with their new User record and remove the cookie and the unique string identifier. You can now simply retrieve their profiles when they log in, based on their User record.
The Profile model can contain any information you would like to store.
If you want to differentiate between registered users and anonymous users, you could create an AnonymousProfile model and a Profile model (each with different attributes), and simply copy over all data from the anonymous profile to the user profile when someone registers.
Update:
Throughout your application you can decide to only use this information when a user is logged in. You might define a before_filter that grabs the current user, and only if there is an actual user logged in, do you use the profile data:
class ApplicationController < ActionController::Base
before_filter :fetch_user_data
def fetch_user_data
#current_user = ... # Work your magic to get current user
end
private
def current_profile
#current_user and #current_user.profile # Use profile association
end
end
Somewhere in a controller action:
if current_profile
# Do stuff with current_profile
# Only available to registered users...
end
You can later change the implementation of current_profile if you change your mind and want anonymous profiles to have effect for your anonymous users.
The only way to identify users is to use cookies. What the site you were using is likely doing is:
For a first time user create an entry in the 'users' table and add them to the 'guests' group. Save down an identifier cookie to the users machine so you can look them up again later.
If the user decides to register you can fill out the rest of their details in the user table (you might even want to have a separate table for user details and registration details like username/password etc...) and add them to the registered users group.
The way you manage your groups can be as simple as a flag in the database.
As this is a rails question...
I would probably handle most of this in a before_filter in your application_controller.rb. The steps would be something like:
if has_cookie
#user = lookup_user
else
#user = create_new_guest_user
end
You could very easily extend one of the existing authentication frameworks like acts_as_authenticated or clearance to do this.

Resources