Adapt authoring tool UI documentation

v1.0.0-rc.4

adapt-authoring-users/lib/UsersModule.js

  1. import AbstractApiModule from 'adapt-authoring-api'
  2. /**
  3. * Module which handles user management
  4. * @memberof users
  5. * @extends {AbstractApiModule}
  6. */
  7. class UsersModule extends AbstractApiModule {
  8. /** @override */
  9. async setValues () {
  10. /** @ignore */ this.root = 'users'
  11. /** @ignore */ this.schemaName = 'user'
  12. /** @ignore */ this.collectionName = 'users'
  13. this.useDefaultRouteConfig()
  14. // remove POST / route
  15. delete this.routes.find(r => r.route === '/').handlers.post
  16. const desc = method => `This endpoint is shorthand for \`${method}\` \`/api/${this.root}/:_id\`, see the documentation for that endpoint for more details`
  17. this.routes = [{
  18. route: '/me',
  19. modifiers: ['put', 'patch'],
  20. handlers: { get: this.requestHandler(), put: this.requestHandler(), patch: this.requestHandler() },
  21. permissions: { get: ['read:me'], put: ['write:me'], patch: ['write:me'] },
  22. meta: {
  23. get: { summary: 'Retrieve your own user data', description: desc('GET') },
  24. put: { summary: 'Replace your own user data', description: desc('PUT') },
  25. patch: { summary: 'Update your own user data', description: desc('PATCH') }
  26. }
  27. }, ...this.routes]
  28. }
  29. /**
  30. * Initialises the module
  31. * @return {Promise}
  32. */
  33. async init () {
  34. await super.init()
  35. const [mongodb, server] = await this.app.waitForModule('mongodb', 'server')
  36. await mongodb.setIndex(this.collectionName, 'email', { unique: true })
  37. server.api.addHandlerMiddleware(this.updateAccess.bind(this))
  38. this.requestHook.tap(this.onRequest.bind(this))
  39. if (this.getConfig('forceLowerCaseEmail')) {
  40. this.preInsertHook.tap(this.forceLowerCaseEmail)
  41. this.preUpdateHook.tap(this.forceLowerCaseEmail)
  42. }
  43. }
  44. forceLowerCaseEmail (data) {
  45. if (data.email) data.email = data.email.toLowerCase()
  46. }
  47. /** @override */
  48. async processRequestMiddleware (req, res, next) {
  49. super.processRequestMiddleware(req, res, () => {
  50. req.apiData.schemaName = req.auth.userSchemaName
  51. next()
  52. })
  53. }
  54. /**
  55. * Updates the user access timestamp
  56. * @param {external:ExpressRequest} req
  57. * @param {external:ExpressResponse} res
  58. * @param {Function} next
  59. */
  60. updateAccess (req, res, next) {
  61. const _id = req.auth?.user?._id
  62. if (_id) { // note we only log any errors, as it's not necessarily a problem
  63. this.update({ _id }, { lastAccess: new Date().toISOString() })
  64. .catch(e => this.log('warn', `Failed to update user lastAccess, ${e}`))
  65. }
  66. next()
  67. }
  68. /**
  69. * Adds the current user _id to an incoming request to API
  70. * @param {external:ExpressRequest} req
  71. */
  72. async onRequest (req) {
  73. if (req.apiData.config.route === '/me') {
  74. req.params._id = req.apiData.query._id = req.auth.user._id
  75. // users shouldn't be able to disable themselves
  76. if (req.apiData.data.isEnabled) delete req.apiData.data.isEnabled
  77. }
  78. if (req.method === 'DELETE' && (req.apiData.query._id === req.auth.user._id)) {
  79. throw this.app.errors.USER_SELF_DELETE_ILLEGAL
  80. .setData({ id: req.user._id })
  81. }
  82. }
  83. /** @override */
  84. async insert (data, options, mongoOptions) {
  85. try {
  86. return await super.insert(data, options, mongoOptions)
  87. } catch (e) {
  88. if (e.code === this.app.errors.MONGO_DUPL_INDEX) throw this.app.errors.DUPL_USER.setData({ email: data.email })
  89. throw e
  90. }
  91. }
  92. /** @override */
  93. async find (query, options = {}, mongoOptions = {}) {
  94. query.email = this.getConfig('forceLowerCaseEmail') ? query.email?.toLowerCase() : undefined
  95. return super.find(query, options, mongoOptions)
  96. }
  97. }
  98. export default UsersModule