You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

users_test.rb 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. # frozen_string_literal: true
  2. # Redmine - project management software
  3. # Copyright (C) 2006- Jean-Philippe Lang
  4. #
  5. # This program is free software; you can redistribute it and/or
  6. # modify it under the terms of the GNU General Public License
  7. # as published by the Free Software Foundation; either version 2
  8. # of the License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. require_relative '../../test_helper'
  19. class Redmine::ApiTest::UsersTest < Redmine::ApiTest::Base
  20. fixtures :users, :groups_users, :email_addresses, :members, :member_roles, :roles, :projects
  21. test "GET /users.xml should return users" do
  22. users = User.active.order('login')
  23. users.last.update(twofa_scheme: 'totp')
  24. Redmine::Configuration.with 'avatar_server_url' => 'https://gravatar.com' do
  25. with_settings :gravatar_enabled => '1', :gravatar_default => 'mm' do
  26. get '/users.xml', :headers => credentials('admin')
  27. end
  28. end
  29. assert_response :success
  30. assert_equal 'application/xml', response.media_type
  31. assert_select 'users' do
  32. assert_select 'user', :count => users.size do |nodeset|
  33. nodeset.zip(users) do |user_element, user|
  34. assert_select user_element, 'id', :text => user.id.to_s
  35. assert_select user_element, 'updated_on', :text => user.updated_on.iso8601
  36. assert_select user_element, 'twofa_scheme', :text => user.twofa_scheme.to_s
  37. # No one has changed password.
  38. assert_select user_element, 'passwd_changed_on', :text => ''
  39. assert_select user_element, 'avatar_url', :text => %r|\Ahttps://gravatar.com/avatar/\h{32}\?default=mm|
  40. if user == users.last
  41. assert_select user_element, 'twofa_scheme', :text => 'totp'
  42. else
  43. assert_select user_element, 'twofa_scheme', :text => ''
  44. end
  45. end
  46. end
  47. end
  48. end
  49. test "GET /users.json should return users" do
  50. users = User.active.order('login')
  51. users.last.update(twofa_scheme: 'totp')
  52. get '/users.json', :headers => credentials('admin')
  53. assert_response :success
  54. assert_equal 'application/json', response.media_type
  55. json = ActiveSupport::JSON.decode(response.body)
  56. assert json.key?('users')
  57. users = User.active.order('login')
  58. assert_equal users.size, json['users'].size
  59. json['users'].zip(users) do |user_json, user|
  60. assert_equal user.id, user_json['id']
  61. assert_equal user.updated_on.iso8601, user_json['updated_on']
  62. assert_equal user.status, user_json['status']
  63. # No one has changed password.
  64. assert_nil user_json['passwd_changed_on']
  65. if user == users.last
  66. assert_equal 'totp', user_json['twofa_scheme']
  67. else
  68. assert_nil user_json['twofa_scheme']
  69. end
  70. end
  71. end
  72. test "GET /users.json with legacy filter params" do
  73. get '/users.json', headers: credentials('admin'), params: { status: 3 }
  74. assert_response :success
  75. json = ActiveSupport::JSON.decode(response.body)
  76. assert json.key?('users')
  77. users = User.where(status: 3)
  78. assert_equal users.size, json['users'].size
  79. get '/users.json', headers: credentials('admin'), params: { status: '*' }
  80. assert_response :success
  81. json = ActiveSupport::JSON.decode(response.body)
  82. assert json.key?('users')
  83. users = User.logged
  84. assert_equal users.size, json['users'].size
  85. get '/users.json', headers: credentials('admin'), params: { name: 'jsmith' }
  86. assert_response :success
  87. json = ActiveSupport::JSON.decode(response.body)
  88. assert json.key?('users')
  89. assert_equal 1, json['users'].size
  90. assert_equal 2, json['users'][0]['id']
  91. get '/users.json', headers: credentials('admin'), params: { group_id: '10' }
  92. assert_response :success
  93. json = ActiveSupport::JSON.decode(response.body)
  94. assert json.key?('users')
  95. assert_equal 1, json['users'].size
  96. assert_equal 8, json['users'][0]['id']
  97. # there should be an implicit filter for status = 1
  98. User.where(id: [2, 8]).update_all status: 3
  99. get '/users.json', headers: credentials('admin'), params: { name: 'jsmith' }
  100. assert_response :success
  101. json = ActiveSupport::JSON.decode(response.body)
  102. assert json.key?('users')
  103. assert_equal 0, json['users'].size
  104. get '/users.json', headers: credentials('admin'), params: { group_id: '10' }
  105. assert_response :success
  106. json = ActiveSupport::JSON.decode(response.body)
  107. assert json.key?('users')
  108. assert_equal 0, json['users'].size
  109. end
  110. test "GET /users.json with include=auth_source" do
  111. user = User.find(2)
  112. user.update(:auth_source_id => 1)
  113. get '/users.json?include=auth_source', :headers => credentials('admin')
  114. json = ActiveSupport::JSON.decode(response.body)
  115. assert json.key?('users')
  116. json['users'].each do |user_json|
  117. if user_json['id'] == user.id
  118. assert_kind_of Hash, user_json['auth_source']
  119. assert_equal user.auth_source.id, user_json['auth_source']['id']
  120. assert_equal user.auth_source.name, user_json['auth_source']['name']
  121. else
  122. assert_nil user_json['auth_source']
  123. end
  124. end
  125. end
  126. test "GET /users.json with short filters" do
  127. get '/users.json', headers: credentials('admin'), params: { status: "1|3" }
  128. assert_response :success
  129. json = ActiveSupport::JSON.decode(response.body)
  130. assert json.key?('users')
  131. users = User.where(status: [1, 3])
  132. assert_equal users.size, json['users'].size
  133. end
  134. test "GET /users/:id.xml should return the user" do
  135. Redmine::Configuration.with 'avatar_server_url' => 'https://gravatar.com' do
  136. with_settings :gravatar_enabled => '1', :gravatar_default => 'robohash' do
  137. get '/users/2.xml'
  138. end
  139. end
  140. assert_response :success
  141. assert_select 'user id', :text => '2'
  142. assert_select 'user updated_on', :text => Time.zone.parse('2006-07-19T20:42:15Z').iso8601
  143. assert_select 'user passwd_changed_on', :text => ''
  144. assert_select 'user avatar_url', :text => %r|\Ahttps://gravatar.com/avatar/\h{32}\?default=robohash|
  145. end
  146. test "GET /users/:id.xml should not return avatar_url when not set email address" do
  147. user = User.find(2)
  148. user.email_addresses.delete_all
  149. assert_equal 'jsmith', user.login
  150. assert_nil user.mail
  151. Redmine::Configuration.with 'avatar_server_url' => 'https://gravatar.com' do
  152. with_settings :gravatar_enabled => '1', :gravatar_default => 'robohash' do
  153. get '/users/2.xml'
  154. end
  155. end
  156. assert_response :success
  157. assert_select 'user id', :text => '2'
  158. assert_select 'user login', :text => 'jsmith'
  159. assert_select 'user avatar_url', :count => 0
  160. end
  161. test "GET /users/:id.json should return the user" do
  162. get '/users/2.json'
  163. assert_response :success
  164. json = ActiveSupport::JSON.decode(response.body)
  165. assert_kind_of Hash, json
  166. assert_kind_of Hash, json['user']
  167. assert_equal 2, json['user']['id']
  168. assert_equal Time.zone.parse('2006-07-19T20:42:15Z').iso8601, json['user']['updated_on']
  169. assert_nil json['user']['passwd_changed_on']
  170. assert_nil json['user']['twofa_scheme']
  171. assert_nil json['user']['auth_source']
  172. end
  173. test "GET /users/:id.xml with include=memberships should include memberships" do
  174. get '/users/2.xml?include=memberships'
  175. assert_response :success
  176. assert_select 'user memberships', 1
  177. end
  178. test "GET /users/:id.json with include=memberships should include memberships" do
  179. get '/users/2.json?include=memberships'
  180. assert_response :success
  181. json = ActiveSupport::JSON.decode(response.body)
  182. assert_kind_of Array, json['user']['memberships']
  183. assert_equal [{
  184. "id"=>1,
  185. "project"=>{"name"=>"eCookbook", "id"=>1},
  186. "roles"=>[{"name"=>"Manager", "id"=>1}]
  187. }], json['user']['memberships']
  188. end
  189. test "GET /users/:id.json with include=auth_source should include auth_source for administrators" do
  190. user = User.find(2)
  191. user.update(:auth_source_id => 1)
  192. get '/users/2.json?include=auth_source', :headers => credentials('admin')
  193. assert_response :success
  194. json = ActiveSupport::JSON.decode(response.body)
  195. assert_equal user.auth_source.id, json['user']['auth_source']['id']
  196. assert_equal user.auth_source.name, json['user']['auth_source']['name']
  197. end
  198. test "GET /users/:id.json without include=auth_source should not include auth_source" do
  199. user = User.find(2)
  200. user.update(:auth_source_id => 1)
  201. get '/users/2.json', :headers => credentials('admin')
  202. assert_response :success
  203. json = ActiveSupport::JSON.decode(response.body)
  204. assert_response :success
  205. assert_nil json['user']['auth_source']
  206. end
  207. test "GET /users/:id.json should not include auth_source for standard user" do
  208. user = User.find(2)
  209. user.update(:auth_source_id => 1)
  210. get '/users/2.json?include=auth_source', :headers => credentials('jsmith')
  211. assert_response :success
  212. json = ActiveSupport::JSON.decode(response.body)
  213. assert_equal user.id, json['user']['id']
  214. assert_nil json['user']['auth_source']
  215. end
  216. test "GET /users/current.xml should require authentication" do
  217. get '/users/current.xml'
  218. assert_response 401
  219. end
  220. test "GET /users/current.xml should return current user" do
  221. get '/users/current.xml', :headers => credentials('jsmith')
  222. assert_select 'user id', :text => '2'
  223. end
  224. test "GET /users/:id should return login for visible user" do
  225. get '/users/3.xml', :headers => credentials('jsmith')
  226. assert_response :success
  227. assert_select 'user login', :text => 'dlopper'
  228. end
  229. test "GET /users/:id should not return api_key for other user" do
  230. get '/users/3.xml', :headers => credentials('jsmith')
  231. assert_response :success
  232. assert_select 'user api_key', 0
  233. end
  234. test "GET /users/:id should return api_key for current user" do
  235. get '/users/2.xml', :headers => credentials('jsmith')
  236. assert_response :success
  237. assert_select 'user api_key', :text => User.find(2).api_key
  238. end
  239. test "GET /users/:id should not return status for standard user" do
  240. get '/users/3.xml', :headers => credentials('jsmith')
  241. assert_response :success
  242. assert_select 'user status', 0
  243. end
  244. test "GET /users/:id should return status for administrators" do
  245. get '/users/2.xml', :headers => credentials('admin')
  246. assert_response :success
  247. assert_select 'user status', :text => User.find(2).status.to_s
  248. end
  249. test "GET /users/:id should return admin status for current user" do
  250. get '/users/2.xml', :headers => credentials('jsmith')
  251. assert_response :success
  252. assert_select 'user admin', :text => 'false'
  253. end
  254. test "GET /users/:id should not return admin status for other user" do
  255. get '/users/3.xml', :headers => credentials('jsmith')
  256. assert_response :success
  257. assert_select 'user admin', 0
  258. end
  259. test "GET /users/:id should not return twofa_scheme for standard user" do
  260. # User and password authentication is disabled when twofa is enabled
  261. # Use token authentication
  262. user = User.find(2)
  263. token = Token.create!(:user => user, :action => 'api')
  264. user.update(twofa_scheme: 'totp')
  265. get '/users/3.xml', :headers => credentials(token.value, 'X')
  266. assert_response :success
  267. assert_select 'twofa_scheme', 0
  268. end
  269. test "GET /users/:id should return twofa_scheme for administrators" do
  270. User.find(2).update(twofa_scheme: 'totp')
  271. get '/users/2.xml', :headers => credentials('admin')
  272. assert_response :success
  273. assert_select 'twofa_scheme', :text => 'totp'
  274. end
  275. test "POST /users.xml with valid parameters should create the user" do
  276. assert_difference('User.count') do
  277. post(
  278. '/users.xml',
  279. :params => {
  280. :user => {
  281. :login => 'foo', :firstname => 'Firstname', :lastname => 'Lastname',
  282. :mail => 'foo@example.net', :password => 'secret123',
  283. :mail_notification => 'only_assigned'
  284. }
  285. },
  286. :headers => credentials('admin'))
  287. end
  288. user = User.order('id DESC').first
  289. assert_equal 'foo', user.login
  290. assert_equal 'Firstname', user.firstname
  291. assert_equal 'Lastname', user.lastname
  292. assert_equal 'foo@example.net', user.mail
  293. assert_equal 'only_assigned', user.mail_notification
  294. assert !user.admin?
  295. assert user.check_password?('secret123')
  296. assert_response :created
  297. assert_equal 'application/xml', @response.media_type
  298. assert_select 'user id', :text => user.id.to_s
  299. end
  300. test "POST /users.xml with generate_password should generate password" do
  301. assert_difference('User.count') do
  302. post(
  303. '/users.xml',
  304. :params => {
  305. :user => {
  306. :login => 'foo', :firstname => 'Firstname', :lastname => 'Lastname',
  307. :mail => 'foo@example.net', :generate_password => 'true'
  308. }
  309. },
  310. :headers => credentials('admin'))
  311. end
  312. user = User.order('id DESC').first
  313. assert user.hashed_password.present?
  314. end
  315. test "POST /users.json with valid parameters should create the user" do
  316. assert_difference('User.count') do
  317. post(
  318. '/users.json',
  319. :params => {
  320. :user => {
  321. :login => 'foo', :firstname => 'Firstname', :lastname => 'Lastname',
  322. :mail => 'foo@example.net', :password => 'secret123',
  323. :mail_notification => 'only_assigned'
  324. }
  325. },
  326. :headers => credentials('admin'))
  327. end
  328. user = User.order('id DESC').first
  329. assert_equal 'foo', user.login
  330. assert_equal 'Firstname', user.firstname
  331. assert_equal 'Lastname', user.lastname
  332. assert_equal 'foo@example.net', user.mail
  333. assert !user.admin?
  334. assert_response :created
  335. assert_equal 'application/json', @response.media_type
  336. json = ActiveSupport::JSON.decode(response.body)
  337. assert_kind_of Hash, json
  338. assert_kind_of Hash, json['user']
  339. assert_equal user.id, json['user']['id']
  340. end
  341. test "POST /users.xml with with invalid parameters should return errors" do
  342. assert_no_difference('User.count') do
  343. post(
  344. '/users.xml',
  345. :params => {
  346. :user =>{
  347. :login => 'foo', :lastname => 'Lastname', :mail => 'foo'
  348. }
  349. },
  350. :headers => credentials('admin'))
  351. end
  352. assert_response :unprocessable_entity
  353. assert_equal 'application/xml', @response.media_type
  354. assert_select 'errors error', :text => "First name cannot be blank"
  355. end
  356. test "POST /users.json with with invalid parameters should return errors" do
  357. assert_no_difference('User.count') do
  358. post(
  359. '/users.json',
  360. :params => {
  361. :user => {
  362. :login => 'foo', :lastname => 'Lastname', :mail => 'foo'
  363. }
  364. },
  365. :headers => credentials('admin'))
  366. end
  367. assert_response :unprocessable_entity
  368. assert_equal 'application/json', @response.media_type
  369. json = ActiveSupport::JSON.decode(response.body)
  370. assert_kind_of Hash, json
  371. assert json.has_key?('errors')
  372. assert_kind_of Array, json['errors']
  373. end
  374. test "PUT /users/:id.xml with valid parameters should update the user" do
  375. assert_no_difference('User.count') do
  376. put(
  377. '/users/2.xml',
  378. :params => {
  379. :user => {
  380. :login => 'jsmith', :firstname => 'John', :lastname => 'Renamed',
  381. :mail => 'jsmith@somenet.foo'
  382. }
  383. },
  384. :headers => credentials('admin'))
  385. end
  386. user = User.find(2)
  387. assert_equal 'jsmith', user.login
  388. assert_equal 'John', user.firstname
  389. assert_equal 'Renamed', user.lastname
  390. assert_equal 'jsmith@somenet.foo', user.mail
  391. assert !user.admin?
  392. assert_response :no_content
  393. assert_equal '', @response.body
  394. end
  395. test "PUT /users/:id.json with valid parameters should update the user" do
  396. assert_no_difference('User.count') do
  397. put(
  398. '/users/2.json',
  399. :params => {
  400. :user => {
  401. :login => 'jsmith', :firstname => 'John', :lastname => 'Renamed',
  402. :mail => 'jsmith@somenet.foo'
  403. }
  404. },
  405. :headers => credentials('admin'))
  406. end
  407. user = User.find(2)
  408. assert_equal 'jsmith', user.login
  409. assert_equal 'John', user.firstname
  410. assert_equal 'Renamed', user.lastname
  411. assert_equal 'jsmith@somenet.foo', user.mail
  412. assert !user.admin?
  413. assert_response :no_content
  414. assert_equal '', @response.body
  415. end
  416. test "PUT /users/:id.xml with invalid parameters" do
  417. assert_no_difference('User.count') do
  418. put(
  419. '/users/2.xml',
  420. :params => {
  421. :user => {
  422. :login => 'jsmith', :firstname => '', :lastname => 'Lastname',
  423. :mail => 'foo'
  424. }
  425. },
  426. :headers => credentials('admin'))
  427. end
  428. assert_response :unprocessable_entity
  429. assert_equal 'application/xml', @response.media_type
  430. assert_select 'errors error', :text => "First name cannot be blank"
  431. end
  432. test "PUT /users/:id.json with invalid parameters" do
  433. assert_no_difference('User.count') do
  434. put(
  435. '/users/2.json',
  436. :params => {
  437. :user => {
  438. :login => 'jsmith', :firstname => '', :lastname => 'Lastname',
  439. :mail => 'foo'
  440. }
  441. },
  442. :headers => credentials('admin'))
  443. end
  444. assert_response :unprocessable_entity
  445. assert_equal 'application/json', @response.media_type
  446. json = ActiveSupport::JSON.decode(response.body)
  447. assert_kind_of Hash, json
  448. assert json.has_key?('errors')
  449. assert_kind_of Array, json['errors']
  450. end
  451. test "DELETE /users/:id.xml should delete the user" do
  452. assert_difference('User.count', -1) do
  453. delete '/users/2.xml', :headers => credentials('admin')
  454. end
  455. assert_response :no_content
  456. assert_equal '', @response.body
  457. end
  458. test "DELETE /users/:id.json should delete the user" do
  459. assert_difference('User.count', -1) do
  460. delete '/users/2.json', :headers => credentials('admin')
  461. end
  462. assert_response :no_content
  463. assert_equal '', @response.body
  464. end
  465. end