X-Git-Url: https://git.openstreetmap.org./rails.git/blobdiff_plain/035254c5fbcf7c4a892532baefc79f5e9e4e7aa7..03fb042591bf60cd74aa277ee3de98f02d8486d3:/app/controllers/user_controller.rb?ds=inline diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb index 36516f60f..aae059252 100644 --- a/app/controllers/user_controller.rb +++ b/app/controllers/user_controller.rb @@ -1,32 +1,95 @@ class UserController < ApplicationController - layout 'site' + layout 'site', :except => :api_details before_filter :authorize, :only => [:api_details, :api_gpx_files] - before_filter :set_locale, :except => [:api_details, :api_gpx_files] before_filter :authorize_web, :except => [:api_details, :api_gpx_files] - before_filter :require_user, :only => [:set_home, :account, :go_public, :make_friend, :remove_friend, :upload_image, :delete_image] + before_filter :set_locale, :except => [:api_details, :api_gpx_files] + before_filter :require_user, :only => [:account, :go_public, :make_friend, :remove_friend] before_filter :check_database_readable, :except => [:api_details, :api_gpx_files] - before_filter :check_database_writable, :only => [:login, :new, :set_home, :account, :go_public, :make_friend, :remove_friend, :upload_image, :delete_image] + before_filter :check_database_writable, :only => [:login, :new, :account, :go_public, :make_friend, :remove_friend] before_filter :check_api_readable, :only => [:api_details, :api_gpx_files] + before_filter :require_allow_read_prefs, :only => [:api_details] + before_filter :require_allow_read_gpx, :only => [:api_gpx_files] + before_filter :require_cookies, :only => [:login, :confirm] + before_filter :require_administrator, :only => [:set_status, :delete, :list] + before_filter :lookup_this_user, :only => [:set_status, :delete] filter_parameter_logging :password, :pass_crypt, :pass_crypt_confirmation + cache_sweeper :user_sweeper, :only => [:account, :set_status, :delete], :unless => OSM_STATUS == :database_offline + + def terms + @title = t 'user.new.title' + @legale = params[:legale] || OSM.IPToCountry(request.remote_ip) || APP_CONFIG['default_legale'] + @text = OSM.legal_text_for_country(@legale) + + if request.xhr? + render :update do |page| + page.replace_html "contributorTerms", :partial => "terms" + end + elsif params[:user] + session[:referer] = params[:referer] + + @user = User.new(params[:user]) + + if params[:user][:openid_url] and @user.pass_crypt.empty? + # We are creating an account with OpenID and no password + # was specified so create a random one + @user.pass_crypt = ActiveSupport::SecureRandom.base64(16) + @user.pass_crypt_confirmation = @user.pass_crypt + end + + if @user.valid? + if params[:user][:openid_url].nil? or + params[:user][:openid_url].empty? + # No OpenID so just move on to the terms + render :action => 'terms' + else + # Verify OpenID before moving on + session[:new_user] = @user + openid_verify(params[:user][:openid_url], @user) + end + else + # Something is wrong, so rerender the form + render :action => 'new' + end + elsif using_open_id? + # The redirect from the OpenID provider reenters here + # again and we need to pass the parameters through to + # the open_id_authentication function + @user = session.delete(:new_user) + + openid_verify(nil, @user) do |user| + end + + if @user.openid_url.nil? or @user.invalid? + render :action => 'new' + else + render :action => 'terms' + end + end + end + def save - @title = 'create account' + @title = t 'user.new.title' if Acl.find_by_address(request.remote_ip, :conditions => {:k => "no_account_creation"}) render :action => 'new' + elsif params[:decline] + redirect_to t('user.terms.declined') else @user = User.new(params[:user]) - @user.visible = true + @user.status = "pending" @user.data_public = true @user.description = "" if @user.description.nil? @user.creation_ip = request.remote_ip + @user.languages = request.user_preferred_languages + @user.terms_agreed = Time.now.getutc if @user.save - flash[:notice] = "User was successfully created. Check your email for a confirmation note, and you\'ll be mapping in no time :-)

Please note that you won't be able to login until you've received and confirmed your email address.

If you use an antispam system which sends confirmation requests then please make sure you whitelist webmaster@openstreetmap.org as we are unable to reply to any confirmation requests." - Notifier.deliver_signup_confirm(@user, @user.tokens.create) + flash[:notice] = t 'user.new.flash create success message' + Notifier.deliver_signup_confirm(@user, @user.tokens.create(:referer => session.delete(:referer))) redirect_to :action => 'login' else render :action => 'new' @@ -35,13 +98,12 @@ class UserController < ApplicationController end def account - @title = 'edit account' - if params[:user] and params[:user][:display_name] and params[:user][:description] - if params[:user][:email] != @user.email - @user.new_email = params[:user][:email] - end + @title = t 'user.account.title' + @tokens = @user.oauth_tokens.find :all, :conditions => 'oauth_tokens.invalidated_at is null and oauth_tokens.authorized_at is not null' + if params[:user] and params[:user][:display_name] and params[:user][:description] @user.display_name = params[:user][:display_name] + @user.new_email = params[:user][:new_email] if params[:user][:pass_crypt].length > 0 or params[:user][:pass_crypt_confirmation].length > 0 @user.pass_crypt = params[:user][:pass_crypt] @@ -49,27 +111,35 @@ class UserController < ApplicationController end @user.description = params[:user][:description] + @user.languages = params[:user][:languages].split(",") + + case params[:image_action] + when "new" then @user.image = params[:user][:image] + when "delete" then @user.image = nil + end + @user.home_lat = params[:user][:home_lat] @user.home_lon = params[:user][:home_lon] - if @user.save - if params[:user][:email] == @user.new_email - @notice = "User information updated successfully. Check your email for a note to confirm your new email address." - Notifier.deliver_email_confirm(@user, @user.tokens.create) - else - @notice = "User information updated successfully." - end - end - end - end + @user.openid_url = nil if params[:user][:openid_url].empty? - def set_home - if params[:user][:home_lat] and params[:user][:home_lon] - @user.home_lat = params[:user][:home_lat].to_f - @user.home_lon = params[:user][:home_lon].to_f - if @user.save - flash[:notice] = "Home location saved successfully." - redirect_to :controller => 'user', :action => 'account' + if params[:user][:openid_url].length > 0 and + params[:user][:openid_url] != @user.openid_url + # If the OpenID has changed, we want to check that it is a + # valid OpenID and one the user has control over before saving + # it as a password equivalent for the user. + session[:new_user] = @user + openid_verify(params[:user][:openid_url], @user) + else + update_user(@user) + end + elsif using_open_id? + # The redirect from the OpenID provider reenters here + # again and we need to pass the parameters through to + # the open_id_authentication function + @user = session.delete(:new_user) + openid_verify(nil, @user) do |user| + update_user(user) end end end @@ -77,99 +147,101 @@ class UserController < ApplicationController def go_public @user.data_public = true @user.save - flash[:notice] = 'All your edits are now public.' + flash[:notice] = t 'user.go_public.flash success' redirect_to :controller => 'user', :action => 'account', :display_name => @user.display_name end def lost_password - @title = 'lost password' + @title = t 'user.lost_password.title' + if params[:user] and params[:user][:email] - user = User.find_by_email(params[:user][:email], :conditions => {:visible => true}) + user = User.find_by_email(params[:user][:email], :conditions => {:status => ["pending", "active", "confirmed"]}) if user token = user.tokens.create Notifier.deliver_lost_password(user, token) - @notice = "Sorry you lost it :-( but an email is on its way so you can reset it soon." + flash[:notice] = t 'user.lost_password.notice email on way' + redirect_to :action => 'login' else - @notice = "Couldn't find that email address, sorry." + flash.now[:error] = t 'user.lost_password.notice email cannot find' end end end def reset_password - @title = 'reset password' - if params['token'] + @title = t 'user.reset_password.title' + + if params[:token] token = UserToken.find_by_token(params[:token]) + if token - pass = OSM::make_token(8) - user = token.user - user.pass_crypt = pass - user.pass_crypt_confirmation = pass - user.active = true - user.email_valid = true - user.save! - token.destroy - Notifier.deliver_reset_password(user, pass) - flash[:notice] = "Your password has been changed and is on its way to your mailbox :-)" + @user = token.user + + if params[:user] + @user.pass_crypt = params[:user][:pass_crypt] + @user.pass_crypt_confirmation = params[:user][:pass_crypt_confirmation] + @user.status = "active" if @user.status == "pending" + @user.email_valid = true + + if @user.save + token.destroy + flash[:notice] = t 'user.reset_password.flash changed' + redirect_to :action => 'login' + end + end else - flash[:notice] = "Didn't find that token, check the URL maybe?" + flash[:error] = t 'user.reset_password.flash token bad' + redirect_to :action => 'lost_password' end end - - redirect_to :action => 'login' end def new - @title = 'create account' - # The user is logged in already, so don't show them the signup page, instead - # send them to the home page - redirect_to :controller => 'site', :action => 'index' if session[:user] - end + @title = t 'user.new.title' + @referer = params[:referer] || session[:referer] - def login if session[:user] - # The user is logged in already, if the referer param exists, redirect them to that - if params[:referer] - redirect_to params[:referer] - else - redirect_to :controller => 'site', :action => 'index' - end - return + # The user is logged in already, so don't show them the signup + # page, instead send them to the home page + redirect_to :controller => 'site', :action => 'index' + elsif not params['openid'].nil? + flash.now[:notice] = t 'user.new.openid association' end - @title = 'login' - if params[:user] - email_or_display_name = params[:user][:email] - pass = params[:user][:password] - user = User.authenticate(:username => email_or_display_name, :password => pass) - if user - session[:user] = user.id - if params[:referer] - redirect_to params[:referer] - else - redirect_to :controller => 'site', :action => 'index' - end - return - elsif User.authenticate(:username => email_or_display_name, :password => pass, :inactive => true) - @notice = "Sorry, your account is not active yet.
Please click on the link in the account confirmation email to activate your account." + end + + def login + if params[:username] or using_open_id? + session[:remember_me] ||= params[:remember_me] + session[:referer] ||= params[:referer] + + if using_open_id? + openid_authentication(params[:openid_url]) else - @notice = "Sorry, couldn't log in with those details." + password_authentication(params[:username], params[:password]) end + else + @title = t 'user.login.title' end end def logout - if session[:token] - token = UserToken.find_by_token(session[:token]) - if token - token.destroy + @title = t 'user.logout.title' + + if params[:session] == request.session_options[:id] + if session[:token] + token = UserToken.find_by_token(session[:token]) + if token + token.destroy + end + session[:token] = nil + end + session[:user] = nil + session_expires_automatically + if params[:referer] + redirect_to params[:referer] + else + redirect_to :controller => 'site', :action => 'index' end - session[:token] = nil - end - session[:user] = nil - if params[:referer] - redirect_to params[:referer] - else - redirect_to :controller => 'site', :action => 'index' end end @@ -178,15 +250,20 @@ class UserController < ApplicationController token = UserToken.find_by_token(params[:confirm_string]) if token and !token.user.active? @user = token.user - @user.active = true + @user.status = "active" @user.email_valid = true @user.save! + referer = token.referer token.destroy - flash[:notice] = 'Confirmed your account, thanks for signing up!' + flash[:notice] = t 'user.confirm.success' session[:user] = @user.id - redirect_to :action => 'account', :display_name => @user.display_name + unless referer.nil? + redirect_to referer + else + redirect_to :action => 'account', :display_name => @user.display_name + end else - @notice = 'Something went wrong confirming that user.' + flash.now[:error] = t 'user.confirm.failure' end end end @@ -198,35 +275,21 @@ class UserController < ApplicationController @user = token.user @user.email = @user.new_email @user.new_email = nil - @user.active = true @user.email_valid = true - @user.save! + if @user.save + flash[:notice] = t 'user.confirm_email.success' + else + flash[:errors] = @user.errors + end token.destroy - flash[:notice] = 'Confirmed your email address, thanks for signing up!' session[:user] = @user.id redirect_to :action => 'account', :display_name => @user.display_name else - @notice = 'Something went wrong confirming that email address.' + flash.now[:error] = t 'user.confirm_email.failure' end end end - def upload_image - @user.image = params[:user][:image] - @user.save! - redirect_to :controller => 'user', :action => 'view', :display_name => @user.display_name - end - - def delete_image - @user.image = nil - @user.save! - redirect_to :controller => 'user', :action => 'view', :display_name => @user.display_name - end - - def api_details - render :text => @user.to_xml.to_s, :content_type => "text/xml" - end - def api_gpx_files doc = OSM::API.new.get_xml_doc @user.traces.each do |trace| @@ -236,50 +299,279 @@ class UserController < ApplicationController end def view - @this_user = User.find_by_display_name(params[:display_name], :conditions => {:visible => true}) + @this_user = User.find_by_display_name(params[:display_name]) - if @this_user + if @this_user and + (@this_user.visible? or (@user and @user.administrator?)) @title = @this_user.display_name else + @title = t 'user.no_such_user.title' @not_found_user = params[:display_name] render :action => 'no_such_user', :status => :not_found end end def make_friend - if params[:display_name] + if params[:display_name] name = params[:display_name] - new_friend = User.find_by_display_name(name, :conditions => {:visible => true}) + new_friend = User.find_by_display_name(name, :conditions => {:status => ["active", "confirmed"]}) friend = Friend.new friend.user_id = @user.id friend.friend_user_id = new_friend.id unless @user.is_friends_with?(new_friend) if friend.save - flash[:notice] = "#{name} is now your friend." + flash[:notice] = t 'user.make_friend.success', :name => name Notifier.deliver_friend_notification(friend) else - friend.add_error("Sorry, failed to add #{name} as a friend.") + friend.add_error(t('user.make_friend.failed', :name => name)) end else - flash[:notice] = "You are already friends with #{name}." + flash[:warning] = t 'user.make_friend.already_a_friend', :name => name end - redirect_to :controller => 'user', :action => 'view' + if params[:referer] + redirect_to params[:referer] + else + redirect_to :controller => 'user', :action => 'view' + end end end def remove_friend - if params[:display_name] + if params[:display_name] name = params[:display_name] - friend = User.find_by_display_name(name, :conditions => {:visible => true}) + friend = User.find_by_display_name(name, :conditions => {:status => ["active", "confirmed"]}) if @user.is_friends_with?(friend) Friend.delete_all "user_id = #{@user.id} AND friend_user_id = #{friend.id}" - flash[:notice] = "#{friend.display_name} was removed from your friends." + flash[:notice] = t 'user.remove_friend.success', :name => friend.display_name + else + flash[:error] = t 'user.remove_friend.not_a_friend', :name => friend.display_name + end + + if params[:referer] + redirect_to params[:referer] + else + redirect_to :controller => 'user', :action => 'view' + end + end + end + + ## + # sets a user's status + def set_status + @this_user.update_attributes(:status => params[:status]) + redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name] + end + + ## + # delete a user, marking them as deleted and removing personal data + def delete + @this_user.delete + redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name] + end + + ## + # display a list of users matching specified criteria + def list + if request.post? + ids = params[:user].keys.collect { |id| id.to_i } + + User.update_all("status = 'confirmed'", :id => ids) if params[:confirm] + User.update_all("status = 'deleted'", :id => ids) if params[:hide] + + redirect_to url_for(:status => params[:status], :ip => params[:ip], :page => params[:page]) + else + conditions = Hash.new + conditions[:status] = params[:status] if params[:status] + conditions[:creation_ip] = params[:ip] if params[:ip] + + @user_pages, @users = paginate(:users, + :conditions => conditions, + :order => :id, + :per_page => 50) + end + end + +private + + ## + # handle password authentication + def password_authentication(username, password) + if user = User.authenticate(:username => username, :password => password) + successful_login(user) + elsif User.authenticate(:username => username, :password => password, :pending => true) + failed_login t('user.login.account not active') + elsif User.authenticate(:username => username, :password => password, :suspended => true) + webmaster = link_to t('user.login.webmaster'), "mailto:webmaster@openstreetmap.org" + failed_login t('user.login.account suspended', :webmaster => webmaster) + else + failed_login t('user.login.auth failure') + end + end + + ## + # handle OpenID authentication + def openid_authentication(openid_url) + # If we don't appear to have a user for this URL then ask the + # provider for some extra information to help with signup + if openid_url and User.find_by_openid_url(openid_url) + optional = nil + else + optional = [:nickname, :email] + end + + # Start the authentication + authenticate_with_open_id(openid_expand_url(openid_url), :optional => optional) do |result, identity_url, registration| + if result.successful? + # We need to use the openid url passed back from the OpenID provider + # rather than the one supplied by the user, as these can be different. + # + # For example, you can simply enter yahoo.com in the login box rather + # than a user specific url. Only once it comes back from the provider + # provider do we know the unique address for the user. + if user = User.find_by_openid_url(identity_url) + case user.status + when "pending" then + failed_login t('user.login.account not active') + when "active", "confirmed" then + successful_login(user) + when "suspended" then + webmaster = link_to t('user.login.webmaster'), "mailto:webmaster@openstreetmap.org" + failed_login t('user.login.account suspended', :webmaster => webmaster) + else + failed_login t('user.login.auth failure') + end + else + # We don't have a user registered to this OpenID, so redirect + # to the create account page with username and email filled + # in if they have been given by the OpenID provider through + # the simple registration protocol. + redirect_to :controller => 'user', :action => 'new', :nickname => registration['nickname'], :email => registration['email'], :openid => identity_url + end + elsif result.missing? + failed_login t('user.login.openid missing provider') + elsif result.invalid? + failed_login t('user.login.openid invalid') + else + failed_login t('user.login.auth failure') + end + end + end + + ## + # verify an OpenID URL + def openid_verify(openid_url, user) + user.openid_url = openid_url + + authenticate_with_open_id(openid_expand_url(openid_url)) do |result, identity_url| + if result.successful? + # We need to use the openid url passed back from the OpenID provider + # rather than the one supplied by the user, as these can be different. + # + # For example, you can simply enter yahoo.com in the login box rather + # than a user specific url. Only once it comes back from the provider + # provider do we know the unique address for the user. + user.openid_url = identity_url + yield user + elsif result.missing? + flash.now[:error] = t 'user.login.openid missing provider' + elsif result.invalid? + flash.now[:error] = t 'user.login.openid invalid' else - flash[:notice] = "#{friend.display_name} is not one of your friends." + flash.now[:error] = t 'user.login.auth failure' end + end + end + + ## + # special case some common OpenID providers by applying heuristics to + # try and come up with the correct URL based on what the user entered + def openid_expand_url(openid_url) + if openid_url.nil? + return nil + elsif openid_url.match(/(.*)gmail.com(\/?)$/) or openid_url.match(/(.*)googlemail.com(\/?)$/) + # Special case gmail.com as it is potentially a popular OpenID + # provider and, unlike yahoo.com, where it works automatically, Google + # have hidden their OpenID endpoint somewhere obscure this making it + # somewhat less user friendly. + return 'https://www.google.com/accounts/o8/id' + else + return openid_url + end + end + + ## + # process a successful login + def successful_login(user) + session[:user] = user.id - redirect_to :controller => 'user', :action => 'view' + session_expires_after 1.month if session[:remember_me] + + if user.blocked_on_view + redirect_to user.blocked_on_view, :referer => params[:referer] + elsif session[:referer] + redirect_to session[:referer] + else + redirect_to :controller => 'site', :action => 'index' end + + session.delete(:remember_me) + session.delete(:referer) + end + + ## + # process a failed login + def failed_login(message) + flash[:error] = message + + redirect_to :action => 'login', :referer => session[:referer] + + session.delete(:remember_me) + session.delete(:referer) + end + + ## + # update a user's details + def update_user(user) + if user.save + set_locale + + if user.new_email.nil? or user.new_email.empty? + flash.now[:notice] = t 'user.account.flash update success' + else + flash.now[:notice] = t 'user.account.flash update success confirm needed' + + begin + Notifier.deliver_email_confirm(user, user.tokens.create) + rescue + # Ignore errors sending email + end + end + end + end + + ## + # require that the user is a administrator, or fill out a helpful error message + # and return them to the user page. + def require_administrator + if @user and not @user.administrator? + flash[:error] = t('user.filter.not_an_administrator') + + if params[:display_name] + redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name] + else + redirect_to :controller => 'user', :action => 'login', :referer => request.request_uri + end + elsif not @user + redirect_to :controller => 'user', :action => 'login', :referer => request.request_uri + end + end + + ## + # ensure that there is a "this_user" instance variable + def lookup_this_user + @this_user = User.find_by_display_name(params[:display_name]) + rescue ActiveRecord::RecordNotFound + redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name] unless @this_user end end