I'm reading Agile web Development to learn Rails 3.0. The author is teaching us how to use sessions using this create method in the sessions controller.
def create
if user = User.authenticate(params[:name], params[:password])
session[:user_id] = user.id
redirect_to admin_url
else
redirect_to login_url, :alert => "Invalid user/password combination"
end
end
In the line
session[:user_id] = user.id
does the symbol :user_id exist somewhere before he assigns user.id to it? or is this symbol :user_id created at the moment that he assigns user.id to it? Is there a set number of symbols that belong to this 'session' or can you basically just create something with any name and assign anything to it?
It is created dynamically on the fly. You can see what is in the session by using the debugger and typing p session. If you are only storing string data, then you can pretty must store whatever you want, though there are size limitations. The default session is stored in a cookie, though you can also set your SessionStore to be on the server-side as well.
I'm not sure if you can change the name using a config file, but the session hash is a special variable that Rails uses. So you should stick to doing session[:my_var] = "whatever'
Related
I am new to rails and have a task that asks me to send an invitation for any user to be admin in my magazine here is my piece of code
def invite
inviteUser = { 'user_id' => current_user.id, 'Magazine_id' => params[:id] }
CollaborationInvitation.create(inviteUser)
#magazine = Magazine.find(params[:id])
redirect_to :back
rescue ActionController::RedirectBackError
redirect_to root_path
end
I need to replace current_user.id with something that refers to any user's id which exists in my database to send him an invitation to be admin with me I tried to add #User=Users.All and then pass it as a variable but it got me an error I tried a lot of things but every time I get an error except for adding current_user.id
ps: I am using devise for authentication
You asked a couple things, and it is kind of confusing what you want to do.
Here is how you get all ids of records in a model.
Rails4: User.ids
Rails3: User.all.map(&:id)
Or (not sure if #pluck is in Rails 3 or not)
User.pluck(:id)
If you want to get a random user (you mentioned "any user") you could do.
User.find(User.pluck(:id).sample)
Though I think what you really want to do is to pass the id or some other attribute of a user as a param to the action and send that user an invitation.
Presumably you either have a post or get route for "users#invite" (the action you wrote in your question). You can add a named parameter there or you can pass a url param or if you are using a post route, you could add the param to the post body.
Then in your contoller you can do something like this (I'll use email as an attribute):
def invite
#user = User.find_by(email: params[:user_email])
#Rails 3 like this
# #user = User.find_by_email(params[:user_email])
# now do stuff with user
end
User.all will return you the collection of users. So,
Find the user object to get an id...
Try this code....
def invite
inviteUser = { 'user_id' => User.find_by_email('user#example.com').id, 'Magazine_id' => params[:id] }
CollaborationInvitation.create(inviteUser)
#magazine = Magazine.find(params[:id])
redirect_to :back
rescue ActionController::RedirectBackError
redirect_to root_path
end
You can try
User.last.id
or
User.find_by_email("xyz#test.com").id
or
User.where(email: "xyz#test.com").first.id
Replace xyz#test.com with desired user email address. To get more details on rails active record query interface, please read rails guides http://guides.rubyonrails.org/active_record_querying.html
I'm attempting to manage user sign ups and log in with omniauth. I'd also like to collect more information about users after they authorize with the provider. At the moment Vendor#from_omniauth returns either an existing vendor or a new Vendor record with their auth hashes already populated. Is it possible to pass along the new record to the Vendor controller so the form can easily use it?
I'm using Rails 4.
class SessionsController < ApplicationController
def create
vendor = Vendor.from_omniauth(env["omniauth.auth"])
if vendor.new_record?
redirect_to new_vendor_url(vendor)
else
session[:vendor_id] = vendor.id
redirect_to root_url, notice: "Signed in!"
end
end
end
This issue was resolved by changing new_vendor_url(vendor) to new_vendor_url(vendor: vendor.attributes).
HTTP is stateless, so you'll need to either include info in the params, in the session (via cookies or other), or in the database.
Given that you seem to be doing a registration process, I might be inclined to put the info in the database as soon as you get it. It's an additional write and then read, but if the user drops out midway through the process, you can still contact them (in theory) to resume the signup.
Pass the vendor.id to as an argument to new_vendor_url, then do the lookup in the rendered action:
# app/controllers/sessions_controller.rb
redirect_to new_vendor_url(:vendor_id => vendor.id)
# app/controllers/vendors_controller.rb
def new
vendor_from_session = Vendor.find(params[:vendor_id])
# code here
#vendor = Vendor.new
end
The RoR Security Guide states that you should "issue a new session identifier and declare the old one invalid after a successful login" using the reset_session method to counter session fixation.
I haven't been able to find any guidance on calling reset_session when using Authlogic. Is it simply a case of including the method in the controller method (as below)?
I'm just concerned about causing problems for Authlogic as I can see both user_credentials and user_credentials_id keys and values in the session hash prior to calling reset_session.
class UserSessionsController < ApplicationController
def create
#user_session = current_client.user_sessions.new(params[:user_session])
if #user_session.save
reset_session
flash[:success] = I18n.t(:msg_login_success)
redirect_back_or_default application_root_path
else
render :action => :new
end
end
For reference this is my current method:
def create
#user_session = current_client.user_sessions.new(params[:user_session])
if #user_session.save
# reset session to counter session fixation
# whilst retaining values except for those that the application has created specific to the current user
temp_session = session.merge("current_user" => {}).clone
reset_session
session.reverse_merge!(temp_session)
# set flash msg and redirect
flash[:success] = I18n.t(:msg_login_success)
redirect_back_or_default application_root_path
else
render :action => :new
end
end
With the call to reset_session still performed after a successful login as per the recommendation in http://guides.rubyonrails.org/security.html#session-fixation-countermeasures
yeah, resetting the session AFTER you log the user in (which is what looks like happening?) is definitely not right. You want to do it BEFORE you log the user in.
Ideally you'd want to do it before you log the user in but only if the login is actually going to be succesful -- but I'm not sure if you can get auth_logic to do that, I'm not very experienced with auth_logic, although it's a REALLY good question for auth_logic, if I were you I'd file it as a support ticket with auth_logic.
But in the meantime, you might want to just try putting the reset_session at the top of the action method, before #user_session = current_client.user_sessions.new(params[:user_session]). I think this will work, and at worse reset the session in some cases where you really didn't have to (if the user's credentials were invalid), but I don't think that will cause a serious problem. (uh-oh, unless it causes you to lose your validation errors?)
But again, not an auth_logic expert here. I don't expect you to accept this answer since I don't have the expertise to really answer it, just sharing what I think in case it helps you and gives you some pointers as to how to think about it.
I am trying to force a user to login once they call this update action in my article controller (I am trying to work with gradual engagement) but once they login, I want to still call this action instead of halting.
def update
#article.attributes = params[:article]
#article.save
#store this article as a session variable
session[:pending_article] = #article.body
respond_with(#article, :location => article_url(#article))
end
Right now I am using a before_filter for the action that requires the user to login
def require_user
unless current_user
store_location
flash[:notice] = "You must be logged in to access this page"
redirect_to login_url
return false
end
end
However, I understand that before filters halt the original action once they redirect, so update never gets called. Basically, I want a user to be logged in to save an article but I want to save their work so I'm storing the article body in a session variable which I grab later. Is there a better way to require a user to login for an action but call it afterwards anyway?
In your require_user method you can do something like this:
session[:article] = params[:article]
Then in your login method (/sessions/create?) do this:
# this should take you back /articles/new,
# you may have to move your call to store_location
# or manually set session[:return_to]
redirect_back_or_default
Then in ArticlesController#new
def new
#article = Article.new(session[:article] || {})
end
Then the saved article params from the session are still there so the form is pre-filled out.
Be careful storing too much content in the session though. In Rails the default session store is a cookie, and cookies only hold about 4k of data. You may need to change your session store to pull this off.
How can I implement PRG in Rails?
I used PRG in Rails, but I am not totally convinced it's right. I was wondering is there any better way to handle it in Rails?
I don't know how popular PRG pattern is and why one has to religiously stick to the "redirect" on failure aspect of it (actually, one good reason is sometimes you dont want to deal with the "setup" complexity at create failure and keep things dry).
What you basically need is to transfer the params for :user to new. I think #Hitesh's solution above is quite close.
class UsersController < ApplicationController
def new
if flash[:user_params]
#user = User.new(flash[:user_params])
#user.valid?
else
#user = User.new
end
end
def create
#user = User.new(params[:user])
if #user.save
# clears previously stored user if there is any
flash[:notice] = "User created."
redirect_to '/'
else
flash[:error] = "Error saving User"
flash[:user_params] = params[:user]
redirect_to :action => :new
end
end
end
Use the session, Luke
The way you implemented it in your blog post is quite fine, however you may want to use session instead of flash to store your #user and optionally use the ActiveRecord session store to keep cookies from getting bloated.
From ActionController::Base documentation
ActiveRecord::SessionStore - Sessions are stored in your database, which works better than PStore with multiple app servers and, unlike CookieStore, hides your session contents from the user. To use ActiveRecord::SessionStore, set
config.action_controller.session_store = :active_record_store
in your config/environment.rb and run rake db:sessions:create.
So you should…
class UsersController < ApplicationController
def new
#user = session[:user] || User.new
end
def create
#user = User.new(params[:user])
if #user.save
# clears previously stored user if there is any
session[:user] = nil
redirect_to '/'
else
session[:user] = #user
redirect_to :action => :new
end
end
end
I'm no expert in these matters, but this looks good. From what I understand flash is a part of the session. So the answers telling you to switch to session seem a bit misguided. In this case you want the data to be cleared after the redirect. Other than shoving it in the session, I'm not sure where you would put it.
As far as your cookie size increasing, well, the default session provider for Rails is a cookie in Rails 3. You could swap the session provider out if you wanted to keep the data server side. It is encrypted though, so you are probably okay with the data in the cookie, unless size is an issue.
use below code
class UsersController < ApplicationController
def new
#user = User.new(session[:user_param])
session[:user_param]=nil
end
def create
#user = User.new(params[:user])
if #user.save
# clears previously stored user if there is any
flash.discard(:user)
redirect_to '/'
else
session[:user_param] = #user
redirect_to :action => :new
end
end
end
It is true, though, that you should not do redirect_to '/'. You should define root in your routes file and then do redirect_to root_path.
Edit: Oops, that was supposed to be a comment to SpyrosP's answer.
Also: Here is some excellence guidance on flash. Particularly this may ease your mind:
The flash is a special part of the session which is cleared with each request. This means that values stored there will only be available in the next request, which is useful for storing error messages etc.
The interesting things there is that, yes it is a part of the session, so answers to "use the session instead of flash" are misguided, as Justin Etheredge's answer already put it. The other thing is that it says it is useful for storing messages instead of only for storing messages. With the added "etc" it would lead me to believe that it is within the intended usage to store user information in there as well.
One last thing, I would agree with Aditya Sanghi that you should just store the user parameters and not an entire user object in the flash.
I didn't read the question properly.
The validation failure you have necessitates going to a different page where a different process will occur. You tried to update a domain object, it doesn't exist. The usual response to a validation failure is to re-render the page, but you need to go to the create page.
The flash hash seems wrong for this. I'd agree with the idea of stuffing your entered data into the session and redirecting.