]> git.openstreetmap.org Git - rails.git/blob - app/controllers/users_controller.rb
Use resourceful routes for terms view/accept/decline
[rails.git] / app / controllers / users_controller.rb
1 class UsersController < ApplicationController
2   include EmailMethods
3   include SessionMethods
4   include UserMethods
5
6   layout "site"
7
8   skip_before_action :verify_authenticity_token, :only => [:auth_success]
9   before_action :authorize_web
10   before_action :set_locale
11   before_action :check_database_readable
12
13   authorize_resource
14
15   before_action :check_database_writable, :only => [:new, :go_public]
16   before_action :require_cookies, :only => [:new]
17   before_action :lookup_user_by_name, :only => [:set_status, :destroy]
18
19   allow_thirdparty_images :only => :show
20   allow_social_login :only => :new
21
22   def show
23     @user = User.find_by(:display_name => params[:display_name])
24
25     if @user &&
26        (@user.visible? || current_user&.administrator?)
27       @title = @user.display_name
28     else
29       render_unknown_user params[:display_name]
30     end
31   end
32
33   def new
34     @title = t ".title"
35     @referer = safe_referer(params[:referer])
36
37     parse_oauth_referer @referer
38
39     if current_user
40       # The user is logged in already, so don't show them the signup
41       # page, instead send them to the home page
42       redirect_to @referer || { :controller => "site", :action => "index" }
43     elsif params.key?(:auth_provider) && params.key?(:auth_uid)
44       @email_hmac = params[:email_hmac]
45
46       self.current_user = User.new(:email => params[:email],
47                                    :display_name => params[:nickname],
48                                    :auth_provider => params[:auth_provider],
49                                    :auth_uid => params[:auth_uid])
50
51       if current_user.valid? || current_user.errors[:email].empty?
52         flash.now[:notice] = render_to_string :partial => "auth_association"
53       else
54         flash.now[:warning] = t ".duplicate_social_email"
55       end
56     else
57       check_signup_allowed
58
59       self.current_user = User.new
60     end
61   end
62
63   def create
64     self.current_user = User.new(user_params)
65
66     if check_signup_allowed(current_user.email)
67       if current_user.auth_uid.present?
68         # We are creating an account with external authentication and
69         # no password was specified so create a random one
70         current_user.pass_crypt = SecureRandom.base64(16)
71         current_user.pass_crypt_confirmation = current_user.pass_crypt
72       end
73
74       if current_user.invalid?
75         # Something is wrong with a new user, so rerender the form
76         render :action => "new"
77       else
78         # Save the user record
79         if save_new_user params[:email_hmac]
80           SIGNUP_IP_LIMITER&.update(request.remote_ip)
81           SIGNUP_EMAIL_LIMITER&.update(canonical_email(current_user.email))
82
83           flash[:matomo_goal] = Settings.matomo["goals"]["signup"] if defined?(Settings.matomo)
84
85           referer = welcome_path(welcome_options(params[:referer]))
86
87           if current_user.status == "active"
88             successful_login(current_user, referer)
89           else
90             session[:pending_user] = current_user.id
91             UserMailer.signup_confirm(current_user, current_user.generate_token_for(:new_user), referer).deliver_later
92             redirect_to :controller => :confirmations, :action => :confirm, :display_name => current_user.display_name
93           end
94         else
95           render :action => "new", :referer => params[:referer]
96         end
97       end
98     end
99   end
100
101   ##
102   # destroy a user, marking them as deleted and removing personal data
103   def destroy
104     @user.soft_destroy!
105     redirect_to user_path(:display_name => params[:display_name])
106   end
107
108   def go_public
109     current_user.data_public = true
110     current_user.save
111     flash[:notice] = t ".flash success"
112     redirect_to edit_account_path
113   end
114
115   ##
116   # sets a user's status
117   def set_status
118     @user.activate! if params[:event] == "activate"
119     @user.confirm! if params[:event] == "confirm"
120     @user.unconfirm! if params[:event] == "unconfirm"
121     @user.hide! if params[:event] == "hide"
122     @user.unhide! if params[:event] == "unhide"
123     @user.unsuspend! if params[:event] == "unsuspend"
124     redirect_to user_path(:display_name => params[:display_name])
125   end
126
127   ##
128   # omniauth success callback
129   def auth_success
130     referer = request.env["omniauth.params"]["referer"]
131     auth_info = request.env["omniauth.auth"]
132
133     provider = auth_info[:provider]
134     uid = auth_info[:uid]
135     name = auth_info[:info][:name]
136     email = auth_info[:info][:email]
137
138     email_verified = case provider
139                      when "openid"
140                        uid.match(%r{https://www.google.com/accounts/o8/id?(.*)}) ||
141                        uid.match(%r{https://me.yahoo.com/(.*)})
142                      when "google", "facebook", "microsoft", "github", "wikipedia"
143                        true
144                      else
145                        false
146                      end
147
148     if settings = session.delete(:new_user_settings)
149       current_user.auth_provider = provider
150       current_user.auth_uid = uid
151
152       update_user(current_user, settings)
153
154       flash.discard
155
156       session[:user_errors] = current_user.errors.as_json
157
158       redirect_to edit_account_path
159     else
160       user = User.find_by(:auth_provider => provider, :auth_uid => uid)
161
162       if user.nil? && provider == "google"
163         openid_url = auth_info[:extra][:id_info]["openid_id"]
164         user = User.find_by(:auth_provider => "openid", :auth_uid => openid_url) if openid_url
165         user&.update(:auth_provider => provider, :auth_uid => uid)
166       end
167
168       if user
169         case user.status
170         when "pending"
171           unconfirmed_login(user, referer)
172         when "active", "confirmed"
173           successful_login(user, referer)
174         when "suspended"
175           failed_login({ :partial => "sessions/suspended_flash" }, user.display_name, referer)
176         else
177           failed_login(t("sessions.new.auth failure"), user.display_name, referer)
178         end
179       else
180         email_hmac = UsersController.message_hmac(email) if email_verified && email
181         redirect_to :action => "new", :nickname => name, :email => email, :email_hmac => email_hmac,
182                     :auth_provider => provider, :auth_uid => uid, :referer => referer
183       end
184     end
185   end
186
187   ##
188   # omniauth failure callback
189   def auth_failure
190     flash[:error] = t(params[:message], :scope => "users.auth_failure", :default => t(".unknown_error"))
191
192     origin = safe_referer(params[:origin]) if params[:origin]
193
194     redirect_to origin || login_url
195   end
196
197   def self.message_hmac(text)
198     sha256 = Digest::SHA256.new
199     sha256 << Rails.application.key_generator.generate_key("openstreetmap/email_address")
200     sha256 << text
201     Base64.urlsafe_encode64(sha256.digest)
202   end
203
204   private
205
206   def save_new_user(email_hmac)
207     current_user.data_public = true
208     current_user.description = "" if current_user.description.nil?
209     current_user.creation_address = request.remote_ip
210     current_user.languages = http_accept_language.user_preferred_languages
211     current_user.terms_agreed = Time.now.utc
212     current_user.tou_agreed = Time.now.utc
213     current_user.terms_seen = true
214
215     if current_user.auth_uid.blank?
216       current_user.auth_provider = nil
217       current_user.auth_uid = nil
218     elsif email_hmac && ActiveSupport::SecurityUtils.secure_compare(email_hmac, UsersController.message_hmac(current_user.email))
219       current_user.activate
220     end
221
222     current_user.save
223   end
224
225   def welcome_options(referer = nil)
226     uri = URI(referer) if referer.present?
227
228     return { "oauth_return_url" => uri&.to_s } if uri&.path == oauth_authorization_path
229
230     begin
231       %r{map=(.*)/(.*)/(.*)}.match(uri.fragment) do |m|
232         editor = Rack::Utils.parse_query(uri.query).slice("editor")
233         return { "zoom" => m[1], "lat" => m[2], "lon" => m[3] }.merge(editor)
234       end
235     rescue StandardError
236       # Use default
237     end
238   end
239
240   ##
241   # ensure that there is a "user" instance variable
242   def lookup_user_by_name
243     @user = User.find_by(:display_name => params[:display_name])
244   rescue ActiveRecord::RecordNotFound
245     redirect_to :action => "view", :display_name => params[:display_name] unless @user
246   end
247
248   ##
249   # return permitted user parameters
250   def user_params
251     params.require(:user).permit(:email, :display_name,
252                                  :auth_provider, :auth_uid,
253                                  :pass_crypt, :pass_crypt_confirmation,
254                                  :consider_pd)
255   end
256
257   ##
258   # check signup acls
259   def check_signup_allowed(email = nil)
260     domain = if email.nil?
261                nil
262              else
263                email.split("@").last
264              end
265
266     mx_servers = if domain.nil?
267                    nil
268                  else
269                    domain_mx_servers(domain)
270                  end
271
272     return true if Acl.allow_account_creation(request.remote_ip, :domain => domain, :mx => mx_servers)
273
274     blocked = Acl.no_account_creation(request.remote_ip, :domain => domain, :mx => mx_servers)
275
276     blocked ||= SIGNUP_IP_LIMITER && !SIGNUP_IP_LIMITER.allow?(request.remote_ip)
277
278     blocked ||= email && SIGNUP_EMAIL_LIMITER && !SIGNUP_EMAIL_LIMITER.allow?(canonical_email(email))
279
280     if blocked
281       logger.info "Blocked signup from #{request.remote_ip} for #{email}"
282
283       render :action => "blocked"
284     end
285
286     !blocked
287   end
288 end