external-account.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. // disable no-return-await for model functions
  2. /* eslint-disable no-return-await */
  3. import { NullUsernameToBeRegisteredError } from '~/server/models/errors';
  4. const debug = require('debug')('growi:models:external-account');
  5. const mongoose = require('mongoose');
  6. const mongoosePaginate = require('mongoose-paginate-v2');
  7. const uniqueValidator = require('mongoose-unique-validator');
  8. const ObjectId = mongoose.Schema.Types.ObjectId;
  9. /*
  10. * define schema
  11. */
  12. const schema = new mongoose.Schema({
  13. providerType: { type: String, required: true },
  14. accountId: { type: String, required: true },
  15. user: { type: ObjectId, ref: 'User', required: true },
  16. }, {
  17. timestamps: { createdAt: true, updatedAt: false },
  18. });
  19. // compound index
  20. schema.index({ providerType: 1, accountId: 1 }, { unique: true });
  21. // apply plugins
  22. schema.plugin(mongoosePaginate);
  23. schema.plugin(uniqueValidator);
  24. /**
  25. * The Exception class thrown when User.username is duplicated when creating user
  26. *
  27. * @class DuplicatedUsernameException
  28. */
  29. class DuplicatedUsernameException {
  30. constructor(message, user) {
  31. this.name = this.constructor.name;
  32. this.message = message;
  33. this.user = user;
  34. }
  35. }
  36. /**
  37. * ExternalAccount Class
  38. *
  39. * @class ExternalAccount
  40. */
  41. class ExternalAccount {
  42. /**
  43. * limit items num for pagination
  44. *
  45. * @readonly
  46. * @static
  47. * @memberof ExternalAccount
  48. */
  49. static get DEFAULT_LIMIT() {
  50. return 50;
  51. }
  52. static set crowi(crowi) {
  53. this._crowi = crowi;
  54. }
  55. static get crowi() {
  56. return this._crowi;
  57. }
  58. /**
  59. * get the populated user entity
  60. *
  61. * @returns Promise<User>
  62. * @memberof ExternalAccount
  63. */
  64. getPopulatedUser() {
  65. return this.populate('user')
  66. .then((account) => {
  67. return account.user;
  68. });
  69. }
  70. /**
  71. * find an account or register if not found
  72. *
  73. * @static
  74. * @param {string} providerType
  75. * @param {string} accountId
  76. * @param {object} usernameToBeRegistered the username of User entity that will be created when accountId is not found
  77. * @param {object} nameToBeRegistered the name of User entity that will be created when accountId is not found
  78. * @param {object} mailToBeRegistered the mail of User entity that will be created when accountId is not found
  79. * @param {boolean} isSameUsernameTreatedAsIdenticalUser
  80. * @param {boolean} isSameEmailTreatedAsIdenticalUser
  81. * @returns {Promise<ExternalAccount>}
  82. * @memberof ExternalAccount
  83. */
  84. static findOrRegister(providerType, accountId,
  85. usernameToBeRegistered, nameToBeRegistered, mailToBeRegistered,
  86. isSameUsernameTreatedAsIdenticalUser, isSameEmailTreatedAsIdenticalUser) {
  87. //
  88. return this.findOne({ providerType, accountId })
  89. .then((account) => {
  90. // ExternalAccount is found
  91. if (account != null) {
  92. debug(`ExternalAccount '${accountId}' is found `, account);
  93. return account;
  94. }
  95. if (usernameToBeRegistered == null) {
  96. throw new NullUsernameToBeRegisteredError('username_should_not_be_null');
  97. }
  98. const User = ExternalAccount.crowi.model('User');
  99. let promise = User.findOne({ username: usernameToBeRegistered });
  100. if (isSameUsernameTreatedAsIdenticalUser && isSameEmailTreatedAsIdenticalUser) {
  101. promise = promise
  102. .then((user) => {
  103. if (user == null) { return User.findOne({ email: mailToBeRegistered }) }
  104. return user;
  105. });
  106. }
  107. else if (isSameEmailTreatedAsIdenticalUser) {
  108. promise = User.findOne({ email: mailToBeRegistered });
  109. }
  110. return promise
  111. .then((user) => {
  112. // when the User that have the same `username` exists
  113. if (user != null) {
  114. throw new DuplicatedUsernameException(`User '${usernameToBeRegistered}' already exists`, user);
  115. }
  116. if (nameToBeRegistered == null) {
  117. // eslint-disable-next-line no-param-reassign
  118. nameToBeRegistered = '';
  119. }
  120. // create a new User with STATUS_ACTIVE
  121. debug(`ExternalAccount '${accountId}' is not found, it is going to be registered.`);
  122. return User.createUser(nameToBeRegistered, usernameToBeRegistered, mailToBeRegistered, undefined, undefined, User.STATUS_ACTIVE);
  123. })
  124. .then((newUser) => {
  125. return this.associate(providerType, accountId, newUser);
  126. });
  127. });
  128. }
  129. /**
  130. * Create ExternalAccount document and associate to existing User
  131. *
  132. * @param {string} providerType
  133. * @param {string} accountId
  134. * @param {object} user
  135. */
  136. static associate(providerType, accountId, user) {
  137. return this.create({ providerType, accountId, user: user._id });
  138. }
  139. /**
  140. * find all entities with pagination
  141. *
  142. * @see https://github.com/edwardhotchkiss/mongoose-paginate
  143. *
  144. * @static
  145. * @param {any} opts mongoose-paginate options object
  146. * @returns {Promise<any>} mongoose-paginate result object
  147. * @memberof ExternalAccount
  148. */
  149. static findAllWithPagination(opts) {
  150. const query = {};
  151. const options = Object.assign({ populate: 'user' }, opts);
  152. if (options.sort == null) {
  153. options.sort = { accountId: 1, createdAt: 1 };
  154. }
  155. if (options.limit == null) {
  156. options.limit = ExternalAccount.DEFAULT_LIMIT;
  157. }
  158. return this.paginate(query, options);
  159. }
  160. }
  161. module.exports = function(crowi) {
  162. ExternalAccount.crowi = crowi;
  163. schema.loadClass(ExternalAccount);
  164. return mongoose.model('ExternalAccount', schema);
  165. };