index.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /**
  2. * @module provider
  3. */
  4. // @ts-ignore
  5. const config = require('@purest/providers')
  6. const dropbox = require('./dropbox')
  7. const box = require('./box')
  8. const drive = require('./drive')
  9. const instagram = require('./instagram/graph')
  10. const facebook = require('./facebook')
  11. const onedrive = require('./onedrive')
  12. const unsplash = require('./unsplash')
  13. const zoom = require('./zoom')
  14. const { getURLBuilder } = require('../helpers/utils')
  15. const logger = require('../logger')
  16. const { getCredentialsResolver } = require('./credentials')
  17. // eslint-disable-next-line
  18. const Provider = require('./Provider')
  19. // eslint-disable-next-line
  20. const SearchProvider = require('./SearchProvider')
  21. // leave here for now until Purest Providers gets updated with Zoom provider
  22. config.zoom = {
  23. 'https://zoom.us/': {
  24. __domain: {
  25. auth: {
  26. auth: { bearer: '[0]' }
  27. }
  28. },
  29. '[version]/{endpoint}': {
  30. __path: {
  31. alias: '__default',
  32. version: 'v2'
  33. }
  34. },
  35. 'oauth/revoke': {
  36. __path: {
  37. alias: 'logout',
  38. auth: {
  39. auth: { basic: '[0]' }
  40. }
  41. }
  42. }
  43. }
  44. }
  45. /**
  46. * adds the desired provider module to the request object,
  47. * based on the providerName parameter specified
  48. *
  49. * @param {Object.<string, (typeof Provider) | typeof SearchProvider>} providers
  50. * @param {boolean=} needsProviderCredentials
  51. */
  52. module.exports.getProviderMiddleware = (providers, needsProviderCredentials) => {
  53. /**
  54. *
  55. * @param {object} req
  56. * @param {object} res
  57. * @param {function} next
  58. * @param {string} providerName
  59. */
  60. const middleware = (req, res, next, providerName) => {
  61. if (providers[providerName] && validOptions(req.companion.options)) {
  62. req.companion.provider = new providers[providerName]({ providerName, config })
  63. if (needsProviderCredentials) {
  64. req.companion.getProviderCredentials = getCredentialsResolver(providerName, req.companion.options, req)
  65. }
  66. } else {
  67. logger.warn('invalid provider options detected. Provider will not be loaded', 'provider.middleware.invalid', req.id)
  68. }
  69. next()
  70. }
  71. return middleware
  72. }
  73. /**
  74. * @param {{server: object, providerOptions: object}} companionOptions
  75. * @return {Object.<string, typeof Provider>}
  76. */
  77. module.exports.getDefaultProviders = (companionOptions) => {
  78. // @todo: we should rename drive to googledrive or google-drive or google
  79. const providers = { dropbox, box, drive, facebook, onedrive, zoom, instagram }
  80. return providers
  81. }
  82. /**
  83. * @return {Object.<string, typeof SearchProvider>}
  84. */
  85. module.exports.getSearchProviders = () => {
  86. return { unsplash }
  87. }
  88. /**
  89. *
  90. * @typedef {{module: typeof Provider, config: object}} CustomProvider
  91. *
  92. * @param {Object.<string, CustomProvider>} customProviders
  93. * @param {Object.<string, typeof Provider>} providers
  94. * @param {object} grantConfig
  95. */
  96. module.exports.addCustomProviders = (customProviders, providers, grantConfig) => {
  97. Object.keys(customProviders).forEach((providerName) => {
  98. providers[providerName] = customProviders[providerName].module
  99. const providerConfig = Object.assign({}, customProviders[providerName].config)
  100. // todo: consider setting these options from a universal point also used
  101. // by official providers. It'll prevent these from getting left out if the
  102. // requirement changes.
  103. providerConfig.callback = `/${providerName}/callback`
  104. providerConfig.transport = 'session'
  105. grantConfig[providerName] = providerConfig
  106. })
  107. }
  108. /**
  109. *
  110. * @param {{server: object, providerOptions: object}} companionOptions
  111. * @param {object} grantConfig
  112. */
  113. module.exports.addProviderOptions = (companionOptions, grantConfig) => {
  114. const { server, providerOptions } = companionOptions
  115. if (!validOptions({ server })) {
  116. logger.warn('invalid provider options detected. Providers will not be loaded', 'provider.options.invalid')
  117. return
  118. }
  119. grantConfig.defaults = {
  120. host: server.host,
  121. protocol: server.protocol,
  122. path: server.path
  123. }
  124. const { oauthDomain } = server
  125. const keys = Object.keys(providerOptions).filter((key) => key !== 'server')
  126. keys.forEach((providerName) => {
  127. const authProvider = providerNameToAuthName(providerName, companionOptions)
  128. if (authProvider && grantConfig[authProvider]) {
  129. // explicitly add providerOptions so users don't override other providerOptions.
  130. grantConfig[authProvider].key = providerOptions[providerName].key
  131. grantConfig[authProvider].secret = providerOptions[providerName].secret
  132. if (providerOptions[providerName].credentialsURL) {
  133. grantConfig[authProvider].dynamic = ['key', 'secret', 'redirect_uri']
  134. }
  135. const provider = exports.getDefaultProviders(companionOptions)[providerName]
  136. Object.assign(grantConfig[authProvider], provider.getExtraConfig())
  137. // override grant.js redirect uri with companion's custom redirect url
  138. const isExternal = !!server.implicitPath
  139. const redirectPath = `/${providerName}/redirect`
  140. grantConfig[authProvider].redirect_uri = getURLBuilder(companionOptions)(redirectPath, isExternal)
  141. if (oauthDomain) {
  142. const fullRedirectPath = getURLBuilder(companionOptions)(redirectPath, isExternal, true)
  143. grantConfig[authProvider].redirect_uri = `${server.protocol}://${oauthDomain}${fullRedirectPath}`
  144. }
  145. if (server.implicitPath) {
  146. // no url builder is used for this because grant internally adds the path
  147. grantConfig[authProvider].callback = `${server.implicitPath}${grantConfig[authProvider].callback}`
  148. } else if (server.path) {
  149. grantConfig[authProvider].callback = `${server.path}${grantConfig[authProvider].callback}`
  150. }
  151. } else if (!['s3', 'searchProviders'].includes(providerName)) {
  152. logger.warn(`skipping one found unsupported provider "${providerName}".`, 'provider.options.skip')
  153. }
  154. })
  155. }
  156. /**
  157. *
  158. * @param {string} name of the provider
  159. * @param {{server: object, providerOptions: object}} options
  160. * @return {string} the authProvider for this provider
  161. */
  162. const providerNameToAuthName = (name, options) => {
  163. const providers = exports.getDefaultProviders(options)
  164. return (providers[name] || {}).authProvider
  165. }
  166. /**
  167. *
  168. * @param {{server: object}} options
  169. */
  170. const validOptions = (options) => {
  171. return options.server.host && options.server.protocol
  172. }