How much can be dangerous a '#class_object_instance.save!(:validate => false)' method? - ruby-on-rails

I am using Ruby on Rails 3.0.9 and I would like to know "how much can be dangerous" to use the following statement in order to save a record in the database:
#user.save!(:validate => false)
That is, what can happen? For example, can a malicious user to hack something (eg: set some not allowed value) in the database?
Notice: I am referring mostly to the :validate => false part of the above code that makes it possible to skip validation processes.

Can a malicious user 'hack something'? That depends. :validate => false disables any validation logic in the class #user belongs to. If you've mass-assigned the parameters you wanted to update in the normal way, you still get all the escaping which will protect you from SQL injection type attacks.
However if the integrity of the application rests on something on #user being valid, you might be in trouble- think of users setting their own account balances, adding extra subscriptions for free, altering expiry dates etc...
I'm not sure what your application does. Look at the relevant validation logic, question why it's there, then ask yourself what would happen if it were disabled and something broke. In general :validate => false is not a good idea.

Related

Ruby on Rails: How to validate if on specific page?

In my Ruby on Rails application I am trying to add in validations that will ensure the user has entered a value in a text box. In my system I have a table called Account which stores users' email account information, when they go onto the views/accounts/_form.html.erb page to add a new email account I want to validate the presence of a port number. I can do this through the following code:
validates :port, presence: true
This successfully ensures that users enter their port number, but when a user signs up through the views/users/_new.html.erb page they have to enter only an email address (e.g example#example.com) and the users_controller will then create a record in the Account table for this email address. My problem is that on the views/accounts/_form.html.erb page the port number is required but on the views/users/_new.html.erb it is not.
Is there a way of validating that the user enters the port number if they are on the views/accounts/_form.html.erb page or invoking the create method in the accounts_controller?
I am aware that I could do this through the HTML required validation like so: <%= f.text_field :port, :required => true %> but I need to add in further validation as well as presence, so this is not suitable.
You can create an attr_accessor field that determines if the validation should occur...
class Account < ActiveRecord:Base
attr_accessor :port_needs_validation
validates :port, presence: true, if: -> {port_needs_validation}
Then just set the accessor in your create method...
def create
#account = Account.new
#account.assign_attributes(account_params)
#account.port_needs_validation = true
if #account.save
...
Extract that part of the logic into a form object, check out the legendary 2012 blog entry from CodeClimate. Things have changed since then, the author uses Virtus to build form objects, more popular & up-to-date gems these days are:
reform
dry-rb
active type
but really you can make anything behave like an ActiveModel object
if it's a one-off thing, just do what Steve said in the other answer but that is a sure way to hell, safe-hate and divorce (at least from personal experience) in any slightly teeny weeny bigger project (i.e. you mean to spend some hours more working on it, it's not like you just finished everything and want to go home).
Actually, just use form classes everywhere and avoid model validations & other callbacks at all. You don't want things sending account activation mails or validating your password complexity when you're writing tests and just need a "post" that belongs to "user".
My own favorite personal fuckup due to model callbacks is sending 240.000 "your account has been upgraded/downgraded" emails because of an innocent spelling change update in an account_type attribute migration just because account_type_changed? was true.
So.. Form classes for ever, model callbacks never.
I would not recommend you have model aware of views. In #SteveTurczyn 's solution, an abstract field is introduced into model to identified the which page it come from, which is an good solution.
As from Ruby on Rail MVC, both View and Model talk to the controller, another solution will be have controller handle validation of params before passing the value to create account.

Rails - ping user without authenticating?

So I'm writing a Facebook clone for a school project using Rails and I need some way to keep track of which users are logged in. At the moment, I'm a bit time-pressed, so I decided just to update the User model every time they visit a page with a last_seen attribute.
Problem is, the user model requires revalidation to successfully update_attributes. So I'm wondering two things:
Is there a better way to do this that I'm missing?
If not (or if it would take too long) is there a way to bypass the validation?
to 1.: I cant give you an exact answer but I think itwould be better to deal with this problem using a javascript on the clientside with a timer that sends an ajax request all xxx secounds and an action that receives this requests and saves it in a seperate table associated with the User.
to 2.: Yes there are some ways to bypass validations The most pragmatic way is to bypass the :validate => false option when saving the object but then you can use update_attributes:
object.save(:validate => false)
So there is also the possibility to use conditional validations that are only used when a specific condition is complyed. There is a railscast about that => http://railscasts.com/episodes/41-conditional-validations .

Is there a way in Rails to say "run all the validates EXCEPT :password"?

I am using Devise for my authentication. If a hashed_password isn't set, Rails/Devise's validations will require a password to be set, as well as the password_confirmation.
When I invite new users, I obviously don't want to set their password, so when I create the invitation in my system, it fails because user.password is blank.
I can set a temporary hashed_password on the user, but when they enter their own password, the validation checks for :password and :password_confirmation will not happen because hashed_password is set, which is a real problem.
Is there any way to tell Rails that I want to run all the validations except for the ones associated with :password?
I know Rails has :if conditions, which might fix my problem, but Devise declares the :password validation on my behalf, so that essentially is hidden.
How can I get the desired result here?, hopefully in a way that is not a hack.
My current hypothetical solution that is somewhat messy: The only thing I can think of is to create a new Invitation model that is not the User model, and use the Invitation model for the form. When the invitation is submitted I can validate that Invitation and copy over all the values to the new User model. I can save that User without any validations at all.
That's the best solution I dreamed up.
It seems like my solution will be a lot more work than saying something simple like:
user.save(validations => {:except => :password})
EDIT: I have found one part of the solution, but I am still having problems. In our user model, we can override a Devise method to prevent the validation of the password for invitations with this bit of code:
#protected
def password_required?
!is_invited && super
end
The is_invited attribute is just a column I added to the users table/model.
However, there is one gotcha here. When a user accepts an invitation and they arrive to the form where they need to set their password/password_confirmation, valid? will always return true.
This one has me deeply perplexed. I don't see how requires_password? and valid? can be true at the same time. If it requires the password, it should do a validation check and cause the validations to fail.
I'm starting to hate Devise - or just the idea of using gems to build parts of your application in a blackbox. I think the real solution probably is to rip out Devise and just do it all from scratch. That way your app has total control of how all of this works :(
I recently started using this great devise add-on: devise_invitable
It's commonly used so users (or any model) can invite other users to join.
But I adapt it for manually (via an admin panel) invite new potential users to my app.
Hope this helps!

Set Authlogic validate_login_field to false on user update

We have a current database of users who can have any symbol in their username. We have started using authlogic for authentication. So, any current users updating any of their information fail validations because their login has unaccepted characters.
We want to prevent new users signing up from using symbols not accepted by authlogic, but those who have them already in their login to continue using them.
I know that I need to use something like this:
acts_as_authentic do |c|
c.validate_login_field = false
end
My questions is how do I set validate_login_field to false for already existing users but leave it to default value of true for new users signing up? Thanks.
I would suggest forgetting about that particular validation feature in Authlogic for your particular case (i.e. keep it as c.validate_login_field = false) and use validates_format_of setting a :with clause to a regex to ensure Authlogic valid user names and an :if clause to some proc or method private method which returns true if the validation should be done at all (using some application specific logic of your own design)
Or you can not to turn of validate_login_field and add :if option to validates_format_of_login_field_options
UPDATE:
Actually forget, it's a bad idea, because in that case you have to provide :if option for every possible validation.
Not only for validates_format_of_login_field_options, but for validates_length_of_login_field_options and validates_uniqueness_of_login_field_options

Authlogic run validations on login prior to create action

I need to run the built-in validations on the login field prior to actually creating the user record, is there a way to do this in Authlogic? The reason for is when a user types in a new login, AJAX is invoked to check and see that the login in unique, valid, etc. Once that is done, the user can enter his email to claim the login, it's a 2 step process.
The User model uses ActiveRecord validations, so this isn't specific to Authlogic. If you want to run the validations on a model you can call user.valid?. This will return true or false depending on if the entire model is valid. However it also fills up the user.errors object so you can then check if a given attribute is valid.
Here is some code that uses RJS to do the AJAX. But you can use anything and organize it however you want.
user = User.new(params[:user])
user.valid? # we aren't interested in the output of this.
error = user.errors.on(:login)
if error
page.insert_html :before, "user_login", content_tag(:span, error, :class => "error_message")
end
You may be interested in my Mastering Rails Forms screencast series where I cover this topic in the 2nd episode.

Resources