2013-02-14 12:57:26 -05:00
require_dependency 'discourse_hub'
2013-06-06 16:40:10 +02:00
require_dependency 'user_name_suggester'
2013-02-05 14:16:51 -05:00
class UsersController < ApplicationController
2013-03-08 15:04:37 -05:00
skip_before_filter :check_xhr , only : [ :show , :password_reset , :update , :activate_account , :avatar , :authorize_email , :user_preferences_redirect ]
2013-02-05 14:16:51 -05:00
skip_before_filter :authorize_mini_profiler , only : [ :avatar ]
skip_before_filter :check_restricted_access , only : [ :avatar ]
before_filter :ensure_logged_in , only : [ :username , :update , :change_email , :user_preferences_redirect ]
2013-02-07 16:45:24 +01:00
2013-05-22 11:20:16 -04:00
# we need to allow account creation with bad CSRF tokens, if people are caching, the CSRF token on the
2013-05-03 16:43:11 +10:00
# page is going to be empty, this means that server will see an invalid CSRF and blow the session
# once that happens you can't log in with social
2013-06-05 14:01:24 +10:00
skip_before_filter :verify_authenticity_token , only : [ :create ]
skip_before_filter :redirect_to_login_if_required , only : [ :check_username , :create , :get_honeypot_value , :activate_account , :send_activation_email , :authorize_email ]
2013-05-03 16:43:11 +10:00
2013-02-07 16:45:24 +01:00
def show
2013-02-05 14:16:51 -05:00
@user = fetch_user_from_params
2013-03-08 15:04:37 -05:00
user_serializer = UserSerializer . new ( @user , scope : guardian , root : 'user' )
respond_to do | format |
format . html do
store_preloaded ( " user_ #{ @user . username } " , MultiJson . dump ( user_serializer ) )
end
format . json do
render_json_dump ( user_serializer )
end
2013-02-05 14:16:51 -05:00
end
end
def user_preferences_redirect
redirect_to email_preferences_path ( current_user . username_lower )
end
def update
2013-03-23 20:32:59 +05:30
user = User . where ( username_lower : params [ :username ] . downcase ) . first
2013-02-05 14:16:51 -05:00
guardian . ensure_can_edit! ( user )
2013-04-12 10:07:46 +10:00
json_result ( user , serializer : UserSerializer ) do | u |
2013-02-05 14:16:51 -05:00
website = params [ :website ]
2013-02-07 16:45:24 +01:00
if website
2013-02-05 14:16:51 -05:00
website = " http:// " + website unless website =~ / ^http /
end
u . bio_raw = params [ :bio_raw ] || u . bio_raw
u . name = params [ :name ] || u . name
u . website = website || u . website
u . digest_after_days = params [ :digest_after_days ] || u . digest_after_days
u . auto_track_topics_after_msecs = params [ :auto_track_topics_after_msecs ] . to_i if params [ :auto_track_topics_after_msecs ]
2013-02-14 17:32:58 +11:00
u . new_topic_duration_minutes = params [ :new_topic_duration_minutes ] . to_i if params [ :new_topic_duration_minutes ]
2013-02-05 14:16:51 -05:00
2013-03-22 14:08:11 -04:00
[ :email_digests , :email_direct , :email_private_messages ,
2013-06-14 23:58:24 -07:00
:external_links_in_new_tab , :enable_quoting , :dynamic_favicon ] . each do | i |
2013-02-05 14:16:51 -05:00
if params [ i ] . present?
u . send ( " #{ i . to_s } = " , params [ i ] == 'true' )
end
end
2013-04-12 10:07:46 +10:00
if u . save
u
else
nil
end
2013-02-07 16:45:24 +01:00
end
2013-02-05 14:16:51 -05:00
end
def username
2013-06-06 00:14:32 -07:00
params . require ( :new_username )
2013-02-05 14:16:51 -05:00
user = fetch_user_from_params
2013-02-07 16:45:24 +01:00
guardian . ensure_can_edit! ( user )
2013-02-05 14:16:51 -05:00
result = user . change_username ( params [ :new_username ] )
raise Discourse :: InvalidParameters . new ( :new_username ) unless result
2013-02-07 16:45:24 +01:00
render nothing : true
2013-02-05 14:16:51 -05:00
end
def preferences
render nothing : true
end
def invited
invited_list = InvitedList . new ( fetch_user_from_params )
render_serialized ( invited_list , InvitedListSerializer )
end
def is_local_username
2013-06-06 00:14:32 -07:00
params . require ( :username )
2013-02-05 14:16:51 -05:00
u = params [ :username ] . downcase
r = User . exec_sql ( 'select 1 from users where username_lower = ?' , u ) . values
render json : { valid : r . length == 1 }
end
def check_username
2013-06-06 00:14:32 -07:00
params . require ( :username )
2013-02-05 14:16:51 -05:00
2013-02-08 14:12:48 -05:00
validator = UsernameValidator . new ( params [ :username ] )
if ! validator . valid_format?
render json : { errors : validator . errors }
2013-02-14 12:57:26 -05:00
elsif ! SiteSetting . call_discourse_hub?
2013-02-05 14:16:51 -05:00
if User . username_available? ( params [ :username ] )
render json : { available : true }
else
2013-06-06 16:40:10 +02:00
render json : { available : false , suggestion : UserNameSuggester . suggest ( params [ :username ] ) }
2013-02-05 14:16:51 -05:00
end
else
2013-02-14 12:57:26 -05:00
# Contact the Discourse Hub server
2013-03-05 01:42:44 +01:00
email_given = ( params [ :email ] . present? || current_user . present? )
2013-02-05 14:16:51 -05:00
available_locally = User . username_available? ( params [ :username ] )
global_match = false
2013-02-14 12:57:26 -05:00
available_globally , suggestion_from_discourse_hub = begin
2013-02-05 14:16:51 -05:00
if email_given
2013-02-14 12:57:26 -05:00
global_match , available , suggestion = DiscourseHub . nickname_match? ( params [ :username ] , params [ :email ] || current_user . email )
2013-02-05 14:16:51 -05:00
[ available || global_match , suggestion ]
else
2013-02-14 12:57:26 -05:00
DiscourseHub . nickname_available? ( params [ :username ] )
2013-02-05 14:16:51 -05:00
end
end
2013-03-05 01:42:44 +01:00
if available_globally && available_locally
2013-02-05 14:16:51 -05:00
render json : { available : true , global_match : ( global_match ? true : false ) }
2013-03-05 01:42:44 +01:00
elsif available_locally && ! available_globally
2013-02-05 14:16:51 -05:00
if email_given
2013-02-14 12:57:26 -05:00
# Nickname and email do not match what's registered on the discourse hub.
render json : { available : false , global_match : false , suggestion : suggestion_from_discourse_hub }
2013-02-05 14:16:51 -05:00
else
2013-02-14 12:57:26 -05:00
# The nickname is available locally, but is registered on the discourse hub.
2013-02-05 14:16:51 -05:00
# We need an email to see if the nickname belongs to this person.
2013-02-14 12:57:26 -05:00
# Don't give a suggestion until we get the email and try to match it with on the discourse hub.
2013-02-05 14:16:51 -05:00
render json : { available : false }
end
2013-03-05 01:42:44 +01:00
elsif available_globally && ! available_locally
2013-02-05 14:16:51 -05:00
# Already registered on this site with the matching nickname and email address. Why are you signing up again?
2013-06-06 16:40:10 +02:00
render json : { available : false , suggestion : UserNameSuggester . suggest ( params [ :username ] ) }
2013-02-05 14:16:51 -05:00
else
# Not available anywhere.
2013-02-14 12:57:26 -05:00
render json : { available : false , suggestion : suggestion_from_discourse_hub }
2013-02-05 14:16:51 -05:00
end
end
rescue RestClient :: Forbidden
2013-02-14 12:57:26 -05:00
render json : { errors : [ I18n . t ( " discourse_hub.access_token_problem " ) ] }
2013-02-05 14:16:51 -05:00
end
def create
2013-06-05 11:08:21 -07:00
return fake_success_reponse if suspicious? params
2013-02-06 19:25:21 -05:00
2013-04-13 00:46:55 +02:00
user = User . new_from_params ( params )
2013-02-05 14:16:51 -05:00
auth = session [ :authentication ]
2013-04-13 00:46:55 +02:00
if valid_session_authentication? ( auth , params [ :email ] )
2013-02-05 14:16:51 -05:00
user . active = true
end
2013-02-28 16:08:56 +03:00
user . password_required! unless auth
2013-02-05 14:16:51 -05:00
2013-04-13 00:46:55 +02:00
if user . valid? && SiteSetting . call_discourse_hub?
DiscourseHub . register_nickname ( user . username , user . email )
end
2013-02-05 14:16:51 -05:00
if user . save
2013-06-05 18:19:27 -07:00
if SiteSetting . must_approve_users?
message = I18n . t ( " login.wait_approval " )
elsif ! user . active?
message = I18n . t ( " login.activate_email " , email : user . email )
Jobs . enqueue ( :user_email ,
type : :signup ,
user_id : user . id ,
2013-04-13 00:46:55 +02:00
email_token : user . email_tokens . first . token
)
2013-06-05 18:19:27 -07:00
else
message = I18n . t ( " login.active " )
log_on_user ( user )
user . enqueue_welcome_message ( 'welcome_user' )
2013-02-05 14:16:51 -05:00
end
2013-04-13 00:46:55 +02:00
create_third_party_auth_records ( user , auth ) if auth . present?
2013-02-07 16:45:24 +01:00
# Clear authentication session.
2013-02-05 14:16:51 -05:00
session [ :authentication ] = nil
2013-06-05 18:19:27 -07:00
render json : { success : true , active : user . active? , message : message }
2013-02-05 14:16:51 -05:00
else
2013-04-13 00:46:55 +02:00
render json : {
success : false ,
message : I18n . t ( " login.errors " , errors : user . errors . full_messages . join ( " \n " ) )
}
2013-02-05 14:16:51 -05:00
end
2013-03-07 14:56:28 -05:00
rescue ActiveRecord :: StatementInvalid
2013-04-13 00:46:55 +02:00
render json : { success : false , message : I18n . t ( " login.something_already_taken " ) }
2013-02-14 12:57:26 -05:00
rescue DiscourseHub :: NicknameUnavailable
2013-04-13 00:46:55 +02:00
render json : { success : false ,
message : I18n . t (
" login.errors " ,
errors : I18n . t (
2013-06-06 16:40:10 +02:00
" login.not_available " , suggestion : UserNameSuggester . suggest ( params [ :username ] )
2013-04-13 00:46:55 +02:00
)
)
}
2013-02-05 14:16:51 -05:00
rescue RestClient :: Forbidden
2013-04-13 00:46:55 +02:00
render json : { errors : [ I18n . t ( " discourse_hub.access_token_problem " ) ] }
2013-02-05 14:16:51 -05:00
end
2013-02-06 19:25:21 -05:00
def get_honeypot_value
render json : { value : honeypot_value , challenge : challenge_value }
end
2013-02-05 14:16:51 -05:00
# all avatars are funneled through here
def avatar
2013-02-07 16:45:24 +01:00
2013-02-05 14:16:51 -05:00
# TEMP to catch all missing spots
# raise ActiveRecord::RecordNotFound
2013-02-07 16:45:24 +01:00
2013-03-23 20:32:59 +05:30
user = User . select ( :email ) . where ( username_lower : params [ :username ] . downcase ) . first
2013-04-13 00:46:55 +02:00
if user . present?
# for now we only support gravatar in square (redirect cached for a day),
# later we can use x-sendfile and/or a cdn to serve local
size = determine_avatar_size ( params [ :size ] )
2013-02-05 14:16:51 -05:00
url = user . avatar_template . gsub ( " {size} " , size . to_s )
expires_in 1 . day
2013-02-07 16:45:24 +01:00
redirect_to url
else
2013-02-05 14:16:51 -05:00
raise ActiveRecord :: RecordNotFound
end
end
def password_reset
expires_now ( )
@user = EmailToken . confirm ( params [ :token ] )
if @user . blank?
flash [ :error ] = I18n . t ( 'password_reset.no_token' )
else
2013-03-05 01:42:44 +01:00
if request . put? && params [ :password ] . present?
2013-02-05 14:16:51 -05:00
@user . password = params [ :password ]
if @user . save
2013-04-03 12:23:28 -04:00
if Guardian . new ( @user ) . can_access_forum?
2013-02-05 14:16:51 -05:00
# Log in the user
log_on_user ( @user )
flash [ :success ] = I18n . t ( 'password_reset.success' )
2013-04-03 12:23:28 -04:00
else
@requires_approval = true
flash [ :success ] = I18n . t ( 'password_reset.success_unapproved' )
2013-02-05 14:16:51 -05:00
end
2013-02-07 16:45:24 +01:00
end
end
2013-02-05 14:16:51 -05:00
end
2013-03-22 14:08:11 -04:00
render layout : 'no_js'
2013-02-05 14:16:51 -05:00
end
2013-02-07 16:45:24 +01:00
2013-02-05 14:16:51 -05:00
def change_email
2013-06-06 00:14:32 -07:00
params . require ( :email )
2013-02-05 14:16:51 -05:00
user = fetch_user_from_params
guardian . ensure_can_edit! ( user )
2013-04-27 23:02:23 -04:00
lower_email = Email . downcase ( params [ :email ] ) . strip
2013-02-05 14:16:51 -05:00
# Raise an error if the email is already in use
2013-04-15 02:20:33 +02:00
if User . where ( " email = ? " , lower_email ) . exists?
2013-04-13 00:46:55 +02:00
raise Discourse :: InvalidParameters . new ( :email )
end
2013-02-05 14:16:51 -05:00
2013-04-15 02:20:33 +02:00
email_token = user . email_tokens . create ( email : lower_email )
2013-04-13 00:46:55 +02:00
Jobs . enqueue (
:user_email ,
2013-04-15 02:20:33 +02:00
to_address : lower_email ,
2013-04-13 00:46:55 +02:00
type : :authorize_email ,
user_id : user . id ,
email_token : email_token . token
)
2013-02-05 14:16:51 -05:00
2013-02-07 16:45:24 +01:00
render nothing : true
2013-02-05 14:16:51 -05:00
end
def authorize_email
expires_now ( )
if @user = EmailToken . confirm ( params [ :token ] )
log_on_user ( @user )
else
flash [ :error ] = I18n . t ( 'change_email.error' )
end
2013-03-22 14:08:11 -04:00
render layout : 'no_js'
2013-02-05 14:16:51 -05:00
end
def activate_account
expires_now ( )
if @user = EmailToken . confirm ( params [ :token ] )
# Log in the user unless they need to be approved
2013-04-03 12:23:28 -04:00
if Guardian . new ( @user ) . can_access_forum?
2013-02-05 14:16:51 -05:00
@user . enqueue_welcome_message ( 'welcome_user' ) if @user . send_welcome_message
log_on_user ( @user )
2013-04-03 12:23:28 -04:00
else
@needs_approval = true
2013-02-05 14:16:51 -05:00
end
else
flash [ :error ] = I18n . t ( 'activation.already_done' )
end
2013-03-22 14:08:11 -04:00
render layout : 'no_js'
2013-02-05 14:16:51 -05:00
end
2013-02-22 11:49:48 -05:00
def send_activation_email
@user = fetch_user_from_params
@email_token = @user . email_tokens . unconfirmed . active . first
if @user
@email_token = @user . email_tokens . create ( email : @user . email ) if @email_token . nil?
Jobs . enqueue ( :user_email , type : :signup , user_id : @user . id , email_token : @email_token . token )
end
render nothing : true
end
2013-02-05 14:16:51 -05:00
def search_users
2013-02-07 05:59:25 -05:00
term = params [ :term ] . to_s . strip
2013-02-05 14:16:51 -05:00
topic_id = params [ :topic_id ]
topic_id = topic_id . to_i if topic_id
2013-02-07 04:34:49 -05:00
results = UserSearch . search term , topic_id
2013-02-05 14:16:51 -05:00
2013-04-13 00:46:55 +02:00
render json : { users : results . as_json ( only : [ :username , :name ] ,
methods : :avatar_template ) }
2013-02-05 14:16:51 -05:00
end
private
2013-02-06 19:25:21 -05:00
def honeypot_value
Digest :: SHA1 :: hexdigest ( " #{ Discourse . current_hostname } : #{ Discourse :: Application . config . secret_token } " ) [ 0 , 15 ]
end
def challenge_value
'3019774c067cc2b'
end
2013-06-05 11:08:21 -07:00
def suspicious? ( params )
honeypot_or_challenge_fails? ( params ) || SiteSetting . invite_only?
end
def fake_success_reponse
render (
json : {
success : true ,
active : false ,
message : I18n . t ( " login.activate_email " , email : params [ :email ] )
}
)
end
2013-04-13 00:46:55 +02:00
def honeypot_or_challenge_fails? ( params )
params [ :password_confirmation ] != honeypot_value ||
params [ :challenge ] != challenge_value . try ( :reverse )
end
def valid_session_authentication? ( auth , email )
auth && auth [ :email ] == email && auth [ :email_valid ]
end
def create_third_party_auth_records ( user , auth )
if twitter_auth? ( auth )
TwitterUserInfo . create (
user_id : user . id ,
screen_name : auth [ :twitter_screen_name ] ,
twitter_user_id : auth [ :twitter_user_id ]
)
end
if facebook_auth? ( auth )
FacebookUserInfo . create! ( auth [ :facebook ] . merge ( user_id : user . id ) )
end
if github_auth? ( auth )
GithubUserInfo . create (
user_id : user . id ,
screen_name : auth [ :github_screen_name ] ,
github_user_id : auth [ :github_user_id ]
)
end
end
def twitter_auth? ( auth )
auth [ :twitter_user_id ] && auth [ :twitter_screen_name ] &&
TwitterUserInfo . find_by_twitter_user_id ( auth [ :twitter_user_id ] ) . nil?
end
def facebook_auth? ( auth )
auth [ :facebook ] . present? &&
FacebookUserInfo . find_by_facebook_user_id ( auth [ :facebook ] [ :facebook_user_id ] ) . nil?
end
def github_auth? ( auth )
auth [ :github_user_id ] && auth [ :github_screen_name ] &&
GithubUserInfo . find_by_github_user_id ( auth [ :github_user_id ] ) . nil?
end
def determine_avatar_size ( size )
size = size . to_i
size = 64 if size == 0
size = 10 if size < 10
size = 128 if size > 128
size
end
2013-02-05 14:16:51 -05:00
end