I have a Rails application and using Devise for authenticating in app.
This Rails app is in API mode. I added some costume fields in devise Users model:
## Costume fields
field :ip, type: String
field :role, type: String , default: 'client'
As you can see, I wan to add IP address of user, during signup action of devise. I want to tell devise that when a you got a new "signup" request, please before adding user into the database, get user IP and add it to fields then save new user in to database like this:
{
email: 'test#example.com',
encrypted_password: "sg4rgtgesrre5erghtr5etrgtrrergre55trgf....",
ip: '1.2.2.1' // The user rote IP
}
Hope you had set up the devise controller
in your registration controller for user you can modify the script
class Users::RegistrationsController < Devise::RegistrationsController
def create
build_resource(sign_up_params)
resource.ip = '1.1.1.1'
resource.save
yield resource if block_given?
if resource.persisted?
....# rest of the create action code
end
but this wont save the ip to the database before saving.... but the ip value will be added to the user object for further saving.
You need to override the modify create action in Devise::RegistrationController for this work.
https://github.com/plataformatec/devise/blob/40f02ae69baf7e9b0449aaab2aba0d0e166f77a3/app/controllers/devise/registrations_controller.rb#L17
Your main goal is to modify the sign_up_params content. You can do it 2 ways:
You can create your your own params like
def user_sign_up_params
sign_up_params.merge ip: request.ip
end
and change the above line build_resource(user_sign_up_params)
You can add ip on place like build_resource(sign_up_params.merge(ip: request.ip))
Related
I have a model called booking and I am using active admin plugin as a resource management. I have a form called booking in the backend in which I need to insert ip address and username of the currently logged in user(who insert the form data) to the database without making any input field in backend. Please suggest. I am using mysql as database platform.
here is my controller syntax
class CustomizedBookingsController < ApplicationController
def create
customized_booking = CustomizedBooking.new(params[:customized_booking])
customized_booking.booked_by = current_admin_user
customized_booking.booking_ip = request.remote_ip
end
end
I tried lots but still not working.
If you're using Devise for authentication, you can use the method current_user, see the documentation here: https://github.com/plataformatec/devise#controller-filters-and-helpers
To add the IP address, you have the ip method, part of the ActionDispatch::Request. Check the documentation here: http://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-ip
So if your booking model has both user and ip fields/methods, you can set them in the create action in your BookingsController
def create
booking = Booking.new(booking_params)
booking.user = current_user
booking.ip = request.ip
end
I have a RoR application (Rails 4.2, Ruby 2.2.0) running Devise. I have set it up so that admin users (identified the "is_admin" boolean I added to the user model) are able to create new user account, provide them with a generated password and confirmation email. This is all working properly. I have also added the datetime column pass_changed which should be updated when a user changes their password, and then checked against created_at to make sure that the password has changed since the account was created. If the two dates are equal then the user is redirected to the password change form.
I wrote a procedure for checking that the user has changed their password and placed it in my application_controller.rb:
def check_changed_pass
#user = current_user
if #user.pass_changed == #user.created_at #make sure user has changed their password before accessing internal pages
redirect_to edit_user_registration_path, alert: "You must change your password before logging in for the first time"
end
end
Then in my internal_controller.rb (used for all internal that require the user to be logged in:
before_action :check_changed_pass
So far so good, but the problem comes with attempting to update pass_changed at the right time. I have tried various configurations, but I can't find where to put this code so that it triggers when a password is updated, but not every time the user model is updated, which includes every login and logout, when I only want it to trigger if the password gets updated.
If I place "before_save :update_pass_change, only: [:edit, :update]" in my controller it doesn't trigger on a password update, and if I place it in the User model, I have to put the procedure in the model as well and then it won't pick up current_user as its not available in the model. The ideal thing would be if Devise had a hook for after_password_edit similar to the after_database_authentication hook. I had to override Devise's registrations_controller.rb to remove the line
prepend_before_filter :require_no_authentication, only: [ :cancel]
So that admin users would be able to add new users. I tried placing update_pass_change there, but it doesn't seem to trigger before_save on a password edit.
In application_controller.rb
def update_pass_change # records that the user has changed their password
unless current_user.pass_changed != current_user.created_at
current_user.pass_changed = Time.now
end
end
Similar unanswered question: Ruby on Rails: Devise - password change on first login
You could use a callback on your model and check, before save, if the changes includes your password attribute. Something like this:
class User < ActiveRecord::Base
before_save :check_password_changed
private
def check_password_changed
self.pass_changed = Time.now if changed.include? 'encrypted_password'
end
end
You can also try this
change datatype of pass_changed to boolean with default value false
Then in your application_controller.rb
before_action :check_pass_changed
private
def check_pass_changed
unless current_user.pass_changed
redirect_to custome_password_edit_path, alert: "You must change your password before logging in for the first time"
end
end
Then Create custom action in ur users_controller to display the password change form and to update.
eg.
To display form : custome_password_edit
To update :
update_user_password
also dont forget to write above in routes.rb
after successful change update the value of pass_changed to true
I am currently using devise_invitable to submit an email that invites a new user. Each email you send out creates unique token which gets connects the two users if they accept the invitation. What I would like to do is for each current_user to have a unique invitation token that stays the same so they can tweet it, email it, etc. and anyone that accepts the invite by clicking on the link (www.mydomain.com/signup/'unique_current_user_token') will be taken to the signup form which will store the token in their 'account' so I can see who invited them. Does it make
You may want to do that manually by overriding the registration page of devise and allowing our new parameters to the model (attr_accessible rails 3 or params.require(:foo).permit)
First you should remove devise invitable as you will not use it for this approach i've searched and did not find a clean way to do it with devise_invitable.
Step 2:
add attribute to the users table called invitation_token (this one is different from devise invitable as this will be unique per user and will be used to send invitation not used when receiving invitation)
and add to the user model (according to rails casts)
before_create :generate_invitation_token
def generate_invitation_token
self.invitation_token = Digest::SHA1.hexdigest([Time.now, rand].join)
end
by this each user will have a unique invitation_token
Step 3: now you can have a url like /signup/:invitation_token for to invite users which is unique per user (you should add this to routes and map it to the Devise::RegistrationsController#create)
Step 4: accept users and recognize who invited them on registration
Now we have to override the Devise::RegistrationsController#create and new and view
in the new you should find the user who invited. and add a virtual attribute that should reference the that user ie: inviter.id
and add a hidden_field to the registration form to set such attribute
Now in the create check for the availability of that attribute if present you can do what ever you want (add relation between users in your case).
And that's it i think
If you need anything else comment and i will edit my answer to help you more
I'll add a more detailed steps, just if someone struggles like me to implement the solution from #khaled_gomaa, that worked fine.
1a. If you are overriding devise registration controller, add it to this controller:
before_filter :configure_permitted_parameters
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :invited_by
end
1b. If you are not using a custom controller, add the following to the application constroller:
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :username
end
2. Run it in your console:
rails generate migration AddInvitationTokenToUsers invitation_token:string invited_by:integer
I'm not sure if it was in the original instructions but I think its really useful to set who invited a user.
Edit user model and add the following lines:
before_create :generate_invitation_token
def generate_invitation_token
self.invitation_token = Digest::SHA1.hexdigest([Time.now, rand].join)
end
3. I did not set the extra route in config/routes.rb
4. That's the part that was more confusing for me. Not sure if I'm not fully following the original instructions, but it worked fine in rails 4.
If you are not overriding registration constroller, you will need it, so my 1a step is stupid...
Add a new file under controllers folder named registrations_controller.rb with the following content:
class RegistrationsController < Devise::RegistrationsController
before_filter :configure_permitted_parameters
def new
if params.has_key?(:invitation_token)
inviter = User.where(:invitation_token => params[:invitation_token]).first
#invited_by = inviter.id
end
super
end
def create
super
if resource.save && params[:user].has_key?(:invited_by)
resource.invited_by = params[:user][:invited_by]
resource.save
end
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :invited_by
end
end
In new method, we are setting the extra field for the sign_up form. We are recovering the id of the inviter (who invites), using the token that comes from a param.
Creates method will save the user in the db. Before do it, we will get the hidden field from the form and add it to the user, in case that the user comes from an invitation.
In the view.html.erb (or whatever you are using) add the following line, inside the form:
<%= f.hidden_field :invited_by, :value => #invited_by %>
And that's it. Run the migration to add the new columns. Just go to http://example.org/users/sign_up?invitation_token=USER_TOKEN
I'm attempting to display a users password along in his confirmation page sent by the Devise mailer. The confirmation page is the default
Welcome test0#test.com!
You can confirm your account email through the link below:
Confirm my account
However, I wish to have
Welcome test0#test.com!
Your password is currently DASADSADS
You can confirm your account email through the link below:
Confirm my account
How do I access the user object in the view? Do I need to override the mailer controller with a custom one? If so, how do I tell what the methods of the current mailer do (tried looking at documentation but can't find any clues)?
I noticed that #email and #resource are used in the view. Can I use any of these to access the current password in its unhashed form?
Note that I am sending this email manually with user.find(1).send_confirmation_instructions
Although this can be done, I would caution very strongly against doing so. Hashed passwords are specifically used so that the password cannot be recreated easily. Passing the original password back to the user will cause it to be sent back in plain text which sort of defeats the whole purpose. Also, shouldn't the user already know their password (they did type it in twice after all)?!?
To do this, you would need to capture the original (unhashed) password in the registration create action and send the email at that point (passing along the password). You can do this by overriding the sign_up method - you can do this in an initializer:
class Devise::RegistrationsController < DeviseController
def sign_up(resource_name, resource)
sign_in(resource_name, resource)
resource.unhashed_password = resource_params[:password]
resource.send_confirmation_instructions
end
end
Alternatively, you can derive a new controller from Devise::RegistrationsController and put this override code there (the recommended approach - but then again, this whole operation isn't really recommended). You'll need to add the unhashed_password accessor for this to work:
class User < ActiveRecord::Base
attr_accessor :unhashed_password
end
And then you can update your confirmation view (at app/views/devise/mailer/confirmation_instructions.html.erb) to contain this:
<p>Your password is currently <%= #resource.unhashed_password %></p>
Devise save password in encrypted form: You can decrypt it using,
Generate new migration:
$ rails g migration AddLegacyPasswordToUser legacy_password:boolean
invoke active_record
create db/migrate/20120508083355_add_legacy_password_to_users.rb
$ rake db:migrate
Using legacy_password method in following code you can decrypt your password:
class User < ActiveRecord::Base
...
def valid_password?(password)
if self.legacy_password?
# Use Devise's secure_compare to avoid timing attacks
if Devise.secure_compare(self.encrypted_password, User.legacy_password(password))
self.password = password
self.password_confirmation = password
self.legacy_password = false
self.save!
else
return false
end
end
super(password)
end
# Put your legacy password hashing method here
def self.legacy_password(password)
return Digest::MD5.hexdigest("#{password}-salty-herring");
end
end
You can just use request.request_parameters[:user][:password] to get the plain text password on the create or update action.
If I've got a rails application and I'd like to add authentication to with Devise, how would I allow users who have a null password in the database to sign in without one?
I'm interested in hearing answers along the lines of the lifecycle and what files I'd have to author to get it done.
Step 1: Allow the record to be saved.
Step 2: Sign in the record
To allow the record to be saved, you'll want to do validations yourself. I describe here how to do custom validations: http://jessewolgamott.com/blog/2011/12/08/the-one-where-devise-validations-are-customized/ .... In your case, you'll want to remove the password validations.
To sign in the record, you'll need to have a custom sign in path. You can override the devise sessions controller, but this could do the trick:
class SessionsController < ApplicationController
def create
user = User.find_by_email!(params[:session][:email])
sign_in user
redirect_to root_path
end
end
It turns out, Devise is built on Warden. This means that I only have to create my own custom Warden strategy:
https://github.com/hassox/warden/wiki/Strategies