blob: 5fb9ff06a0d90363ff20d43c1b57527e1efcfd25 [file] [log] [blame]
# == Schema Information
# Schema version: 1
#
# Table name: users
#
# id :integer(11) not null, primary key
# email :string(250) default(), not null
# name :string(50) default(), not null
# ip_address :string(20) default(), not null
# hashed_password :string(40)
# hashed_password_new :string(40)
# admin :string(1) default(N), not null
# notify_daily :integer(1) default(0), not null
# notify_weekly :integer(1) default(0), not null
# notify_monthly :integer(1) default(0), not null
# site_id :integer(10)
# created_on :datetime
# updated_on :datetime
# http_user_agent :string(250)
# logon_count :integer(5) default(0)
# logon_using_cookie_count :integer(5) default(0)
# last_logon :datetime
# confirmed_on :datetime
#
require 'digest/sha1'
# More information:
# * {EPF Wiki Data model}[link:files/doc/DATAMODEL.html]
#--######################################################################
# Copyright (c) 2006 LogicaCMG
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
# Contributors:
#
# Onno van der Straaten:: initial implementation
#++######################################################################
# {Copyright (c) 2006 LogicaCMG}[link:files/COPYRIGHT.html]
class User < ActiveRecord::Base
DOMAIN_PATTERN = /@.*/
has_many :sites
has_many :checkouts
has_many :versions
has_many :comments
has_many :notifications
has_many :versions2review, :class_name => "Version", :foreign_key => "reviewer_id"
has_many :comments2review, :class_name => "Comment", :foreign_key => "reviewer_id"
belongs_to :user_thatmarkeddone, :class_name => "User", :foreign_key => "user_id_markdone"
belongs_to :user_thatmarkedtodo, :class_name => "User", :foreign_key => "user_id_marktodo"
belongs_to :site # default site of user TODO: not used
validates_confirmation_of :password
validates_presence_of :name, :email
validates_uniqueness_of :name, :email
validates_format_of :email, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
validates_format_of :admin, :with => /Y|N|C/
attr_accessor :password, :remember_me, :email_extension
# Changing account to admin or cadmin requires that
# you specify the user that is requesting the change
attr_accessor :user
validates_associated :user
# #new_cadmin creates the central adminstrator user
def self.new_cadmin(params)
raise 'Already create central admin' if User.count > 0
user = User.new(params)
user.hashed_password = hash_pw(user.password) if user.password
user.admin = "C"
user.confirmed_on = Time.now
return user
end
# #new_signup creates an ordinary user account
def self.new_signup(params)
user = User.new(params)
user.email = user.email + user.email_extension if ENV['EPFWIKI_DOMAINS'] && user.email_extension
if ENV['EPFWIKI_GENERATE_PASSWORDS'] == '1'
logger.info("Creating account with generated password for #{user.email}")
user.password = user.generate_new_pw
user.password_confirmation = user.password
user.confirmed_on = Time.now # account does not need to be confirmed
else
logger.info("Creating account with supplied password for #{user.email}")
end
user.hashed_password = hash_pw(user.password) if user.password
return user
end
# #login searches the user on email and hashed_password and returns it, see also #try_to_login
def self.login(email, password)
logger.info("Doing login for #{email} with password #{password}, hash_pw is #{hash_pw(password)}")
hashed_password = hash_pw(password)
user = find(:first, :conditions => ["email = ? and hashed_password = ?", email.downcase, hashed_password])
return nil if user && (password.nil? || user.confirmed_on.nil?)
return user
end
# #confirm_account is used to confirm new accounts or confirm new passwords in case user requested on
def confirm_account(token)
logger.debug("Confirming account with token: " + token)
logger.debug("Hashed password is: " + self.hashed_password)
logger.debug("Hashed password new is: " + (self.hashed_password_new || ''))
if self.hashed_password && (hash_pw(self.hashed_password) == token)
self.confirmed_on = Time.now
elsif self.hashed_password_new && (hash_pw(self.hashed_password_new) == token)
self.confirmed_on = Time.now
self.hashed_password = self.hashed_password_new
self.hashed_password_new = nil
else
raise "Failed to activate account for #{self.email}"
end
end
# Use #set_new_pw to set and return a new password for a user.
# Needs to be confirmed using #confirm_account
def set_new_pw
new_pw = generate_new_pw
self.password = new_pw
self.hashed_password_new = hash_pw(new_pw)
end
# Log in if the name and password (after hashing)
# match the database, or if the name matches
# an entry in the database with no password
def try_to_login
User.login(self.email.downcase, self.password)
end
# #change_password changes the password of a User
def change_password(user)
raise "Password can't be blank" if user.password.blank?
self.password = user.password
self.password_confirmation = user.password_confirmation
self.hashed_password = hash_pw(user.password)
self.confirmed_on = Time.now
end
def self.cadmin(from, to)
raise 'From needs to be central admin' if !from.cadmin?
User.transaction(from, to) do
to.admin = 'C'
to.user = from
from.admin = 'Y'
to.save
from.save
end
end
# Count of versions of user without counting base versions
def versions_count_excluding_baseversions
return Version.count_by_sql('select count(*) from versions where user_id = ' + self.id.to_s + ' and version <> 0 ' )
end
# Token that can be used to confirm a account
def token
return Digest::SHA1.hexdigest(self.hashed_password)
end
def user?
return admin == 'N'
end
def admin?
return admin == 'Y' || admin == 'C'
end
def cadmin?
return admin == 'C'
end
def self.find_central_admin
return User.find(:first, :conditions => ["admin=?", "C"] )
end
def documents_path
return "users/" + id.to_s + "/docs"
end
def images_path
return "users/" + id.to_s + "/images"
end
# #sites returns Site records where user created versions or comments
def sites
return Site.find(:all, :conditions => ['exists (select * from versions where user_id = ? and site_id = sites.id) or exists (select * from comments where user_id = ? and site_id = sites.id)', id, id])
end
# Returns list of uploaded images
def images
path = "#{ENV['EPFWIKI_ROOT_DIR']}public/#{self.images_path}"
logger.debug("Getting entries in directory #{path}")
File.makedirs(path)
return Dir.entries(path) - [".", "..", "Thumbs.db"] # NOTE: dir entires returns array like [".", "..", "config.h", "main.rb"]
end
# Returns list of uploaded documents
def documents
path = "#{ENV['EPFWIKI_ROOT_DIR']}public/#{self.documents_path}"
logger.debug("Getting documents in #{path}")
File.makedirs(path)
return Dir.entries(path) - [".", "..", "Thumbs.db"]
end
def before_validation_on_update
end
def validate
if ENV['EPFWIKI_DOMAINS']
valid_domain = !ENV['EPFWIKI_DOMAINS'].split(" ").index(DOMAIN_PATTERN.match(email.downcase).to_s).nil?
errors.add(:email, "domain not valid") if !valid_domain && !self.cadmin?
end
end
def validate_on_create
if ENV['EPFWIKI_GENERATE_PASSWORDS'] == '1'
else
errors.add(:password, "can't be blank") if password.blank? || hashed_password.blank?
errors.add(:password_confirmation, "can't be blank") if password_confirmation.blank?
end
errors.add("Central admin already exists") if User.count > 0 && admin == 'C'
end
def validate_on_update
errors.add(:hashed_password, "can't be blank") if hashed_password.blank?
old_admin = User.find(id).admin
if admin == 'C' and old_admin != 'C'
if user.nil? || User.find(user.id).admin != 'C'
errors.add(:admin, 'can only be set to C by the central admin')
end
end
if admin == 'Y' and old_admin == 'N'
errors.add(:admin, 'can only be set by an admin') if user.nil? || user.admin == 'N'
end
if admin == 'N' and !old_admin.index(/Y|C/).nil?
errors.add(:admin, 'can only be revoked by the central admin') if user.nil? || user.admin != 'C'
end
end
def before_save
self.email = self.email.downcase
end
def generate_new_pw
random = rand(10-6+1)+6 # TODO: random is not used anymore?
chars = ["A".."Z","a".."z","0".."9"].collect { |r| r.to_a }.join + %q(!@$%^&*)
(1..8).collect { chars[rand(chars.size)] }.pack("C*")
end
end