testdomains.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. from django.core.urlresolvers import reverse
  2. from rest_framework import status
  3. from rest_framework.test import APITestCase
  4. from .utils import utils
  5. from django.db import transaction
  6. from desecapi.models import Domain
  7. from django.core import mail
  8. import httpretty
  9. from django.conf import settings
  10. class UnauthenticatedDomainTests(APITestCase):
  11. def testExpectUnauthorizedOnGet(self):
  12. url = reverse('domain-list')
  13. response = self.client.get(url, format='json')
  14. self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
  15. def testExpectUnauthorizedOnPost(self):
  16. url = reverse('domain-list')
  17. response = self.client.post(url, format='json')
  18. self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
  19. def testExpectUnauthorizedOnPut(self):
  20. url = reverse('domain-detail', args=(1,))
  21. response = self.client.put(url, format='json')
  22. self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
  23. def testExpectUnauthorizedOnDelete(self):
  24. url = reverse('domain-detail', args=(1,))
  25. response = self.client.delete(url, format='json')
  26. self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
  27. class AuthenticatedDomainTests(APITestCase):
  28. def setUp(self):
  29. httpretty.reset()
  30. httpretty.disable()
  31. if not hasattr(self, 'owner'):
  32. self.owner = utils.createUser()
  33. self.ownedDomains = [utils.createDomain(self.owner), utils.createDomain(self.owner)]
  34. self.otherDomains = [utils.createDomain(), utils.createDomain()]
  35. self.token = utils.createToken(user=self.owner)
  36. self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
  37. def testExpectOnlyOwnedDomains(self):
  38. url = reverse('domain-list')
  39. response = self.client.get(url, format='json')
  40. self.assertEqual(response.status_code, status.HTTP_200_OK)
  41. self.assertEqual(len(response.data), 2)
  42. self.assertEqual(response.data[0]['name'], self.ownedDomains[0].name)
  43. self.assertEqual(response.data[1]['name'], self.ownedDomains[1].name)
  44. def testCanDeleteOwnedDomain(self):
  45. httpretty.enable()
  46. httpretty.register_uri(httpretty.DELETE, settings.NSLORD_PDNS_API + '/zones/' + self.ownedDomains[1].name + '.')
  47. httpretty.register_uri(httpretty.DELETE, settings.NSMASTER_PDNS_API + '/zones/' + self.ownedDomains[1].name+ '.')
  48. url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
  49. response = self.client.delete(url)
  50. self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
  51. self.assertEqual(httpretty.last_request().method, 'DELETE')
  52. self.assertEqual(httpretty.last_request().headers['Host'], 'nsmaster:8081')
  53. httpretty.reset()
  54. httpretty.register_uri(httpretty.DELETE, settings.NSLORD_PDNS_API + '/zones/' + self.ownedDomains[1].name + '.')
  55. httpretty.register_uri(httpretty.DELETE, settings.NSMASTER_PDNS_API + '/zones/' + self.ownedDomains[1].name+ '.')
  56. response = self.client.get(url)
  57. self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
  58. self.assertTrue(isinstance(httpretty.last_request(), httpretty.core.HTTPrettyRequestEmpty))
  59. def testCantDeleteOtherDomains(self):
  60. httpretty.enable()
  61. httpretty.register_uri(httpretty.DELETE, settings.NSLORD_PDNS_API + '/zones/' + self.otherDomains[1].name + '.')
  62. httpretty.register_uri(httpretty.DELETE, settings.NSMASTER_PDNS_API + '/zones/' + self.otherDomains[1].name+ '.')
  63. url = reverse('domain-detail', args=(self.otherDomains[1].pk,))
  64. response = self.client.delete(url)
  65. self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
  66. self.assertTrue(isinstance(httpretty.last_request(), httpretty.core.HTTPrettyRequestEmpty))
  67. self.assertTrue(Domain.objects.filter(pk=self.otherDomains[1].pk).exists())
  68. def testCanGetOwnedDomains(self):
  69. url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
  70. response = self.client.get(url)
  71. self.assertEqual(response.status_code, status.HTTP_200_OK)
  72. self.assertEqual(response.data['name'], self.ownedDomains[1].name)
  73. def testCantGetOtherDomains(self):
  74. url = reverse('domain-detail', args=(self.otherDomains[1].pk,))
  75. response = self.client.get(url)
  76. self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
  77. def testCanPutOwnedDomain(self):
  78. url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
  79. response = self.client.get(url)
  80. response.data['arecord'] = '1.2.3.4'
  81. response = self.client.put(url, response.data)
  82. self.assertEqual(response.status_code, status.HTTP_200_OK)
  83. response = self.client.get(url)
  84. self.assertEqual(response.status_code, status.HTTP_200_OK)
  85. self.assertEqual(response.data['arecord'], '1.2.3.4')
  86. def testCantChangeDomainName(self):
  87. url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
  88. response = self.client.get(url)
  89. newname = utils.generateDomainname()
  90. response.data['name'] = newname
  91. response = self.client.put(url, response.data)
  92. self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)
  93. response = self.client.get(url)
  94. self.assertEqual(response.status_code, status.HTTP_200_OK)
  95. self.assertEqual(response.data['name'], self.ownedDomains[1].name)
  96. def testCantPutOtherDomains(self):
  97. url = reverse('domain-detail', args=(self.otherDomains[1].pk,))
  98. response = self.client.put(url, {})
  99. self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
  100. def testCanPostDomains(self):
  101. url = reverse('domain-list')
  102. data = {'name': utils.generateDomainname()}
  103. response = self.client.post(url, data)
  104. self.assertEqual(response.status_code, status.HTTP_201_CREATED)
  105. self.assertEqual(len(mail.outbox), 0)
  106. def testCantPostSameDomainTwice(self):
  107. url = reverse('domain-list')
  108. data = {'name': utils.generateDomainname()}
  109. response = self.client.post(url, data)
  110. self.assertEqual(response.status_code, status.HTTP_201_CREATED)
  111. response = self.client.post(url, data)
  112. self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)
  113. def testCantPostUnavailableDomain(self):
  114. name = utils.generateDomainname()
  115. httpretty.enable()
  116. httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones',
  117. body='{"error": "Domain \'' + name + '.\' already exists"}', status=422)
  118. url = reverse('domain-list')
  119. data = {'name': name}
  120. response = self.client.post(url, data)
  121. self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)
  122. def testCanPostComplicatedDomains(self):
  123. url = reverse('domain-list')
  124. data = {'name': 'very.long.domain.name.' + utils.generateDomainname()}
  125. response = self.client.post(url, data)
  126. self.assertEqual(response.status_code, status.HTTP_201_CREATED)
  127. def testCanUpdateARecord(self):
  128. url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
  129. response = self.client.get(url)
  130. response.data['arecord'] = '10.13.3.7'
  131. response = self.client.put(url, response.data)
  132. self.assertEqual(response.status_code, status.HTTP_200_OK)
  133. response = self.client.get(url)
  134. self.assertEqual(response.status_code, status.HTTP_200_OK)
  135. self.assertEqual(response.data['arecord'], '10.13.3.7')
  136. def testCanUpdateAAAARecord(self):
  137. url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
  138. response = self.client.get(url)
  139. response.data['aaaarecord'] = 'fe80::a11:10ff:fee0:ff77'
  140. response = self.client.put(url, response.data)
  141. self.assertEqual(response.status_code, status.HTTP_200_OK)
  142. response = self.client.get(url)
  143. self.assertEqual(response.status_code, status.HTTP_200_OK)
  144. self.assertEqual(response.data['aaaarecord'], 'fe80::a11:10ff:fee0:ff77')
  145. def testCanUpdateAcmeChallenge(self):
  146. url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
  147. response = self.client.get(url)
  148. response.data['acme_challenge'] = 'test_challenge'
  149. response = self.client.put(url, response.data)
  150. self.assertEqual(response.status_code, status.HTTP_200_OK)
  151. response = self.client.get(url)
  152. self.assertEqual(response.status_code, status.HTTP_200_OK)
  153. self.assertEqual(response.data['acme_challenge'], 'test_challenge')
  154. def testPostingCausesPdnsAPICall(self):
  155. httpretty.enable()
  156. httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones')
  157. url = reverse('domain-list')
  158. data = {'name': utils.generateDomainname()}
  159. response = self.client.post(url, data)
  160. self.assertTrue(data['name'] in httpretty.last_request().parsed_body)
  161. self.assertTrue('ns1.desec.io' in httpretty.last_request().parsed_body)
  162. def testPostingWithRecordsCausesPdnsAPIPatch(self):
  163. name = utils.generateDomainname()
  164. httpretty.enable()
  165. httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones')
  166. httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/' + name + '.')
  167. url = reverse('domain-list')
  168. data = {'name': name, 'arecord': '1.3.3.7', 'aaaarecord': 'dead::beef', 'acme_challenge': 'letsencrypt_ftw'}
  169. response = self.client.post(url, data)
  170. self.assertEqual(httpretty.last_request().method, 'PATCH')
  171. self.assertTrue(data['name'] in httpretty.last_request().parsed_body)
  172. self.assertTrue('1.3.3.7' in httpretty.last_request().parsed_body)
  173. self.assertTrue('dead::beef' in httpretty.last_request().parsed_body)
  174. self.assertTrue('letsencrypt_ftw' in httpretty.last_request().parsed_body)
  175. def testPostDomainCausesPdnsAPIPatch(self):
  176. name = utils.generateDomainname()
  177. httpretty.enable()
  178. httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones')
  179. httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/' + name + '.')
  180. url = reverse('domain-list')
  181. data = {'name': name, 'acme_challenge': 'letsencrypt_ftw'}
  182. self.client.post(url, data)
  183. self.assertEqual(httpretty.last_request().method, 'PATCH')
  184. self.assertTrue(data['name'] in httpretty.last_request().parsed_body)
  185. self.assertTrue('letsencrypt_ftw' in httpretty.last_request().parsed_body)
  186. def testUpdateingCausesPdnsAPICall(self):
  187. url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
  188. response = self.client.get(url)
  189. httpretty.enable()
  190. httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/' + response.data['name'] + '.')
  191. response.data['arecord'] = '10.13.3.7'
  192. response = self.client.put(url, response.data)
  193. self.assertTrue('10.13.3.7' in httpretty.last_request().parsed_body)
  194. def testDomainDetailURL(self):
  195. url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
  196. urlByName = reverse('domain-detail/byName', args=(self.ownedDomains[1].name,))
  197. self.assertTrue(("/%d" % self.ownedDomains[1].pk) in url)
  198. self.assertTrue("/" + self.ownedDomains[1].name in urlByName)
  199. def testRollback(self):
  200. name = utils.generateDomainname()
  201. httpretty.enable()
  202. httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones', body="some error", status=500)
  203. url = reverse('domain-list')
  204. data = {'name': name}
  205. try:
  206. response = self.client.post(url, data)
  207. except:
  208. pass
  209. self.assertFalse(Domain.objects.filter(name=name).exists())
  210. class AuthenticatedDynDomainTests(APITestCase):
  211. def setUp(self):
  212. httpretty.reset()
  213. httpretty.disable()
  214. if not hasattr(self, 'owner'):
  215. self.owner = utils.createUser(dyn=True)
  216. self.ownedDomains = [utils.createDomain(self.owner, dyn=True), utils.createDomain(self.owner, dyn=True)]
  217. self.otherDomains = [utils.createDomain(), utils.createDomain()]
  218. self.token = utils.createToken(user=self.owner)
  219. self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
  220. def testCanDeleteOwnedDynDomain(self):
  221. httpretty.enable()
  222. httpretty.register_uri(httpretty.DELETE, settings.NSLORD_PDNS_API + '/zones/' + self.ownedDomains[1].name + '.')
  223. httpretty.register_uri(httpretty.DELETE, settings.NSMASTER_PDNS_API + '/zones/' + self.ownedDomains[1].name+ '.')
  224. httpretty.register_uri(httpretty.PATCH, settings.NSLORD_PDNS_API + '/zones/dedyn.io.')
  225. url = reverse('domain-detail', args=(self.ownedDomains[1].pk,))
  226. response = self.client.delete(url)
  227. self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
  228. self.assertEqual(httpretty.last_request().method, 'PATCH')
  229. self.assertEqual(httpretty.last_request().headers['Host'], 'nslord:8081')
  230. self.assertTrue('"NS"' in httpretty.last_request().parsed_body
  231. and '"' + self.ownedDomains[1].name + '."' in httpretty.last_request().parsed_body
  232. and '"DELETE"' in httpretty.last_request().parsed_body)
  233. httpretty.reset()
  234. httpretty.register_uri(httpretty.DELETE, settings.NSLORD_PDNS_API + '/zones/' + self.ownedDomains[1].name + '.')
  235. httpretty.register_uri(httpretty.DELETE, settings.NSMASTER_PDNS_API + '/zones/' + self.ownedDomains[1].name+ '.')
  236. response = self.client.get(url)
  237. self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
  238. self.assertTrue(isinstance(httpretty.last_request(), httpretty.core.HTTPrettyRequestEmpty))
  239. def testCantDeleteOtherDynDomains(self):
  240. httpretty.enable()
  241. httpretty.register_uri(httpretty.DELETE, settings.NSLORD_PDNS_API + '/zones/' + self.otherDomains[1].name + '.')
  242. httpretty.register_uri(httpretty.DELETE, settings.NSMASTER_PDNS_API + '/zones/' + self.otherDomains[1].name+ '.')
  243. url = reverse('domain-detail', args=(self.otherDomains[1].pk,))
  244. response = self.client.delete(url)
  245. self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
  246. self.assertTrue(isinstance(httpretty.last_request(), httpretty.core.HTTPrettyRequestEmpty))
  247. self.assertTrue(Domain.objects.filter(pk=self.otherDomains[1].pk).exists())
  248. def testCanPostDynDomains(self):
  249. url = reverse('domain-list')
  250. data = {'name': utils.generateDynDomainname()}
  251. response = self.client.post(url, data)
  252. email = str(mail.outbox[0].message())
  253. self.assertEqual(response.status_code, status.HTTP_201_CREATED)
  254. self.assertEqual(len(mail.outbox), 1)
  255. self.assertTrue(data['name'] in email)
  256. self.assertTrue(self.token in email)
  257. def testCantPostNonDynDomains(self):
  258. url = reverse('domain-list')
  259. data = {'name': utils.generateDomainname()}
  260. response = self.client.post(url, data)
  261. self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)
  262. data = {'name': 'very.long.domain.' + utils.generateDynDomainname()}
  263. response = self.client.post(url, data)
  264. self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)
  265. def testLimitDynDomains(self):
  266. httpretty.enable()
  267. httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones')
  268. outboxlen = len(mail.outbox)
  269. url = reverse('domain-list')
  270. for i in range(settings.LIMIT_USER_DOMAIN_COUNT_DEFAULT-2):
  271. data = {'name': utils.generateDynDomainname()}
  272. response = self.client.post(url, data)
  273. self.assertEqual(response.status_code, status.HTTP_201_CREATED)
  274. self.assertEqual(len(mail.outbox), outboxlen+i+1)
  275. data = {'name': utils.generateDynDomainname()}
  276. response = self.client.post(url, data)
  277. self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
  278. self.assertEqual(len(mail.outbox), outboxlen + settings.LIMIT_USER_DOMAIN_COUNT_DEFAULT-2)
  279. def testCantUseInvalidCharactersInDomainNamePDNS(self):
  280. httpretty.enable()
  281. httpretty.register_uri(httpretty.POST, settings.NSLORD_PDNS_API + '/zones')
  282. outboxlen = len(mail.outbox)
  283. invalidnames = [
  284. 'with space.dedyn.io',
  285. 'another space.de',
  286. ' spaceatthebeginning.com',
  287. 'percentage%sign.com',
  288. '%percentagesign.dedyn.io',
  289. 'slash/desec.io',
  290. '/slashatthebeginning.dedyn.io',
  291. '\\backslashatthebeginning.dedyn.io',
  292. 'backslash\\inthemiddle.at',
  293. '@atsign.com',
  294. 'at@sign.com',
  295. ]
  296. url = reverse('domain-list')
  297. for domainname in invalidnames:
  298. data = {'name': domainname}
  299. response = self.client.post(url, data)
  300. self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
  301. self.assertEqual(len(mail.outbox), outboxlen)