Hiding an Attribute in an Activerecord Model

Hiding an attribute in an ActiveRecord model

Based on the above answers, I have done some experimentation to obtain the desired result. I ended up making a "private" password_hash attribute and a virtual accessor called password.

I made some observations in this process:

  • It seems that ActiveRecord doesn't have any concept of private attributes. Making the accessor methods private using symbols such as private :password, :password= is not an option, because Rails throws an NameError: undefined method when instantiating a model, as the model itself does not have these two methods defined (they seem to be inherited from ActiveRecord::Base).

  • Overriding the password_hash accessors with pure nothing is great for preventing the manipulation of the attribute, however it also means that ActiveRecord itself fails when updating the password_hash attribute as it is calling an empty implementation.

So making the accessors private fails because they're undefined in the actual model. Defining them also fails, because it breaks ActiveRecord. So what can you do?

I did both and a bit more. I made the accessors private, defined them and implemented both by calling super. This prevents the controller (and the rails console) from accessing them by throwing a NoMethodError, but doesn't reject ActiveRecord.

A side note: Validation issues

One problem I encountered with my approach was broken validation. Enforcing a minimum length on the password_hash was no good as any password (even nothing) results in a 128 character SHA512 hash. So validating the hash made little sense. Instead I added validation to the virtual password accessor and added a before_save :hash_password callback which checks to see if the virtual accessor attribute has been set and if so, hashes it and writes it to the password_hash attribute.

Final implementation

My implementation ended up this way:

class User < ActiveRecord::Base
attr_accessible :first_name, :last_name, :email
attr_accessor :password
validates :password, :length => { :minimum => 8 }, :if => :password_changed?
validates :first_name, :last_name, :email, presence: true
# Various associations
before_save :hash_password

def password_correct?(p)
if(password.present?)
password == p
else
read_attribute(:password_hash) == hash_string(p)
end
end

def role_symbols
roles.collect do |r|
r.name.to_sym
end
end

private

def hash_string(input)
Digest::SHA2.new(512).update(input).hexdigest
end

def hash_password
if(password.present?)
write_attribute(:password_hash, hash_string(password))
self.password = nil
end
end

def password_changed?
password.present? or new_record?
end

def password_hash
super
end

def password_hash=(p)
super
end

end

Rails: How to hide a model's attribute?

Try using the :only or :except options for the #to_json method. For example:

@comment.to_json(:only => [ :username, :date_added, :modify_date ])

... or without hash rockets if you're on Ruby 1.9 ...

@comment.to_json(only: [ :username, :date_added, :modify_date ])

Hiding Rails Model Attributes

How about overriding as_json method in your Group model?

class Group < ActiveRecord:Base
...
def as_json(options={})
{
:id => id,
:name => name,
:description => description,
:created_at => created_at,
:updated_at => updated_at
}
end
end

rails 4 hide a model attribute before rendering

For the JSON you can override as_json on the model:

def as_json(options={})
options.reverse_merge! except: :accesstoken
super(options)
end

For the logging, in config/application.rb you can add the attribute to the filter list

config.filter_parameters << :accesstoken

Hide a field in Ruby on Rails

You can do it in a format block in your controller like this:

respond_to do |format|
format.json { render :json => @user, :except=> [:ip] } # or without format block: @user.to_json(:except => :ip)
end

If you want to generally exclude specific fields, just overwrite the to_json method in your user model:

class User < ActiveRecord::Base
def to_json(options={})
options[:except] ||= [:ip]
super(options)
end
end

Update: In Rails 6, the method became as_json:

class User < ApplicationRecord
def as_json(options={})
options[:except] ||= [:ip]
super(options)
end
end

ActiveRecord : Hide column while returning object

Using the built-in serialization, you can override the as_json method on your model to pass in additional default options:

class User < ActiveRecord::Base
# ...
def as_json(options = {})
super(options.merge({ except: [:password, :oauth_token] }))
end
end

There are probably better serialization tools out there - if you are looking for more fine-grained control I would recommend checking out active_model_serializers or rabl.

Is there any way to hide attributes in a Rails Jbuilder template?

Try json.merge! model.attributes.except("field_one", "field_two")

Is there a way to make Rails ActiveRecord attributes private?

Jordini was most of the way there

Most of active_record happens in method_missing. If you define the method up front, it won't hit method_missing for that method, and use yours instead (effectively overwriting, but not really)

class YourModel < ActiveRecord::Base

private

def my_private_attribute
self[:my_private_attribute]
end

def my_private_attribute=(val)
write_attribute :my_private_attribute, val
end

end


Related Topics



Leave a reply



Submit