Merge branch 'master' of github.com:discourse/discourse

This commit is contained in:
Sam Saffron 2013-02-07 12:32:39 +11:00
commit 554ba7b676
8 changed files with 88 additions and 6 deletions

View File

@ -190,9 +190,9 @@ window.Discourse.User.reopenClass
error: (xhr) -> promise.reject(xhr) error: (xhr) -> promise.reject(xhr)
promise promise
createAccount: (name, email, password, username) -> createAccount: (name, email, password, username, passwordConfirm, challenge) ->
$.ajax $.ajax
url: '/users' url: '/users'
dataType: 'json' dataType: 'json'
data: {name: name, email: email, password: password, username: username} data: {name: name, email: email, password: password, username: username, password_confirmation: passwordConfirm, challenge: challenge}
type: 'POST' type: 'POST'

View File

@ -49,6 +49,14 @@
</tr> </tr>
{{/if}} {{/if}}
<tr class="password-confirmation">
<td><label for='new-account-password-confirmation'>Password Again</label></td>
<td>
{{view Ember.TextField valueBinding="view.accountPasswordConfirm" type="password" id="new-account-password-confirmation"}}
{{view Ember.TextField valueBinding="view.accountChallenge" id="new-account-challenge"}}
</td>
</tr>
</table> </table>
</form> </form>
</div> </div>

View File

@ -3,6 +3,8 @@ window.Discourse.CreateAccountView = window.Discourse.ModalBodyView.extend Disco
title: Em.String.i18n('create_account.title') title: Em.String.i18n('create_account.title')
uniqueUsernameValidation: null uniqueUsernameValidation: null
complete: false complete: false
accountPasswordConfirm: 0
accountChallenge: 0
submitDisabled: (-> submitDisabled: (->
@ -22,6 +24,8 @@ window.Discourse.CreateAccountView = window.Discourse.ModalBodyView.extend Disco
# If blank, fail without a reason # If blank, fail without a reason
return Discourse.InputValidation.create(failed: true) if @blank('accountName') return Discourse.InputValidation.create(failed: true) if @blank('accountName')
@fetchConfirmationValue() if @get('accountPasswordConfirm') == 0
# If too short # If too short
return Discourse.InputValidation.create(failed: true, reason: Em.String.i18n('user.name.too_short')) if @get('accountName').length < 3 return Discourse.InputValidation.create(failed: true, reason: Em.String.i18n('user.name.too_short')) if @get('accountName').length < 3
@ -120,13 +124,22 @@ window.Discourse.CreateAccountView = window.Discourse.ModalBodyView.extend Disco
).property('accountPassword') ).property('accountPassword')
fetchConfirmationValue: ->
$.ajax
url: '/users/hp.json',
success: (json) =>
@set('accountPasswordConfirm', json.value)
@set('accountChallenge', json.challenge.split("").reverse().join(""))
createAccount: -> createAccount: ->
name = @get('accountName') name = @get('accountName')
email = @get('accountEmail') email = @get('accountEmail')
password = @get('accountPassword') password = @get('accountPassword')
username = @get('accountUsername') username = @get('accountUsername')
passwordConfirm = @get('accountPasswordConfirm')
challenge = @get('accountChallenge')
Discourse.User.createAccount(name, email, password, username).then (result) => Discourse.User.createAccount(name, email, password, username, passwordConfirm, challenge).then (result) =>
if result.success if result.success
@flash(result.message) @flash(result.message)

View File

@ -152,6 +152,9 @@
margin-bottom: 20px; margin-bottom: 20px;
} }
} }
.password-confirmation {
display: none;
}
} }
#move-selected { #move-selected {

View File

@ -123,6 +123,12 @@ class UsersController < ApplicationController
end end
def create def create
if params[:password_confirmation] != honeypot_value or params[:challenge] != challenge_value.try(:reverse)
# Don't give any indication that we caught you in the honeypot
return render(:json => {success: true, active: false, message: I18n.t("login.activate_email", email: params[:email]) })
end
user = User.new user = User.new
user.name = params[:name] user.name = params[:name]
user.email = params[:email] user.email = params[:email]
@ -183,6 +189,10 @@ class UsersController < ApplicationController
render json: {errors: [I18n.t("mothership.access_token_problem")]} render json: {errors: [I18n.t("mothership.access_token_problem")]}
end end
def get_honeypot_value
render json: {value: honeypot_value, challenge: challenge_value}
end
# all avatars are funneled through here # all avatars are funneled through here
def avatar def avatar
@ -320,6 +330,14 @@ class UsersController < ApplicationController
private private
def honeypot_value
Digest::SHA1::hexdigest("#{Discourse.current_hostname}:#{Discourse::Application.config.secret_token}")[0,15]
end
def challenge_value
'3019774c067cc2b'
end
def fetch_user_from_params def fetch_user_from_params
username_lower = params[:username].downcase username_lower = params[:username].downcase
username_lower.gsub!(/\.json$/, '') username_lower.gsub!(/\.json$/, '')

View File

@ -80,6 +80,7 @@ Discourse::Application.routes.draw do
put 'users/password-reset/:token' => 'users#password_reset' put 'users/password-reset/:token' => 'users#password_reset'
get 'users/activate-account/:token' => 'users#activate_account' get 'users/activate-account/:token' => 'users#activate_account'
get 'users/authorize-email/:token' => 'users#authorize_email' get 'users/authorize-email/:token' => 'users#authorize_email'
get 'users/hp' => 'users#get_honeypot_value'
get 'user_preferences' => 'users#user_preferences_redirect' get 'user_preferences' => 'users#user_preferences_redirect'
get 'users/:username/private-messages' => 'user_actions#private_messages', :format => false, :constraints => {:username => USERNAME_ROUTE_FORMAT} get 'users/:username/private-messages' => 'user_actions#private_messages', :format => false, :constraints => {:username => USERNAME_ROUTE_FORMAT}

View File

@ -31,8 +31,8 @@ module SiteSettingExtension
end end
# just like a setting, except that it is available in javascript via DiscourseSession # just like a setting, except that it is available in javascript via DiscourseSession
def client_setting(name, defualt = nil, type = nil) def client_setting(name, default = nil, type = nil)
setting(name,defualt,type) setting(name,default,type)
@@client_settings ||= [] @@client_settings ||= []
@@client_settings << name @@client_settings << name
end end

View File

@ -2,6 +2,11 @@ require 'spec_helper'
describe UsersController do describe UsersController do
before do
UsersController.any_instance.stubs(:honeypot_value).returns(nil)
UsersController.any_instance.stubs(:challenge_value).returns(nil)
end
describe '.show' do describe '.show' do
let!(:user) { log_in } let!(:user) { log_in }
@ -339,7 +344,41 @@ describe UsersController do
User.where(username: @user.username).first.active.should be_false User.where(username: @user.username).first.active.should be_false
end end
end end
shared_examples_for 'honeypot fails' do
it 'should not create a new user' do
expect {
xhr :post, :create, create_params
}.to_not change { User.count }
end
it 'should not send an email' do
User.any_instance.expects(:enqueue_welcome_message).never
xhr :post, :create, create_params
end
it 'should say it was successful' do
xhr :post, :create, create_params
json = JSON::parse(response.body)
json["success"].should be_true
end
end
context 'when honeypot value is wrong' do
before do
UsersController.any_instance.stubs(:honeypot_value).returns('abc')
end
let(:create_params) { {:name => @user.name, :username => @user.username, :password => "strongpassword", :email => @user.email, :password_confirmation => 'wrong'} }
it_should_behave_like 'honeypot fails'
end
context 'when challenge answer is wrong' do
before do
UsersController.any_instance.stubs(:challenge_value).returns('abc')
end
let(:create_params) { {:name => @user.name, :username => @user.username, :password => "strongpassword", :email => @user.email, :challenge => 'abc'} }
it_should_behave_like 'honeypot fails'
end
end end
context '.username' do context '.username' do