Add links to the ToU and include them in signup
[rails.git] / app / controllers / users_controller.rb
1 class UsersController < ApplicationController
2   layout "site"
3
4   skip_before_action :verify_authenticity_token, :only => [:auth_success]
5   before_action :disable_terms_redirect, :only => [:terms, :save, :logout]
6   before_action :authorize_web
7   before_action :set_locale
8   before_action :check_database_readable
9
10   authorize_resource
11
12   before_action :require_self, :only => [:account]
13   before_action :check_database_writable, :only => [:new, :account, :confirm, :confirm_email, :lost_password, :reset_password, :go_public, :make_friend, :remove_friend]
14   before_action :require_cookies, :only => [:new, :login, :confirm]
15   before_action :lookup_user_by_name, :only => [:set_status, :delete]
16   before_action :allow_thirdparty_images, :only => [:show, :account]
17
18   def terms
19     @legale = params[:legale] || OSM.ip_to_country(request.remote_ip) || Settings.default_legale
20     @text = OSM.legal_text_for_country(@legale)
21
22     if request.xhr?
23       render :partial => "terms"
24     else
25       @title = t "users.terms.title"
26
27       if current_user&.terms_agreed?
28         # Already agreed to terms, so just show settings
29         redirect_to :action => :account, :display_name => current_user.display_name
30       elsif current_user.nil? && session[:new_user].nil?
31         redirect_to :action => :login, :referer => request.fullpath
32       end
33     end
34   end
35
36   def save
37     @title = t "users.new.title"
38
39     if params[:decline]
40       if current_user
41         current_user.terms_seen = true
42
43         flash[:notice] = t("users.new.terms declined", :url => t("users.new.terms declined url")).html_safe if current_user.save
44
45         if params[:referer]
46           redirect_to params[:referer]
47         else
48           redirect_to :action => :account, :display_name => current_user.display_name
49         end
50       else
51         redirect_to t("users.terms.declined")
52       end
53     elsif current_user
54       unless current_user.terms_agreed?
55         current_user.consider_pd = params[:user][:consider_pd]
56         current_user.tou_agreed = Time.now.getutc
57         current_user.terms_agreed = Time.now.getutc
58         current_user.terms_seen = true
59
60         flash[:notice] = t "users.new.terms accepted" if current_user.save
61       end
62
63       if params[:referer]
64         redirect_to params[:referer]
65       else
66         redirect_to :action => :account, :display_name => current_user.display_name
67       end
68     else
69       self.current_user = session.delete(:new_user)
70
71       if check_signup_allowed(current_user.email)
72         current_user.data_public = true
73         current_user.description = "" if current_user.description.nil?
74         current_user.creation_ip = request.remote_ip
75         current_user.languages = http_accept_language.user_preferred_languages
76         current_user.terms_agreed = Time.now.getutc
77         current_user.tou_agreed = Time.now.getutc
78         current_user.terms_seen = true
79
80         if current_user.auth_uid.blank?
81           current_user.auth_provider = nil
82           current_user.auth_uid = nil
83         end
84
85         if current_user.save
86           flash[:piwik_goal] = PIWIK["goals"]["signup"] if defined?(PIWIK)
87
88           referer = welcome_path
89
90           begin
91             uri = URI(session[:referer])
92             %r{map=(.*)/(.*)/(.*)}.match(uri.fragment) do |m|
93               editor = Rack::Utils.parse_query(uri.query).slice("editor")
94               referer = welcome_path({ "zoom" => m[1],
95                                        "lat" => m[2],
96                                        "lon" => m[3] }.merge(editor))
97             end
98           rescue StandardError
99             # Use default
100           end
101
102           if current_user.status == "active"
103             session[:referer] = referer
104             successful_login(current_user)
105           else
106             session[:token] = current_user.tokens.create.token
107             Notifier.signup_confirm(current_user, current_user.tokens.create(:referer => referer)).deliver_later
108             redirect_to :action => "confirm", :display_name => current_user.display_name
109           end
110         else
111           render :action => "new", :referer => params[:referer]
112         end
113       end
114     end
115   end
116
117   def account
118     @tokens = current_user.oauth_tokens.authorized
119
120     if params[:user] && params[:user][:display_name] && params[:user][:description]
121       if params[:user][:auth_provider].blank? ||
122          (params[:user][:auth_provider] == current_user.auth_provider &&
123           params[:user][:auth_uid] == current_user.auth_uid)
124         update_user(current_user, params)
125       else
126         session[:new_user_settings] = params
127         redirect_to auth_url(params[:user][:auth_provider], params[:user][:auth_uid])
128       end
129     elsif errors = session.delete(:user_errors)
130       errors.each do |attribute, error|
131         current_user.errors.add(attribute, error)
132       end
133     end
134     @title = t "users.account.title"
135   end
136
137   def go_public
138     current_user.data_public = true
139     current_user.save
140     flash[:notice] = t "users.go_public.flash success"
141     redirect_to :action => "account", :display_name => current_user.display_name
142   end
143
144   def lost_password
145     @title = t "users.lost_password.title"
146
147     if params[:user] && params[:user][:email]
148       user = User.visible.find_by(:email => params[:user][:email])
149
150       if user.nil?
151         users = User.visible.where("LOWER(email) = LOWER(?)", params[:user][:email])
152
153         user = users.first if users.count == 1
154       end
155
156       if user
157         token = user.tokens.create
158         Notifier.lost_password(user, token).deliver_later
159         flash[:notice] = t "users.lost_password.notice email on way"
160         redirect_to :action => "login"
161       else
162         flash.now[:error] = t "users.lost_password.notice email cannot find"
163       end
164     end
165   end
166
167   def reset_password
168     @title = t "users.reset_password.title"
169
170     if params[:token]
171       token = UserToken.find_by(:token => params[:token])
172
173       if token
174         self.current_user = token.user
175
176         if params[:user]
177           current_user.pass_crypt = params[:user][:pass_crypt]
178           current_user.pass_crypt_confirmation = params[:user][:pass_crypt_confirmation]
179           current_user.status = "active" if current_user.status == "pending"
180           current_user.email_valid = true
181
182           if current_user.save
183             token.destroy
184             flash[:notice] = t "users.reset_password.flash changed"
185             successful_login(current_user)
186           end
187         end
188       else
189         flash[:error] = t "users.reset_password.flash token bad"
190         redirect_to :action => "lost_password"
191       end
192     else
193       head :bad_request
194     end
195   end
196
197   def new
198     @title = t "users.new.title"
199     @referer = params[:referer] || session[:referer]
200
201     append_content_security_policy_directives(
202       :form_action => %w[accounts.google.com *.facebook.com login.live.com github.com meta.wikimedia.org]
203     )
204
205     if current_user
206       # The user is logged in already, so don't show them the signup
207       # page, instead send them to the home page
208       if @referer
209         redirect_to @referer
210       else
211         redirect_to :controller => "site", :action => "index"
212       end
213     elsif params.key?(:auth_provider) && params.key?(:auth_uid)
214       self.current_user = User.new(:email => params[:email],
215                                    :email_confirmation => params[:email],
216                                    :display_name => params[:nickname],
217                                    :auth_provider => params[:auth_provider],
218                                    :auth_uid => params[:auth_uid])
219
220       flash.now[:notice] = render_to_string :partial => "auth_association"
221     else
222       check_signup_allowed
223
224       self.current_user = User.new
225     end
226   end
227
228   def create
229     self.current_user = User.new(user_params)
230
231     if check_signup_allowed(current_user.email)
232       session[:referer] = params[:referer]
233
234       current_user.status = "pending"
235
236       if current_user.auth_provider.present? && current_user.pass_crypt.empty?
237         # We are creating an account with external authentication and
238         # no password was specified so create a random one
239         current_user.pass_crypt = SecureRandom.base64(16)
240         current_user.pass_crypt_confirmation = current_user.pass_crypt
241       end
242
243       if current_user.invalid?
244         # Something is wrong with a new user, so rerender the form
245         render :action => "new"
246       elsif current_user.auth_provider.present?
247         # Verify external authenticator before moving on
248         session[:new_user] = current_user
249         redirect_to auth_url(current_user.auth_provider, current_user.auth_uid)
250       else
251         # Save the user record
252         session[:new_user] = current_user
253         redirect_to :action => :terms
254       end
255     end
256   end
257
258   def login
259     session[:referer] = params[:referer] if params[:referer]
260
261     if params[:username].present? && params[:password].present?
262       session[:remember_me] ||= params[:remember_me]
263       password_authentication(params[:username], params[:password])
264     end
265   end
266
267   def logout
268     @title = t "users.logout.title"
269
270     if params[:session] == session.id
271       if session[:token]
272         token = UserToken.find_by(:token => session[:token])
273         token&.destroy
274         session.delete(:token)
275       end
276       session.delete(:user)
277       session_expires_automatically
278       if params[:referer]
279         redirect_to params[:referer]
280       else
281         redirect_to :controller => "site", :action => "index"
282       end
283     end
284   end
285
286   def confirm
287     if request.post?
288       token = UserToken.find_by(:token => params[:confirm_string])
289       if token&.user&.active?
290         flash[:error] = t("users.confirm.already active")
291         redirect_to :action => "login"
292       elsif !token || token.expired?
293         flash[:error] = t("users.confirm.unknown token")
294         redirect_to :action => "confirm"
295       else
296         user = token.user
297         user.status = "active"
298         user.email_valid = true
299         flash[:notice] = gravatar_status_message(user) if gravatar_enable(user)
300         user.save!
301         referer = token.referer
302         token.destroy
303
304         if session[:token]
305           token = UserToken.find_by(:token => session[:token])
306           session.delete(:token)
307         else
308           token = nil
309         end
310
311         if token.nil? || token.user != user
312           flash[:notice] = t("users.confirm.success")
313           redirect_to :action => :login, :referer => referer
314         else
315           token.destroy
316
317           session[:user] = user.id
318
319           redirect_to referer || welcome_path
320         end
321       end
322     else
323       user = User.find_by(:display_name => params[:display_name])
324
325       redirect_to root_path if user.nil? || user.active?
326     end
327   end
328
329   def confirm_resend
330     user = User.find_by(:display_name => params[:display_name])
331     token = UserToken.find_by(:token => session[:token])
332
333     if user.nil? || token.nil? || token.user != user
334       flash[:error] = t "users.confirm_resend.failure", :name => params[:display_name]
335     else
336       Notifier.signup_confirm(user, user.tokens.create).deliver_later
337       flash[:notice] = t("users.confirm_resend.success", :email => user.email, :sender => Settings.support_email).html_safe
338     end
339
340     redirect_to :action => "login"
341   end
342
343   def confirm_email
344     if request.post?
345       token = UserToken.find_by(:token => params[:confirm_string])
346       if token&.user&.new_email?
347         self.current_user = token.user
348         current_user.email = current_user.new_email
349         current_user.new_email = nil
350         current_user.email_valid = true
351         gravatar_enabled = gravatar_enable(current_user)
352         if current_user.save
353           flash[:notice] = if gravatar_enabled
354                              t("users.confirm_email.success") + " " + gravatar_status_message(current_user)
355                            else
356                              t("users.confirm_email.success")
357                            end
358         else
359           flash[:errors] = current_user.errors
360         end
361         token.destroy
362         session[:user] = current_user.id
363         redirect_to :action => "account", :display_name => current_user.display_name
364       elsif token
365         flash[:error] = t "users.confirm_email.failure"
366         redirect_to :action => "account", :display_name => token.user.display_name
367       else
368         flash[:error] = t "users.confirm_email.unknown_token"
369       end
370     end
371   end
372
373   def show
374     @user = User.find_by(:display_name => params[:display_name])
375
376     if @user &&
377        (@user.visible? || (current_user&.administrator?))
378       @title = @user.display_name
379     else
380       render_unknown_user params[:display_name]
381     end
382   end
383
384   def make_friend
385     @new_friend = User.find_by(:display_name => params[:display_name])
386
387     if @new_friend
388       if request.post?
389         friend = Friend.new
390         friend.befriender = current_user
391         friend.befriendee = @new_friend
392         if current_user.is_friends_with?(@new_friend)
393           flash[:warning] = t "users.make_friend.already_a_friend", :name => @new_friend.display_name
394         elsif friend.save
395           flash[:notice] = t "users.make_friend.success", :name => @new_friend.display_name
396           Notifier.friend_notification(friend).deliver_later
397         else
398           friend.add_error(t("users.make_friend.failed", :name => @new_friend.display_name))
399         end
400
401         if params[:referer]
402           redirect_to params[:referer]
403         else
404           redirect_to :action => "show"
405         end
406       end
407     else
408       render_unknown_user params[:display_name]
409     end
410   end
411
412   def remove_friend
413     @friend = User.find_by(:display_name => params[:display_name])
414
415     if @friend
416       if request.post?
417         if current_user.is_friends_with?(@friend)
418           Friend.where(:user_id => current_user.id, :friend_user_id => @friend.id).delete_all
419           flash[:notice] = t "users.remove_friend.success", :name => @friend.display_name
420         else
421           flash[:error] = t "users.remove_friend.not_a_friend", :name => @friend.display_name
422         end
423
424         if params[:referer]
425           redirect_to params[:referer]
426         else
427           redirect_to :action => "show"
428         end
429       end
430     else
431       render_unknown_user params[:display_name]
432     end
433   end
434
435   ##
436   # sets a user's status
437   def set_status
438     @user.status = params[:status]
439     @user.save
440     redirect_to user_path(:display_name => params[:display_name])
441   end
442
443   ##
444   # delete a user, marking them as deleted and removing personal data
445   def delete
446     @user.delete
447     redirect_to user_path(:display_name => params[:display_name])
448   end
449
450   ##
451   # display a list of users matching specified criteria
452   def index
453     if request.post?
454       ids = params[:user].keys.collect(&:to_i)
455
456       User.where(:id => ids).update_all(:status => "confirmed") if params[:confirm]
457       User.where(:id => ids).update_all(:status => "deleted") if params[:hide]
458
459       redirect_to url_for(:status => params[:status], :ip => params[:ip], :page => params[:page])
460     else
461       @params = params.permit(:status, :ip)
462
463       conditions = {}
464       conditions[:status] = @params[:status] if @params[:status]
465       conditions[:creation_ip] = @params[:ip] if @params[:ip]
466
467       @user_pages, @users = paginate(:users,
468                                      :conditions => conditions,
469                                      :order => :id,
470                                      :per_page => 50)
471     end
472   end
473
474   ##
475   # omniauth success callback
476   def auth_success
477     auth_info = request.env["omniauth.auth"]
478
479     provider = auth_info[:provider]
480     uid = auth_info[:uid]
481     name = auth_info[:info][:name]
482     email = auth_info[:info][:email]
483
484     case provider
485     when "openid"
486       email_verified = uid.match(%r{https://www.google.com/accounts/o8/id?(.*)}) ||
487                        uid.match(%r{https://me.yahoo.com/(.*)})
488     when "google", "facebook"
489       email_verified = true
490     else
491       email_verified = false
492     end
493
494     if settings = session.delete(:new_user_settings)
495       current_user.auth_provider = provider
496       current_user.auth_uid = uid
497
498       update_user(current_user, settings)
499
500       session[:user_errors] = current_user.errors.as_json
501
502       redirect_to :action => "account", :display_name => current_user.display_name
503     elsif session[:new_user]
504       session[:new_user].auth_provider = provider
505       session[:new_user].auth_uid = uid
506
507       session[:new_user].status = "active" if email_verified && email == session[:new_user].email
508
509       redirect_to :action => "terms"
510     else
511       user = User.find_by(:auth_provider => provider, :auth_uid => uid)
512
513       if user.nil? && provider == "google"
514         openid_url = auth_info[:extra][:id_info]["openid_id"]
515         user = User.find_by(:auth_provider => "openid", :auth_uid => openid_url) if openid_url
516         user&.update(:auth_provider => provider, :auth_uid => uid)
517       end
518
519       if user
520         case user.status
521         when "pending" then
522           unconfirmed_login(user)
523         when "active", "confirmed" then
524           successful_login(user, request.env["omniauth.params"]["referer"])
525         when "suspended" then
526           failed_login t("users.login.account is suspended", :webmaster => "mailto:#{Settings.support_email}").html_safe
527         else
528           failed_login t("users.login.auth failure")
529         end
530       else
531         redirect_to :action => "new", :nickname => name, :email => email,
532                     :auth_provider => provider, :auth_uid => uid
533       end
534     end
535   end
536
537   ##
538   # omniauth failure callback
539   def auth_failure
540     flash[:error] = t("users.auth_failure." + params[:message])
541     redirect_to params[:origin] || login_url
542   end
543
544   private
545
546   ##
547   # handle password authentication
548   def password_authentication(username, password)
549     if user = User.authenticate(:username => username, :password => password)
550       successful_login(user)
551     elsif user = User.authenticate(:username => username, :password => password, :pending => true)
552       unconfirmed_login(user)
553     elsif User.authenticate(:username => username, :password => password, :suspended => true)
554       failed_login t("users.login.account is suspended", :webmaster => "mailto:#{Settings.support_email}").html_safe, username
555     else
556       failed_login t("users.login.auth failure"), username
557     end
558   end
559
560   ##
561   # return the URL to use for authentication
562   def auth_url(provider, uid, referer = nil)
563     params = { :provider => provider }
564
565     params[:openid_url] = openid_expand_url(uid) if provider == "openid"
566
567     if referer.nil?
568       params[:origin] = request.path
569     else
570       params[:origin] = request.path + "?referer=" + CGI.escape(referer)
571       params[:referer] = referer
572     end
573
574     auth_path(params)
575   end
576
577   ##
578   # special case some common OpenID providers by applying heuristics to
579   # try and come up with the correct URL based on what the user entered
580   def openid_expand_url(openid_url)
581     if openid_url.nil?
582       nil
583     elsif openid_url.match(%r{(.*)gmail.com(/?)$}) || openid_url.match(%r{(.*)googlemail.com(/?)$})
584       # Special case gmail.com as it is potentially a popular OpenID
585       # provider and, unlike yahoo.com, where it works automatically, Google
586       # have hidden their OpenID endpoint somewhere obscure this making it
587       # somewhat less user friendly.
588       "https://www.google.com/accounts/o8/id"
589     else
590       openid_url
591     end
592   end
593
594   ##
595   # process a successful login
596   def successful_login(user, referer = nil)
597     session[:user] = user.id
598     session_expires_after 28.days if session[:remember_me]
599
600     target = referer || session[:referer] || url_for(:controller => :site, :action => :index)
601
602     # The user is logged in, so decide where to send them:
603     #
604     # - If they haven't seen the contributor terms, send them there.
605     # - If they have a block on them, show them that.
606     # - If they were referred to the login, send them back there.
607     # - Otherwise, send them to the home page.
608     if !user.terms_seen
609       redirect_to :action => :terms, :referer => target
610     elsif user.blocked_on_view
611       redirect_to user.blocked_on_view, :referer => target
612     else
613       redirect_to target
614     end
615
616     session.delete(:remember_me)
617     session.delete(:referer)
618   end
619
620   ##
621   # process a failed login
622   def failed_login(message, username = nil)
623     flash[:error] = message
624
625     redirect_to :action => "login", :referer => session[:referer],
626                 :username => username, :remember_me => session[:remember_me]
627
628     session.delete(:remember_me)
629     session.delete(:referer)
630   end
631
632   ##
633   #
634   def unconfirmed_login(user)
635     session[:token] = user.tokens.create.token
636
637     redirect_to :action => "confirm", :display_name => user.display_name
638
639     session.delete(:remember_me)
640     session.delete(:referer)
641   end
642
643   ##
644   # update a user's details
645   def update_user(user, params)
646     user.display_name = params[:user][:display_name]
647     user.new_email = params[:user][:new_email]
648
649     unless params[:user][:pass_crypt].empty? && params[:user][:pass_crypt_confirmation].empty?
650       user.pass_crypt = params[:user][:pass_crypt]
651       user.pass_crypt_confirmation = params[:user][:pass_crypt_confirmation]
652     end
653
654     if params[:user][:description] != user.description
655       user.description = params[:user][:description]
656       user.description_format = "markdown"
657     end
658
659     user.languages = params[:user][:languages].split(",")
660
661     case params[:image_action]
662     when "new" then
663       user.image = params[:user][:image]
664       user.image_use_gravatar = false
665     when "delete" then
666       user.image = nil
667       user.image_use_gravatar = false
668     when "gravatar" then
669       user.image = nil
670       user.image_use_gravatar = true
671     end
672
673     user.home_lat = params[:user][:home_lat]
674     user.home_lon = params[:user][:home_lon]
675
676     user.preferred_editor = if params[:user][:preferred_editor] == "default"
677                               nil
678                             else
679                               params[:user][:preferred_editor]
680                             end
681
682     if params[:user][:auth_provider].nil? || params[:user][:auth_provider].blank?
683       user.auth_provider = nil
684       user.auth_uid = nil
685     end
686
687     if user.save
688       set_locale(true)
689
690       if user.new_email.blank? || user.new_email == user.email
691         flash.now[:notice] = t "users.account.flash update success"
692       else
693         user.email = user.new_email
694
695         if user.valid?
696           flash.now[:notice] = t "users.account.flash update success confirm needed"
697
698           begin
699             Notifier.email_confirm(user, user.tokens.create).deliver_later
700           rescue StandardError
701             # Ignore errors sending email
702           end
703         else
704           current_user.errors.add(:new_email, current_user.errors[:email])
705           current_user.errors.add(:email, [])
706         end
707
708         user.restore_email!
709       end
710     end
711   end
712
713   ##
714   # require that the user in the URL is the logged in user
715   def require_self
716     head :forbidden if params[:display_name] != current_user.display_name
717   end
718
719   ##
720   # ensure that there is a "user" instance variable
721   def lookup_user_by_name
722     @user = User.find_by(:display_name => params[:display_name])
723   rescue ActiveRecord::RecordNotFound
724     redirect_to :action => "view", :display_name => params[:display_name] unless @user
725   end
726
727   ##
728   #
729   def disable_terms_redirect
730     # this is necessary otherwise going to the user terms page, when
731     # having not agreed already would cause an infinite redirect loop.
732     # it's .now so that this doesn't propagate to other pages.
733     flash.now[:skip_terms] = true
734   end
735
736   ##
737   # return permitted user parameters
738   def user_params
739     params.require(:user).permit(:email, :email_confirmation, :display_name,
740                                  :auth_provider, :auth_uid,
741                                  :pass_crypt, :pass_crypt_confirmation)
742   end
743
744   ##
745   # check signup acls
746   def check_signup_allowed(email = nil)
747     domain = if email.nil?
748                nil
749              else
750                email.split("@").last
751              end
752
753     if blocked = Acl.no_account_creation(request.remote_ip, domain)
754       logger.info "Blocked signup from #{request.remote_ip} for #{email}"
755
756       render :action => "blocked"
757     end
758
759     !blocked
760   end
761
762   ##
763   # check if this user has a gravatar and set the user pref is true
764   def gravatar_enable(user)
765     # code from example https://en.gravatar.com/site/implement/images/ruby/
766     return false if user.image.present?
767
768     hash = Digest::MD5.hexdigest(user.email.downcase)
769     url = "https://www.gravatar.com/avatar/#{hash}?d=404" # without d=404 we will always get an image back
770     response = OSM.http_client.get(URI.parse(url))
771     oldsetting = user.image_use_gravatar
772     user.image_use_gravatar = response.success?
773     oldsetting != user.image_use_gravatar
774   end
775
776   ##
777   # display a message about th current status of the gravatar setting
778   def gravatar_status_message(user)
779     if user.image_use_gravatar
780       t "users.account.gravatar.enabled"
781     else
782       t "users.account.gravatar.disabled"
783     end
784   end
785 end