companion.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. /* global jest:false, test:false, expect:false, describe:false */
  2. jest.mock('tus-js-client')
  3. jest.mock('purest')
  4. jest.mock('../../src/server/helpers/oauth-state', () => {
  5. return {
  6. generateState: () => 'some-cool-nice-encrytpion',
  7. addToState: () => 'some-cool-nice-encrytpion',
  8. getFromState: (state, key) => {
  9. if (state === 'state-with-invalid-instance-url') {
  10. return 'http://localhost:3452'
  11. }
  12. if (state === 'state-with-older-version' && key === 'clientVersion') {
  13. return '@uppy/companion-client=1.0.1'
  14. }
  15. if (state === 'state-with-newer-version' && key === 'clientVersion') {
  16. return '@uppy/companion-client=1.0.3'
  17. }
  18. if (state === 'state-with-newer-version-old-style' && key === 'clientVersion') {
  19. return 'companion-client:1.0.2'
  20. }
  21. return 'http://localhost:3020'
  22. }
  23. }
  24. })
  25. const request = require('supertest')
  26. const tokenService = require('../../src/server/helpers/jwt')
  27. const { authServer } = require('../mockserver')
  28. const authData = {
  29. dropbox: 'token value',
  30. drive: 'token value'
  31. }
  32. const token = tokenService.generateToken(authData, process.env.COMPANION_SECRET)
  33. const OAUTH_STATE = 'some-cool-nice-encrytpion'
  34. describe('set i-am header', () => {
  35. test('set i-am header in response', () => {
  36. return request(authServer)
  37. .get('/dropbox/list/')
  38. .set('uppy-auth-token', token)
  39. .expect(200)
  40. .then((res) => expect(res.header['i-am']).toBe('http://localhost:3020'))
  41. })
  42. })
  43. describe('list provider files', () => {
  44. test('list files for dropbox', () => {
  45. return request(authServer)
  46. .get('/dropbox/list/')
  47. .set('uppy-auth-token', token)
  48. .expect(200)
  49. .then((res) => expect(res.body.username).toBe('foo@bar.com'))
  50. })
  51. test('list files for google drive', () => {
  52. return request(authServer)
  53. .get('/drive/list/')
  54. .set('uppy-auth-token', token)
  55. .expect(200)
  56. .then((res) => expect(res.body.username).toBe('ife@bala.com'))
  57. })
  58. })
  59. describe('validate upload data', () => {
  60. test('invalid upload protocol gets rejected', () => {
  61. return request(authServer)
  62. .post('/drive/get/README.md')
  63. .set('uppy-auth-token', token)
  64. .set('Content-Type', 'application/json')
  65. .send({
  66. endpoint: 'http://master.tus.com/files',
  67. protocol: 'tusInvalid'
  68. })
  69. .expect(400)
  70. .then((res) => expect(res.body.error).toBe('unsupported protocol specified'))
  71. })
  72. test('invalid upload fieldname gets rejected', () => {
  73. return request(authServer)
  74. .post('/drive/get/README.md')
  75. .set('uppy-auth-token', token)
  76. .set('Content-Type', 'application/json')
  77. .send({
  78. endpoint: 'http://master.tus.com/files',
  79. protocol: 'tus',
  80. fieldname: 390
  81. })
  82. .expect(400)
  83. .then((res) => expect(res.body.error).toBe('fieldname must be a string'))
  84. })
  85. test('invalid upload metadata gets rejected', () => {
  86. return request(authServer)
  87. .post('/drive/get/README.md')
  88. .set('uppy-auth-token', token)
  89. .set('Content-Type', 'application/json')
  90. .send({
  91. endpoint: 'http://master.tus.com/files',
  92. protocol: 'tus',
  93. metadata: 'I am a string instead of object'
  94. })
  95. .expect(400)
  96. .then((res) => expect(res.body.error).toBe('metadata must be an object'))
  97. })
  98. test('invalid upload headers get rejected', () => {
  99. return request(authServer)
  100. .post('/drive/get/README.md')
  101. .set('uppy-auth-token', token)
  102. .set('Content-Type', 'application/json')
  103. .send({
  104. endpoint: 'http://master.tus.com/files',
  105. protocol: 'tus',
  106. headers: 'I am a string instead of object'
  107. })
  108. .expect(400)
  109. .then((res) => expect(res.body.error).toBe('headers must be an object'))
  110. })
  111. test('invalid upload HTTP Method gets rejected', () => {
  112. return request(authServer)
  113. .post('/drive/get/README.md')
  114. .set('uppy-auth-token', token)
  115. .set('Content-Type', 'application/json')
  116. .send({
  117. endpoint: 'http://master.tus.com/files',
  118. protocol: 'tus',
  119. httpMethod: 'DELETE'
  120. })
  121. .expect(400)
  122. .then((res) => expect(res.body.error).toBe('unsupported HTTP METHOD specified'))
  123. })
  124. test('valid upload data is allowed', () => {
  125. return request(authServer)
  126. .post('/drive/get/README.md')
  127. .set('uppy-auth-token', token)
  128. .set('Content-Type', 'application/json')
  129. .send({
  130. endpoint: 'http://master.tus.com/files',
  131. protocol: 'tus',
  132. httpMethod: 'PUT',
  133. headers: {
  134. customheader: 'header value'
  135. },
  136. metadata: {
  137. mymetadata: 'matadata value'
  138. },
  139. fieldname: 'uploadField'
  140. })
  141. .expect(200)
  142. })
  143. })
  144. describe('download provdier file', () => {
  145. test('specified file gets downloaded from provider', () => {
  146. return request(authServer)
  147. .post('/drive/get/README.md')
  148. .set('uppy-auth-token', token)
  149. .set('Content-Type', 'application/json')
  150. .send({
  151. endpoint: 'http://master.tus.com/files',
  152. protocol: 'tus'
  153. })
  154. .expect(200)
  155. .then((res) => expect(res.body.token).toBeTruthy())
  156. })
  157. })
  158. describe('test authentication', () => {
  159. test('authentication callback redirects to send-token url', () => {
  160. return request(authServer)
  161. .get('/drive/callback')
  162. .expect(302)
  163. .expect((res) => {
  164. expect(res.header.location).toContain('http://localhost:3020/drive/send-token?uppyAuthToken=')
  165. })
  166. })
  167. test('the token gets sent via cookie and html', () => {
  168. // see mock ../../src/server/helpers/oauth-state above for state values
  169. return request(authServer)
  170. .get(`/dropbox/send-token?uppyAuthToken=${token}&state=state-with-newer-version`)
  171. .expect(200)
  172. .expect((res) => {
  173. const authToken = res.header['set-cookie'][0].split(';')[0].split('uppyAuthToken--dropbox=')[1]
  174. expect(authToken).toEqual(token)
  175. const body = `
  176. <!DOCTYPE html>
  177. <html>
  178. <head>
  179. <meta charset="utf-8" />
  180. <script>
  181. window.opener.postMessage(JSON.stringify({token: "${token}"}), "http://localhost:3020")
  182. window.close()
  183. </script>
  184. </head>
  185. <body></body>
  186. </html>`
  187. expect(res.text).toBe(body)
  188. })
  189. })
  190. test('the token gets to older clients without stringify', () => {
  191. // see mock ../../src/server/helpers/oauth-state above for state values
  192. return request(authServer)
  193. .get(`/drive/send-token?uppyAuthToken=${token}&state=state-with-older-version`)
  194. .expect(200)
  195. .expect((res) => {
  196. const body = `
  197. <!DOCTYPE html>
  198. <html>
  199. <head>
  200. <meta charset="utf-8" />
  201. <script>
  202. window.opener.postMessage({token: "${token}"}, "http://localhost:3020")
  203. window.close()
  204. </script>
  205. </head>
  206. <body></body>
  207. </html>`
  208. expect(res.text).toBe(body)
  209. })
  210. })
  211. test('the token gets sent to newer clients with old version style', () => {
  212. // see mock ../../src/server/helpers/oauth-state above for state values
  213. return request(authServer)
  214. .get(`/drive/send-token?uppyAuthToken=${token}&state=state-with-newer-version-old-style`)
  215. .expect(200)
  216. .expect((res) => {
  217. const body = `
  218. <!DOCTYPE html>
  219. <html>
  220. <head>
  221. <meta charset="utf-8" />
  222. <script>
  223. window.opener.postMessage(JSON.stringify({token: "${token}"}), "http://localhost:3020")
  224. window.close()
  225. </script>
  226. </head>
  227. <body></body>
  228. </html>`
  229. expect(res.text).toBe(body)
  230. })
  231. })
  232. test('logout provider', () => {
  233. return request(authServer)
  234. .get('/drive/logout/')
  235. .set('uppy-auth-token', token)
  236. .expect(200)
  237. .then((res) => expect(res.body.ok).toBe(true))
  238. })
  239. })
  240. describe('connect to provider', () => {
  241. test('connect to dropbox via grant.js endpoint', () => {
  242. return request(authServer)
  243. .get('/dropbox/connect?foo=bar')
  244. .set('uppy-auth-token', token)
  245. .expect(302)
  246. .expect('Location', `http://localhost:3020/connect/dropbox?state=${OAUTH_STATE}`)
  247. })
  248. test('connect to drive via grant.js endpoint', () => {
  249. return request(authServer)
  250. .get('/drive/connect?foo=bar')
  251. .set('uppy-auth-token', token)
  252. .expect(302)
  253. .expect('Location', `http://localhost:3020/connect/google?state=${OAUTH_STATE}`)
  254. })
  255. })
  256. describe('handle oauth redirect', () => {
  257. test('redirect to a valid uppy instance', () => {
  258. return request(authServer)
  259. .get(`/dropbox/redirect?state=${OAUTH_STATE}`)
  260. .set('uppy-auth-token', token)
  261. .expect(302)
  262. .expect('Location', `http://localhost:3020/connect/dropbox/callback?state=${OAUTH_STATE}`)
  263. })
  264. test('do not redirect to invalid uppy instances', () => {
  265. const state = 'state-with-invalid-instance-url' // see mock ../../src/server/helpers/oauth-state above
  266. return request(authServer)
  267. .get(`/dropbox/redirect?state=${state}`)
  268. .set('uppy-auth-token', token)
  269. .expect(400)
  270. })
  271. })