index.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /**
  2. * @module provider
  3. */
  4. // @ts-ignore
  5. const config = require('@purest/providers')
  6. const dropbox = require('./dropbox')
  7. const drive = require('./drive')
  8. const instagram = require('./instagram')
  9. const instagramGraph = require('./instagram/graph')
  10. const facebook = require('./facebook')
  11. const onedrive = require('./onedrive')
  12. const { getURLBuilder } = require('../helpers/utils')
  13. const logger = require('../logger')
  14. // eslint-disable-next-line
  15. const Provider = require('./Provider')
  16. /**
  17. * adds the desired provider module to the request object,
  18. * based on the providerName parameter specified
  19. *
  20. * @param {Object.<string, typeof Provider>} providers
  21. */
  22. module.exports.getProviderMiddleware = (providers) => {
  23. /**
  24. *
  25. * @param {object} req
  26. * @param {object} res
  27. * @param {function} next
  28. * @param {string} providerName
  29. */
  30. const middleware = (req, res, next, providerName) => {
  31. if (providers[providerName] && validOptions(req.companion.options)) {
  32. req.companion.provider = new providers[providerName]({ providerName, config })
  33. } else {
  34. logger.warn('invalid provider options detected. Provider will not be loaded', 'provider.middleware.invalid', req.id)
  35. }
  36. next()
  37. }
  38. return middleware
  39. }
  40. /**
  41. * @param {{server: object, providerOptions: object}} companionOptions
  42. * @return {Object.<string, typeof Provider>}
  43. */
  44. module.exports.getDefaultProviders = (companionOptions) => {
  45. const { providerOptions } = companionOptions || { providerOptions: null }
  46. // @todo: we should rename drive to googledrive or google-drive or google
  47. const providers = { dropbox, drive, facebook, onedrive }
  48. // Instagram's Graph API key is just numbers, while the old API key is hex
  49. const usesGraphAPI = () => /^\d+$/.test(providerOptions.instagram.key)
  50. if (providerOptions && providerOptions.instagram && usesGraphAPI()) {
  51. providers.instagram = instagramGraph
  52. } else {
  53. providers.instagram = instagram
  54. }
  55. return providers
  56. }
  57. /**
  58. *
  59. * @typedef {{module: typeof Provider, config: object}} CustomProvider
  60. *
  61. * @param {Object.<string, CustomProvider>} customProviders
  62. * @param {Object.<string, typeof Provider>} providers
  63. * @param {object} grantConfig
  64. */
  65. module.exports.addCustomProviders = (customProviders, providers, grantConfig) => {
  66. Object.keys(customProviders).forEach((providerName) => {
  67. providers[providerName] = customProviders[providerName].module
  68. grantConfig[providerName] = customProviders[providerName].config
  69. })
  70. }
  71. /**
  72. *
  73. * @param {{server: object, providerOptions: object}} companionOptions
  74. * @param {object} grantConfig
  75. */
  76. module.exports.addProviderOptions = (companionOptions, grantConfig) => {
  77. const { server, providerOptions } = companionOptions
  78. if (!validOptions({ server })) {
  79. logger.warn('invalid provider options detected. Providers will not be loaded', 'provider.options.invalid')
  80. return
  81. }
  82. grantConfig.defaults = {
  83. host: server.host,
  84. protocol: server.protocol,
  85. path: server.path
  86. }
  87. const { oauthDomain } = server
  88. const keys = Object.keys(providerOptions).filter((key) => key !== 'server')
  89. keys.forEach((authProvider) => {
  90. if (grantConfig[authProvider]) {
  91. // explicitly add providerOptions so users don't override other providerOptions.
  92. grantConfig[authProvider].key = providerOptions[authProvider].key
  93. grantConfig[authProvider].secret = providerOptions[authProvider].secret
  94. const { provider, name } = authNameToProvider(authProvider, companionOptions)
  95. Object.assign(grantConfig[authProvider], provider.getExtraConfig())
  96. // override grant.js redirect uri with companion's custom redirect url
  97. if (oauthDomain) {
  98. const providerName = name
  99. const redirectPath = `/${providerName}/redirect`
  100. const isExternal = !!server.implicitPath
  101. const fullRedirectPath = getURLBuilder(companionOptions)(redirectPath, isExternal, true)
  102. grantConfig[authProvider].redirect_uri = `${server.protocol}://${oauthDomain}${fullRedirectPath}`
  103. }
  104. if (server.implicitPath) {
  105. // no url builder is used for this because grant internally adds the path
  106. grantConfig[authProvider].callback = `${server.implicitPath}${grantConfig[authProvider].callback}`
  107. } else if (server.path) {
  108. grantConfig[authProvider].callback = `${server.path}${grantConfig[authProvider].callback}`
  109. }
  110. } else if (authProvider !== 's3') {
  111. logger.warn(`skipping one found unsupported provider "${authProvider}".`, 'provider.options.skip')
  112. }
  113. })
  114. }
  115. /**
  116. *
  117. * @param {string} authProvider
  118. * @param {{server: object, providerOptions: object}} options
  119. * @return {{name: string, provider: typeof Provider}}
  120. */
  121. const authNameToProvider = (authProvider, options) => {
  122. const providers = exports.getDefaultProviders(options)
  123. const providerNames = Object.keys(providers)
  124. for (const name of providerNames) {
  125. const provider = providers[name]
  126. if (provider.authProvider === authProvider) {
  127. return { name, provider }
  128. }
  129. }
  130. }
  131. /**
  132. *
  133. * @param {{server: object}} options
  134. */
  135. const validOptions = (options) => {
  136. return options.server.host && options.server.protocol
  137. }