test_discuss_controller.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. # -*- coding: utf-8 -*-
  2. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  3. import json
  4. import odoo
  5. from odoo.tools import mute_logger
  6. from odoo.tests import HttpCase
  7. @odoo.tests.tagged("-at_install", "post_install")
  8. class TestDiscussController(HttpCase):
  9. @classmethod
  10. def setUpClass(cls):
  11. super().setUpClass()
  12. cls.channel = cls.env["mail.channel"].create(
  13. {
  14. "group_public_id": None,
  15. "name": "Test channel",
  16. }
  17. )
  18. cls.public_user = cls.env.ref("base.public_user")
  19. cls.attachments = (
  20. cls.env["ir.attachment"]
  21. .with_user(cls.public_user)
  22. .sudo()
  23. .create(
  24. [
  25. {
  26. "access_token": cls.env["ir.attachment"]._generate_access_token(),
  27. "name": "File 1",
  28. "res_id": 0,
  29. "res_model": "mail.compose.message",
  30. },
  31. {
  32. "access_token": cls.env["ir.attachment"]._generate_access_token(),
  33. "name": "File 2",
  34. "res_id": 0,
  35. "res_model": "mail.compose.message",
  36. },
  37. ]
  38. )
  39. )
  40. cls.guest = cls.env["mail.guest"].create({"name": "Guest"})
  41. cls.channel.add_members(guest_ids=cls.guest.ids)
  42. @mute_logger("odoo.addons.http_routing.models.ir_http", "odoo.http")
  43. def test_channel_message_attachments(self):
  44. self.authenticate(None, None)
  45. self.opener.cookies[
  46. self.guest._cookie_name
  47. ] = f"{self.guest.id}{self.guest._cookie_separator}{self.guest.access_token}"
  48. # test message post: token error
  49. res1 = self.url_open(
  50. url="/mail/message/post",
  51. data=json.dumps(
  52. {
  53. "params": {
  54. "thread_model": self.channel._name,
  55. "thread_id": self.channel.id,
  56. "post_data": {
  57. "body": "test",
  58. "attachment_ids": [self.attachments[0].id],
  59. "attachment_tokens": ["wrong token"],
  60. },
  61. },
  62. }
  63. ),
  64. headers={"Content-Type": "application/json"},
  65. )
  66. self.assertEqual(res1.status_code, 200)
  67. self.assertIn(
  68. f"The attachment {self.attachments[0].id} does not exist or you do not have the rights to access it",
  69. res1.text,
  70. "guest should not be allowed to add attachment without token when posting message",
  71. )
  72. # test message post: token ok
  73. res2 = self.url_open(
  74. url="/mail/message/post",
  75. data=json.dumps(
  76. {
  77. "params": {
  78. "thread_model": self.channel._name,
  79. "thread_id": self.channel.id,
  80. "post_data": {
  81. "body": "test",
  82. "attachment_ids": [self.attachments[0].id],
  83. "attachment_tokens": [self.attachments[0].access_token],
  84. "message_type": "comment",
  85. },
  86. },
  87. }
  88. ),
  89. headers={"Content-Type": "application/json"},
  90. )
  91. self.assertEqual(res2.status_code, 200)
  92. message_format1 = res2.json()["result"]
  93. self.assertEqual(
  94. message_format1["attachment_ids"],
  95. json.loads(json.dumps(self.attachments[0]._attachment_format())),
  96. "guest should be allowed to add attachment with token when posting message",
  97. )
  98. # test message update: token error
  99. res3 = self.url_open(
  100. url="/mail/message/update_content",
  101. data=json.dumps(
  102. {
  103. "params": {
  104. "message_id": message_format1["id"],
  105. "body": "test",
  106. "attachment_ids": [self.attachments[1].id],
  107. "attachment_tokens": ["wrong token"],
  108. },
  109. }
  110. ),
  111. headers={"Content-Type": "application/json"},
  112. )
  113. self.assertEqual(res3.status_code, 200)
  114. self.assertIn(
  115. f"The attachment {self.attachments[1].id} does not exist or you do not have the rights to access it",
  116. res3.text,
  117. "guest should not be allowed to add attachment without token when updating message",
  118. )
  119. # test message update: token ok
  120. res4 = self.url_open(
  121. url="/mail/message/update_content",
  122. data=json.dumps(
  123. {
  124. "params": {
  125. "message_id": message_format1["id"],
  126. "body": "test",
  127. "attachment_ids": [self.attachments[1].id],
  128. "attachment_tokens": [self.attachments[1].access_token],
  129. },
  130. }
  131. ),
  132. headers={"Content-Type": "application/json"},
  133. )
  134. self.assertEqual(res4.status_code, 200)
  135. message_format2 = res4.json()["result"]
  136. self.assertEqual(
  137. message_format2["attachments"],
  138. json.loads(json.dumps(self.attachments.sorted()._attachment_format())),
  139. "guest should be allowed to add attachment with token when updating message",
  140. )
  141. # test message update: own attachment ok
  142. res5 = self.url_open(
  143. url="/mail/message/update_content",
  144. data=json.dumps(
  145. {
  146. "params": {
  147. "message_id": message_format2["id"],
  148. "body": "test",
  149. "attachment_ids": [self.attachments[1].id],
  150. },
  151. }
  152. ),
  153. headers={"Content-Type": "application/json"},
  154. )
  155. self.assertEqual(res5.status_code, 200)
  156. message_format3 = res5.json()["result"]
  157. self.assertEqual(
  158. message_format3["attachments"],
  159. json.loads(json.dumps(self.attachments.sorted()._attachment_format())),
  160. "guest should be allowed to add own attachment without token when updating message",
  161. )
  162. @mute_logger("odoo.addons.http_routing.models.ir_http", "odoo.http")
  163. def test_attachment_hijack(self):
  164. att = self.env["ir.attachment"].create(
  165. [
  166. {
  167. "name": "arguments_for_firing_marc_demo",
  168. "res_id": 0,
  169. "res_model": "mail.compose.message",
  170. },
  171. ]
  172. )
  173. demo = self.authenticate("demo", "demo")
  174. channel = self.env["mail.channel"].create({"group_public_id": None, "name": "public_channel"})
  175. channel.add_members(
  176. self.env["res.users"].browse(demo.uid).partner_id.ids
  177. ) # don't care, we just need a channel where demo is follower
  178. no_access_request = self.url_open("/web/content/" + str(att.id))
  179. self.assertFalse(
  180. no_access_request.ok
  181. ) # if this test breaks, it might be due to a change in /web/content, or the default rules for accessing an attachment. This is not an issue but it makes this test irrelevant.
  182. response = self.url_open(
  183. url="/mail/message/post",
  184. headers={"Content-Type": "application/json"}, # route called as demo
  185. data=json.dumps(
  186. {
  187. "params": {
  188. "post_data": {
  189. "attachment_ids": [att.id], # demo does not have access to this attachment id
  190. "body": "",
  191. "message_type": "comment",
  192. "partner_ids": [],
  193. "subtype_xmlid": "mail.mt_comment",
  194. },
  195. "thread_id": channel.id,
  196. "thread_model": "mail.channel",
  197. }
  198. },
  199. ),
  200. )
  201. self.assertNotIn(
  202. "arguments_for_firing_marc_demo", response.text
  203. ) # demo should not be able to see the name of the document