authentication.js 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. const _ = require('lodash')
  2. /* global WIKI */
  3. // ------------------------------------
  4. // SAML Account
  5. // ------------------------------------
  6. const SAMLStrategy = require('passport-saml').Strategy
  7. module.exports = {
  8. init (passport, conf) {
  9. const samlConfig = {
  10. callbackUrl: conf.callbackURL,
  11. entryPoint: conf.entryPoint,
  12. issuer: conf.issuer,
  13. cert: (conf.cert || '').split('|'),
  14. signatureAlgorithm: conf.signatureAlgorithm,
  15. digestAlgorithm: conf.digestAlgorithm,
  16. identifierFormat: conf.identifierFormat,
  17. wantAssertionsSigned: conf.wantAssertionsSigned,
  18. acceptedClockSkewMs: _.toSafeInteger(conf.acceptedClockSkewMs),
  19. disableRequestedAuthnContext: conf.disableRequestedAuthnContext,
  20. authnContext: (conf.authnContext || '').split('|'),
  21. racComparison: conf.racComparison,
  22. forceAuthn: conf.forceAuthn,
  23. passive: conf.passive,
  24. providerName: conf.providerName,
  25. skipRequestCompression: conf.skipRequestCompression,
  26. authnRequestBinding: conf.authnRequestBinding,
  27. passReqToCallback: true
  28. }
  29. if (!_.isEmpty(conf.audience)) {
  30. samlConfig.audience = conf.audience
  31. }
  32. if (!_.isEmpty(conf.privateKey)) {
  33. samlConfig.privateKey = conf.privateKey
  34. }
  35. if (!_.isEmpty(conf.decryptionPvk)) {
  36. samlConfig.decryptionPvk = conf.decryptionPvk
  37. }
  38. passport.use(conf.key,
  39. new SAMLStrategy(samlConfig, async (req, profile, cb) => {
  40. try {
  41. const userId = _.get(profile, [conf.mappingUID], null) || _.get(profile, 'nameID', null)
  42. if (!userId) {
  43. throw new Error('Invalid or Missing Unique ID field!')
  44. }
  45. const user = await WIKI.models.users.processProfile({
  46. providerKey: req.params.strategy,
  47. profile: {
  48. id: userId,
  49. email: _.get(profile, conf.mappingEmail, ''),
  50. displayName: _.get(profile, conf.mappingDisplayName, '???'),
  51. picture: _.get(profile, conf.mappingPicture, '')
  52. }
  53. })
  54. // map users provider groups to wiki groups with the same name, and remove any groups that don't match
  55. // Code copied from the LDAP implementation with a slight variation on the field we extract the value from
  56. // In SAML v2 groups come in profile.attributes and can be 1 string or an array of strings
  57. if (conf.mapGroups) {
  58. const maybeArrayOfGroups = _.get(profile.attributes, conf.mappingGroups)
  59. const groups = (maybeArrayOfGroups && !_.isArray(maybeArrayOfGroups)) ? [maybeArrayOfGroups] : maybeArrayOfGroups
  60. if (groups && _.isArray(groups)) {
  61. const currentGroups = (await user.$relatedQuery('groups').select('groups.id')).map(g => g.id)
  62. const expectedGroups = Object.values(WIKI.auth.groups).filter(g => groups.includes(g.name)).map(g => g.id)
  63. for (const groupId of _.difference(expectedGroups, currentGroups)) {
  64. await user.$relatedQuery('groups').relate(groupId)
  65. }
  66. for (const groupId of _.difference(currentGroups, expectedGroups)) {
  67. await user.$relatedQuery('groups').unrelate().where('groupId', groupId)
  68. }
  69. }
  70. }
  71. cb(null, user)
  72. } catch (err) {
  73. cb(err, null)
  74. }
  75. })
  76. )
  77. }
  78. }