require 'digest/sha1' class User < ActiveRecord::Base has_many :projects # Virtual attribute for the unencrypted password attr_accessor :password attr_accessor :host attr_accessor :port validates_presence_of :login, :email, :name, :pi, :institution validates_presence_of :password, :if => :password_required? validates_presence_of :password_confirmation, :if => :password_required? validates_length_of :password, :within => 4..40, :if => :password_required? validates_confirmation_of :password, :if => :password_required? validates_length_of :login, :within => 3..40 validates_length_of :email, :within => 3..100 validates_uniqueness_of :login, :case_sensitive => false validates_format_of :email, :with => /(^([^@\s]+)@((?:[-_a-z0-9]+\.)+[a-z]{2,})$)|(^$)/i validates_length_of :new_email, :within => 6..100, :if => :new_email_entered? validates_format_of :new_email, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/, :if => :new_email_entered? before_save :encrypt_password before_create :make_activation_code #after_save :update_ftp_password # Authenticates a user by their login name and unencrypted password. Returns the user or nil. def self.authenticate(login, password) # hide records with a nil activated_at u = find :first, :conditions => ['login = ? and activated_at IS NOT NULL', login] u && u.authenticated?(password) ? u : nil end def self.authenticateAs(login, password, loginAs) # hide records with a nil activated_at u = find :first, :conditions => ['login = ? and activated_at IS NOT NULL', login] if u && u.authenticated?(password) && u.role == "admin" u = find :first, :conditions => ['login = ? and activated_at IS NOT NULL', loginAs] else u = nil end return u end # Encrypts some data with the salt. def self.encrypt(password, salt) Digest::SHA1.hexdigest("--#{salt}--#{password}--") end # Encrypts the password with the user salt def encrypt(password) self.class.encrypt(password, salt) end def authenticated?(password) crypted_password == encrypt(password) end def remember_token? remember_token_expires_at && Time.now.utc < remember_token_expires_at end # These create and unset the fields required for remembering users between browser closes def remember_me self.remember_token_expires_at = 2.weeks.from_now.utc self.remember_token = encrypt("#{email}--#{remember_token_expires_at}") save(false) end def forget_me self.remember_token_expires_at = nil self.remember_token = nil save(false) end # Activates the user in the database. def activate @activated = true update_attributes(:activated_at => Time.now.utc, :activation_code => nil) end # Returns true if the user has just been activated. def recently_activated? @activated end # password reset section def forgot_password @forgotten_password = true self.make_password_reset_code end def reset_password # First update the password_reset_code before setting the # reset_password flag to avoid duplicate email notifications. update_attributes(:password_reset_code => nil) @reset_password = true end def recently_reset_password? @reset_password end def recently_forgot_password? @forgotten_password end # change email section def change_email_address(new_email_address) @change_email = true self.new_email = new_email_address self.make_email_activation_code end def activate_new_email @activated_email = true update_attributes(:email=> self.new_email, :new_email => nil, :email_activation_code => nil) end def recently_changed_email? @change_email end # Assures that updated email addresses do not conflict with # existing email addresses. def validate if User.find_by_email(new_email) errors.add(new_email, "is already being used") end if login == "admin" temp = User.find(self.id) if (not temp) or (temp.login != "admin") errors.add("login", "admin reserved") end end end def update_ftp_password return if password.blank? create_ftp_user # if needed #logger.info "\n\nGALT! password=[#{password}]\n\n" #debug #logger.info "GALT! login=[#{login}]" #debug ftpMount = ActiveRecord::Base.configurations[RAILS_ENV]['ftpMount'] #logger.info "GALT! ftpMount=[#{ftpMount}]" #debug fullName = ftpMount + "/ftp_passwd" begin; FileUtils.chmod 0664, fullName if File.exists?(fullName); rescue; end # otherwise it can't over-write the file cmd = "echo #{password} | ftpasswd --passwd --name=#{login} --change-password --stdin --file=#{ftpMount}/ftp_passwd" # note ftpasswd will over-write the file and then try to change its perms to 444 #logger.info "GALT! cmd=[#{cmd}]\n\n" #debug system(cmd) #logger.info "GALT! $?=[#{$?}]\n\n" #debug return # don't want it to return any value end protected # before filter def create_ftp_user ftpMount = ActiveRecord::Base.configurations[RAILS_ENV]['ftpMount'] userFtpDir = File.join(ftpMount,login) return if File.exists?(userFtpDir) ftpLocal = ActiveRecord::Base.configurations[RAILS_ENV]['ftpLocal'] ftpUserId = ActiveRecord::Base.configurations[RAILS_ENV]['ftpUserId'] ftpGroupId = ActiveRecord::Base.configurations[RAILS_ENV]['ftpGroupId'] Dir.mkdir(userFtpDir,0775) fullName = ftpMount + "/ftp_passwd" begin; FileUtils.chmod 0664, fullName if File.exists?(fullName); rescue; end # otherwise it can't over-write the file cmd = "echo password | ftpasswd --passwd --name=#{login} --stdin --uid=#{ftpUserId} --gid=#{ftpGroupId}" + " --home=#{ftpLocal}/#{login} --shell=/sbin/nologin --file=#{ftpMount}/ftp_passwd" # note ftpasswd will over-write the file and then try to change its perms to 444 #logger.info "\n\nGALT! cmd=[#{cmd}]" #debug system(cmd) #logger.info "GALT! $?=[#{$?}]\n\n" #debug return # don't want it to return any value end #before_save # I don't know why, but this gets called up to 4 or more times for one actual save?! def encrypt_password return if password.blank? self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record? self.crypted_password = encrypt(password) end def password_required? crypted_password.blank? || !password.blank? end # before_create def make_activation_code self.activation_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join ) end def make_password_reset_code self.password_reset_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join ) end def make_email_activation_code self.email_activation_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join ) end def new_email_entered? !self.new_email.blank? end end