##// END OF EJS Templates
Improved on-the-fly account creation. If some attributes are missing (eg. not present in the LDAP) or are invalid, the registration form is displayed so that the user is able to fill or fix these attributes....
Jean-Philippe Lang -
r1661:eb1d969237a9
parent child
Show More
@@ -1,173 +1,191
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class AccountController < ApplicationController
18 class AccountController < ApplicationController
19 layout 'base'
19 layout 'base'
20 helper :custom_fields
20 helper :custom_fields
21 include CustomFieldsHelper
21 include CustomFieldsHelper
22
22
23 # prevents login action to be filtered by check_if_login_required application scope filter
23 # prevents login action to be filtered by check_if_login_required application scope filter
24 skip_before_filter :check_if_login_required, :only => [:login, :lost_password, :register, :activate]
24 skip_before_filter :check_if_login_required, :only => [:login, :lost_password, :register, :activate]
25
25
26 # Show user's account
26 # Show user's account
27 def show
27 def show
28 @user = User.find_active(params[:id])
28 @user = User.find_active(params[:id])
29 @custom_values = @user.custom_values.find(:all, :include => :custom_field)
29 @custom_values = @user.custom_values.find(:all, :include => :custom_field)
30
30
31 # show only public projects and private projects that the logged in user is also a member of
31 # show only public projects and private projects that the logged in user is also a member of
32 @memberships = @user.memberships.select do |membership|
32 @memberships = @user.memberships.select do |membership|
33 membership.project.is_public? || (User.current.member_of?(membership.project))
33 membership.project.is_public? || (User.current.member_of?(membership.project))
34 end
34 end
35 rescue ActiveRecord::RecordNotFound
35 rescue ActiveRecord::RecordNotFound
36 render_404
36 render_404
37 end
37 end
38
38
39 # Login request and validation
39 # Login request and validation
40 def login
40 def login
41 if request.get?
41 if request.get?
42 # Logout user
42 # Logout user
43 self.logged_user = nil
43 self.logged_user = nil
44 else
44 else
45 # Authenticate user
45 # Authenticate user
46 user = User.try_to_login(params[:username], params[:password])
46 user = User.try_to_login(params[:username], params[:password])
47 if user
47 if user.nil?
48 # Invalid credentials
49 flash.now[:error] = l(:notice_account_invalid_creditentials)
50 elsif user.new_record?
51 # Onthefly creation failed, display the registration form to fill/fix attributes
52 @user = user
53 session[:auth_source_registration] = {:login => user.login, :auth_source_id => user.auth_source_id }
54 render :action => 'register'
55 else
56 # Valid user
48 self.logged_user = user
57 self.logged_user = user
49 # generate a key and set cookie if autologin
58 # generate a key and set cookie if autologin
50 if params[:autologin] && Setting.autologin?
59 if params[:autologin] && Setting.autologin?
51 token = Token.create(:user => user, :action => 'autologin')
60 token = Token.create(:user => user, :action => 'autologin')
52 cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now }
61 cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now }
53 end
62 end
54 redirect_back_or_default :controller => 'my', :action => 'page'
63 redirect_back_or_default :controller => 'my', :action => 'page'
55 else
56 flash.now[:error] = l(:notice_account_invalid_creditentials)
57 end
64 end
58 end
65 end
59 rescue User::OnTheFlyCreationFailure
60 flash.now[:error] = 'Redmine could not retrieve the required information from the LDAP to create your account. Please, contact your Redmine administrator.'
61 end
66 end
62
67
63 # Log out current user and redirect to welcome page
68 # Log out current user and redirect to welcome page
64 def logout
69 def logout
65 cookies.delete :autologin
70 cookies.delete :autologin
66 Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin']) if User.current.logged?
71 Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin']) if User.current.logged?
67 self.logged_user = nil
72 self.logged_user = nil
68 redirect_to home_url
73 redirect_to home_url
69 end
74 end
70
75
71 # Enable user to choose a new password
76 # Enable user to choose a new password
72 def lost_password
77 def lost_password
73 redirect_to(home_url) && return unless Setting.lost_password?
78 redirect_to(home_url) && return unless Setting.lost_password?
74 if params[:token]
79 if params[:token]
75 @token = Token.find_by_action_and_value("recovery", params[:token])
80 @token = Token.find_by_action_and_value("recovery", params[:token])
76 redirect_to(home_url) && return unless @token and !@token.expired?
81 redirect_to(home_url) && return unless @token and !@token.expired?
77 @user = @token.user
82 @user = @token.user
78 if request.post?
83 if request.post?
79 @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
84 @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
80 if @user.save
85 if @user.save
81 @token.destroy
86 @token.destroy
82 flash[:notice] = l(:notice_account_password_updated)
87 flash[:notice] = l(:notice_account_password_updated)
83 redirect_to :action => 'login'
88 redirect_to :action => 'login'
84 return
89 return
85 end
90 end
86 end
91 end
87 render :template => "account/password_recovery"
92 render :template => "account/password_recovery"
88 return
93 return
89 else
94 else
90 if request.post?
95 if request.post?
91 user = User.find_by_mail(params[:mail])
96 user = User.find_by_mail(params[:mail])
92 # user not found in db
97 # user not found in db
93 flash.now[:error] = l(:notice_account_unknown_email) and return unless user
98 flash.now[:error] = l(:notice_account_unknown_email) and return unless user
94 # user uses an external authentification
99 # user uses an external authentification
95 flash.now[:error] = l(:notice_can_t_change_password) and return if user.auth_source_id
100 flash.now[:error] = l(:notice_can_t_change_password) and return if user.auth_source_id
96 # create a new token for password recovery
101 # create a new token for password recovery
97 token = Token.new(:user => user, :action => "recovery")
102 token = Token.new(:user => user, :action => "recovery")
98 if token.save
103 if token.save
99 Mailer.deliver_lost_password(token)
104 Mailer.deliver_lost_password(token)
100 flash[:notice] = l(:notice_account_lost_email_sent)
105 flash[:notice] = l(:notice_account_lost_email_sent)
101 redirect_to :action => 'login'
106 redirect_to :action => 'login'
102 return
107 return
103 end
108 end
104 end
109 end
105 end
110 end
106 end
111 end
107
112
108 # User self-registration
113 # User self-registration
109 def register
114 def register
110 redirect_to(home_url) && return unless Setting.self_registration?
115 redirect_to(home_url) && return unless Setting.self_registration? || session[:auth_source_registration]
111 if request.get?
116 if request.get?
117 session[:auth_source_registration] = nil
112 @user = User.new(:language => Setting.default_language)
118 @user = User.new(:language => Setting.default_language)
113 else
119 else
114 @user = User.new(params[:user])
120 @user = User.new(params[:user])
115 @user.admin = false
121 @user.admin = false
116 @user.login = params[:user][:login]
117 @user.status = User::STATUS_REGISTERED
122 @user.status = User::STATUS_REGISTERED
123 if session[:auth_source_registration]
124 @user.status = User::STATUS_ACTIVE
125 @user.login = session[:auth_source_registration][:login]
126 @user.auth_source_id = session[:auth_source_registration][:auth_source_id]
127 if @user.save
128 session[:auth_source_registration] = nil
129 self.logged_user = @user
130 flash[:notice] = l(:notice_account_activated)
131 redirect_to :controller => 'my', :action => 'account'
132 end
133 else
134 @user.login = params[:user][:login]
118 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
135 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
119 case Setting.self_registration
136 case Setting.self_registration
120 when '1'
137 when '1'
121 # Email activation
138 # Email activation
122 token = Token.new(:user => @user, :action => "register")
139 token = Token.new(:user => @user, :action => "register")
123 if @user.save and token.save
140 if @user.save and token.save
124 Mailer.deliver_register(token)
141 Mailer.deliver_register(token)
125 flash[:notice] = l(:notice_account_register_done)
142 flash[:notice] = l(:notice_account_register_done)
126 redirect_to :action => 'login'
143 redirect_to :action => 'login'
127 end
144 end
128 when '3'
145 when '3'
129 # Automatic activation
146 # Automatic activation
130 @user.status = User::STATUS_ACTIVE
147 @user.status = User::STATUS_ACTIVE
131 if @user.save
148 if @user.save
132 self.logged_user = @user
149 self.logged_user = @user
133 flash[:notice] = l(:notice_account_activated)
150 flash[:notice] = l(:notice_account_activated)
134 redirect_to :controller => 'my', :action => 'account'
151 redirect_to :controller => 'my', :action => 'account'
135 end
152 end
136 else
153 else
137 # Manual activation by the administrator
154 # Manual activation by the administrator
138 if @user.save
155 if @user.save
139 # Sends an email to the administrators
156 # Sends an email to the administrators
140 Mailer.deliver_account_activation_request(@user)
157 Mailer.deliver_account_activation_request(@user)
141 flash[:notice] = l(:notice_account_pending)
158 flash[:notice] = l(:notice_account_pending)
142 redirect_to :action => 'login'
159 redirect_to :action => 'login'
143 end
160 end
144 end
161 end
145 end
162 end
146 end
163 end
164 end
147
165
148 # Token based account activation
166 # Token based account activation
149 def activate
167 def activate
150 redirect_to(home_url) && return unless Setting.self_registration? && params[:token]
168 redirect_to(home_url) && return unless Setting.self_registration? && params[:token]
151 token = Token.find_by_action_and_value('register', params[:token])
169 token = Token.find_by_action_and_value('register', params[:token])
152 redirect_to(home_url) && return unless token and !token.expired?
170 redirect_to(home_url) && return unless token and !token.expired?
153 user = token.user
171 user = token.user
154 redirect_to(home_url) && return unless user.status == User::STATUS_REGISTERED
172 redirect_to(home_url) && return unless user.status == User::STATUS_REGISTERED
155 user.status = User::STATUS_ACTIVE
173 user.status = User::STATUS_ACTIVE
156 if user.save
174 if user.save
157 token.destroy
175 token.destroy
158 flash[:notice] = l(:notice_account_activated)
176 flash[:notice] = l(:notice_account_activated)
159 end
177 end
160 redirect_to :action => 'login'
178 redirect_to :action => 'login'
161 end
179 end
162
180
163 private
181 private
164 def logged_user=(user)
182 def logged_user=(user)
165 if user && user.is_a?(User)
183 if user && user.is_a?(User)
166 User.current = user
184 User.current = user
167 session[:user_id] = user.id
185 session[:user_id] = user.id
168 else
186 else
169 User.current = User.anonymous
187 User.current = User.anonymous
170 session[:user_id] = nil
188 session[:user_id] = nil
171 end
189 end
172 end
190 end
173 end
191 end
@@ -1,51 +1,48
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class AuthSource < ActiveRecord::Base
18 class AuthSource < ActiveRecord::Base
19 has_many :users
19 has_many :users
20
20
21 validates_presence_of :name
21 validates_presence_of :name
22 validates_uniqueness_of :name
22 validates_uniqueness_of :name
23 validates_length_of :name, :host, :maximum => 60
23 validates_length_of :name, :maximum => 60
24 validates_length_of :account_password, :maximum => 60, :allow_nil => true
25 validates_length_of :account, :base_dn, :maximum => 255
26 validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30
27
24
28 def authenticate(login, password)
25 def authenticate(login, password)
29 end
26 end
30
27
31 def test_connection
28 def test_connection
32 end
29 end
33
30
34 def auth_method_name
31 def auth_method_name
35 "Abstract"
32 "Abstract"
36 end
33 end
37
34
38 # Try to authenticate a user not yet registered against available sources
35 # Try to authenticate a user not yet registered against available sources
39 def self.authenticate(login, password)
36 def self.authenticate(login, password)
40 AuthSource.find(:all, :conditions => ["onthefly_register=?", true]).each do |source|
37 AuthSource.find(:all, :conditions => ["onthefly_register=?", true]).each do |source|
41 begin
38 begin
42 logger.debug "Authenticating '#{login}' against '#{source.name}'" if logger && logger.debug?
39 logger.debug "Authenticating '#{login}' against '#{source.name}'" if logger && logger.debug?
43 attrs = source.authenticate(login, password)
40 attrs = source.authenticate(login, password)
44 rescue
41 rescue
45 attrs = nil
42 attrs = nil
46 end
43 end
47 return attrs if attrs
44 return attrs if attrs
48 end
45 end
49 return nil
46 return nil
50 end
47 end
51 end
48 end
@@ -1,84 +1,87
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'net/ldap'
18 require 'net/ldap'
19 require 'iconv'
19 require 'iconv'
20
20
21 class AuthSourceLdap < AuthSource
21 class AuthSourceLdap < AuthSource
22 validates_presence_of :host, :port, :attr_login
22 validates_presence_of :host, :port, :attr_login
23 validates_presence_of :attr_firstname, :attr_lastname, :attr_mail, :if => Proc.new { |a| a.onthefly_register? }
23 validates_length_of :name, :host, :account_password, :maximum => 60, :allow_nil => true
24 validates_length_of :account, :base_dn, :maximum => 255, :allow_nil => true
25 validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30, :allow_nil => true
26 validates_numericality_of :port, :only_integer => true
24
27
25 def after_initialize
28 def after_initialize
26 self.port = 389 if self.port == 0
29 self.port = 389 if self.port == 0
27 end
30 end
28
31
29 def authenticate(login, password)
32 def authenticate(login, password)
30 return nil if login.blank? || password.blank?
33 return nil if login.blank? || password.blank?
31 attrs = []
34 attrs = []
32 # get user's DN
35 # get user's DN
33 ldap_con = initialize_ldap_con(self.account, self.account_password)
36 ldap_con = initialize_ldap_con(self.account, self.account_password)
34 login_filter = Net::LDAP::Filter.eq( self.attr_login, login )
37 login_filter = Net::LDAP::Filter.eq( self.attr_login, login )
35 object_filter = Net::LDAP::Filter.eq( "objectClass", "*" )
38 object_filter = Net::LDAP::Filter.eq( "objectClass", "*" )
36 dn = String.new
39 dn = String.new
37 ldap_con.search( :base => self.base_dn,
40 ldap_con.search( :base => self.base_dn,
38 :filter => object_filter & login_filter,
41 :filter => object_filter & login_filter,
39 # only ask for the DN if on-the-fly registration is disabled
42 # only ask for the DN if on-the-fly registration is disabled
40 :attributes=> (onthefly_register? ? ['dn', self.attr_firstname, self.attr_lastname, self.attr_mail] : ['dn'])) do |entry|
43 :attributes=> (onthefly_register? ? ['dn', self.attr_firstname, self.attr_lastname, self.attr_mail] : ['dn'])) do |entry|
41 dn = entry.dn
44 dn = entry.dn
42 attrs = [:firstname => AuthSourceLdap.get_attr(entry, self.attr_firstname),
45 attrs = [:firstname => AuthSourceLdap.get_attr(entry, self.attr_firstname),
43 :lastname => AuthSourceLdap.get_attr(entry, self.attr_lastname),
46 :lastname => AuthSourceLdap.get_attr(entry, self.attr_lastname),
44 :mail => AuthSourceLdap.get_attr(entry, self.attr_mail),
47 :mail => AuthSourceLdap.get_attr(entry, self.attr_mail),
45 :auth_source_id => self.id ] if onthefly_register?
48 :auth_source_id => self.id ] if onthefly_register?
46 end
49 end
47 return nil if dn.empty?
50 return nil if dn.empty?
48 logger.debug "DN found for #{login}: #{dn}" if logger && logger.debug?
51 logger.debug "DN found for #{login}: #{dn}" if logger && logger.debug?
49 # authenticate user
52 # authenticate user
50 ldap_con = initialize_ldap_con(dn, password)
53 ldap_con = initialize_ldap_con(dn, password)
51 return nil unless ldap_con.bind
54 return nil unless ldap_con.bind
52 # return user's attributes
55 # return user's attributes
53 logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
56 logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
54 attrs
57 attrs
55 rescue Net::LDAP::LdapError => text
58 rescue Net::LDAP::LdapError => text
56 raise "LdapError: " + text
59 raise "LdapError: " + text
57 end
60 end
58
61
59 # test the connection to the LDAP
62 # test the connection to the LDAP
60 def test_connection
63 def test_connection
61 ldap_con = initialize_ldap_con(self.account, self.account_password)
64 ldap_con = initialize_ldap_con(self.account, self.account_password)
62 ldap_con.open { }
65 ldap_con.open { }
63 rescue Net::LDAP::LdapError => text
66 rescue Net::LDAP::LdapError => text
64 raise "LdapError: " + text
67 raise "LdapError: " + text
65 end
68 end
66
69
67 def auth_method_name
70 def auth_method_name
68 "LDAP"
71 "LDAP"
69 end
72 end
70
73
71 private
74 private
72 def initialize_ldap_con(ldap_user, ldap_password)
75 def initialize_ldap_con(ldap_user, ldap_password)
73 options = { :host => self.host,
76 options = { :host => self.host,
74 :port => self.port,
77 :port => self.port,
75 :encryption => (self.tls ? :simple_tls : nil)
78 :encryption => (self.tls ? :simple_tls : nil)
76 }
79 }
77 options.merge!(:auth => { :method => :simple, :username => ldap_user, :password => ldap_password }) unless ldap_user.blank? && ldap_password.blank?
80 options.merge!(:auth => { :method => :simple, :username => ldap_user, :password => ldap_password }) unless ldap_user.blank? && ldap_password.blank?
78 Net::LDAP.new options
81 Net::LDAP.new options
79 end
82 end
80
83
81 def self.get_attr(entry, attr_name)
84 def self.get_attr(entry, attr_name)
82 entry[attr_name].is_a?(Array) ? entry[attr_name].first : entry[attr_name]
85 entry[attr_name].is_a?(Array) ? entry[attr_name].first : entry[attr_name]
83 end
86 end
84 end
87 end
@@ -1,298 +1,295
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require "digest/sha1"
18 require "digest/sha1"
19
19
20 class User < ActiveRecord::Base
20 class User < ActiveRecord::Base
21
21
22 class OnTheFlyCreationFailure < Exception; end
22 class OnTheFlyCreationFailure < Exception; end
23
23
24 # Account statuses
24 # Account statuses
25 STATUS_ANONYMOUS = 0
25 STATUS_ANONYMOUS = 0
26 STATUS_ACTIVE = 1
26 STATUS_ACTIVE = 1
27 STATUS_REGISTERED = 2
27 STATUS_REGISTERED = 2
28 STATUS_LOCKED = 3
28 STATUS_LOCKED = 3
29
29
30 USER_FORMATS = {
30 USER_FORMATS = {
31 :firstname_lastname => '#{firstname} #{lastname}',
31 :firstname_lastname => '#{firstname} #{lastname}',
32 :firstname => '#{firstname}',
32 :firstname => '#{firstname}',
33 :lastname_firstname => '#{lastname} #{firstname}',
33 :lastname_firstname => '#{lastname} #{firstname}',
34 :lastname_coma_firstname => '#{lastname}, #{firstname}',
34 :lastname_coma_firstname => '#{lastname}, #{firstname}',
35 :username => '#{login}'
35 :username => '#{login}'
36 }
36 }
37
37
38 has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name", :dependent => :delete_all
38 has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name", :dependent => :delete_all
39 has_many :projects, :through => :memberships
39 has_many :projects, :through => :memberships
40 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
40 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
41 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
41 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
42 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
42 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
43 belongs_to :auth_source
43 belongs_to :auth_source
44
44
45 acts_as_customizable
45 acts_as_customizable
46
46
47 attr_accessor :password, :password_confirmation
47 attr_accessor :password, :password_confirmation
48 attr_accessor :last_before_login_on
48 attr_accessor :last_before_login_on
49 # Prevents unauthorized assignments
49 # Prevents unauthorized assignments
50 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
50 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
51
51
52 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
52 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
53 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }
53 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }
54 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }
54 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }
55 # Login must contain lettres, numbers, underscores only
55 # Login must contain lettres, numbers, underscores only
56 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
56 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
57 validates_length_of :login, :maximum => 30
57 validates_length_of :login, :maximum => 30
58 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
58 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
59 validates_length_of :firstname, :lastname, :maximum => 30
59 validates_length_of :firstname, :lastname, :maximum => 30
60 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
60 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
61 validates_length_of :mail, :maximum => 60, :allow_nil => true
61 validates_length_of :mail, :maximum => 60, :allow_nil => true
62 validates_length_of :password, :minimum => 4, :allow_nil => true
62 validates_length_of :password, :minimum => 4, :allow_nil => true
63 validates_confirmation_of :password, :allow_nil => true
63 validates_confirmation_of :password, :allow_nil => true
64
64
65 def before_create
65 def before_create
66 self.mail_notification = false
66 self.mail_notification = false
67 true
67 true
68 end
68 end
69
69
70 def before_save
70 def before_save
71 # update hashed_password if password was set
71 # update hashed_password if password was set
72 self.hashed_password = User.hash_password(self.password) if self.password
72 self.hashed_password = User.hash_password(self.password) if self.password
73 end
73 end
74
74
75 def self.active
75 def self.active
76 with_scope :find => { :conditions => [ "status = ?", STATUS_ACTIVE ] } do
76 with_scope :find => { :conditions => [ "status = ?", STATUS_ACTIVE ] } do
77 yield
77 yield
78 end
78 end
79 end
79 end
80
80
81 def self.find_active(*args)
81 def self.find_active(*args)
82 active do
82 active do
83 find(*args)
83 find(*args)
84 end
84 end
85 end
85 end
86
86
87 # Returns the user that matches provided login and password, or nil
87 # Returns the user that matches provided login and password, or nil
88 def self.try_to_login(login, password)
88 def self.try_to_login(login, password)
89 # Make sure no one can sign in with an empty password
89 # Make sure no one can sign in with an empty password
90 return nil if password.to_s.empty?
90 return nil if password.to_s.empty?
91 user = find(:first, :conditions => ["login=?", login])
91 user = find(:first, :conditions => ["login=?", login])
92 if user
92 if user
93 # user is already in local database
93 # user is already in local database
94 return nil if !user.active?
94 return nil if !user.active?
95 if user.auth_source
95 if user.auth_source
96 # user has an external authentication method
96 # user has an external authentication method
97 return nil unless user.auth_source.authenticate(login, password)
97 return nil unless user.auth_source.authenticate(login, password)
98 else
98 else
99 # authentication with local password
99 # authentication with local password
100 return nil unless User.hash_password(password) == user.hashed_password
100 return nil unless User.hash_password(password) == user.hashed_password
101 end
101 end
102 else
102 else
103 # user is not yet registered, try to authenticate with available sources
103 # user is not yet registered, try to authenticate with available sources
104 attrs = AuthSource.authenticate(login, password)
104 attrs = AuthSource.authenticate(login, password)
105 if attrs
105 if attrs
106 onthefly = new(*attrs)
106 user = new(*attrs)
107 onthefly.login = login
107 user.login = login
108 onthefly.language = Setting.default_language
108 user.language = Setting.default_language
109 if onthefly.save
109 if user.save
110 user = find(:first, :conditions => ["login=?", login])
110 user.reload
111 logger.info("User '#{user.login}' created from the LDAP") if logger
111 logger.info("User '#{user.login}' created from the LDAP") if logger
112 else
113 logger.error("User '#{onthefly.login}' found in LDAP but could not be created (#{onthefly.errors.full_messages.join(', ')})") if logger
114 raise OnTheFlyCreationFailure.new
115 end
112 end
116 end
113 end
117 end
114 end
118 user.update_attribute(:last_login_on, Time.now) if user
115 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
119 user
116 user
120 rescue => text
117 rescue => text
121 raise text
118 raise text
122 end
119 end
123
120
124 # Return user's full name for display
121 # Return user's full name for display
125 def name(formatter = nil)
122 def name(formatter = nil)
126 f = USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname]
123 f = USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname]
127 eval '"' + f + '"'
124 eval '"' + f + '"'
128 end
125 end
129
126
130 def active?
127 def active?
131 self.status == STATUS_ACTIVE
128 self.status == STATUS_ACTIVE
132 end
129 end
133
130
134 def registered?
131 def registered?
135 self.status == STATUS_REGISTERED
132 self.status == STATUS_REGISTERED
136 end
133 end
137
134
138 def locked?
135 def locked?
139 self.status == STATUS_LOCKED
136 self.status == STATUS_LOCKED
140 end
137 end
141
138
142 def check_password?(clear_password)
139 def check_password?(clear_password)
143 User.hash_password(clear_password) == self.hashed_password
140 User.hash_password(clear_password) == self.hashed_password
144 end
141 end
145
142
146 def pref
143 def pref
147 self.preference ||= UserPreference.new(:user => self)
144 self.preference ||= UserPreference.new(:user => self)
148 end
145 end
149
146
150 def time_zone
147 def time_zone
151 self.pref.time_zone.nil? ? nil : TimeZone[self.pref.time_zone]
148 self.pref.time_zone.nil? ? nil : TimeZone[self.pref.time_zone]
152 end
149 end
153
150
154 def wants_comments_in_reverse_order?
151 def wants_comments_in_reverse_order?
155 self.pref[:comments_sorting] == 'desc'
152 self.pref[:comments_sorting] == 'desc'
156 end
153 end
157
154
158 # Return user's RSS key (a 40 chars long string), used to access feeds
155 # Return user's RSS key (a 40 chars long string), used to access feeds
159 def rss_key
156 def rss_key
160 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
157 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
161 token.value
158 token.value
162 end
159 end
163
160
164 # Return an array of project ids for which the user has explicitly turned mail notifications on
161 # Return an array of project ids for which the user has explicitly turned mail notifications on
165 def notified_projects_ids
162 def notified_projects_ids
166 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
163 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
167 end
164 end
168
165
169 def notified_project_ids=(ids)
166 def notified_project_ids=(ids)
170 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
167 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
171 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
168 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
172 @notified_projects_ids = nil
169 @notified_projects_ids = nil
173 notified_projects_ids
170 notified_projects_ids
174 end
171 end
175
172
176 def self.find_by_rss_key(key)
173 def self.find_by_rss_key(key)
177 token = Token.find_by_value(key)
174 token = Token.find_by_value(key)
178 token && token.user.active? ? token.user : nil
175 token && token.user.active? ? token.user : nil
179 end
176 end
180
177
181 def self.find_by_autologin_key(key)
178 def self.find_by_autologin_key(key)
182 token = Token.find_by_action_and_value('autologin', key)
179 token = Token.find_by_action_and_value('autologin', key)
183 token && (token.created_on > Setting.autologin.to_i.day.ago) && token.user.active? ? token.user : nil
180 token && (token.created_on > Setting.autologin.to_i.day.ago) && token.user.active? ? token.user : nil
184 end
181 end
185
182
186 def <=>(user)
183 def <=>(user)
187 if user.nil?
184 if user.nil?
188 -1
185 -1
189 elsif lastname.to_s.downcase == user.lastname.to_s.downcase
186 elsif lastname.to_s.downcase == user.lastname.to_s.downcase
190 firstname.to_s.downcase <=> user.firstname.to_s.downcase
187 firstname.to_s.downcase <=> user.firstname.to_s.downcase
191 else
188 else
192 lastname.to_s.downcase <=> user.lastname.to_s.downcase
189 lastname.to_s.downcase <=> user.lastname.to_s.downcase
193 end
190 end
194 end
191 end
195
192
196 def to_s
193 def to_s
197 name
194 name
198 end
195 end
199
196
200 def logged?
197 def logged?
201 true
198 true
202 end
199 end
203
200
204 def anonymous?
201 def anonymous?
205 !logged?
202 !logged?
206 end
203 end
207
204
208 # Return user's role for project
205 # Return user's role for project
209 def role_for_project(project)
206 def role_for_project(project)
210 # No role on archived projects
207 # No role on archived projects
211 return nil unless project && project.active?
208 return nil unless project && project.active?
212 if logged?
209 if logged?
213 # Find project membership
210 # Find project membership
214 membership = memberships.detect {|m| m.project_id == project.id}
211 membership = memberships.detect {|m| m.project_id == project.id}
215 if membership
212 if membership
216 membership.role
213 membership.role
217 else
214 else
218 @role_non_member ||= Role.non_member
215 @role_non_member ||= Role.non_member
219 end
216 end
220 else
217 else
221 @role_anonymous ||= Role.anonymous
218 @role_anonymous ||= Role.anonymous
222 end
219 end
223 end
220 end
224
221
225 # Return true if the user is a member of project
222 # Return true if the user is a member of project
226 def member_of?(project)
223 def member_of?(project)
227 role_for_project(project).member?
224 role_for_project(project).member?
228 end
225 end
229
226
230 # Return true if the user is allowed to do the specified action on project
227 # Return true if the user is allowed to do the specified action on project
231 # action can be:
228 # action can be:
232 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
229 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
233 # * a permission Symbol (eg. :edit_project)
230 # * a permission Symbol (eg. :edit_project)
234 def allowed_to?(action, project, options={})
231 def allowed_to?(action, project, options={})
235 if project
232 if project
236 # No action allowed on archived projects
233 # No action allowed on archived projects
237 return false unless project.active?
234 return false unless project.active?
238 # No action allowed on disabled modules
235 # No action allowed on disabled modules
239 return false unless project.allows_to?(action)
236 return false unless project.allows_to?(action)
240 # Admin users are authorized for anything else
237 # Admin users are authorized for anything else
241 return true if admin?
238 return true if admin?
242
239
243 role = role_for_project(project)
240 role = role_for_project(project)
244 return false unless role
241 return false unless role
245 role.allowed_to?(action) && (project.is_public? || role.member?)
242 role.allowed_to?(action) && (project.is_public? || role.member?)
246
243
247 elsif options[:global]
244 elsif options[:global]
248 # authorize if user has at least one role that has this permission
245 # authorize if user has at least one role that has this permission
249 roles = memberships.collect {|m| m.role}.uniq
246 roles = memberships.collect {|m| m.role}.uniq
250 roles.detect {|r| r.allowed_to?(action)}
247 roles.detect {|r| r.allowed_to?(action)}
251 else
248 else
252 false
249 false
253 end
250 end
254 end
251 end
255
252
256 def self.current=(user)
253 def self.current=(user)
257 @current_user = user
254 @current_user = user
258 end
255 end
259
256
260 def self.current
257 def self.current
261 @current_user ||= User.anonymous
258 @current_user ||= User.anonymous
262 end
259 end
263
260
264 def self.anonymous
261 def self.anonymous
265 anonymous_user = AnonymousUser.find(:first)
262 anonymous_user = AnonymousUser.find(:first)
266 if anonymous_user.nil?
263 if anonymous_user.nil?
267 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
264 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
268 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
265 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
269 end
266 end
270 anonymous_user
267 anonymous_user
271 end
268 end
272
269
273 private
270 private
274 # Return password digest
271 # Return password digest
275 def self.hash_password(clear_password)
272 def self.hash_password(clear_password)
276 Digest::SHA1.hexdigest(clear_password || "")
273 Digest::SHA1.hexdigest(clear_password || "")
277 end
274 end
278 end
275 end
279
276
280 class AnonymousUser < User
277 class AnonymousUser < User
281
278
282 def validate_on_create
279 def validate_on_create
283 # There should be only one AnonymousUser in the database
280 # There should be only one AnonymousUser in the database
284 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
281 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
285 end
282 end
286
283
287 def available_custom_fields
284 def available_custom_fields
288 []
285 []
289 end
286 end
290
287
291 # Overrides a few properties
288 # Overrides a few properties
292 def logged?; false end
289 def logged?; false end
293 def admin; false end
290 def admin; false end
294 def name; 'Anonymous' end
291 def name; 'Anonymous' end
295 def mail; nil end
292 def mail; nil end
296 def time_zone; nil end
293 def time_zone; nil end
297 def rss_key; nil end
294 def rss_key; nil end
298 end
295 end
@@ -1,37 +1,39
1 <h2><%=l(:label_register)%></h2>
1 <h2><%=l(:label_register)%></h2>
2
2
3 <% form_tag({:action => 'register'}, :class => "tabular") do %>
3 <% form_tag({:action => 'register'}, :class => "tabular") do %>
4 <%= error_messages_for 'user' %>
4 <%= error_messages_for 'user' %>
5
5
6 <div class="box">
6 <div class="box">
7 <!--[form:user]-->
7 <!--[form:user]-->
8 <% if @user.auth_source_id.nil? %>
8 <p><label for="user_login"><%=l(:field_login)%> <span class="required">*</span></label>
9 <p><label for="user_login"><%=l(:field_login)%> <span class="required">*</span></label>
9 <%= text_field 'user', 'login', :size => 25 %></p>
10 <%= text_field 'user', 'login', :size => 25 %></p>
10
11
11 <p><label for="password"><%=l(:field_password)%> <span class="required">*</span></label>
12 <p><label for="password"><%=l(:field_password)%> <span class="required">*</span></label>
12 <%= password_field_tag 'password', nil, :size => 25 %><br />
13 <%= password_field_tag 'password', nil, :size => 25 %><br />
13 <em><%= l(:text_caracters_minimum, 4) %></em></p>
14 <em><%= l(:text_caracters_minimum, 4) %></em></p>
14
15
15 <p><label for="password_confirmation"><%=l(:field_password_confirmation)%> <span class="required">*</span></label>
16 <p><label for="password_confirmation"><%=l(:field_password_confirmation)%> <span class="required">*</span></label>
16 <%= password_field_tag 'password_confirmation', nil, :size => 25 %></p>
17 <%= password_field_tag 'password_confirmation', nil, :size => 25 %></p>
18 <% end %>
17
19
18 <p><label for="user_firstname"><%=l(:field_firstname)%> <span class="required">*</span></label>
20 <p><label for="user_firstname"><%=l(:field_firstname)%> <span class="required">*</span></label>
19 <%= text_field 'user', 'firstname' %></p>
21 <%= text_field 'user', 'firstname' %></p>
20
22
21 <p><label for="user_lastname"><%=l(:field_lastname)%> <span class="required">*</span></label>
23 <p><label for="user_lastname"><%=l(:field_lastname)%> <span class="required">*</span></label>
22 <%= text_field 'user', 'lastname' %></p>
24 <%= text_field 'user', 'lastname' %></p>
23
25
24 <p><label for="user_mail"><%=l(:field_mail)%> <span class="required">*</span></label>
26 <p><label for="user_mail"><%=l(:field_mail)%> <span class="required">*</span></label>
25 <%= text_field 'user', 'mail' %></p>
27 <%= text_field 'user', 'mail' %></p>
26
28
27 <p><label for="user_language"><%=l(:field_language)%></label>
29 <p><label for="user_language"><%=l(:field_language)%></label>
28 <%= select("user", "language", lang_options_for_select) %></p>
30 <%= select("user", "language", lang_options_for_select) %></p>
29
31
30 <% @user.custom_field_values.each do |value| %>
32 <% @user.custom_field_values.each do |value| %>
31 <p><%= custom_field_tag_with_label :user, value %></p>
33 <p><%= custom_field_tag_with_label :user, value %></p>
32 <% end %>
34 <% end %>
33 <!--[eoform:user]-->
35 <!--[eoform:user]-->
34 </div>
36 </div>
35
37
36 <%= submit_tag l(:button_submit) %>
38 <%= submit_tag l(:button_submit) %>
37 <% end %>
39 <% end %>
@@ -1,48 +1,44
1 <%= error_messages_for 'auth_source' %>
1 <%= error_messages_for 'auth_source' %>
2
2
3 <div class="box">
3 <div class="box">
4 <!--[form:auth_source]-->
4 <!--[form:auth_source]-->
5 <p><label for="auth_source_name"><%=l(:field_name)%> <span class="required">*</span></label>
5 <p><label for="auth_source_name"><%=l(:field_name)%> <span class="required">*</span></label>
6 <%= text_field 'auth_source', 'name' %></p>
6 <%= text_field 'auth_source', 'name' %></p>
7
7
8 <p><label for="auth_source_host"><%=l(:field_host)%> <span class="required">*</span></label>
8 <p><label for="auth_source_host"><%=l(:field_host)%> <span class="required">*</span></label>
9 <%= text_field 'auth_source', 'host' %></p>
9 <%= text_field 'auth_source', 'host' %></p>
10
10
11 <p><label for="auth_source_port"><%=l(:field_port)%> <span class="required">*</span></label>
11 <p><label for="auth_source_port"><%=l(:field_port)%> <span class="required">*</span></label>
12 <%= text_field 'auth_source', 'port', :size => 6 %> <%= check_box 'auth_source', 'tls' %> LDAPS</p>
12 <%= text_field 'auth_source', 'port', :size => 6 %> <%= check_box 'auth_source', 'tls' %> LDAPS</p>
13
13
14 <p><label for="auth_source_account"><%=l(:field_account)%></label>
14 <p><label for="auth_source_account"><%=l(:field_account)%></label>
15 <%= text_field 'auth_source', 'account' %></p>
15 <%= text_field 'auth_source', 'account' %></p>
16
16
17 <p><label for="auth_source_account_password"><%=l(:field_password)%></label>
17 <p><label for="auth_source_account_password"><%=l(:field_password)%></label>
18 <%= password_field 'auth_source', 'account_password', :name => 'ignore',
18 <%= password_field 'auth_source', 'account_password', :name => 'ignore',
19 :value => ((@auth_source.new_record? || @auth_source.account_password.blank?) ? '' : ('x'*15)),
19 :value => ((@auth_source.new_record? || @auth_source.account_password.blank?) ? '' : ('x'*15)),
20 :onfocus => "this.value=''; this.name='auth_source[account_password]';",
20 :onfocus => "this.value=''; this.name='auth_source[account_password]';",
21 :onchange => "this.name='auth_source[account_password]';" %></p>
21 :onchange => "this.name='auth_source[account_password]';" %></p>
22
22
23 <p><label for="auth_source_base_dn"><%=l(:field_base_dn)%> <span class="required">*</span></label>
23 <p><label for="auth_source_base_dn"><%=l(:field_base_dn)%> <span class="required">*</span></label>
24 <%= text_field 'auth_source', 'base_dn', :size => 60 %></p>
24 <%= text_field 'auth_source', 'base_dn', :size => 60 %></p>
25 </div>
26
25
27 <div class="box">
28 <p><label for="auth_source_onthefly_register"><%=l(:field_onthefly)%></label>
26 <p><label for="auth_source_onthefly_register"><%=l(:field_onthefly)%></label>
29 <%= check_box 'auth_source', 'onthefly_register' %></p>
27 <%= check_box 'auth_source', 'onthefly_register' %></p>
28 </div>
30
29
31 <p>
30 <fieldset class="box"><legend><%=l(:label_attribute_plural)%></legend>
32 <fieldset><legend><%=l(:label_attribute_plural)%></legend>
33 <p><label for="auth_source_attr_login"><%=l(:field_login)%> <span class="required">*</span></label>
31 <p><label for="auth_source_attr_login"><%=l(:field_login)%> <span class="required">*</span></label>
34 <%= text_field 'auth_source', 'attr_login', :size => 20 %></p>
32 <%= text_field 'auth_source', 'attr_login', :size => 20 %></p>
35
33
36 <p><label for="auth_source_attr_firstname"><%=l(:field_firstname)%></label>
34 <p><label for="auth_source_attr_firstname"><%=l(:field_firstname)%></label>
37 <%= text_field 'auth_source', 'attr_firstname', :size => 20 %></p>
35 <%= text_field 'auth_source', 'attr_firstname', :size => 20 %></p>
38
36
39 <p><label for="auth_source_attr_lastname"><%=l(:field_lastname)%></label>
37 <p><label for="auth_source_attr_lastname"><%=l(:field_lastname)%></label>
40 <%= text_field 'auth_source', 'attr_lastname', :size => 20 %></p>
38 <%= text_field 'auth_source', 'attr_lastname', :size => 20 %></p>
41
39
42 <p><label for="auth_source_attr_mail"><%=l(:field_mail)%></label>
40 <p><label for="auth_source_attr_mail"><%=l(:field_mail)%></label>
43 <%= text_field 'auth_source', 'attr_mail', :size => 20 %></p>
41 <%= text_field 'auth_source', 'attr_mail', :size => 20 %></p>
44 </fieldset>
42 </fieldset>
45 </p>
46 </div>
47 <!--[eoform:auth_source]-->
43 <!--[eoform:auth_source]-->
48
44
@@ -1,105 +1,153
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require "#{File.dirname(__FILE__)}/../test_helper"
18 require "#{File.dirname(__FILE__)}/../test_helper"
19
19
20 begin
21 require 'mocha'
22 rescue
23 # Won't run some tests
24 end
25
20 class AccountTest < ActionController::IntegrationTest
26 class AccountTest < ActionController::IntegrationTest
21 fixtures :users
27 fixtures :users
22
28
23 # Replace this with your real tests.
29 # Replace this with your real tests.
24 def test_login
30 def test_login
25 get "my/page"
31 get "my/page"
26 assert_redirected_to "account/login"
32 assert_redirected_to "account/login"
27 log_user('jsmith', 'jsmith')
33 log_user('jsmith', 'jsmith')
28
34
29 get "my/account"
35 get "my/account"
30 assert_response :success
36 assert_response :success
31 assert_template "my/account"
37 assert_template "my/account"
32 end
38 end
33
39
34 def test_lost_password
40 def test_lost_password
35 Token.delete_all
41 Token.delete_all
36
42
37 get "account/lost_password"
43 get "account/lost_password"
38 assert_response :success
44 assert_response :success
39 assert_template "account/lost_password"
45 assert_template "account/lost_password"
40
46
41 post "account/lost_password", :mail => 'jsmith@somenet.foo'
47 post "account/lost_password", :mail => 'jsmith@somenet.foo'
42 assert_redirected_to "account/login"
48 assert_redirected_to "account/login"
43
49
44 token = Token.find(:first)
50 token = Token.find(:first)
45 assert_equal 'recovery', token.action
51 assert_equal 'recovery', token.action
46 assert_equal 'jsmith@somenet.foo', token.user.mail
52 assert_equal 'jsmith@somenet.foo', token.user.mail
47 assert !token.expired?
53 assert !token.expired?
48
54
49 get "account/lost_password", :token => token.value
55 get "account/lost_password", :token => token.value
50 assert_response :success
56 assert_response :success
51 assert_template "account/password_recovery"
57 assert_template "account/password_recovery"
52
58
53 post "account/lost_password", :token => token.value, :new_password => 'newpass', :new_password_confirmation => 'newpass'
59 post "account/lost_password", :token => token.value, :new_password => 'newpass', :new_password_confirmation => 'newpass'
54 assert_redirected_to "account/login"
60 assert_redirected_to "account/login"
55 assert_equal 'Password was successfully updated.', flash[:notice]
61 assert_equal 'Password was successfully updated.', flash[:notice]
56
62
57 log_user('jsmith', 'newpass')
63 log_user('jsmith', 'newpass')
58 assert_equal 0, Token.count
64 assert_equal 0, Token.count
59 end
65 end
60
66
61 def test_register_with_automatic_activation
67 def test_register_with_automatic_activation
62 Setting.self_registration = '3'
68 Setting.self_registration = '3'
63
69
64 get 'account/register'
70 get 'account/register'
65 assert_response :success
71 assert_response :success
66 assert_template 'account/register'
72 assert_template 'account/register'
67
73
68 post 'account/register', :user => {:login => "newuser", :language => "en", :firstname => "New", :lastname => "User", :mail => "newuser@foo.bar"},
74 post 'account/register', :user => {:login => "newuser", :language => "en", :firstname => "New", :lastname => "User", :mail => "newuser@foo.bar"},
69 :password => "newpass", :password_confirmation => "newpass"
75 :password => "newpass", :password_confirmation => "newpass"
70 assert_redirected_to 'my/account'
76 assert_redirected_to 'my/account'
71 follow_redirect!
77 follow_redirect!
72 assert_response :success
78 assert_response :success
73 assert_template 'my/account'
79 assert_template 'my/account'
74
80
75 assert User.find_by_login('newuser').active?
81 assert User.find_by_login('newuser').active?
76 end
82 end
77
83
78 def test_register_with_manual_activation
84 def test_register_with_manual_activation
79 Setting.self_registration = '2'
85 Setting.self_registration = '2'
80
86
81 post 'account/register', :user => {:login => "newuser", :language => "en", :firstname => "New", :lastname => "User", :mail => "newuser@foo.bar"},
87 post 'account/register', :user => {:login => "newuser", :language => "en", :firstname => "New", :lastname => "User", :mail => "newuser@foo.bar"},
82 :password => "newpass", :password_confirmation => "newpass"
88 :password => "newpass", :password_confirmation => "newpass"
83 assert_redirected_to 'account/login'
89 assert_redirected_to 'account/login'
84 assert !User.find_by_login('newuser').active?
90 assert !User.find_by_login('newuser').active?
85 end
91 end
86
92
87 def test_register_with_email_activation
93 def test_register_with_email_activation
88 Setting.self_registration = '1'
94 Setting.self_registration = '1'
89 Token.delete_all
95 Token.delete_all
90
96
91 post 'account/register', :user => {:login => "newuser", :language => "en", :firstname => "New", :lastname => "User", :mail => "newuser@foo.bar"},
97 post 'account/register', :user => {:login => "newuser", :language => "en", :firstname => "New", :lastname => "User", :mail => "newuser@foo.bar"},
92 :password => "newpass", :password_confirmation => "newpass"
98 :password => "newpass", :password_confirmation => "newpass"
93 assert_redirected_to 'account/login'
99 assert_redirected_to 'account/login'
94 assert !User.find_by_login('newuser').active?
100 assert !User.find_by_login('newuser').active?
95
101
96 token = Token.find(:first)
102 token = Token.find(:first)
97 assert_equal 'register', token.action
103 assert_equal 'register', token.action
98 assert_equal 'newuser@foo.bar', token.user.mail
104 assert_equal 'newuser@foo.bar', token.user.mail
99 assert !token.expired?
105 assert !token.expired?
100
106
101 get 'account/activate', :token => token.value
107 get 'account/activate', :token => token.value
102 assert_redirected_to 'account/login'
108 assert_redirected_to 'account/login'
103 log_user('newuser', 'newpass')
109 log_user('newuser', 'newpass')
104 end
110 end
111
112 if Object.const_defined?(:Mocha)
113
114 def test_onthefly_registration
115 # disable registration
116 Setting.self_registration = '0'
117 AuthSource.expects(:authenticate).returns([:login => 'foo', :firstname => 'Foo', :lastname => 'Smith', :mail => 'foo@bar.com', :auth_source_id => 66])
118
119 post 'account/login', :username => 'foo', :password => 'bar'
120 assert_redirected_to 'my/page'
121
122 user = User.find_by_login('foo')
123 assert user.is_a?(User)
124 assert_equal 66, user.auth_source_id
125 assert user.hashed_password.blank?
126 end
127
128 def test_onthefly_registration_with_invalid_attributes
129 # disable registration
130 Setting.self_registration = '0'
131 AuthSource.expects(:authenticate).returns([:login => 'foo', :lastname => 'Smith', :auth_source_id => 66])
132
133 post 'account/login', :username => 'foo', :password => 'bar'
134 assert_response :success
135 assert_template 'account/register'
136 assert_tag :input, :attributes => { :name => 'user[firstname]', :value => '' }
137 assert_tag :input, :attributes => { :name => 'user[lastname]', :value => 'Smith' }
138 assert_no_tag :input, :attributes => { :name => 'user[login]' }
139 assert_no_tag :input, :attributes => { :name => 'user[password]' }
140
141 post 'account/register', :user => {:firstname => 'Foo', :lastname => 'Smith', :mail => 'foo@bar.com'}
142 assert_redirected_to 'my/account'
143
144 user = User.find_by_login('foo')
145 assert user.is_a?(User)
146 assert_equal 66, user.auth_source_id
147 assert user.hashed_password.blank?
148 end
149
150 else
151 puts 'Mocha is missing. Skipping tests.'
152 end
105 end
153 end
General Comments 0
You need to be logged in to leave comments. Login now