When a user registers on my app they have to confirm their email, powered by Devise + Rails 3.
The email address defines the user's permissions so I don't want the user to be able to change it once registered. so removed :email from the users.rb attr_accessible which worked for a logged in user, but now user's can't register.
What's the right way to handle this? So users can't update their email but can register with their email using devise.
Thanks
This is the perfect case for a custom validator. Since Rails3, they are much easier to do than before.
class ImmutableValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors[attribute] << "cannot be changed after creation" if record.send("#{attribute}_changed?") && !record.new_record?
end
end
class User < ActiveRecord::Base
validates :email, :immutable => true
end
attr_readonly :email
That solved the problem easily.
https://groups.google.com/forum/#!topic/plataformatec-devise/skCarCHr0p8
I would personally leave the attr_accessible for :email and just remove the email field from the edit view. Also, you will want to strip out any email param from the params hash in the update action.
Related
Having trouble with users registering using our own company email address.
Example: I work at twitter. Users trying to use: user#twitter.com as their email address.
What would I add to the user model to block this from happening?
Try using a custom validation. Here is a super slimmed down example. You may want a bit more power, but this should get you started.
class User < ActiveRecord::Base
validates :email, blacklist: true
end
class BlacklistValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors.add attribute, (options[:message] || "is not a valid email") if
value =~ /twitter\.com/
end
end
Example:
Accepted extensions: "#blogsllc.org"
A user signs up with the email "joe#blogsllc.org" would be able to create an account.
Wondering what would be the best way to do this in Rails and how others would approach this? I imagined trying to check the format of the email address against a bunch of regular expressions but this could be tedious as the list of supported extensions grow.
The other way to do this would be to have a database of the supported extensions and check the created email address against the database to see if the extension is accepted but I'm not sure what would be the best way to implement this in Rails.
I'm looking to implement something similar to what Facebook did in it's early days.
Any help appreciated.
EDIT for misunderstanding:
If you don't need anything more fancy than a straight-up match of the domain (files have extensions, emails have domains), just splitting on # and matching the second part with a database column is the easiest way.
You can add the following code
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless value =~ /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
record.errors[attribute] << (options[:message] || "is not an email")
end
end
end
class Person < ActiveRecord::Base
validates :email, :presence => true, :email => true
end
This Link will be useful
Trying to validate the email field in a rails devise app with a word exclusion.
validates :email, :exclusion => {:in => ["admin", "root",,
:message => "is reserved"}
Wich works great on the :username field but not on the email.
I suspect that Devise is "taken over" the validation of the email field and I need to super/ overrule the registrations controller of Devise.
How could I:
Prevent emails with words based on an :exclusion
Prevent emails with #myapp.com so users don't use the domain the app runs on.
You can always write custom validations to achieve what you want. Example from the guide:
The easiest way to add custom validators for validating individual
attributes is with the convenient ActiveModel::EachValidator. In this
case, the custom validator class must implement a validate_each method
which takes three arguments: record, attribute and value which
correspond to the instance, the attribute to be validated and the
value of the attribute in the passed instance.
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless value =~ /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
record.errors[attribute] << (options[:message] || "is not an email")
end
end
end
class Person < ActiveRecord::Base
validates :email, :presence => true, :email => true
end
After checking if it is an e-mail, I would split the two validations. One for exclusion, and other for domain. You can retrieve the current domain when using passenger.
validate :email_username_is_not_on_blacklist, :email_domain_is_allowed
EXCLUDED_USERNAMES = "admin", "root"
EXCLUDED_DOMAINS = "myapp.com"
def email_username_is_not_on_blacklist
user, domain = email.split("#")
errors.add(:email, "E-mail user is not allowed") if self.class.EXCLUDED_USERNAMES.include?(user)
end
def email_domain_is_allowed
user, domain = email.split("#")
errors.add(:email, "E-mail domain is not allowed") if self.class.EXCLUDED_DOMAINS.include?(domain)
end
I've setup Devise (on Rails 3) to use Basecamp-style subdomain authentication. Under this model, a user could be registered twice under different subdomains with the same email address.
For example:
class User < ActiveRecord::Base
belongs_to :account
end
class Account < ActiveRecord::Base
# subdomain attribute stored here
end
User 1 registered on company1.myapp.com with email address bob#acme.com
User 2 registered on company2.myapp.com with email address bob#acme.com
(Both user account are controlled by the same human, but belong to different subdomains.)
Logging in works fine, but the standard Password Reset only looks up by email address, so you can only ever reset the password for User 1. What I'd like to do is take into account the request subdomain, so a password reset from company2.myapp.com/password/new would reset the password for User 2.
The Devise looks up the user using a find_first method, which I don't think accepts joins, so I can't include a :account => {:subodmain => 'comapny2'} condition.
I can reimplement send_reset_password_instructions to manually look up the user record, but it feels hacky and I'll need to do it for send_confirmation_instructions, too.
Is there a better way?
It looks like this may be configurable with devise_for in the routes file.
From my reading of the source (and I haven't actually tried this), you can add a reset_password_keys option. These should include the subdomain. This is passed to find_or_initialize_with_errors from send_reset_password_instructions in lib/devise/models/recoverable.rb. In find_or_initialize_with_errors it's only these keys which are used to find the resource.
You'll probably also want to override Devise::PasswordsController#new template to include the user's subdomain when they submit the reset password request.
UPDATE: to address the fact that the subdomain is stored on Account and User belongs_to :account you can probably use Rails' delegate method.
We experienced this same issue. Mike Mazur's answer worked, but for one difference:
We put :reset_password_keys => [:email, :subdomain] in the call to the devise method in our Users model.
I recently implement this behaviour in a Rails 4 App.
…/config/initializers/devise.rb
(…)
# ==> Configuration for :recoverable
#
# Defines which key will be used when recovering the password for an account
config.reset_password_keys = [:email, :subdomain]
(…)
…/app/views/devise/passwords/new.html.erb
(…)
<%= f.input :subdomain, required: true %>
(…)
…/app/controllers/users/passwords_controller.rb
class Users::PasswordsController < Devise::PasswordsController
def resource_params
params.require(:user).permit(:email, :subdomain, ...)
end
private :resource_params
end
Let's say I have a form_tag in a view gathering a view user input . With this input that I get through the params hash, I want to verify some of htis information such as email phone name.
Can I just verify in the controller or is this a bad thing? I don't plan to save the thing I get to a db or anything, it's just getting put into an email and sent off, but I want to verify these things before it's sent.
Thank you.
EDIT:
Found this http://www.viddler.com/explore/rails3/videos/6/
class Test
include ActiveRecord::Validations
validates_presence_of :name, :email
attr_accessor :name, :email
end
You can use the model for whatever you need that is related to the object and don't need to save it. Keeping stuff like this in the model is desirable in order to keep controller tidy. Say you have a user model:
#controller
#user.new params[:user]
#user.check_valid
#user.save # is optional
#user.rb
def check_valid
!email.blank? and !phone.blank?
end