For some reason, current_user returns nil in my model-less controller (Subscriptions). I have found nothing on the Internet to justify this behavior...
class SubscriptionsController < ApplicationController
def new
...
end
def create
current_user # returns nil
end
end
I have a csrf meta tag :
<meta content="xxx" name="csrf-token">
I can provide more code, but I'm not sure what would be useful.
UPDATE
So thanks to the comments/answers, I have pinpointed the problem to one particular action : create.
if I add #user = current_user to the new, I can show the current user's email in my new view. However, in my create controller, current_user returns nil.
I accessed the create action through a form (submit).
Before the form is submitted, I validate the input and then send a request to Stripe to get a token out of the form. If there are no errors (validation and stripe), I then send the form.
Could that be the cause?
UPDATE 2
In my error message, my session dump is empty, while it should contains the current_user info...
It turned out the AJAX request I was making didn't carry the CSRF token. For that reason, Rails was killing my session.
I added skip_before_filter :verify_authenticity_token in my SubscriptionsController and it is now working. It might not be the most secure solution, but it works for now, so I continue to develop and come back to this issue later.
Note that when you create forms using the form_tag helper, they do not automatically generate the hidden field which holds the token for CSRF authentication. I ran into this same issue with a form I had constructed using the form_tag which I sometimes prefer using.
I fixed the issue by including the following helpers within the form:
<%= hidden_field_tag 'authenticity_token', form_authenticity_token %>
It's basically a manual way of generating the hidden field you need for the CSRF stuff.
for current_user to work you need to add before_filter :authenticate_user! to your class, like:
class SubscriptionsController < ApplicationController
before_filter :authenticate_user!
def new
...
end
def create
curent_user # returns nil
end
end
and the authenticate_user! method will set the current user for you :)
I had a similar issue but I was editing the model. So everytime I updated the model suddenly that would happen:
current_model to nil
After analyzing things, it turns out that if you leave the password in the form, when the user tries to edit some attribute, the person is then forced to write a password.
Once the form is delivered and updated, Devise does the rational thing when someone updates a password, which is to destroy the session and ask the user to sign in again.
So that was why current_model was suddenly turning to nil. Hope this helps, have a great day!
Related
Environment:
rails 4.2.6
devise 4.1.1
I am using a rails app, and there is a form to update user's profile. By default, devise asks the user to input user's password to update the user's data. I have put the <%=devise_error_messages! %> in the form, of course there is a update function in the controller, which looks like
def update
super
#email = resource.email
#event = resource.event
#name = resource.name
NoticeMailer.notice_confirm(#email, #name, #event).deliver_later
end
Here comes the problem. When I am editing the user's profile data, by default, devise asks the user to input the password to update the data and save it to the database. If I input the wrong password or leave the password field blank, and press the submi button(form.submit), there will be an error message, and I am still in the form. However even though there is an error message in the form, the update function in the controller still runs anyway. I think the logic is that the update function should not run if the update is failed.
Try01:
Try to input the data without password. I use the method
protected
def resource_update(params, resource)
resouce.update_without_password(params, resource)
end
in the controller,but it threw error message.
Try02
I am thinking using ajax to catch the submit click action and pass the password field to backend to check password. however i don't know how to implement this.
Try03
I tried to put a after_update filter function to do the mail sending function. However the result is the same, the mail function is sending no matter what.
Any suggestion?
I would expect the resource to be not valid? if the update failed. Therefore I would try:
def update
super
NoticeMailer.notice_confirm(
resource.email, resource.name, resource.event
).deliver_later if resource.valid?
end
Btw unless you use the instance variables somewhere else in the same
controller or its view then it should not be required assigning value to them first but you could pass the values directly to the mailer.
The correct update function in the controller:
def update
super
#email = resource.email
#event = resource.event
#name = resource.name
unless resource.errors.any?
NoticeMailer.notice_confirm(#email, #name, #event).deliver_later
end
end
Update02
def update
super
NoticeMailer.notice_confirm(
resource.email, resouce.name, resouce.event
).deliver_later unless resource.errors.any?
end
How to send a variable/parameter from an action without using the URL?
I need to create a user in multiple steps, and the user is created on the 2nd step. I need to receive the email of the user from the first step.
One first step, I receive the email in params, and use this line of code: redirect_to new_user_registration_path(email: params[:email]) to send it out to the next page/action.
For some reasons, I have been told that I can't use emails in URLs, so now I need to send the email under the hood which surely is possible through the POST method, but redirect_to doesn't support POSTs requests.
There could be a suggestion of using render instead of redirect_to, but I'm using Devise, so I would like to hand over the functionality to Devise, instead of manually doing it all by myself.
There is another idea of using cookies to store the email address, but I'm interested in more of a Rails way.
There can be another way too, one way is to using session
On the first step of form submission store email in session variable and use it on the further step and after that clear that session variable.
Eg -
def first_step
#user = User.new
end
def second_step
# Assuming after first step form is being submitted to second step
session[:email] = params[:email]
end
def next_step
user_email = session[:email]
end
Hereby session[:email] will be available everywhere except model layer unless it is set to blank (session[:email] = nil), that should be set to blank after the user is created.
You can use the flash for this
flash[:email] = params[:email]
redirect_to new_user_registration_path
in your view, something like this
<%= hidden_field_tag :email, flash[:email]
You will need to add this line
class ApplicationController < ActionController::Base
add_flash_types :email
I'm just posting this as a possible solution, I realize this is not some best-practice solution for every case
New to concepts of security, so I feel like this must have been done before.
For a classic submission form:
def new
end
def create
unless params[:key] != ENV["key"]
end
end
I have on the new.html.erb a hidden_input with a key that I'm checking in the create action. But the problem is that this key is just a static environment variable.
I'm wondering if there's a way to dynamically create a new key each time the new.html.erb page is rendered, and then have the create action check against this dynamic key. This would require a variable generated in the new action that I can check against in the create action. Output would be something like this:
def new
key = SecureRandom
end
def create
unless params[:key] != key
end
end
Of course, this solution would also have to work with multiple users simultaneously submitting the form, so that one key doesn't get cross referenced with another request, and then mistakenly rejected.
Rails already provides a level of protection called CSRF-tokens. Within each form, an authenticity_token gets generated and inserted as a hidden field. This token is associated with a user's session. Upon submitting a POST request, the authenticity_token will be checked.
https://nvisium.com/blog/2014/09/10/understanding-protectfromforgery/
Use protect_from_forgery in your controllers to enable this functionality.
class ApplicationController < ActionController::Base
protect_from_forgery
end
http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection/ClassMethods.html
It is often a bad idea to implement your own security mechanism. If you don't intent to protect your app against CSRF (this is built-in) and still want to build your own thing, maybe you will be interested in this rotp feature.
I have a resource in my project that collects some information from a user. Basically it's a form that they fill out before they can access another area of the site. It then sets a cookie for a week, but if they come back it will look up their previous entry and keep their preferences tied to them (and will update any details as long as the email address matches).
Currently I have a Applicants controller that looks like this:
class ApplicantsController < ApplicationController
...
def create
#applicant = Applicant.find_or_initialize_by_email(params[:applicant])
if #applicant.new_record? ? #applicant.save : #applicant.update_attributes(params[:applicant])
set_cookie_and_redirect
else
render 'new'
end
end
def update
if #applicant.update_attributes(params[:applicant])
set_cookie_and_redirect
else
render 'new'
end
end
end
The set_cookie_and_redirect is a private method that just sets some cookies and redirects the user to a page. The code works, but it just feels dirty. It's essentially updating a record within the create method under the condition that it's not a new record. I'm also forced to have an update method in case an existing record comes back with a validation error--the form helper will then switch the form over to sending to the update method.
So to my point... is there a more appropriate way to push the update_attributes call in the create method to the update method? Or better put, is there a better way to respect the RESTful methods in isolating the create and update functionality?
UPDATE: I wanted to be a little more specific too. If the user has filled this form out before it will set a cookie so they don't have to fill it out again for seven days. However after seven days the cookie is expired and they see the form again. The controller doesn't know if the user is new or existing until they add user input into the form which is then compared based on the email address.
Thanks in advance! I definitely look forward to anyone's thoughts on this.
The create method should only create, and the update method should only update. Let Rails decide which is going to happen based on what is inside of #applicant when the form is rendered - It essentially does what you're doing: Checks if the record is new or not, and sends it to update/create accordingly. Example:
def applicant
#applicant = Applicant.find_or_initialize_by_email(cookies[:email])
# renders applicant.html.erb form
end
<%= form_for #applicant do |f| %>
# ... fields ...
<% end %>
def create
#applicant = Applicant.new(params[:applicant])
#applicant.save
# .. etc.
end
def update
#applicant = Applicant.find_by_email(cookies[:email])
#applicant.update_attributes(params[:applicant])
# ... etc.
end
Rails will send the request to the correct action based on the new_record? status of the Applicant object.
I'm using a Chrome extension to send data to my Rails app (the staged_images controller). So I have a form that sends the data, along with an authentication token. Each user has a token which is generated by Devise, and is saved into the Users table. What I need to do now is receive that data in the controller and check the submitted token against the one stored in the Users table.
So the create action in my controller should look something like this:
def create
#user = User.find(params[:staged_image][:user_id])
if #user.authentication_token == submitted_token # this is pseudo code, don't know exactly what to say here
# execute code
else
# raise error
end
end
I'm just having trouble with the specifics of how to check the one token against the other. I'm a newbie. Help me!
Thanks
You should pass to the token controller and have a filter on the top and pass a auth_token in your call (http://yoursite/staged_images?auth_token=YOUR_STORED_TOKEN').
class staged_images < ApplicationController
before_filter :authenticate_user!
end
Devise will raise an exception if the token is invalid.
You can access the user information with the helper current_user