test_auth_api.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. from django.contrib.auth import get_user_model
  2. from django.core import mail
  3. from django.test import TestCase
  4. from misago.users.models import Ban
  5. from misago.users.tokens import make_password_change_token
  6. UserModel = get_user_model()
  7. class GatewayTests(TestCase):
  8. def test_api_invalid_credentials(self):
  9. """login api returns 400 on invalid POST"""
  10. response = self.client.post(
  11. '/api/auth/', data={
  12. 'username': 'nope',
  13. 'password': 'nope',
  14. }
  15. )
  16. self.assertContains(response, "Login or password is incorrect.", status_code=400)
  17. response = self.client.get('/api/auth/')
  18. self.assertEqual(response.status_code, 200)
  19. user_json = response.json()
  20. self.assertIsNone(user_json['id'])
  21. def test_login(self):
  22. """api signs user in"""
  23. user = UserModel.objects.create_user('Bob', 'bob@test.com', 'Pass.123')
  24. response = self.client.post(
  25. '/api/auth/',
  26. data={
  27. 'username': 'Bob',
  28. 'password': 'Pass.123',
  29. },
  30. )
  31. self.assertEqual(response.status_code, 200)
  32. response = self.client.get('/api/auth/')
  33. self.assertEqual(response.status_code, 200)
  34. user_json = response.json()
  35. self.assertEqual(user_json['id'], user.id)
  36. self.assertEqual(user_json['username'], user.username)
  37. def test_login_whitespaces_password(self):
  38. """api signs user in with password left untouched"""
  39. user = UserModel.objects.create_user('Bob', 'bob@test.com', ' Pass.123 ')
  40. response = self.client.post(
  41. '/api/auth/',
  42. data={
  43. 'username': 'Bob',
  44. 'password': 'Pass.123',
  45. },
  46. )
  47. self.assertEqual(response.status_code, 400)
  48. response = self.client.post(
  49. '/api/auth/',
  50. data={
  51. 'username': 'Bob',
  52. 'password': ' Pass.123 ',
  53. },
  54. )
  55. self.assertEqual(response.status_code, 200)
  56. response = self.client.get('/api/auth/')
  57. self.assertEqual(response.status_code, 200)
  58. user_json = response.json()
  59. self.assertEqual(user_json['id'], user.id)
  60. self.assertEqual(user_json['username'], user.username)
  61. def test_submit_no_data(self):
  62. """login api errors for no body"""
  63. response = self.client.post('/api/auth/')
  64. self.assertEqual(response.status_code, 400)
  65. self.assertEqual(response.json(), {
  66. 'username': ['This field is required.'],
  67. 'password': ['This field is required.'],
  68. })
  69. def test_submit_empty(self):
  70. """login api errors for empty fields"""
  71. response = self.client.post('/api/auth/', data={
  72. 'username': '',
  73. 'password': '',
  74. })
  75. self.assertEqual(response.status_code, 400)
  76. self.assertEqual(response.json(), {
  77. 'username': ['This field may not be blank.'],
  78. 'password': ['This field may not be blank.'],
  79. })
  80. def test_submit_invalid(self):
  81. """login api errors for invalid data"""
  82. response = self.client.post(
  83. '/api/auth/',
  84. 'false',
  85. content_type="application/json",
  86. )
  87. self.assertContains(response, "Invalid data.", status_code=400)
  88. def test_login_banned(self):
  89. """login api fails to sign banned user in"""
  90. UserModel.objects.create_user('Bob', 'bob@test.com', 'Pass.123')
  91. ban = Ban.objects.create(
  92. check_type=Ban.USERNAME,
  93. banned_value='bob',
  94. user_message='You are tragically banned.',
  95. )
  96. response = self.client.post(
  97. '/api/auth/',
  98. data={
  99. 'username': 'Bob',
  100. 'password': 'Pass.123',
  101. },
  102. )
  103. self.assertEqual(response.status_code, 403)
  104. self.assertEqual(response.json(), {
  105. 'detail': {
  106. 'html': '<p>%s</p>' % ban.user_message,
  107. 'plain': ban.user_message,
  108. },
  109. 'expires_on': None,
  110. })
  111. response = self.client.get('/api/auth/')
  112. self.assertEqual(response.status_code, 200)
  113. user_json = response.json()
  114. self.assertIsNone(user_json['id'])
  115. def test_login_banned_staff(self):
  116. """login api signs banned staff member in"""
  117. user = UserModel.objects.create_user('Bob', 'bob@test.com', 'Pass.123')
  118. user.is_staff = True
  119. user.save()
  120. Ban.objects.create(
  121. check_type=Ban.USERNAME,
  122. banned_value='bob',
  123. user_message='You are tragically banned.',
  124. )
  125. response = self.client.post(
  126. '/api/auth/',
  127. data={
  128. 'username': 'Bob',
  129. 'password': 'Pass.123',
  130. },
  131. )
  132. self.assertEqual(response.status_code, 200)
  133. response = self.client.get('/api/auth/')
  134. self.assertEqual(response.status_code, 200)
  135. user_json = response.json()
  136. self.assertEqual(user_json['id'], user.id)
  137. self.assertEqual(user_json['username'], user.username)
  138. def test_login_inactive_admin(self):
  139. """login api fails to sign admin-activated user in"""
  140. UserModel.objects.create_user('Bob', 'bob@test.com', 'Pass.123', requires_activation=2)
  141. response = self.client.post(
  142. '/api/auth/',
  143. data={
  144. 'username': 'Bob',
  145. 'password': 'Pass.123',
  146. },
  147. )
  148. self.assertEqual(response.status_code, 400)
  149. self.assertEqual(response.json(), {
  150. 'non_field_errors': [
  151. "Your account has to be activated by Administrator before you will be able to sign in.",
  152. ],
  153. })
  154. response = self.client.get('/api/auth/')
  155. self.assertEqual(response.status_code, 200)
  156. user_json = response.json()
  157. self.assertIsNone(user_json['id'])
  158. def test_login_inactive_user(self):
  159. """login api fails to sign user-activated user in"""
  160. UserModel.objects.create_user('Bob', 'bob@test.com', 'Pass.123', requires_activation=1)
  161. response = self.client.post(
  162. '/api/auth/',
  163. data={
  164. 'username': 'Bob',
  165. 'password': 'Pass.123',
  166. },
  167. )
  168. self.assertEqual(response.status_code, 400)
  169. self.assertEqual(response.json(), {
  170. 'non_field_errors': [
  171. "You have to activate your account before you will be able to sign in.",
  172. ],
  173. })
  174. response = self.client.get('/api/auth/')
  175. self.assertEqual(response.status_code, 200)
  176. user_json = response.json()
  177. self.assertIsNone(user_json['id'])
  178. def test_login_disabled_user(self):
  179. """its impossible to sign in to disabled account"""
  180. user = UserModel.objects.create_user('Bob', 'bob@test.com', 'Pass.123', is_active=False)
  181. user.is_staff = True
  182. user.save()
  183. response = self.client.post(
  184. '/api/auth/',
  185. data={
  186. 'username': 'Bob',
  187. 'password': 'Pass.123',
  188. },
  189. )
  190. self.assertContains(response, "Login or password is incorrect.", status_code=400)
  191. response = self.client.get('/api/auth/')
  192. self.assertEqual(response.status_code, 200)
  193. user_json = response.json()
  194. self.assertIsNone(user_json['id'])
  195. class UserRequirementsTests(TestCase):
  196. def test_edge_returns_response(self):
  197. """api edge has no showstoppers"""
  198. response = self.client.get('/api/auth/requirements/')
  199. self.assertEqual(response.status_code, 200)
  200. class SendActivationAPITests(TestCase):
  201. def setUp(self):
  202. self.user = UserModel.objects.create_user('Bob', 'bob@test.com', 'Pass.123')
  203. self.user.requires_activation = 1
  204. self.user.save()
  205. self.link = '/api/auth/send-activation/'
  206. def test_submit_valid(self):
  207. """request activation link api sends reset link mail"""
  208. response = self.client.post(
  209. self.link,
  210. data={
  211. 'email': self.user.email,
  212. },
  213. )
  214. self.assertEqual(response.status_code, 200)
  215. self.assertIn('Activate Bob', mail.outbox[0].subject)
  216. def test_submit_banned(self):
  217. """request activation link api errors for banned users"""
  218. ban = Ban.objects.create(
  219. check_type=Ban.USERNAME,
  220. banned_value=self.user.username,
  221. user_message='Nope!',
  222. )
  223. response = self.client.post(
  224. self.link,
  225. data={
  226. 'email': self.user.email,
  227. },
  228. )
  229. self.assertEqual(response.status_code, 403)
  230. self.assertEqual(response.json(), {
  231. 'detail': {
  232. 'html': '<p>%s</p>' % ban.user_message,
  233. 'plain': ban.user_message,
  234. },
  235. 'expires_on': None,
  236. })
  237. self.assertTrue(not mail.outbox)
  238. def test_submit_disabled(self):
  239. """request activation link api fails disabled users"""
  240. self.user.is_active = False
  241. self.user.save()
  242. response = self.client.post(
  243. self.link,
  244. data={
  245. 'email': self.user.email,
  246. },
  247. )
  248. self.assertContains(response, "No user with this e-mail exists.", status_code=400)
  249. self.assertTrue(not mail.outbox)
  250. def test_submit_empty(self):
  251. """request activation link api errors for no body"""
  252. response = self.client.post(self.link)
  253. self.assertEqual(response.status_code, 400)
  254. self.assertEqual(response.json(), {'email': ["This field is required."]})
  255. self.assertTrue(not mail.outbox)
  256. def test_submit_invalid_data(self):
  257. """login api errors for invalid data"""
  258. response = self.client.post(
  259. self.link,
  260. 'false',
  261. content_type="application/json",
  262. )
  263. self.assertContains(response, "Invalid data.", status_code=400)
  264. def test_submit_invalid_email(self):
  265. """request activation link api errors for invalid email"""
  266. response = self.client.post(
  267. self.link,
  268. data={
  269. 'email': 'fake@mail.com',
  270. },
  271. )
  272. self.assertContains(response, "No user with this e-mail exists.", status_code=400)
  273. self.assertTrue(not mail.outbox)
  274. def test_submit_active_user(self):
  275. """request activation link api errors for active user"""
  276. self.user.requires_activation = 0
  277. self.user.save()
  278. response = self.client.post(
  279. self.link,
  280. data={
  281. 'email': self.user.email,
  282. },
  283. )
  284. self.assertContains(response, "Bob, your account is already active.", status_code=400)
  285. def test_submit_inactive_user(self):
  286. """request activation link api errors for admin-activated users"""
  287. self.user.requires_activation = 2
  288. self.user.save()
  289. response = self.client.post(
  290. self.link,
  291. data={
  292. 'email': self.user.email,
  293. },
  294. )
  295. self.assertContains(response, "only administrator may activate your account", status_code=400)
  296. self.assertTrue(not mail.outbox)
  297. # but succeed for user-activated
  298. self.user.requires_activation = 1
  299. self.user.save()
  300. response = self.client.post(
  301. self.link, data={
  302. 'email': self.user.email,
  303. }
  304. )
  305. self.assertEqual(response.json(), {
  306. 'username': self.user.username,
  307. 'email': self.user.email,
  308. })
  309. self.assertTrue(mail.outbox)
  310. class SendPasswordFormAPITests(TestCase):
  311. def setUp(self):
  312. self.user = UserModel.objects.create_user('Bob', 'bob@test.com', 'Pass.123')
  313. self.link = '/api/auth/send-password-form/'
  314. def test_submit_valid(self):
  315. """request change password form link api sends reset link mail"""
  316. response = self.client.post(
  317. self.link,
  318. data={
  319. 'email': self.user.email,
  320. },
  321. )
  322. self.assertEqual(response.status_code, 200)
  323. self.assertIn('Change Bob password', mail.outbox[0].subject)
  324. def test_submit_banned(self):
  325. """request change password form link api errors for banned users"""
  326. ban = Ban.objects.create(
  327. check_type=Ban.USERNAME,
  328. banned_value=self.user.username,
  329. user_message='Nope!',
  330. )
  331. response = self.client.post(
  332. self.link,
  333. data={
  334. 'email': self.user.email,
  335. },
  336. )
  337. self.assertEqual(response.status_code, 403)
  338. self.assertEqual(response.json(), {
  339. 'detail': {
  340. 'html': '<p>%s</p>' % ban.user_message,
  341. 'plain': ban.user_message,
  342. },
  343. 'expires_on': None,
  344. })
  345. self.assertTrue(not mail.outbox)
  346. def test_submit_disabled(self):
  347. """request change password form api fails disabled users"""
  348. self.user.is_active = False
  349. self.user.save()
  350. response = self.client.post(
  351. self.link,
  352. data={
  353. 'email': self.user.email,
  354. },
  355. )
  356. self.assertContains(response, "No user with this e-mail exists.", status_code=400)
  357. self.assertTrue(not mail.outbox)
  358. def test_submit_empty(self):
  359. """request change password form link api errors for no body"""
  360. response = self.client.post(self.link)
  361. self.assertEqual(response.status_code, 400)
  362. self.assertEqual(response.json(), {'email': ["This field is required."]})
  363. self.assertTrue(not mail.outbox)
  364. def test_submit_invalid(self):
  365. """request change password form link api errors for invalid email"""
  366. response = self.client.post(
  367. self.link,
  368. data={
  369. 'email': 'fake@mail.com',
  370. },
  371. )
  372. self.assertContains(response, "No user with this e-mail exists.", status_code=400)
  373. self.assertTrue(not mail.outbox)
  374. def test_submit_invalid_data(self):
  375. """login api errors for invalid data"""
  376. response = self.client.post(
  377. self.link,
  378. 'false',
  379. content_type="application/json",
  380. )
  381. self.assertContains(response, "Invalid data.", status_code=400)
  382. def test_submit_inactive_user(self):
  383. """request change password form link api errors for inactive users"""
  384. self.user.requires_activation = 1
  385. self.user.save()
  386. response = self.client.post(
  387. self.link,
  388. data={
  389. 'email': self.user.email,
  390. },
  391. )
  392. self.assertContains(response, "You have to activate your account", status_code=400)
  393. self.assertTrue(not mail.outbox)
  394. self.user.requires_activation = 2
  395. self.user.save()
  396. response = self.client.post(
  397. self.link,
  398. data={
  399. 'email': self.user.email,
  400. },
  401. )
  402. self.assertContains(response, "Administrator has to activate your account", status_code=400)
  403. self.assertTrue(not mail.outbox)
  404. class ChangePasswordAPITests(TestCase):
  405. def setUp(self):
  406. self.user = UserModel.objects.create_user('Bob', 'bob@test.com', 'Pass.123')
  407. self.link = '/api/auth/change-password/%s/'
  408. def test_submit_valid(self):
  409. """submit change password form api changes password"""
  410. response = self.client.post(
  411. self.link % self.user.pk,
  412. data={
  413. 'password': 'n3wp4ss!',
  414. 'token': make_password_change_token(self.user),
  415. },
  416. )
  417. self.assertEqual(response.status_code, 200)
  418. user = UserModel.objects.get(id=self.user.pk)
  419. self.assertTrue(user.check_password('n3wp4ss!'))
  420. def test_submit_with_whitespaces(self):
  421. """submit change password form api changes password with whitespaces"""
  422. response = self.client.post(
  423. self.link % self.user.pk,
  424. data={
  425. 'password': ' n3wp4ss! ',
  426. 'token': make_password_change_token(self.user),
  427. },
  428. )
  429. self.assertEqual(response.status_code, 200)
  430. user = UserModel.objects.get(id=self.user.pk)
  431. self.assertTrue(user.check_password(' n3wp4ss! '))
  432. def test_submit_invalid_data(self):
  433. """login api errors for invalid data"""
  434. response = self.client.post(
  435. self.link % self.user.pk,
  436. 'false',
  437. content_type="application/json",
  438. )
  439. self.assertContains(response, "Invalid data.", status_code=400)
  440. def test_invalid_token(self):
  441. """api errors on invalid user id link"""
  442. response = self.client.post(
  443. self.link % self.user.pk,
  444. data={
  445. 'password': 'n3wp4ss!',
  446. 'token': 'invalid!',
  447. },
  448. )
  449. self.assertContains(response, "Form link is invalid or expired.", status_code=400)
  450. def test_banned_user_link(self):
  451. """request errors because user is banned"""
  452. ban = Ban.objects.create(
  453. check_type=Ban.USERNAME,
  454. banned_value=self.user.username,
  455. user_message='Nope!',
  456. )
  457. response = self.client.post(
  458. self.link % self.user.pk,
  459. data={
  460. 'password': 'n3wp4ss!',
  461. 'token': make_password_change_token(self.user),
  462. },
  463. )
  464. self.assertEqual(response.status_code, 403)
  465. self.assertEqual(response.json(), {
  466. 'detail': {
  467. 'html': '<p>%s</p>' % ban.user_message,
  468. 'plain': ban.user_message,
  469. },
  470. 'expires_on': None,
  471. })
  472. def test_inactive_user(self):
  473. """change password api errors for inactive users"""
  474. self.user.requires_activation = 1
  475. self.user.save()
  476. response = self.client.post(
  477. self.link % self.user.pk,
  478. data={
  479. 'password': 'n3wp4ss!',
  480. 'token': make_password_change_token(self.user),
  481. },
  482. )
  483. self.assertContains(response, "You have to activate your account", status_code=400)
  484. self.user.requires_activation = 2
  485. self.user.save()
  486. response = self.client.post(
  487. self.link % self.user.pk,
  488. data={
  489. 'password': 'n3wp4ss!',
  490. 'token': make_password_change_token(self.user),
  491. },
  492. )
  493. self.assertContains(response, "Administrator has to activate your account", status_code=400)
  494. def test_disabled_user(self):
  495. """change password api errors for disabled users"""
  496. self.user.is_active = False
  497. self.user.save()
  498. response = self.client.post(self.link % self.user.pk)
  499. self.assertEqual(response.status_code, 404)
  500. def test_submit_empty(self):
  501. """change password api errors for empty body"""
  502. response = self.client.post(self.link % self.user.pk)
  503. self.assertContains(response, "This field is required.", status_code=400)