server/models/user.js

  1. const _ = require('lodash');
  2. const bcrypt = require('bcryptjs');
  3. const Abstract = require('./abstract');
  4. const config = require('../../config');
  5. const db = require('../db');
  6. const jwt = require('../utils/jwt');
  7. const proto = Abstract.prototype;
  8. /**
  9. * A user of the BioPocket platform.
  10. *
  11. * ## Database columns
  12. *
  13. * * **id** (`bigint`) - Internal ID (used for joins).
  14. * * **api_id** (`uuid`) - External ID (used in the API).
  15. * * **email** (`string`) - E-mail address.
  16. * * **password_hash** (`string`) - Bcrypt hash of the user's password.
  17. * * **active** (`boolean`) - Indicates whether the user can use the platform or has been deactivated by an administrator.
  18. * * **roles** (`string[]`) - Roles of the user (used for authorization).
  19. * * **created_at** (`datetime`) - Time at which the user was created.
  20. * * **updated_at** (`datetime`) - Time at which the user was last modified (equal to the creation date if never modified).
  21. *
  22. * ## Virtual properties
  23. *
  24. * * **password** (`string`) - Setting this property generates a new bcrypt hash and updates the `password_hash` column.
  25. *
  26. * @class
  27. * @extends Abstract
  28. * @see http://bookshelfjs.org
  29. */
  30. const User = Abstract.extend({
  31. tableName: 'users',
  32. timestamps: true,
  33. virtuals: _.merge({
  34. password: {
  35. get: function() {
  36. return this._password;
  37. },
  38. set: function(password) {
  39. this._password = password;
  40. if (_.isString(password) && password.length) {
  41. const salt = bcrypt.genSaltSync(config.bcryptCost);
  42. this.set('password_hash', bcrypt.hashSync(password, salt));
  43. } else {
  44. this.unset('password_hash');
  45. }
  46. }
  47. }
  48. }, proto.virtuals),
  49. /**
  50. * Returns a JWT that can be used to authenticate as this user.
  51. *
  52. * @instance
  53. * @memberof User
  54. * @param {object} properties - JWT properties, passed to `generateToken` in the `utils/jwt` module.
  55. * @returns {string} A JWT.
  56. */
  57. generateJwt: function(properties) {
  58. return jwt.generateToken(_.extend({
  59. sub: this.get('api_id')
  60. }, properties));
  61. },
  62. /**
  63. * Indicates whether this user has the specified password or not.
  64. *
  65. * **WARNING:** this method is slow and blocking, as it computes a bcrypt
  66. * hash synchronously. Do not overuse it.
  67. *
  68. * @instance
  69. * @memberof User
  70. * @param {string} password - The password to check.
  71. * @returns {boolean} True if the user's password is the same as the specified one.
  72. */
  73. hasPassword: function(password) {
  74. return !!password && bcrypt.compareSync(password, this.get('password_hash'));
  75. },
  76. /**
  77. * Indicates whether this user has the specified role.
  78. *
  79. * **WARNING:** this methods always returns true if the user has the role,
  80. * even if the user is inactive. It is not sufficient to determine whether
  81. * the user is currently authorized to perform the role.
  82. *
  83. * @instance
  84. * @memberof User
  85. * @param {string} role - The role to check.
  86. * @returns {boolean} True if the specified role is among the user's assigned roles.
  87. */
  88. hasRole: function(role) {
  89. return _.includes(this.get('roles'), role);
  90. },
  91. /**
  92. * Indicates whether this user is active. Users may be deactivated by administrators.
  93. *
  94. * @instance
  95. * @memberof User
  96. * @returns {boolean} True if this user is active.
  97. */
  98. isActive: function() {
  99. return !!this.get('active');
  100. }
  101. });
  102. module.exports = db.bookshelf.model('User', User);