aboutsummaryrefslogtreecommitdiffstats
path: root/app/models/user.rb
blob: bc1028ae1f8af7d1d8f3061dc7144f0b9bea6942 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# == Schema Information
# Schema version: 39
#
# Table name: users
#
#  id              :integer         not null, primary key
#  email           :string(255)     not null
#  name            :string(255)     not null
#  hashed_password :string(255)     not null
#  salt            :string(255)     not null
#  created_at      :datetime        not null
#  updated_at      :datetime        not null
#  email_confirmed :boolean         default(false), not null
#  url_name        :text            not null
#

# models/user.rb:
# Model of people who use the site to file requests, make comments etc.
#
# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
# Email: francis@mysociety.org; WWW: http://www.mysociety.org/
#
# $Id: user.rb,v 1.33 2008-02-28 16:25:30 francis Exp $

require 'digest/sha1'

class User < ActiveRecord::Base
    validates_presence_of :email, :message => "^Please enter your email address"

    validates_presence_of :name, :message => "^Please enter your name"
    validates_presence_of :url_name

    validates_presence_of :hashed_password, :message => "^Please enter a password"

    has_many :info_requests
    has_many :user_info_request_sent_alerts
    has_many :post_redirects

    attr_accessor :password_confirmation
    validates_confirmation_of :password, :message =>"^Please enter the same password twice"

    def validate
        errors.add(:email, "doesn't look like a valid address") unless MySociety::Validate.is_valid_email(self.email)
    end

    # Return user given login email, password and other form parameters (e.g. name)
    #  
    # The specific_user_login parameter says that login as a particular user is
    # expected, so no parallel registration form is being displayed.
    def self.authenticate_from_form(params, specific_user_login = false)
        if specific_user_login
            auth_fail_message = "Either the email or password was not recognised, please try again."
        else
            auth_fail_message = "Either the email or password was not recognised, please try again. Or create a new account using the form on the right."
        end

        user = self.find_user_by_email(params[:email])
        if user
            # There is user with email, check password
            expected_password = encrypted_password(params[:password], user.salt)
            if user.hashed_password != expected_password
                user.errors.add_to_base(auth_fail_message)
            end
        else
            # No user of same email, make one (that we don't save in the database)
            # for the forms code to use.
            user = User.new(params)
            # deliberately same message as above so as not to leak whether registered
            user.errors.add_to_base(auth_fail_message)
        end
        user
    end

    # Case-insensitively find a user from their email
    def self.find_user_by_email(email)
        return self.find(:first, :conditions => [ 'email ilike ?', email ] ) # using ilike for case insensitive
    end

    # When name is changed, also change the url name
    def name=(name)
        write_attribute(:name, name)
        self.update_url_name
    end
    def update_url_name
        url_name = MySociety::Format.simplify_url_part(self.name)
        write_attribute(:url_name, url_name)
    end

    # Virtual password attribute, which stores the hashed password, rather than plain text.
    def password
        @password
    end
    def password=(pwd)
        @password = pwd
        if pwd.blank?
            self.hashed_password = nil
            return
        end
        create_new_salt
        self.hashed_password = User.encrypted_password(self.password, self.salt)
    end

    # For use in to/from in email messages
    def name_and_email
        return self.name + " <" + self.email + ">"
    end

    private

    def self.encrypted_password(password, salt)
        string_to_hash = password + salt # XXX need to add a secret here too?
        Digest::SHA1.hexdigest(string_to_hash)
    end
    
    def create_new_salt
        self.salt = self.object_id.to_s + rand.to_s
    end
end