Rails 3 update_attribute not firing - ruby-on-rails

I have a member model with a reset_token method (which assigns a user a new token in order to send them an email to reset their password). But update_attribute never saves anything in the database. I have :new_password_token assigned to attr_accessible and attr_accessor. The log picks up the salt and token but it always returns nil
def self.reset_token(email)
member = find_by_email(email)
if member
#Reset token, and then send email
salt = BCrypt::Engine.generate_salt
logger.error "Salt is #{salt}"
token = BCrypt::Engine.hash_secret(email, salt)
logger.error "token is #{token}"
if member.update_attribute(:new_password_token, token)
member
end
end
nil
end
Controller method in which it is called:
def reset_password
#member = Member.reset_token(params[:email])
if #member
redirect_to(root_url, :notice => "Please check your email for instructions")
else
redirect_to(root_url, :notice => "Sorry we have no record of your account")
end
end

Try removing attr_accessor from your model. attr_accessor is creating reader and writer methods for new_password_token. The writer method is equivalent to:
def new_password_token=(new_password_token)
#new_password_token = new_password_token
end
So when you update_attribute it is just setting an instance variable on your object and bypassing the database altogether.

Related

confirmation token Automatically generated NIL

I have an issue with an automatically generated token. In a model, I generate the token automatically using:
class User < ApplicationRecord
before_create :generate_confirm_token
def generate_confirm_token
self.confirm_token = generate_token
end
def generate_token
loop do
token = SecureRandom.hex(10)
break token unless User.where(confirm_token: token).exists?
end
end
After creating of user, the token is generated correctly, but the issue is in a controller:
class Companies::StudentsController < ApplicationController
def create
#company = Company.find(params[:company_id])
#student = #company.students.create(student_params)
raise #student.inspect
if #student.save
StudentMailer.with(student: #student).welcome_email.deliver_now
redirect_to company_students_path
else
render :new
end
end
student contains confirm_token BUT in params the confirm token is empty.
I need the token in params because in the mailer I use Find_by(params[:confirm_token]).
Here is how I use a confirm_token in my view. I assume I need the confirm_token in params so I have to have it in a view also:
<%= f.hidden_field :confirm_token %>
The process which is described above is OK.
The issue was in mailer.
student should be in mailer created like this:
#student = params[:student]
but I did it like this:
#student = Student.find_by(confirm_token: :confirm_token)
Which is not correct according to the mailer documentation:
Any key value pair passed to with just becomes the params for the
mailer action. So with(user: #user, account: #user.account) makes
params[:user] and params[:account] available in the mailer action.
Just like controllers have params.

Devise - how to check if reset password is token is valid

I'm trying to figure out how I can check if a user reset token is valid BEFORE loading the reset password form. The issue is, currently users don't find out until after they submit.
Here is what I have
class PasswordsController < Devise::PasswordsController
before_action :check_valid_token
private
def check_valid_token
resetCode = (params['resetCode'])
reset_password_token = Devise.token_generator.digest(self, :reset_password_by_token, resetCode)
user = User.find_by(reset_password_token: #reset_password_token)
if user == nil
redirect_to root_path
end
end
end
This doesn't work and I can't find much documentation.
Devise reset password token will be stored as hashed value. You need to decode it.
def check_valid_token
token = Devise.token_generator.digest(User, :reset_password_token, params['reset_password_token'])
user = User.find_by(reset_password_token: token)
user.present?
end
This method will return, true or false
I would do something basic, like this:
def check_valid_token
#user = User.find_by!(reset_password_token: params[:token])
rescue ActiveRecord::RecordNotFound
redirect_to root_path
end
so you will have #user instance if token fits and if not it will redirect user to the root_path. You can also add some message before redirecting, like
flash.now[:error] = "Some message here"

Rails: Default admin for first registration user on devise and milia

I'm working for a multi-tenant application using Ruby on Rails, Devise, Stripe and milia.
Basic Concept: At first a user create an account by procedural way like using devise sign_up page and create an organization this user has the ability to add multiple members for this organization at this point first user is organization admin and others member/members are the just members like there has no writing permission just reading permission.
At second the first user an send an invitation to a member who will have an organization member, now everything is working like registration, payment and invite to member email and join this member.
I have tried like this
on the user.rb
before_save {self.is_admin = true}
But this saving for all including members.
This is my registration code
class RegistrationsController < Milia::RegistrationsController
skip_before_action :authenticate_tenant!, :only => [:new, :create, :cancel]
def create
# have a working copy of the params in case Tenant callbacks
# make any changes
tenant_params = sign_up_params_tenant
user_params = sign_up_params_user
coupon_params = sign_up_params_coupon
sign_out_session!
# next two lines prep signup view parameters
prep_signup_view( tenant_params, user_params, coupon_params )
# validate recaptcha first unless not enabled
if !::Milia.use_recaptcha || verify_recaptcha
Tenant.transaction do
#tenant = Tenant.create_new_tenant( tenant_params, user_params, coupon_params)
if #tenant.errors.empty? # tenant created
if #tenant.plan == 'premium'
#payment = Payment.new({email: user_params["email"],
token: params[:payment]["token"],
tenant: #tenant
})
flash[:error] = "Please check registration errors" unless #payment.valid?
begin
#payment.process_payment
#payment.save
rescue Exception => e
flash[:error] = e.message
#tenant.destroy
log_action('Payment Failed')
render :new and return
end
end
else
resource.valid?
log_action( "tenant create failed", #tenant )
render :new
end # if .. then .. else no tenant errors
if flash[:error].blank? || flash[:error].empty?
initiate_tenant( #tenant ) # first time stuff for new tenant
devise_create( user_params ) # devise resource(user) creation; sets resource
if resource.errors.empty? # SUCCESS!
log_action( "signup user/tenant success", resource )
# do any needed tenant initial setup
Tenant.tenant_signup(resource, #tenant, coupon_params)
else # user creation failed; force tenant rollback
log_action( "signup user create failed", resource )
raise ActiveRecord::Rollback # force the tenant transaction to be rolled back
end # if..then..else for valid user creation
else
resource.valid?
log_action('Payment proccesing fails', #tenant)
render :new and return
end # if. . then .. else no tenant errors
end # wrap tenant/user creation in a transaction
else
flash[:error] = "Recaptcha codes didn't match; please try again"
# all validation errors are passed when the sign_up form is re-rendered
resource.valid?
#tenant.valid?
log_action( "recaptcha failed", resource )
render :new
end
end # def create
end
My question is: How to create is_admin: true for who create organization?
Thanks!
If I understood your concept like you need to assign is_admin: true for who can register using this registration controller, Right? If yes then it's very easy update this user_params = sign_up_params_user line of code
Try the following
user_params = sign_up_params_user.merge({ is_admin: true })
Now assign is_admin: true for only who can create an account with an organization.
Now if you block to special permission for normal members then create a method to user.rb file like
def is_admin?
is_admin
end
Then for permission
if current_user.is_admin?
#=> Permission for admin
else
#=> Permission denied for normal members
end
Hope it helps
If using something like rolify gem, you can add the line
#user.add_role(:admin)
in the following context in registrations_controller.rb`:
Tenant.transaction do
#tenant = Tenant.create_new_tenant( tenant_params, user_params, coupon_params)
if #tenant.errors.empty? # tenant created
initiate_tenant( #tenant ) # first time stuff for new tenant
devise_create( user_params ) # devise resource(user) creation; sets resource
#user.add_role(:admin)

don't understand some code from a RailsCast tutorial

I watched the RailCasts tutorial #274 on Remember Me and Reset Password. The code he adds is the following inside user.rb
def send_password_reset
generate_token(:password_reset_token)
save!
UserMailer.password_reset(self).deliver
end
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
Here what I don't understand is why the save! call inside send_password_reset? Also, I'm not familiar with the syntax in generate_token: self[column]=. Is this the way to set a column inside a database table?
Here's the create action of the password_resets_controller
def create
user = User.find_by_email(params[:email])
user.send_password_reset if user
redirect_to root_path, notice: "Email sent with password reset instructions."
end
save! saves the object and raises an exception if it fails.
self[column]=, is a slight meta-programming.
Usually, when you know the column name, you'd do: self.password_reset_token=. Which is the same as self[:password_reset_token]= or self["password_reset_token"]=.
So it's easy to abstract it a bit passing column name as string/symbol.
Clearer?
1) save! is like save, but raise a RecordInvalid exception instead of returning false if the record is not valid.
Example from my console:
User.new().save # => false
User.new().save! # ActiveRecord::RecordInvalid: Validation failed: Password can't be blank, Email can't be blank
2) self[column]= there for setting users column.

No method error when trying to call a function with an instantiated object

I have a model Token with three fields user_id,product_id and unique_token.In the controller i instantiate a #token object with user_id and product_id values collected from the form.Then i call save_with_payment function with that object,where within the function i want to generate random string 3 times and save in unique_token field.The problem is self.tokens.create!( unique_token: Digest::SHA1.hexdigest("random string") ) give me no method error undefined method tokens.What am i doing wrong here?To clarify what i want to accomplish,I want to be able to retrieve list of generated unique_tokens associated to that user_id or product_id like User.find(1).tokens or Product.find(1).tokens.The model association is User has_many Tokens Product has_many Tokens.Note: unique_token field is from Token model originally,user_id and product_id are just ref primary keys.Much Thanks!
def create
#token=Token.new(params[:token])
if #token.save_with_payment
redirect_to :controller => "products", :action => "index"
else
redirect_to :action => "new"
end
end
class Token < ActiveRecord::Base
require 'digest/sha1'
def save_with_payment
# if valid?
# customer = Stripe::Charge.create(amount:buck,:currency => "usd",card:stripe_card_token,:description => "Charge for bucks")
#self.stripe_customer_token = customer.id
3.times do
self.tokens.create!(unique_token: Digest::SHA1.hexdigest("random string"))
end
save!
end
end
There is no tokens method on the Token class. Since you're creating three tokens you don't need the #token instance. Just have save_with_payment be a class method:
def create
if Token.save_with_payment(params[:token])
redirect_to :controller => "products", :action => "index"
else
redirect_to :action => "new"
end
end
class Token < ActiveRecord::Base
require 'digest/sha1'
def self.save_with_payment(attributes)
attributes.merge!(unique_token: Digest::SHA1.hexdigest("foo"))
3.times do
self.create!(attributes)
end
end
end
Hope this helps.
You might want to wrap the loop in a begin/rescue, too. Otherwise if the 2nd or 3 create! fails you end up with tokens AND redirecting to "new".
Response to 1st comment:
That won't work if you use a class method. You can't call valid? because you're not in the context of an instance of Token. I don't recommend sticking with an instance method. If you do change it to a class method you'll want to wrap it in a transaction block:
def self.save_with_payment(attributes)
transaction do
attributes.merge!(unique_token: Digest::SHA1.hexdigest("foo"))
3.times do
self.create!(attributes)
end
rescue
false
end
end
That should roll back the SQL transactions if any of the create! calls fail and return false to the controller create action.
I'd pull that customer code out of Token (Token shouldn't care about creating/retrieving a customer) and put it in the controller action. Pass the pertinent information into save_with_payments. Like:
self.save_with_payments(customer, attributes)
...
end

Resources