X-Git-Url: https://git.openstreetmap.org/rails.git/blobdiff_plain/69c24009975fa03abf84397335043def82b33901..92951c6c00be0cb488a97463e25a5ecc8272c49e:/app/controllers/user_controller.rb?ds=sidebyside diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb index eb1471b00..01863692e 100644 --- a/app/controllers/user_controller.rb +++ b/app/controllers/user_controller.rb @@ -1,32 +1,60 @@ class UserController < ApplicationController - layout 'site' + layout 'site', :except => :api_details before_filter :authorize, :only => [:api_details, :api_gpx_files] before_filter :authorize_web, :except => [:api_details, :api_gpx_files] + before_filter :set_locale, :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 :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_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 => [:activate, :deactivate, :hide, :unhide, :delete] + before_filter :lookup_this_user, :only => [:activate, :deactivate, :hide, :unhide, :delete] filter_parameter_logging :password, :pass_crypt, :pass_crypt_confirmation 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' else + #The redirect from the OpenID provider reenters here again + #and we need to pass the parameters through to the + #open_id_authentication function + if params[:open_id_complete] + openid_verify('', true) + redirect_to :action => 'login' + return + end + @user = User.new(params[:user]) @user.visible = true @user.data_public = true @user.description = "" if @user.description.nil? @user.creation_ip = request.remote_ip + @user.languages = request.user_preferred_languages 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) - redirect_to :action => 'login' + flash[:notice] = t 'user.new.flash create success message' + Notifier.deliver_signup_confirm(@user, @user.tokens.create(:referer => params[:referer])) + if (params[:user][:openid_url].length > 0) + begin + session[:new_usr_name] = @user.display_name + @norm_openid_url = OpenIdAuthentication.normalize_identifier(params[:user][:openid_url]) + #TODO: error messages in the openid_verify aren't correctly returned yet + openid_verify(@norm_openid_url, true) + #Will have sent the redirect_to in the if open_id_complete section of this method + rescue + flash.now[:error] = t 'user.login.openid invalid' + end + else + redirect_to :action => 'login' + end else render :action => 'new' end @@ -34,7 +62,17 @@ class UserController < ApplicationController end def account - @title = 'edit account' + @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' + + #The redirect from the OpenID provider reenters here again + #and we need to pass the parameters through to the + #open_id_authentication function + if params[:open_id_complete] + openid_verify('', false) + return + end + if params[:user] and params[:user][:display_name] and params[:user][:description] if params[:user][:email] != @user.email @user.new_email = params[:user][:email] @@ -46,28 +84,91 @@ class UserController < ApplicationController @user.pass_crypt = params[:user][:pass_crypt] @user.pass_crypt_confirmation = params[:user][:pass_crypt_confirmation] end + if (params[:user][:openid_url].length == 0) + #Clearing doesn't need OpenID validation, so we can set it here. + @user.openid_url = nil + end @user.description = params[:user][:description] + @user.languages = params[:user][:languages].split(",") @user.home_lat = params[:user][:home_lat] @user.home_lon = params[:user][:home_lon] if @user.save + set_locale + if params[:user][:email] == @user.new_email - @notice = "User information updated successfully. Check your email for a note to confirm your new email address." + flash.now[:notice] = t 'user.account.flash update success confirm needed' Notifier.deliver_email_confirm(@user, @user.tokens.create) else - @notice = "User information updated successfully." + flash.now[:notice] = t 'user.account.flash update success' + end + end + + if (params[:user][:openid_url].length > 0) + begin + @norm_openid_url = OpenIdAuthentication.normalize_identifier(params[:user][:openid_url]) + if (@norm_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 the openID as a password equivalent for + #the user. + openid_verify(@norm_openid_url, false) + end + rescue + flash.now[:error] = t 'user.login.openid invalid' + end + end + end + end + + def openid_specialcase_mapping(openid_url) + #Special case gmail.com, as it is pontentially a popular OpenID provider and unlike + #yahoo.com, where it works automatically, Google have hidden their OpenID endpoint + #somewhere obscure making it less userfriendly. + if (openid_url.match(/(.*)gmail.com(\/?)$/) or openid_url.match(/(.*)googlemail.com(\/?)$/) ) + return 'https://www.google.com/accounts/o8/id' + end + + return nil + end + + def openid_verify(openid_url,account_create) + authenticate_with_open_id(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. + #e.g. one can simply enter yahoo.com in the login box, i.e. no user specific url + #only once it comes back from the OpenID provider do we know the unique address for + #the user. + @user = User.find_by_display_name(session[:new_usr_name]) unless @user + @user.openid_url = identity_url + if @user.save + flash.now[:notice] = t 'user.account.flash update success' unless account_create end + else if result.missing? + mapped_id = openid_specialcase_mapping(openid_url) + if mapped_id + openid_verify(mapped_id, account_create) + else + flash.now[:error] = t 'user.login.openid missing provider' + end + else if result.invalid? + flash.now[:error] = t 'user.login.openid invalid' + else + flash.now[:error] = t 'user.login.auth failure' + end + end end end end + 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." + flash[:notice] = t 'user.set_home.flash success' redirect_to :controller => 'user', :action => 'account' end end @@ -76,82 +177,154 @@ 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}) 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.now[:notice] = t 'user.lost_password.notice email on way' 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.active = true + @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' + @title = t 'user.new.title' + # 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] + + @nickname = params['nickname'] + @email = params['email'] + @openID = params['openid'] end def login + + #The redirect from the OpenID provider reenters here again + #and we need to pass the parameters through to the + # open_id_authentication function + if params[:open_id_complete] + open_id_authentication('') + end + + + if params[:user] and session[:user].nil? + + if !params[:user][:openid_url].empty? + open_id_authentication(params[:user][:openid_url]) + else + 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 + elsif User.authenticate(:username => email_or_display_name, :password => pass, :inactive => true) + flash.now[:error] = t 'user.login.account not active' + else + flash.now[:error] = t 'user.login.auth failure' + end + end + end + if session[:user] - # The user is logged in already, if the referer param exists, redirect them to that - if params[:referer] + # The user is logged in, if the referer param exists, redirect them to that + # unless they've also got a block on them, in which case redirect them to + # the block so they can clear it. + user = User.find(session[:user]) + block = user.blocked_on_view + if block + redirect_to block, :referrer => params[:referrer] + elsif params[:referer] redirect_to params[:referer] else redirect_to :controller => 'site', :action => 'index' end return 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] + + @title = t 'user.login.title' + end + + def open_id_authentication(openid_url) + #TODO: only ask for nickname and email, if we don't already have a user for that openID, in which case + #email and nickname are already filled out. I don't know how to do that with ruby syntax though, as we + #don't want to duplicate the do block + #On the other hand it also doesn't matter too much if we ask every time, as the OpenID provider should + #remember these results, and shouldn't repromt the user for these data each time. + authenticate_with_open_id(openid_url, :return_to => request.protocol + request.host_with_port + '/login?referer=' + params[:referer], :optional => [:nickname, :email]) 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. + #e.g. one can simply enter yahoo.com in the login box, i.e. no user specific url + #only once it comes back from the OpenID provider do we know the unique address for + #the user. + user = User.find_by_openid_url(identity_url) + if user + if user.visible? and user.active? + session[:user] = user.id + else + user = nil + flash.now[:error] = t 'user.login.account not active' + end else - redirect_to :controller => 'site', :action => 'index' + #We don't have a user registered to this OpenID. 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 - 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." - else - @notice = "Sorry, couldn't log in with those details." + else if result.missing? + #Try and apply some heuristics to make common cases more userfriendly + mapped_id = openid_specialcase_mapping(openid_url) + if mapped_id + open_id_authentication(mapped_id) + else + flash.now[:error] = t 'user.login.openid missing provider' + end + else if result.invalid? + flash.now[:error] = t 'user.login.openid invalid' + else + flash.now[:error] = t 'user.login.auth failure' + end + end end end end @@ -180,12 +353,17 @@ class UserController < ApplicationController @user.active = true @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 @@ -201,11 +379,11 @@ class UserController < ApplicationController @user.email_valid = true @user.save! token.destroy - flash[:notice] = 'Confirmed your email address, thanks for signing up!' + flash[:notice] = t 'user.confirm_email.success' 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 @@ -222,10 +400,6 @@ class UserController < ApplicationController 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| @@ -235,18 +409,20 @@ 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}) friend = Friend.new @@ -254,13 +430,13 @@ class UserController < ApplicationController 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' @@ -268,17 +444,70 @@ class UserController < ApplicationController 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}) 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[:notice] = "#{friend.display_name} is not one of your friends." + flash[:error] = t 'user.remove_friend.not_a_friend', :name => friend.display_name end redirect_to :controller => 'user', :action => 'view' end end + + ## + # activate a user, allowing them to log in + def activate + @this_user.update_attributes(:active => true) + redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name] + end + + ## + # deactivate a user, preventing them from logging in + def deactivate + @this_user.update_attributes(:active => false) + redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name] + end + + ## + # hide a user, marking them as logically deleted + def hide + @this_user.update_attributes(:visible => false) + redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name] + end + + ## + # unhide a user, clearing the logically deleted flag + def unhide + @this_user.update_attributes(:visible => true) + 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 +private + ## + # require that the user is a administrator, or fill out a helpful error message + # and return them to the user page. + def require_administrator + unless @user.administrator? + flash[:error] = t('user.filter.not_an_administrator') + redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name] + 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