PageRenderTime 58ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/spec/controllers/session_controller_spec.rb

https://bitbucket.org/bitworkvn/cs_discourse
Ruby | 985 lines | 823 code | 154 blank | 8 comment | 9 complexity | 27b9e19a320746834853c1553060a394 MD5 | raw file
  1. require 'rails_helper'
  2. describe SessionController do
  3. shared_examples 'failed to continue local login' do
  4. it 'should return the right response' do
  5. expect(response).not_to be_success
  6. expect(response.status.to_i).to eq 500
  7. end
  8. end
  9. describe '#become' do
  10. let!(:user) { Fabricate(:user) }
  11. it "does not work when in production mode" do
  12. Rails.env.stubs(:production?).returns(true)
  13. get :become, params: { session_id: user.username }, format: :json
  14. expect(response.status).to eq(403)
  15. expect(JSON.parse(response.body)["error_type"]).to eq("invalid_access")
  16. expect(session[:current_user_id]).to be_blank
  17. end
  18. it "works in developmenet mode" do
  19. Rails.env.stubs(:development?).returns(true)
  20. get :become, params: { session_id: user.username }, format: :json
  21. expect(response).to be_redirect
  22. expect(session[:current_user_id]).to eq(user.id)
  23. end
  24. end
  25. describe '#sso_login' do
  26. before do
  27. @sso_url = "http://somesite.com/discourse_sso"
  28. @sso_secret = "shjkfdhsfkjh"
  29. request.host = Discourse.current_hostname
  30. SiteSetting.sso_url = @sso_url
  31. SiteSetting.enable_sso = true
  32. SiteSetting.sso_secret = @sso_secret
  33. # We have 2 options, either fabricate an admin or don't
  34. # send welcome messages
  35. Fabricate(:admin)
  36. # skip for now
  37. # SiteSetting.send_welcome_message = false
  38. end
  39. def get_sso(return_path)
  40. nonce = SecureRandom.hex
  41. dso = DiscourseSingleSignOn.new
  42. dso.nonce = nonce
  43. dso.register_nonce(return_path)
  44. sso = SingleSignOn.new
  45. sso.nonce = nonce
  46. sso.sso_secret = @sso_secret
  47. sso
  48. end
  49. it 'can take over an account' do
  50. sso = get_sso("/")
  51. user = Fabricate(:user)
  52. sso.email = user.email
  53. sso.external_id = 'abc'
  54. sso.username = 'sam'
  55. get :sso_login, params: Rack::Utils.parse_query(sso.payload)
  56. expect(response).to redirect_to('/')
  57. logged_on_user = Discourse.current_user_provider.new(request.env).current_user
  58. expect(logged_on_user.email).to eq(user.email)
  59. expect(logged_on_user.single_sign_on_record.external_id).to eq("abc")
  60. expect(logged_on_user.single_sign_on_record.external_username).to eq('sam')
  61. end
  62. def sso_for_ip_specs
  63. sso = get_sso('/a/')
  64. sso.external_id = '666' # the number of the beast
  65. sso.email = 'bob@bob.com'
  66. sso.name = 'Sam Saffron'
  67. sso.username = 'sam'
  68. sso
  69. end
  70. it 'respects IP restrictions on create' do
  71. screened_ip = Fabricate(:screened_ip_address)
  72. ActionDispatch::Request.any_instance.stubs(:remote_ip).returns(screened_ip.ip_address)
  73. sso = sso_for_ip_specs
  74. get :sso_login, params: Rack::Utils.parse_query(sso.payload)
  75. logged_on_user = Discourse.current_user_provider.new(request.env).current_user
  76. expect(logged_on_user).to eq(nil)
  77. end
  78. it 'respects IP restrictions on login' do
  79. sso = sso_for_ip_specs
  80. _user = DiscourseSingleSignOn.parse(sso.payload).lookup_or_create_user(request.remote_ip)
  81. sso = sso_for_ip_specs
  82. screened_ip = Fabricate(:screened_ip_address)
  83. ActionDispatch::Request.any_instance.stubs(:remote_ip).returns(screened_ip.ip_address)
  84. get :sso_login, params: Rack::Utils.parse_query(sso.payload)
  85. logged_on_user = Discourse.current_user_provider.new(request.env).current_user
  86. expect(logged_on_user).to be_blank
  87. end
  88. it 'respects email restrictions' do
  89. sso = get_sso('/a/')
  90. sso.external_id = '666' # the number of the beast
  91. sso.email = 'bob@bob.com'
  92. sso.name = 'Sam Saffron'
  93. sso.username = 'sam'
  94. ScreenedEmail.block('bob@bob.com')
  95. get :sso_login, params: Rack::Utils.parse_query(sso.payload)
  96. logged_on_user = Discourse.current_user_provider.new(request.env).current_user
  97. expect(logged_on_user).to eq(nil)
  98. end
  99. it 'allows you to create an admin account' do
  100. sso = get_sso('/a/')
  101. sso.external_id = '666' # the number of the beast
  102. sso.email = 'bob@bob.com'
  103. sso.name = 'Sam Saffron'
  104. sso.username = 'sam'
  105. sso.custom_fields["shop_url"] = "http://my_shop.com"
  106. sso.custom_fields["shop_name"] = "Sam"
  107. sso.admin = true
  108. get :sso_login, params: Rack::Utils.parse_query(sso.payload)
  109. logged_on_user = Discourse.current_user_provider.new(request.env).current_user
  110. expect(logged_on_user.admin).to eq(true)
  111. end
  112. it 'redirects to a non-relative url' do
  113. sso = get_sso("#{Discourse.base_url}/b/")
  114. sso.external_id = '666' # the number of the beast
  115. sso.email = 'bob@bob.com'
  116. sso.name = 'Sam Saffron'
  117. sso.username = 'sam'
  118. get :sso_login, params: Rack::Utils.parse_query(sso.payload)
  119. expect(response).to redirect_to('/b/')
  120. end
  121. it 'redirects to random url if it is allowed' do
  122. SiteSetting.sso_allows_all_return_paths = true
  123. sso = get_sso('https://gusundtrout.com')
  124. sso.external_id = '666' # the number of the beast
  125. sso.email = 'bob@bob.com'
  126. sso.name = 'Sam Saffron'
  127. sso.username = 'sam'
  128. get :sso_login, params: Rack::Utils.parse_query(sso.payload)
  129. expect(response).to redirect_to('https://gusundtrout.com')
  130. end
  131. it 'redirects to root if the host of the return_path is different' do
  132. sso = get_sso('//eviltrout.com')
  133. sso.external_id = '666' # the number of the beast
  134. sso.email = 'bob@bob.com'
  135. sso.name = 'Sam Saffron'
  136. sso.username = 'sam'
  137. get :sso_login, params: Rack::Utils.parse_query(sso.payload)
  138. expect(response).to redirect_to('/')
  139. end
  140. it 'redirects to root if the host of the return_path is different' do
  141. sso = get_sso('http://eviltrout.com')
  142. sso.external_id = '666' # the number of the beast
  143. sso.email = 'bob@bob.com'
  144. sso.name = 'Sam Saffron'
  145. sso.username = 'sam'
  146. get :sso_login, params: Rack::Utils.parse_query(sso.payload)
  147. expect(response).to redirect_to('/')
  148. end
  149. it 'allows you to create an account' do
  150. sso = get_sso('/a/')
  151. sso.external_id = '666' # the number of the beast
  152. sso.email = 'bob@bob.com'
  153. sso.name = 'Sam Saffron'
  154. sso.username = 'sam'
  155. sso.custom_fields["shop_url"] = "http://my_shop.com"
  156. sso.custom_fields["shop_name"] = "Sam"
  157. events = DiscourseEvent.track_events do
  158. get :sso_login, params: Rack::Utils.parse_query(sso.payload)
  159. end
  160. expect(events.map { |event| event[:event_name] }).to include(
  161. :user_logged_in, :user_first_logged_in
  162. )
  163. expect(response).to redirect_to('/a/')
  164. logged_on_user = Discourse.current_user_provider.new(request.env).current_user
  165. # ensure nothing is transient
  166. logged_on_user = User.find(logged_on_user.id)
  167. expect(logged_on_user.admin).to eq(false)
  168. expect(logged_on_user.email).to eq('bob@bob.com')
  169. expect(logged_on_user.name).to eq('Sam Saffron')
  170. expect(logged_on_user.username).to eq('sam')
  171. expect(logged_on_user.single_sign_on_record.external_id).to eq("666")
  172. expect(logged_on_user.single_sign_on_record.external_username).to eq('sam')
  173. expect(logged_on_user.active).to eq(true)
  174. expect(logged_on_user.custom_fields["shop_url"]).to eq("http://my_shop.com")
  175. expect(logged_on_user.custom_fields["shop_name"]).to eq("Sam")
  176. expect(logged_on_user.custom_fields["bla"]).to eq(nil)
  177. end
  178. context 'when sso emails are not trusted' do
  179. context 'if you have not activated your account' do
  180. it 'does not log you in' do
  181. sso = get_sso('/a/')
  182. sso.external_id = '666' # the number of the beast
  183. sso.email = 'bob@bob.com'
  184. sso.name = 'Sam Saffron'
  185. sso.username = 'sam'
  186. sso.require_activation = true
  187. get :sso_login, params: Rack::Utils.parse_query(sso.payload)
  188. logged_on_user = Discourse.current_user_provider.new(request.env).current_user
  189. expect(logged_on_user).to eq(nil)
  190. end
  191. it 'sends an activation email' do
  192. Jobs.expects(:enqueue).with(:critical_user_email, has_entries(type: :signup))
  193. sso = get_sso('/a/')
  194. sso.external_id = '666' # the number of the beast
  195. sso.email = 'bob@bob.com'
  196. sso.name = 'Sam Saffron'
  197. sso.username = 'sam'
  198. sso.require_activation = true
  199. get :sso_login, params: Rack::Utils.parse_query(sso.payload)
  200. end
  201. end
  202. context 'if you have activated your account' do
  203. it 'allows you to log in' do
  204. sso = get_sso('/hello/world')
  205. sso.external_id = '997'
  206. sso.sso_url = "http://somewhere.over.com/sso_login"
  207. sso.require_activation = true
  208. user = Fabricate(:user)
  209. user.create_single_sign_on_record(external_id: '997', last_payload: '')
  210. user.stubs(:active?).returns(true)
  211. get :sso_login, params: Rack::Utils.parse_query(sso.payload)
  212. logged_on_user = Discourse.current_user_provider.new(request.env).current_user
  213. expect(user.id).to eq(logged_on_user.id)
  214. end
  215. end
  216. end
  217. it 'allows login to existing account with valid nonce' do
  218. sso = get_sso('/hello/world')
  219. sso.external_id = '997'
  220. sso.sso_url = "http://somewhere.over.com/sso_login"
  221. user = Fabricate(:user)
  222. user.create_single_sign_on_record(external_id: '997', last_payload: '')
  223. get :sso_login, params: Rack::Utils.parse_query(sso.payload)
  224. user.single_sign_on_record.reload
  225. expect(user.single_sign_on_record.last_payload).to eq(sso.unsigned_payload)
  226. expect(response).to redirect_to('/hello/world')
  227. logged_on_user = Discourse.current_user_provider.new(request.env).current_user
  228. expect(user.id).to eq(logged_on_user.id)
  229. # nonce is bad now
  230. get :sso_login, params: Rack::Utils.parse_query(sso.payload)
  231. expect(response.code).to eq('419')
  232. end
  233. describe 'can act as an SSO provider' do
  234. before do
  235. SiteSetting.enable_sso_provider = true
  236. SiteSetting.enable_sso = false
  237. SiteSetting.enable_local_logins = true
  238. SiteSetting.sso_secret = "topsecret"
  239. @sso = SingleSignOn.new
  240. @sso.nonce = "mynonce"
  241. @sso.sso_secret = SiteSetting.sso_secret
  242. @sso.return_sso_url = "http://somewhere.over.rainbow/sso"
  243. @user = Fabricate(:user, password: "myfrogs123ADMIN", active: true, admin: true)
  244. group = Fabricate(:group)
  245. group.add(@user)
  246. @user.reload
  247. EmailToken.update_all(confirmed: true)
  248. end
  249. it "successfully logs in and redirects user to return_sso_url when the user is not logged in" do
  250. get :sso_provider, params: Rack::Utils.parse_query(@sso.payload)
  251. expect(response).to redirect_to("/login")
  252. post :create,
  253. params: { login: @user.username, password: "myfrogs123ADMIN" },
  254. format: :json,
  255. xhr: true
  256. location = response.cookies["sso_destination_url"]
  257. # javascript code will handle redirection of user to return_sso_url
  258. expect(location).to match(/^http:\/\/somewhere.over.rainbow\/sso/)
  259. payload = location.split("?")[1]
  260. sso2 = SingleSignOn.parse(payload, "topsecret")
  261. expect(sso2.email).to eq(@user.email)
  262. expect(sso2.name).to eq(@user.name)
  263. expect(sso2.username).to eq(@user.username)
  264. expect(sso2.external_id).to eq(@user.id.to_s)
  265. expect(sso2.admin).to eq(true)
  266. expect(sso2.moderator).to eq(false)
  267. expect(sso2.groups).to eq(@user.groups.pluck(:name).join(","))
  268. end
  269. it "successfully redirects user to return_sso_url when the user is logged in" do
  270. log_in_user(@user)
  271. get :sso_provider, params: Rack::Utils.parse_query(@sso.payload)
  272. location = response.header["Location"]
  273. expect(location).to match(/^http:\/\/somewhere.over.rainbow\/sso/)
  274. payload = location.split("?")[1]
  275. sso2 = SingleSignOn.parse(payload, "topsecret")
  276. expect(sso2.email).to eq(@user.email)
  277. expect(sso2.name).to eq(@user.name)
  278. expect(sso2.username).to eq(@user.username)
  279. expect(sso2.external_id).to eq(@user.id.to_s)
  280. expect(sso2.admin).to eq(true)
  281. expect(sso2.moderator).to eq(false)
  282. end
  283. end
  284. describe 'local attribute override from SSO payload' do
  285. before do
  286. SiteSetting.email_editable = false
  287. SiteSetting.sso_overrides_email = true
  288. SiteSetting.sso_overrides_username = true
  289. SiteSetting.sso_overrides_name = true
  290. @user = Fabricate(:user)
  291. @sso = get_sso('/hello/world')
  292. @sso.external_id = '997'
  293. @reversed_username = @user.username.reverse
  294. @sso.username = @reversed_username
  295. @sso.email = "#{@reversed_username}@garbage.org"
  296. @reversed_name = @user.name.reverse
  297. @sso.name = @reversed_name
  298. @suggested_username = UserNameSuggester.suggest(@sso.username || @sso.name || @sso.email)
  299. @suggested_name = User.suggest_name(@sso.name || @sso.username || @sso.email)
  300. @user.create_single_sign_on_record(external_id: '997', last_payload: '')
  301. end
  302. it 'stores the external attributes' do
  303. get :sso_login, params: Rack::Utils.parse_query(@sso.payload)
  304. @user.single_sign_on_record.reload
  305. expect(@user.single_sign_on_record.external_username).to eq(@sso.username)
  306. expect(@user.single_sign_on_record.external_email).to eq(@sso.email)
  307. expect(@user.single_sign_on_record.external_name).to eq(@sso.name)
  308. end
  309. it 'overrides attributes' do
  310. get :sso_login, params: Rack::Utils.parse_query(@sso.payload)
  311. logged_on_user = Discourse.current_user_provider.new(request.env).current_user
  312. expect(logged_on_user.username).to eq(@suggested_username)
  313. expect(logged_on_user.email).to eq("#{@reversed_username}@garbage.org")
  314. expect(logged_on_user.name).to eq(@sso.name)
  315. end
  316. it 'does not change matching attributes for an existing account' do
  317. @sso.username = @user.username
  318. @sso.name = @user.name
  319. @sso.email = @user.email
  320. get :sso_login, params: Rack::Utils.parse_query(@sso.payload)
  321. logged_on_user = Discourse.current_user_provider.new(request.env).current_user
  322. expect(logged_on_user.username).to eq(@user.username)
  323. expect(logged_on_user.name).to eq(@user.name)
  324. expect(logged_on_user.email).to eq(@user.email)
  325. end
  326. end
  327. end
  328. describe '#sso_provider' do
  329. before do
  330. SiteSetting.enable_sso_provider = true
  331. SiteSetting.enable_sso = false
  332. SiteSetting.enable_local_logins = true
  333. SiteSetting.sso_secret = "topsecret"
  334. @sso = SingleSignOn.new
  335. @sso.nonce = "mynonce"
  336. @sso.sso_secret = SiteSetting.sso_secret
  337. @sso.return_sso_url = "http://somewhere.over.rainbow/sso"
  338. @user = Fabricate(:user, password: "myfrogs123ADMIN", active: true, admin: true)
  339. EmailToken.update_all(confirmed: true)
  340. end
  341. it "successfully logs in and redirects user to return_sso_url when the user is not logged in" do
  342. get :sso_provider, params: Rack::Utils.parse_query(@sso.payload)
  343. expect(response).to redirect_to("/login")
  344. post :create,
  345. params: { login: @user.username, password: "myfrogs123ADMIN" },
  346. format: :json,
  347. xhr: true
  348. location = response.cookies["sso_destination_url"]
  349. # javascript code will handle redirection of user to return_sso_url
  350. expect(location).to match(/^http:\/\/somewhere.over.rainbow\/sso/)
  351. payload = location.split("?")[1]
  352. sso2 = SingleSignOn.parse(payload, "topsecret")
  353. expect(sso2.email).to eq(@user.email)
  354. expect(sso2.name).to eq(@user.name)
  355. expect(sso2.username).to eq(@user.username)
  356. expect(sso2.external_id).to eq(@user.id.to_s)
  357. expect(sso2.admin).to eq(true)
  358. expect(sso2.moderator).to eq(false)
  359. end
  360. it "successfully redirects user to return_sso_url when the user is logged in" do
  361. log_in_user(@user)
  362. get :sso_provider, params: Rack::Utils.parse_query(@sso.payload)
  363. location = response.header["Location"]
  364. expect(location).to match(/^http:\/\/somewhere.over.rainbow\/sso/)
  365. payload = location.split("?")[1]
  366. sso2 = SingleSignOn.parse(payload, "topsecret")
  367. expect(sso2.email).to eq(@user.email)
  368. expect(sso2.name).to eq(@user.name)
  369. expect(sso2.username).to eq(@user.username)
  370. expect(sso2.external_id).to eq(@user.id.to_s)
  371. expect(sso2.admin).to eq(true)
  372. expect(sso2.moderator).to eq(false)
  373. end
  374. end
  375. describe '#create' do
  376. let(:user) { Fabricate(:user) }
  377. context 'local login is disabled' do
  378. before do
  379. SiteSetting.enable_local_logins = false
  380. post :create, params: {
  381. login: user.username, password: 'myawesomepassword'
  382. }, format: :json
  383. end
  384. it_behaves_like "failed to continue local login"
  385. end
  386. context 'SSO is enabled' do
  387. before do
  388. SiteSetting.sso_url = "https://www.example.com/sso"
  389. SiteSetting.enable_sso = true
  390. post :create, params: {
  391. login: user.username, password: 'myawesomepassword'
  392. }, format: :json
  393. end
  394. it_behaves_like "failed to continue local login"
  395. end
  396. context 'when email is confirmed' do
  397. before do
  398. token = user.email_tokens.find_by(email: user.email)
  399. EmailToken.confirm(token.token)
  400. end
  401. it "raises an error when the login isn't present" do
  402. expect do
  403. post :create, format: :json
  404. end.to raise_error(ActionController::ParameterMissing)
  405. end
  406. describe 'invalid password' do
  407. it "should return an error with an invalid password" do
  408. post :create, params: {
  409. login: user.username, password: 'sssss'
  410. }, format: :json
  411. expect(::JSON.parse(response.body)['error']).to eq(
  412. I18n.t("login.incorrect_username_email_or_password")
  413. )
  414. end
  415. end
  416. describe 'invalid password' do
  417. it "should return an error with an invalid password if too long" do
  418. User.any_instance.expects(:confirm_password?).never
  419. post :create, params: {
  420. login: user.username, password: ('s' * (User.max_password_length + 1))
  421. }, format: :json
  422. expect(::JSON.parse(response.body)['error']).to eq(
  423. I18n.t("login.incorrect_username_email_or_password")
  424. )
  425. end
  426. end
  427. describe 'suspended user' do
  428. it 'should return an error' do
  429. user.suspended_till = 2.days.from_now
  430. user.suspended_at = Time.now
  431. user.save!
  432. StaffActionLogger.new(user).log_user_suspend(user, "<strike>banned</strike>")
  433. post :create, params: {
  434. login: user.username, password: 'myawesomepassword'
  435. }, format: :json
  436. expect(JSON.parse(response.body)['error']).to eq(I18n.t('login.suspended_with_reason',
  437. date: I18n.l(user.suspended_till, format: :date_only),
  438. reason: Rack::Utils.escape_html(user.suspend_reason)
  439. ))
  440. end
  441. end
  442. describe 'deactivated user' do
  443. it 'should return an error' do
  444. User.any_instance.stubs(:active).returns(false)
  445. post :create, params: {
  446. login: user.username, password: 'myawesomepassword'
  447. }, format: :json
  448. expect(JSON.parse(response.body)['error']).to eq(I18n.t('login.not_activated'))
  449. end
  450. end
  451. describe 'success by username' do
  452. it 'logs in correctly' do
  453. events = DiscourseEvent.track_events do
  454. post :create, params: {
  455. login: user.username, password: 'myawesomepassword'
  456. }, format: :json
  457. end
  458. expect(events.map { |event| event[:event_name] }).to include(
  459. :user_logged_in, :user_first_logged_in
  460. )
  461. user.reload
  462. expect(session[:current_user_id]).to eq(user.id)
  463. expect(user.user_auth_tokens.count).to eq(1)
  464. expect(UserAuthToken.hash_token(cookies[:_t])).to eq(user.user_auth_tokens.first.auth_token)
  465. end
  466. end
  467. context 'when user has 2-factor logins' do
  468. let!(:user_second_factor) { Fabricate(:user_second_factor, user: user) }
  469. describe 'when second factor token is missing' do
  470. it 'should return the right response' do
  471. post :create, params: {
  472. login: user.username,
  473. password: 'myawesomepassword',
  474. }, format: :json
  475. expect(JSON.parse(response.body)['error']).to eq(I18n.t(
  476. 'login.invalid_second_factor_code'
  477. ))
  478. end
  479. end
  480. describe 'when second factor token is invalid' do
  481. it 'should return the right response' do
  482. post :create, params: {
  483. login: user.username,
  484. password: 'myawesomepassword',
  485. second_factor_token: '00000000'
  486. }, format: :json
  487. expect(JSON.parse(response.body)['error']).to eq(I18n.t(
  488. 'login.invalid_second_factor_code'
  489. ))
  490. end
  491. end
  492. describe 'when second factor token is valid' do
  493. it 'should log the user in' do
  494. post :create, params: {
  495. login: user.username,
  496. password: 'myawesomepassword',
  497. second_factor_token: ROTP::TOTP.new(user_second_factor.data).now
  498. }, format: :json
  499. user.reload
  500. expect(session[:current_user_id]).to eq(user.id)
  501. expect(user.user_auth_tokens.count).to eq(1)
  502. expect(UserAuthToken.hash_token(cookies[:_t]))
  503. .to eq(user.user_auth_tokens.first.auth_token)
  504. end
  505. end
  506. end
  507. describe 'with a blocked IP' do
  508. before do
  509. screened_ip = Fabricate(:screened_ip_address)
  510. ActionDispatch::Request.any_instance.stubs(:remote_ip).returns(screened_ip.ip_address)
  511. post :create, params: {
  512. login: "@" + user.username, password: 'myawesomepassword'
  513. }, format: :json
  514. user.reload
  515. end
  516. it "doesn't log in" do
  517. expect(session[:current_user_id]).to be_nil
  518. end
  519. end
  520. describe 'strips leading @ symbol' do
  521. before do
  522. post :create, params: {
  523. login: "@" + user.username, password: 'myawesomepassword'
  524. }, format: :json
  525. user.reload
  526. end
  527. it 'sets a session id' do
  528. expect(session[:current_user_id]).to eq(user.id)
  529. end
  530. end
  531. describe 'also allow login by email' do
  532. before do
  533. post :create, params: {
  534. login: user.email, password: 'myawesomepassword'
  535. }, format: :json
  536. end
  537. it 'sets a session id' do
  538. expect(session[:current_user_id]).to eq(user.id)
  539. end
  540. end
  541. context 'login has leading and trailing space' do
  542. let(:username) { " #{user.username} " }
  543. let(:email) { " #{user.email} " }
  544. it "strips spaces from the username" do
  545. post :create, params: {
  546. login: username, password: 'myawesomepassword'
  547. }, format: :json
  548. expect(::JSON.parse(response.body)['error']).not_to be_present
  549. end
  550. it "strips spaces from the email" do
  551. post :create, params: {
  552. login: email, password: 'myawesomepassword'
  553. }, format: :json
  554. expect(::JSON.parse(response.body)['error']).not_to be_present
  555. end
  556. end
  557. describe "when the site requires approval of users" do
  558. before do
  559. SiteSetting.expects(:must_approve_users?).returns(true)
  560. end
  561. context 'with an unapproved user' do
  562. before do
  563. post :create, params: {
  564. login: user.email, password: 'myawesomepassword'
  565. }, format: :json
  566. end
  567. it "doesn't log in the user" do
  568. expect(session[:current_user_id]).to be_blank
  569. end
  570. it "shows the 'not approved' error message" do
  571. expect(JSON.parse(response.body)['error']).to eq(
  572. I18n.t('login.not_approved')
  573. )
  574. end
  575. end
  576. context "with an unapproved user who is an admin" do
  577. before do
  578. User.any_instance.stubs(:admin?).returns(true)
  579. post :create, params: {
  580. login: user.email, password: 'myawesomepassword'
  581. }, format: :json
  582. end
  583. it 'sets a session id' do
  584. expect(session[:current_user_id]).to eq(user.id)
  585. end
  586. end
  587. end
  588. context 'when admins are restricted by ip address' do
  589. let(:permitted_ip_address) { '111.234.23.11' }
  590. before do
  591. Fabricate(:screened_ip_address, ip_address: permitted_ip_address, action_type: ScreenedIpAddress.actions[:allow_admin])
  592. SiteSetting.use_admin_ip_whitelist = true
  593. end
  594. it 'is successful for admin at the ip address' do
  595. User.any_instance.stubs(:admin?).returns(true)
  596. ActionDispatch::Request.any_instance.stubs(:remote_ip).returns(permitted_ip_address)
  597. post :create, params: {
  598. login: user.username, password: 'myawesomepassword'
  599. }, format: :json
  600. expect(session[:current_user_id]).to eq(user.id)
  601. end
  602. it 'returns an error for admin not at the ip address' do
  603. User.any_instance.stubs(:admin?).returns(true)
  604. ActionDispatch::Request.any_instance.stubs(:remote_ip).returns("111.234.23.12")
  605. post :create, params: {
  606. login: user.username, password: 'myawesomepassword'
  607. }, format: :json
  608. expect(JSON.parse(response.body)['error']).to be_present
  609. expect(session[:current_user_id]).not_to eq(user.id)
  610. end
  611. it 'is successful for non-admin not at the ip address' do
  612. User.any_instance.stubs(:admin?).returns(false)
  613. ActionDispatch::Request.any_instance.stubs(:remote_ip).returns("111.234.23.12")
  614. post :create, params: {
  615. login: user.username, password: 'myawesomepassword'
  616. }, format: :json
  617. expect(session[:current_user_id]).to eq(user.id)
  618. end
  619. end
  620. end
  621. context 'when email has not been confirmed' do
  622. def post_login
  623. post :create, params: {
  624. login: user.email, password: 'myawesomepassword'
  625. }, format: :json
  626. end
  627. it "doesn't log in the user" do
  628. post_login
  629. expect(session[:current_user_id]).to be_blank
  630. end
  631. it "shows the 'not activated' error message" do
  632. post_login
  633. expect(JSON.parse(response.body)['error']).to eq(
  634. I18n.t 'login.not_activated'
  635. )
  636. end
  637. context "and the 'must approve users' site setting is enabled" do
  638. before { SiteSetting.expects(:must_approve_users?).returns(true) }
  639. it "shows the 'not approved' error message" do
  640. post_login
  641. expect(JSON.parse(response.body)['error']).to eq(
  642. I18n.t 'login.not_approved'
  643. )
  644. end
  645. end
  646. end
  647. context 'rate limited' do
  648. it 'rate limits login' do
  649. SiteSetting.max_logins_per_ip_per_hour = 2
  650. RateLimiter.enable
  651. RateLimiter.clear_all!
  652. 2.times do
  653. post :create, params: {
  654. login: user.username, password: 'myawesomepassword'
  655. }, format: :json
  656. expect(response).to be_success
  657. end
  658. post :create, params: {
  659. login: user.username, password: 'myawesomepassword'
  660. }, format: :json
  661. expect(response.status).to eq(429)
  662. json = JSON.parse(response.body)
  663. expect(json["error_type"]).to eq("rate_limit")
  664. end
  665. it 'rate limits second factor attempts' do
  666. RateLimiter.enable
  667. RateLimiter.clear_all!
  668. 3.times do
  669. post :create, params: {
  670. login: user.username,
  671. password: 'myawesomepassword',
  672. second_factor_token: '000000'
  673. }, format: :json
  674. expect(response).to be_success
  675. end
  676. post :create, params: {
  677. login: user.username,
  678. password: 'myawesomepassword',
  679. second_factor_token: '000000'
  680. }, format: :json
  681. expect(response.status).to eq(429)
  682. json = JSON.parse(response.body)
  683. expect(json["error_type"]).to eq("rate_limit")
  684. end
  685. end
  686. end
  687. describe '.destroy' do
  688. before do
  689. @user = log_in
  690. delete :destroy, params: { id: @user.username }, format: :json
  691. end
  692. it 'removes the session variable' do
  693. expect(session[:current_user_id]).to be_blank
  694. end
  695. it 'removes the auth token cookie' do
  696. expect(response.cookies["_t"]).to be_blank
  697. end
  698. end
  699. describe '.forgot_password' do
  700. it 'raises an error without a username parameter' do
  701. expect do
  702. post :forgot_password, format: :json
  703. end.to raise_error(ActionController::ParameterMissing)
  704. end
  705. context 'for a non existant username' do
  706. it "doesn't generate a new token for a made up username" do
  707. expect do
  708. post :forgot_password, params: { login: 'made_up' }, format: :json
  709. end.not_to change(EmailToken, :count)
  710. end
  711. it "doesn't enqueue an email" do
  712. Jobs.expects(:enqueue).with(:user_mail, anything).never
  713. post :forgot_password, params: { login: 'made_up' }, format: :json
  714. end
  715. end
  716. context 'for an existing username' do
  717. let(:user) { Fabricate(:user) }
  718. context 'local login is disabled' do
  719. before do
  720. SiteSetting.enable_local_logins = false
  721. post :forgot_password, params: { login: user.username }, format: :json
  722. end
  723. it_behaves_like "failed to continue local login"
  724. end
  725. context 'SSO is enabled' do
  726. before do
  727. SiteSetting.sso_url = "https://www.example.com/sso"
  728. SiteSetting.enable_sso = true
  729. post :create, params: {
  730. login: user.username, password: 'myawesomepassword'
  731. }, format: :json
  732. end
  733. it_behaves_like "failed to continue local login"
  734. end
  735. it "generates a new token for a made up username" do
  736. expect do
  737. post :forgot_password, params: { login: user.username }, format: :json
  738. end.to change(EmailToken, :count)
  739. end
  740. it "enqueues an email" do
  741. Jobs.expects(:enqueue).with(:critical_user_email, has_entries(type: :forgot_password, user_id: user.id))
  742. post :forgot_password, params: { login: user.username }, format: :json
  743. end
  744. end
  745. context 'do nothing to system username' do
  746. let(:system) { Discourse.system_user }
  747. it 'generates no token for system username' do
  748. expect do
  749. post :forgot_password, params: { login: system.username }, format: :json
  750. end.not_to change(EmailToken, :count)
  751. end
  752. it 'enqueues no email' do
  753. Jobs.expects(:enqueue).never
  754. post :forgot_password, params: { login: system.username }, format: :json
  755. end
  756. end
  757. context 'for a staged account' do
  758. let!(:staged) { Fabricate(:staged) }
  759. it 'generates no token for staged username' do
  760. expect do
  761. post :forgot_password, params: { login: staged.username }, format: :json
  762. end.not_to change(EmailToken, :count)
  763. end
  764. it 'enqueues no email' do
  765. Jobs.expects(:enqueue).never
  766. post :forgot_password, params: { login: staged.username }, format: :json
  767. end
  768. end
  769. end
  770. describe '#current' do
  771. context "when not logged in" do
  772. it "retuns 404" do
  773. get :current, format: :json
  774. expect(response).not_to be_success
  775. end
  776. end
  777. context "when logged in" do
  778. let!(:user) { log_in }
  779. it "returns the JSON for the user" do
  780. get :current, format: :json
  781. expect(response).to be_success
  782. json = ::JSON.parse(response.body)
  783. expect(json['current_user']).to be_present
  784. expect(json['current_user']['id']).to eq(user.id)
  785. end
  786. end
  787. end
  788. end