You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

163 lines
4.6 KiB

  1. const passport = require('passport')
  2. const passportJWT = require('passport-jwt')
  3. const fs = require('fs-extra')
  4. const _ = require('lodash')
  5. const path = require('path')
  6. const jwt = require('jsonwebtoken')
  7. const moment = require('moment')
  8. const securityHelper = require('../helpers/security')
  9. /* global WIKI */
  10. module.exports = {
  11. strategies: {},
  12. guest: {
  13. cacheExpiration: moment.utc().subtract(1, 'd')
  14. },
  15. /**
  16. * Initialize the authentication module
  17. */
  18. init() {
  19. this.passport = passport
  20. passport.serializeUser(function (user, done) {
  21. done(null, user.id)
  22. })
  23. passport.deserializeUser(function (id, done) {
  24. WIKI.models.users.query().findById(id).then((user) => {
  25. if (user) {
  26. done(null, user)
  27. } else {
  28. done(new Error(WIKI.lang.t('auth:errors:usernotfound')), null)
  29. }
  30. return true
  31. }).catch((err) => {
  32. done(err, null)
  33. })
  34. })
  35. return this
  36. },
  37. /**
  38. * Load authentication strategies
  39. */
  40. async activateStrategies() {
  41. try {
  42. // Unload any active strategies
  43. WIKI.auth.strategies = {}
  44. const currentStrategies = _.keys(passport._strategies)
  45. _.pull(currentStrategies, 'session')
  46. _.forEach(currentStrategies, stg => { passport.unuse(stg) })
  47. // Load JWT
  48. passport.use('jwt', new passportJWT.Strategy({
  49. jwtFromRequest: securityHelper.extractJWT,
  50. secretOrKey: WIKI.config.certs.public,
  51. audience: WIKI.config.auth.audience,
  52. issuer: 'urn:wiki.js'
  53. }, (jwtPayload, cb) => {
  54. cb(null, jwtPayload)
  55. }))
  56. // Load enabled strategies
  57. const enabledStrategies = await WIKI.models.authentication.getStrategies()
  58. for (let idx in enabledStrategies) {
  59. const stg = enabledStrategies[idx]
  60. if (!stg.isEnabled) { continue }
  61. const strategy = require(`../modules/authentication/${stg.key}/authentication.js`)
  62. stg.config.callbackURL = `${WIKI.config.host}/login/${stg.key}/callback`
  63. strategy.init(passport, stg.config)
  64. fs.readFile(path.join(WIKI.ROOTPATH, `assets/svg/auth-icon-${strategy.key}.svg`), 'utf8').then(iconData => {
  65. strategy.icon = iconData
  66. }).catch(err => {
  67. if (err.code === 'ENOENT') {
  68. strategy.icon = '[missing icon]'
  69. } else {
  70. WIKI.logger.warn(err)
  71. }
  72. })
  73. WIKI.auth.strategies[stg.key] = strategy
  74. WIKI.logger.info(`Authentication Strategy ${stg.key}: [ OK ]`)
  75. }
  76. } catch (err) {
  77. WIKI.logger.error(`Authentication Strategy: [ FAILED ]`)
  78. WIKI.logger.error(err)
  79. }
  80. },
  81. /**
  82. * Authenticate current request
  83. *
  84. * @param {Express Request} req
  85. * @param {Express Response} res
  86. * @param {Express Next Callback} next
  87. */
  88. authenticate(req, res, next) {
  89. WIKI.auth.passport.authenticate('jwt', {session: false}, async (err, user, info) => {
  90. if (err) { return next() }
  91. // Expired but still valid within N days, just renew
  92. if (info instanceof Error && info.name === 'TokenExpiredError' && moment().subtract(14, 'days').isBefore(info.expiredAt)) {
  93. const jwtPayload = jwt.decode(securityHelper.extractJWT(req))
  94. try {
  95. const newToken = await WIKI.models.users.refreshToken(jwtPayload.id)
  96. user = newToken.user
  97. // Try headers, otherwise cookies for response
  98. if (req.get('content-type') === 'application/json') {
  99. res.set('new-jwt', newToken.token)
  100. } else {
  101. res.cookie('jwt', newToken.token, { expires: moment().add(365, 'days').toDate() })
  102. }
  103. } catch (err) {
  104. return next()
  105. }
  106. }
  107. // JWT is NOT valid, set as guest
  108. if (!user) {
  109. if (WIKI.auth.guest.cacheExpiration ) {
  110. WIKI.auth.guest = await WIKI.models.users.getGuestUser()
  111. WIKI.auth.guest.cacheExpiration = moment.utc().add(1, 'm')
  112. }
  113. req.user = WIKI.auth.guest
  114. return next()
  115. }
  116. // JWT is valid
  117. req.logIn(user, { session: false }, (err) => {
  118. if (err) { return next(err) }
  119. next()
  120. })
  121. })(req, res, next)
  122. },
  123. /**
  124. * Check if user has access to resource
  125. *
  126. * @param {User} user
  127. * @param {Array<String>} permissions
  128. * @param {String|Boolean} path
  129. */
  130. checkAccess(user, permissions = [], path = false) {
  131. // System Admin
  132. if (_.includes(user.permissions, 'manage:system')) {
  133. return true
  134. }
  135. // Check Global Permissions
  136. if (_.intersection(user.permissions, permissions).length < 1) {
  137. return false
  138. }
  139. // Check Page Rules
  140. return false
  141. }
  142. }