Authenticating a User In a Separate Controller with Restful_authentication - ruby-on-rails

I am trying to make it possible for users to login as quick as possible, so I want users to be able to login and create records in the same form.
Is it possible to authenticate a user with the restful_authentication plugin from any controller by somehow calling the create method in the session controller, and return the authenticated user? It seems like this could be done easily somehow, but I just can't figure out how to do it in Rails.
Maybe something like:
#Records Controller
def create
if params[:login] && params[:password]
#This method would call /session/ and pass the login/password params
user = authenticate_user(params[:login'], params[:password])
end
#record = Record.new(params[:record])
#record.user = user
if #question.save && user
flash[:notice] = 'Record was successfully created.'
redirect_to(#record)
end
end
Any ideas on how to do this would be appreciated!

I've tested this code on Rails 2.3.4 and it works; the user remains logged in. Bear in mind that you should try to refactor so that the authentication code lives in a single place, rather than having it duplicated in several controllers.
Note also that the authentication code in this snippet is a simplified version of that in the Sessions controller, & so doesn't handle any of the 'remember me' functionality.
# POST /stacks
# POST /stacks.xml
def create
#stack = Stack.new(params[:stack])
if params[:login] && params[:password]
logout_keeping_session!
user = User.authenticate(params[:login], params[:password])
self.current_user = user
end
respond_to do |format|
if !user
flash[:error] = 'Login details incorrect.'
format.html { render :action => "new" }
format.xml { render :xml => #stack.errors, :status => :unprocessable_entity }
elsif #stack.save
flash[:notice] = 'Stack was successfully created.'
format.html { redirect_to(#stack) }
format.xml { render :xml => #stack, :status => :created, :location => #stack }
else
format.html { render :action => "new" }
format.xml { render :xml => #stack.errors, :status => :unprocessable_entity }
end
end
end

Related

rails devise sign_in doesn't work on redirect

I have this method:
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
if params[:mypmnode]
session[:return_to] = projects_pmnode_path(params[:mypmnode])
sign_in(#user)
end
format.html { redirect_to(session[:return_to], :notice => 'User was successfully updated.') }
format.xml { head :ok }
else
#create_company = true if params[:user][:company_id].blank? and params[:user][:company_attributes].length > 0
#create_department = true if params[:user][:department_id].blank? and params[:user][:department_attributes].length > 0
format.html { render :action => "edit" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
The idea is that if the user is updated, He is automatically signed-in and redirected to a page where authentication is required.
In this page, I have: before_filter :authenticate_user!
This doesn't work on redirect.
If I then go to another page making use of this sign_in function, then the user logs-in correctly.
Any idea why redirect doesn't work? Thx!
UPDATE:
to make it clearer, I insert the second page code (controller):
class PmnodesController < Projects::BaseController
before_filter authenticate_user!
def index
#pmnodes = Pmnode.all
respond_to do |format|
format.html
end
end
If the password is updated on #user, devise will invalidate the session. After the update_attributes, you could try calling sign_out first.
sign_out(#user)
sign_in(#user)
Are you sure that your progam goes inside this blog
if params[:mypmnode]
session[:return_to] = projects_pmnode_path(params[:mypmnode])
sign_in(#user)
end
if not this should sign in your use automatically.
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
if params[:mypmnode]
session[:return_to] = projects_pmnode_path(params[:mypmnode])
end
sign_in(#user)
format.html { redirect_to(session[:return_to], :notice => 'User was successfully updated.') }
format.xml { head :ok }
else
#create_company = true if params[:user][:company_id].blank? and params[:user][:company_attributes].length > 0
#create_department = true if params[:user][:department_id].blank? and params[:user][:department_attributes].length > 0
format.html { render :action => "edit" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
I had a similar problem:
I had a controller method that created and signed in a user
def new
#user = User.create!
sign_in #user
redirect_to some_nondefault_path
end
where some_nondefault_path required authentication. The new action did not require authentication. The user was getting created and signed in, but the user session wasn't persisting and the user was getting 401-unauthorized and redirected to the signin page instead of some_nondefault_path.
I ended up solving it by adding
skip_before_filter :verify_authenticity_token, :only => :new
to the first controller. It seemed to be trying to verify the CSRF token before creating the user session, which was failing and blocking the creation of a normal user session (even though it wasn't trying to authenticate_user!).
Hope this helps!

How to handle update of a single user model data separately: user info, password, extra information

I want to present users with separate pages/dialogs for editing their own information. However, the information is held in a single model (called User). Now I'm trying to find the best approach for handling the update calls from partials. My code currently:
def edit
render :layout=>!request.xhr?
end
def edit_password
render :layout=>!request.xhr?
end
def edit_extra
unless #user.extra
#user.build_extra
#user.extra.value = 2047
end
render :layout=>!request.xhr?
end
def update
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to #user, :notice => 'User was successfully updated.' }
format.json { head :no_content }
else
format.html { render :action => "edit", :layout=>!request.xhr? }
end
end
end
The thing is, all forms in methods (edit, edit_password and edit_extra) call the update method. However, there are two problems:
If the data parsing isn't validated, user is presented with the "edit" form, which is incorrect.
I want to have a password confirmation on extra data. User shouldn't be able to edit that information unless they supply a correct password.
I would like to make more generalized solution than just duplicating the update -code. The largest problem is rendering correct layout (edit, edit_password) based on the current action.
For now, I solved the problem by creating separate edit_section parameter that will be handled in update.
def update
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to #user, :notice => (t :actionsuccesful) }
format.json { head :no_content }
else
action = if params[:edit_section] then "edit_" + params[:edit_section] else "edit" end
format.html { render :action => action, :layout=>!request.xhr? }
end
end
end
And in forms (edit_password, etc)
=form_for(#user, :remote => true) do |f|
= hidden_field_tag :edit_section, "password"

ruby on rails redirecting to old param

I have an option for a user to update their username in their profile. However, when the url for their profile has been set as localhost/user/username and when they submit their changes, they are redirected to their old username (not the new updated one).
Here is my update from users_controller.rb
Any suggestions?
def update
#user = User.find_by_username(params[:id])
#page_title = "Edit Profile"
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to(user_url,
:notice => "Your profile has been saved.") }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #user.errors,
:status => :unprocessable_entity }
end
end
end
also, I'm using
def to_param
username
end
Doesn't user_url take #user as an argument? How have you defined your route for that?
One thing I can think of immediately is #user.reload!.

Restricting url manipulation to return other user records

I have just begun Rails 3. I have generated the below code using the scaffold from Rails 3 on a table called "Logs".
The 'index' function below provides only the records associated with the current_user.id (from the session stored in the session table). The users records are only presented with the following route logged in as user = 3 (see index code below)
localhost:3000/logs
Problem: As a user, I can view a record which is not my record (being user=3) by editing the url manually to show any other record:
localhost:3000/logs/5 'this was entered by user.id=2'
Seeking Solution: How do I prevent manually hacking of the url to prevent a user viewing other user records?
class LogsController < ApplicationController
before_filter :login_required
def index
#logs = Log.where(:user_id => current_user)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #logs }
end
Please ignore that the new function is missing from the create function below. The code below is to merely demonstrate how I put the user_id into the "Logs" table
def create
#log = Log.new(params[:log])
#log.user_id = current_user.id
respond_to do |format|
if #log.save
format.html { redirect_to(#log)}
format.xml { render :xml => #log, :status => :created, :location => #log }
else
format.html { render :action => "new" }
format.xml { render :xml => #log.errors, :status => :unprocessable_entity }
end
end
The simplest solution would be to check in the show method if the Log to display really belongs to the logged in user:
def show
#log = Log.find(params[:id])
unless #log.user_id == current_user.id
flash[:error] = "unauthorized"
redirect_to :index
end
end
But you will soon have some more things you want to restrict access to, so you should look for an authentication plugin which allows to define the access rights in a declarative manner. Maybe this one: https://github.com/be9/acl9

Redirect view after saving Rails model

When I:
self.save
or
save!
in a model it automatically redirects me to the show view for that given model. How can I override this? I want to save the model and then go to another action/view in the same controller.
In particular, I have a process where I save a members details, and then I want to continue the process by going to the next page, e.g. payment page, that uses the id of the saved model.
In your controller you might have a block like:
def create
#user = User.new(params[:place])
respond_to do |format|
if #user.save
format.html { redirect_to(#user, :notice => 'User was successfully created.') }
format.xml { render :xml => #user, :status => :created, :location => #user }
else
format.html { render :action => "new" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
You can change the target of the redirect_to (after format.html) from here - at present it is directing you to the record for that user, ie. #user. Take a look at http://api.rubyonrails.org/classes/ActionController/Base.html for a bit more info.
You likely have a block like this in your create/update methods:
respond_to do |format|
if #post.save
format.html { redirect_to(#post, :notice => 'Post was successfully created.') }
format.xml { render :xml => #post, :status => :created, :location => #post }
else
format.html { render :action => "new" }
format.xml { render :xml => #post.errors, :status => :unprocessable_entity }
end
end
So if your instance variable is named #post, and it's redirecting to the show view for the post after it saves, all you have to do is change the "redirect_to(#post, ..." part to whatever you want. Say you wanted to redirect to the root of your site - you could instead have
redirect_to(root_path, :notice => 'Post was successfully created.')
In your particular case, you could use something like this if you have your routes set up:
redirect_to(payment_page_path(#post), :notice => 'Post was successfully created.')
Hope that helps!
if you call save from your Model you will not be directed anywhere, it just does a direct model access save to the database. Your redirections are described in your controller in your create and update actions. you can find a list of routes by running rake routes and then pick the path you want your app to render when you save your model instance. you may have a route called payment_path which might look like this in your controller
map.payment :controller => :payments_controller, :action => index
and you would say in your create action
def create
if #item.save(params[:item])
redirect_to payment_path
else
flash[:error] = "there was a problem"
render :action => buy
end
end
if you need to pass a param, like user id to your route, then you need to include that in the path parameters
redirect_to payment_path(#user) #=> automagically finds the id of active record models

Resources