1
0

JsonApiTest.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. <?php
  2. use PHPUnit\Framework\TestCase;
  3. use PrivateBin\Controller;
  4. use PrivateBin\Data\Filesystem;
  5. use PrivateBin\Persistence\ServerSalt;
  6. use PrivateBin\Request;
  7. class JsonApiTest extends TestCase
  8. {
  9. protected $_model;
  10. protected $_path;
  11. public function setUp(): void
  12. {
  13. /* Setup Routine */
  14. $this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data';
  15. if (!is_dir($this->_path)) {
  16. mkdir($this->_path);
  17. }
  18. $this->_model = new Filesystem(array('dir' => $this->_path));
  19. ServerSalt::setStore($this->_model);
  20. $_POST = array();
  21. $_GET = array();
  22. $_SERVER = array();
  23. if ($this->_model->exists(Helper::getPasteId())) {
  24. $this->_model->delete(Helper::getPasteId());
  25. }
  26. $options = parse_ini_file(CONF_SAMPLE, true);
  27. $options['model_options']['dir'] = $this->_path;
  28. Helper::confBackup();
  29. Helper::createIniFile(CONF, $options);
  30. }
  31. public function tearDown(): void
  32. {
  33. /* Tear Down Routine */
  34. unlink(CONF);
  35. Helper::confRestore();
  36. Helper::rmDir($this->_path);
  37. }
  38. /**
  39. * @runInSeparateProcess
  40. */
  41. public function testCreate()
  42. {
  43. $options = parse_ini_file(CONF, true);
  44. $options['traffic']['limit'] = 0;
  45. Helper::createIniFile(CONF, $options);
  46. $paste = Helper::getPasteJson();
  47. $file = tempnam(sys_get_temp_dir(), 'FOO');
  48. file_put_contents($file, $paste);
  49. Request::setInputStream($file);
  50. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  51. $_SERVER['REQUEST_METHOD'] = 'POST';
  52. $_SERVER['REMOTE_ADDR'] = '::1';
  53. $_SERVER['REQUEST_URI'] = '/';
  54. ob_start();
  55. new Controller;
  56. $content = ob_get_contents();
  57. ob_end_clean();
  58. $response = json_decode($content, true);
  59. $this->assertEquals(0, $response['status'], 'outputs status');
  60. $this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
  61. $this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
  62. $paste = $this->_model->read($response['id']);
  63. $this->assertEquals(
  64. hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
  65. $response['deletetoken'],
  66. 'outputs valid delete token'
  67. );
  68. }
  69. /**
  70. * @runInSeparateProcess
  71. */
  72. public function testPut()
  73. {
  74. $options = parse_ini_file(CONF, true);
  75. $options['traffic']['limit'] = 0;
  76. Helper::createIniFile(CONF, $options);
  77. $paste = Helper::getPasteJson();
  78. $file = tempnam(sys_get_temp_dir(), 'FOO');
  79. file_put_contents($file, $paste);
  80. Request::setInputStream($file);
  81. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  82. $_GET[Helper::getPasteId()] = '';
  83. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  84. $_SERVER['REQUEST_METHOD'] = 'PUT';
  85. $_SERVER['REMOTE_ADDR'] = '::1';
  86. ob_start();
  87. new Controller;
  88. $content = ob_get_contents();
  89. ob_end_clean();
  90. unlink($file);
  91. $response = json_decode($content, true);
  92. $this->assertEquals(0, $response['status'], 'outputs status');
  93. $this->assertEquals(Helper::getPasteId(), $response['id'], 'outputted paste ID matches input');
  94. $this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
  95. $this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
  96. $paste = $this->_model->read($response['id']);
  97. $this->assertEquals(
  98. hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
  99. $response['deletetoken'],
  100. 'outputs valid delete token'
  101. );
  102. }
  103. /**
  104. * @runInSeparateProcess
  105. */
  106. public function testDelete()
  107. {
  108. $this->_model->create(Helper::getPasteId(), Helper::getPaste());
  109. $this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
  110. $paste = $this->_model->read(Helper::getPasteId());
  111. $file = tempnam(sys_get_temp_dir(), 'FOO');
  112. file_put_contents($file, json_encode(array(
  113. 'deletetoken' => hash_hmac('sha256', Helper::getPasteId(), $paste['meta']['salt']),
  114. )));
  115. Request::setInputStream($file);
  116. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  117. $_GET[Helper::getPasteId()] = '';
  118. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  119. $_SERVER['REQUEST_METHOD'] = 'DELETE';
  120. ob_start();
  121. new Controller;
  122. $content = ob_get_contents();
  123. ob_end_clean();
  124. unlink($file);
  125. $response = json_decode($content, true);
  126. $this->assertEquals(0, $response['status'], 'outputs status');
  127. $this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste successfully deleted');
  128. }
  129. /**
  130. * @runInSeparateProcess
  131. */
  132. public function testDeleteWithPost()
  133. {
  134. $this->_model->create(Helper::getPasteId(), Helper::getPaste());
  135. $this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
  136. $paste = $this->_model->read(Helper::getPasteId());
  137. $file = tempnam(sys_get_temp_dir(), 'FOO');
  138. file_put_contents($file, json_encode(array(
  139. 'pasteid' => Helper::getPasteId(),
  140. 'deletetoken' => hash_hmac('sha256', Helper::getPasteId(), $paste['meta']['salt']),
  141. )));
  142. Request::setInputStream($file);
  143. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  144. $_SERVER['REQUEST_METHOD'] = 'POST';
  145. ob_start();
  146. new Controller;
  147. $content = ob_get_contents();
  148. ob_end_clean();
  149. $response = json_decode($content, true);
  150. $this->assertEquals(0, $response['status'], 'outputs status');
  151. $this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste successfully deleted');
  152. }
  153. /**
  154. * @runInSeparateProcess
  155. */
  156. public function testRead()
  157. {
  158. $paste = Helper::getPaste();
  159. $this->_model->create(Helper::getPasteId(), $paste);
  160. $_SERVER['QUERY_STRING'] = Helper::getPasteId();
  161. $_GET[Helper::getPasteId()] = '';
  162. $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
  163. ob_start();
  164. new Controller;
  165. $content = ob_get_contents();
  166. ob_end_clean();
  167. $response = json_decode($content, true);
  168. $this->assertEquals(0, $response['status'], 'outputs success status');
  169. $this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly');
  170. $this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
  171. $this->assertEquals($paste['ct'], $response['ct'], 'outputs data correctly');
  172. $this->assertEquals($paste['meta']['created'], $response['meta']['created'], 'outputs postdate correctly');
  173. $this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
  174. $this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
  175. }
  176. /**
  177. * @runInSeparateProcess
  178. */
  179. public function testJsonLdPaste()
  180. {
  181. $_GET['jsonld'] = 'paste';
  182. ob_start();
  183. new Controller;
  184. $content = ob_get_contents();
  185. ob_end_clean();
  186. $this->assertEquals(str_replace(
  187. '?jsonld=',
  188. '/?jsonld=',
  189. file_get_contents(PUBLIC_PATH . '/js/paste.jsonld')
  190. ), $content, 'outputs data correctly');
  191. }
  192. /**
  193. * @runInSeparateProcess
  194. */
  195. public function testJsonLdComment()
  196. {
  197. $_GET['jsonld'] = 'comment';
  198. ob_start();
  199. new Controller;
  200. $content = ob_get_contents();
  201. ob_end_clean();
  202. $this->assertEquals(str_replace(
  203. '?jsonld=',
  204. '/?jsonld=',
  205. file_get_contents(PUBLIC_PATH . '/js/comment.jsonld')
  206. ), $content, 'outputs data correctly');
  207. }
  208. /**
  209. * @runInSeparateProcess
  210. */
  211. public function testJsonLdPasteMeta()
  212. {
  213. $_GET['jsonld'] = 'pastemeta';
  214. ob_start();
  215. new Controller;
  216. $content = ob_get_contents();
  217. ob_end_clean();
  218. $this->assertEquals(str_replace(
  219. '?jsonld=',
  220. '/?jsonld=',
  221. file_get_contents(PUBLIC_PATH . '/js/pastemeta.jsonld')
  222. ), $content, 'outputs data correctly');
  223. }
  224. /**
  225. * @runInSeparateProcess
  226. */
  227. public function testJsonLdCommentMeta()
  228. {
  229. $_GET['jsonld'] = 'commentmeta';
  230. ob_start();
  231. new Controller;
  232. $content = ob_get_contents();
  233. ob_end_clean();
  234. $this->assertEquals(str_replace(
  235. '?jsonld=',
  236. '/?jsonld=',
  237. file_get_contents(PUBLIC_PATH . '/js/commentmeta.jsonld')
  238. ), $content, 'outputs data correctly');
  239. }
  240. /**
  241. * @runInSeparateProcess
  242. */
  243. public function testJsonLdTypes()
  244. {
  245. $_GET['jsonld'] = 'types';
  246. ob_start();
  247. new Controller;
  248. $content = ob_get_contents();
  249. ob_end_clean();
  250. $this->assertEquals(str_replace(
  251. '?jsonld=',
  252. '/?jsonld=',
  253. file_get_contents(PUBLIC_PATH . '/js/types.jsonld')
  254. ), $content, 'outputs data correctly');
  255. }
  256. /**
  257. * @runInSeparateProcess
  258. */
  259. public function testJsonLdInvalid()
  260. {
  261. $_GET['jsonld'] = CONF;
  262. ob_start();
  263. new Controller;
  264. $content = ob_get_contents();
  265. ob_end_clean();
  266. $this->assertEquals('{}', $content, 'does not output nasty data');
  267. }
  268. /**
  269. * @runInSeparateProcess
  270. */
  271. public function testShortenViaYourls()
  272. {
  273. $mock_yourls_service = $this->_path . DIRECTORY_SEPARATOR . 'yourls.json';
  274. $options = parse_ini_file(CONF, true);
  275. $options['main']['basepath'] = 'https://example.com/path'; // missing slash gets added by Configuration constructor
  276. $options['main']['urlshortener'] = 'https://example.com/path/shortenviayourls?link=';
  277. $options['yourls']['apiurl'] = $mock_yourls_service;
  278. Helper::createIniFile(CONF, $options);
  279. // the real service answer is more complex, but we only look for the shorturl & statusCode
  280. file_put_contents($mock_yourls_service, '{"shorturl":"https:\/\/example.com\/1","statusCode":200}');
  281. $_SERVER['REQUEST_URI'] = '/path/shortenviayourls?link=https%3A%2F%2Fexample.com%2Fpath%2F%3Ffoo%23bar';
  282. $_GET['link'] = 'https://example.com/path/?foo#bar';
  283. ob_start();
  284. new Controller;
  285. $content = ob_get_contents();
  286. ob_end_clean();
  287. $this->assertStringContainsString('id="pasteurl" href="https://example.com/1"', $content, 'outputs shortened URL correctly');
  288. }
  289. /**
  290. * @runInSeparateProcess
  291. */
  292. public function testShortenViaYourlsFailure()
  293. {
  294. $_SERVER['REQUEST_URI'] = '/path/shortenviayourls?link=https%3A%2F%2Fexample.com%2Fpath%2F%3Ffoo%23bar';
  295. $_GET['link'] = 'https://example.com/path/?foo#bar';
  296. ob_start();
  297. new Controller;
  298. $content = ob_get_contents();
  299. ob_end_clean();
  300. $this->assertStringContainsString('Error calling YOURLS.', $content, 'outputs error correctly');
  301. }
  302. }