Creating users (with no registration) using Devise - ruby-on-rails

I want to be able to create users and save them to the database but Devise doesn't pass the request to the database, and doesn't return any error.
alice = User.new
alice.name = "Alice"
alice.save!
alice gets saved successfully, just not in the database.
I can do User.find(alice.id) and it returns data as long as I'm in the same session.

If someone have this problem, I found that Devise enforces email even when setting
email_required?
false
end
so even hitting save! will not raise an error.
Therefore the only way i found is to create a fake email, using something like this
"guest_#{Time.now.to_i}#{rand(99)}#anyemail.com"
then assign a self.role = guest, so the account would be easily identified.
I hope this helps

Related

Rails devise reset password fails if user record contains invalid data

I have an existing application in production where we have added new mandatory fields to the user record. As a result, the forgot password function is failing. I believe the failure occurs in the following method in recoverable.rb
# Update password saving the record and clearing token. Returns true if
# the passwords are valid and the record was saved, false otherwise.
def reset_password(new_password, new_password_confirmation)
if new_password.present?
self.password = new_password
self.password_confirmation = new_password_confirmation
save
else
errors.add(:password, :blank)
false
end
end
The new attributes cannot be generated mechanically. Only the user himself will know the correct values. Does anyone know of a way to get around this problem?
The only idea that I've come up with so far is as follows:
use a rake script to populate the new fields with values that I know would never occur in real life but would be accepted by the model validation rule
when the user logs in, detect if the user record contains invalid data and force them to do an update. In the user edit form, I would use some javascript to change the bogus values to blank.
You could use a custom validation context to make sure the validations on these added fields are only run when a user edits their profile, but skipped when (for example) Devise saves the user record.
In the User model, add an on:-clause to these validations
validates_presence_of :recently_added_mandatory_field, on: :user_updates_profile
And in the appropriate controller action, add this validation context when calling #valid? or #save:
# Existing code uses `#user.valid?` -> change to:
if #user.valid?(:user_updates_profile)
…
# Existing code uses `#user.save` -> change to:
if #user.save(context: :user_updates_profile)
…
See here for a full description of validation contexts: https://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validates

Trouble assigning user access levels

I'm following this tutorial, as I wanted to learn how to create user authorization with singular roles (each user has one role) from scratch rather than using a gem like rolify that does it all for me, but I'm hung up on assigning the users access levels.
When I type erin = User.find(9) in the console it finds my test#test.com user. I try to issue the erin.admin! command but it throws an error about the password? (ActiveRecord::RecordInvalid: Validation failed: Password can't be blank).
I've also tried erin.access_level = "admin" which returns "admin" while I'm still in the console but no longer exists when I exit the console, fire up the rails server and try to test out my test#test.com user in my app.
Is there any other way to assign access levels? Am I just doing it wrong?
The User record cannot be saved because a validation exists that requires a password. I don't know if there are special rules for the format of the password, but you can easily set a password so that you can save the user:
user = User.find(9)
user.password = 'Test1234'
user.password_confirmation = 'Test1234' # you might need this as well
user.access_level = 'admin'
user.save #=> true
If user.save returns false, check user.errors for any other validation errors that would cause the record not to save.
For the second part of my question, where my database didn't seem to deploy to Heroku, it's because I was working in the dev db, not the production db.
To do that, I ran "heroku run rails console" and then followed the above steps to give a user admin access levels. More here: https://devcenter.heroku.com/articles/getting-started-with-rails4#console

customizing Devise's authenticate and current_user methods to work with soft delete / acts as paranoid

I am using Devise as authentication for a rails 3.2 app. My user model is called User, and I've been trying to implement a soft delete. I am trying to do this by using acts_as_paranoid, which automatically does the work for you. Things work as expected (others can no longer see the "deleted" user), except that I want the deactivated user to still be able to log in and see a "deactivated" screen, and give them the chance to reactivate their account.
The problem is that Devise (Warden?) is no longer able to find the deleted user. Acts_as_paranoid lets you access the soft deleted records if you use the scope "with_deleted". So I am able to get part of the way there with:
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
where(conditions).with_deleted.first
end
I put this into my user model, and so now when I input the log in information, I'll get the flash message that I have successfully logged in, and it'll touch the "updated_at" column in the user model and increment the sign_in_count, etc. However, it doesn't really authenticate in the sense that the authenticated method returns false and the current_user helper method returns nil. So what is my best strategy to get these working? Can I override the current_user method somehow so that it queries the User model with the with_deleted scope? Do I have to do something with warden, such as added conditions like I did with the find_first_by_auth_conditions method? I cannot figure out how to do this. Any help is appreciated!
also, after I do get it working, I would like to automatically send all soft deleted users to a "deactivated" page where their only options are to permanently delete or reactivate. Is there some way to do this with routing via the "authenticated :user do {} end", or do i have to put a before_filter in the application_controller and check for the users at every request?
I can answer your last question, about routing to the "deactivated page". You can use a lambda to isolate different types of users, like this:
authenticated :user, lambda {|u| u.deactivated? } do
root :to => 'application#deactivated'
end
authenticated :user, lambda {|u| u.active? } do
root :to => 'application#active'
end
Thanks to a tip from the Devise group, it turns out I needed to override the serialize_from_session method, which is inside the authenticatable module and looks like this:
def serialize_from_session(key, salt)
record = to_adapter.get(key)
record if record && record.authenticatable_salt == salt
end
I'd been trying with no success to override modules using initializers (I was trying to override existing strategies, and also to try writing a new one for warden); but I kept getting weird name errors. I still haven't figured that out. However, with the tip, I went ahead and overrode the method inside my User model. Since I don't foresee using some other resource, I didn't mind just editing it like this. The code was just:
def self.serialize_from_session(key, salt)
record = with_deleted.find(key).first
record if record && record.authenticatable_salt == salt
end
This skips the to_adapter.get method altogether (which, to anyone interested, is in orm_adapter, not devise; this took me awhile to find as I was thinking I needed to override this). This probably isn't the cleanest way to do this, but it works well enough for me. And even if I do have a different resource, this should only override the User resource, so I think everything would work fine otherwise.
If problems crop up, I'll add them here. If not, hopefully this helps someone in some way. Certainly took me long enough to get to the bottom of!

Ruby on Rails - ApplicationController

Am working on some guest account feature in my game application.If u register as a game user then devise gem loads the current account id to the session automatically. But when a user is a guest, am creating a account and a user type object with hard coded values and saving that to the database successfully. But when I try to store the object into the session its not working properly.
My code is like this,
def play_as_guest
account = Account.new(email: "guest_#{Time.now.to_i}#{rand(99)}#eossys.com", password: "password", is_guest: true)
account.save
game_user = GameUser.new(account: account, game_profile_attributes: {first_name: "Guest", last_name: "guest", nick_name: "guest"})
game_user.save
session[:current_account] = game_user
redirect_to "/game_profiles/#{account.id}/summary"
end
If i store the account obj
session[:current_account] = game_user
this is throwing an error of "ActionDispatch::Cookies::CookieOverflow in HomeController#play_as_guest"
if I try
session[:current_account_account] = account
its not working, because after redirecting its again asking for the login. since I am using the 'authenticate_account!' filter in every class.
What I want basically is to load the account object I have created into the session, like as if its a registered user.
Thanks for the help in advance.
Rails cookies have a maximum limit of 4k ( http://guides.rubyonrails.org/security.html#session-storage ). When you do something like obj session[:current_account] = game_user, you are attempting to stuff the whole game_user object in there, which causes you to run out of room with the ActionDispatch::Cookies::CookieOverflow error you mentioned. The preferred way to do this is to save only the id, like this obj session[:current_account_id] = game_user.id,and look it up when you need it. This is much more efficient, even if your whole object did fit into memory somehow.
I think we need the code of your authenticate_account! filter to answer it properly but for your error, this is because you try to put an entire object in your session, it is better to only put the id of the object in it :
session[:current_account] = game_user.id
UPDATE
With devise you should use the sign_in method to set the session for this user instead of manually setting the session variable, depending of how your are using devise it could be:
sign_in account or sign_in game_user

How can I find a devise user by it's session id?

Given session["session_id"] is it possible to find the logged in User to which that session belongs to?
At least on my system (rails 3.2, devise 2.0.4), you can do it like this:
session is:
{"session_id"=>"be02f27d504672bab3408a0ccf5c1db5", "_csrf_token"=>"DKaCNX3/DMloaCHbVSNq33NJjYIg51X0z/p2T1VRzfY=", "warden.user.user.key"=>["User", [3], "$2a$10$5HFWNuz5p6fT3Z4ZvJfQq."]}
session["warden.user.user.key"][1][0], then is 3.
So, I'd find it as:
User.find(session["warden.user.user.key"][1][0])
I'm not sure what you are trying to accomplish but will try to answer
If you want only the User from the current session, a simple way would be to store his id on session, for example:
def login(username, pass)
# do the usual authentication stuff and get user
if logedin
session[:user_id] = user.id
end
end
Then get the current user would be something like
current_user =User.find(session[:user_id])
If what you want is finding all the users that are currently logged in, I think you need to config your app to save session at DB, because predefined is that session data is store in cookies in the browser. For more information on this check this answer
How to track online users in Rails?
EDIT: Just noticed you are using devise so the first way is actually there for you. You just need to call current_user method at any controller.
For the second way check this answer "Who's Online" using Devise in Rails
And i might add this, as i was trying to do it the other way, if you are using ActiveRecord session_store you can find all stored sessions of a user like so:
ActiveRecord::SessionStore::Session.select{ |s| s.data["warden.user.user.key"] && s.data["warden.user.user.key"][0][0] == user_id }

Resources