user.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. 'use strict';
  2. const _ = require('lodash');
  3. const error = require('../lib/error');
  4. const userModel = require('../models/user');
  5. const authModel = require('../models/auth');
  6. const gravatar = require('gravatar');
  7. const internalToken = require('./token');
  8. function omissions () {
  9. return ['is_deleted'];
  10. }
  11. const internalUser = {
  12. /**
  13. * @param {Access} access
  14. * @param {Object} data
  15. * @returns {Promise}
  16. */
  17. create: (access, data) => {
  18. let auth = data.auth || null;
  19. delete data.auth;
  20. data.avatar = data.avatar || '';
  21. data.roles = data.roles || [];
  22. if (typeof data.is_disabled !== 'undefined') {
  23. data.is_disabled = data.is_disabled ? 1 : 0;
  24. }
  25. return access.can('users:create', data)
  26. .then(() => {
  27. data.avatar = gravatar.url(data.email, {default: 'mm'});
  28. return userModel
  29. .query()
  30. .omit(omissions())
  31. .insertAndFetch(data);
  32. })
  33. .then(user => {
  34. if (auth) {
  35. return authModel
  36. .query()
  37. .insert({
  38. user_id: user.id,
  39. type: auth.type,
  40. secret: auth.secret,
  41. meta: {}
  42. })
  43. .then(() => {
  44. return user;
  45. });
  46. } else {
  47. return user;
  48. }
  49. })
  50. .then(user => {
  51. return internalUser.get(access, {id: user.id});
  52. });
  53. },
  54. /**
  55. * @param {Access} access
  56. * @param {Object} data
  57. * @param {Integer} data.id
  58. * @param {String} [data.email]
  59. * @param {String} [data.name]
  60. * @return {Promise}
  61. */
  62. update: (access, data) => {
  63. if (typeof data.is_disabled !== 'undefined') {
  64. data.is_disabled = data.is_disabled ? 1 : 0;
  65. }
  66. return access.can('users:update', data.id)
  67. .then(() => {
  68. // Make sure that the user being updated doesn't change their email to another user that is already using it
  69. // 1. get user we want to update
  70. return internalUser.get(access, {id: data.id})
  71. .then(user => {
  72. // 2. if email is to be changed, find other users with that email
  73. if (typeof data.email !== 'undefined') {
  74. data.email = data.email.toLowerCase().trim();
  75. if (user.email !== data.email) {
  76. return internalUser.isEmailAvailable(data.email, data.id)
  77. .then(available => {
  78. if (!available) {
  79. throw new error.ValidationError('Email address already in use - ' + data.email);
  80. }
  81. return user;
  82. });
  83. }
  84. }
  85. // No change to email:
  86. return user;
  87. });
  88. })
  89. .then(user => {
  90. if (user.id !== data.id) {
  91. // Sanity check that something crazy hasn't happened
  92. throw new error.InternalValidationError('User could not be updated, IDs do not match: ' + user.id + ' !== ' + data.id);
  93. }
  94. data.avatar = gravatar.url(data.email || user.email, {default: 'mm'});
  95. return userModel
  96. .query()
  97. .omit(omissions())
  98. .patchAndFetchById(user.id, data)
  99. .then(saved_user => {
  100. return _.omit(saved_user, omissions());
  101. });
  102. })
  103. .then(() => {
  104. return internalUser.get(access, {id: data.id});
  105. });
  106. },
  107. /**
  108. * @param {Access} access
  109. * @param {Object} [data]
  110. * @param {Integer} [data.id] Defaults to the token user
  111. * @param {Array} [data.expand]
  112. * @param {Array} [data.omit]
  113. * @return {Promise}
  114. */
  115. get: (access, data) => {
  116. if (typeof data === 'undefined') {
  117. data = {};
  118. }
  119. if (typeof data.id === 'undefined' || !data.id) {
  120. data.id = access.token.get('attrs').id;
  121. }
  122. return access.can('users:get', data.id)
  123. .then(() => {
  124. let query = userModel
  125. .query()
  126. .where('is_deleted', 0)
  127. .andWhere('id', data.id)
  128. .first();
  129. // Custom omissions
  130. if (typeof data.omit !== 'undefined' && data.omit !== null) {
  131. query.omit(data.omit);
  132. }
  133. if (typeof data.expand !== 'undefined' && data.expand !== null) {
  134. query.eager('[' + data.expand.join(', ') + ']');
  135. }
  136. return query;
  137. })
  138. .then(row => {
  139. if (row) {
  140. return _.omit(row, omissions());
  141. } else {
  142. throw new error.ItemNotFoundError(data.id);
  143. }
  144. });
  145. },
  146. /**
  147. * Checks if an email address is available, but if a user_id is supplied, it will ignore checking
  148. * against that user.
  149. *
  150. * @param email
  151. * @param user_id
  152. */
  153. isEmailAvailable: (email, user_id) => {
  154. let query = userModel
  155. .query()
  156. .where('email', '=', email.toLowerCase().trim())
  157. .where('is_deleted', 0)
  158. .first();
  159. if (typeof user_id !== 'undefined') {
  160. query.where('id', '!=', user_id);
  161. }
  162. return query
  163. .then(user => {
  164. return !user;
  165. });
  166. },
  167. /**
  168. * @param {Access} access
  169. * @param {Object} data
  170. * @param {Integer} data.id
  171. * @param {String} [data.reason]
  172. * @returns {Promise}
  173. */
  174. delete: (access, data) => {
  175. return access.can('users:delete', data.id)
  176. .then(() => {
  177. return internalUser.get(access, {id: data.id});
  178. })
  179. .then(user => {
  180. if (!user) {
  181. throw new error.ItemNotFoundError(data.id);
  182. }
  183. // Make sure user can't delete themselves
  184. if (user.id === access.token.get('attrs').id) {
  185. throw new error.PermissionError('You cannot delete yourself.');
  186. }
  187. return userModel
  188. .query()
  189. .where('id', user.id)
  190. .patch({
  191. is_deleted: 1
  192. });
  193. })
  194. .then(() => {
  195. return true;
  196. });
  197. },
  198. /**
  199. * This will only count the users
  200. *
  201. * @param {Access} access
  202. * @param {String} [search_query]
  203. * @returns {*}
  204. */
  205. getCount: (access, search_query) => {
  206. return access.can('users:list')
  207. .then(() => {
  208. let query = userModel
  209. .query()
  210. .count('id as count')
  211. .where('is_deleted', 0)
  212. .first();
  213. // Query is used for searching
  214. if (typeof search_query === 'string') {
  215. query.where(function () {
  216. this.where('user.name', 'like', '%' + search_query + '%')
  217. .orWhere('user.email', 'like', '%' + search_query + '%');
  218. });
  219. }
  220. return query;
  221. })
  222. .then(row => {
  223. return parseInt(row.count, 10);
  224. });
  225. },
  226. /**
  227. * All users
  228. *
  229. * @param {Access} access
  230. * @param {Array} [expand]
  231. * @param {String} [search_query]
  232. * @returns {Promise}
  233. */
  234. getAll: (access, expand, search_query) => {
  235. return access.can('users:list')
  236. .then(() => {
  237. let query = userModel
  238. .query()
  239. .where('is_deleted', 0)
  240. .groupBy('id')
  241. .omit(['is_deleted'])
  242. .orderBy('name', 'ASC');
  243. // Query is used for searching
  244. if (typeof search_query === 'string') {
  245. query.where(function () {
  246. this.where('name', 'like', '%' + search_query + '%')
  247. .orWhere('email', 'like', '%' + search_query + '%');
  248. });
  249. }
  250. if (typeof expand !== 'undefined' && expand !== null) {
  251. query.eager('[' + expand.join(', ') + ']');
  252. }
  253. return query;
  254. });
  255. },
  256. /**
  257. * @param {Access} access
  258. * @param {Integer} [id_requested]
  259. * @returns {[String]}
  260. */
  261. getUserOmisionsByAccess: (access, id_requested) => {
  262. let response = []; // Admin response
  263. if (!access.token.hasScope('admin') && access.token.get('attrs').id !== id_requested) {
  264. response = ['roles', 'is_deleted']; // Restricted response
  265. }
  266. return response;
  267. },
  268. /**
  269. * @param {Access} access
  270. * @param {Object} data
  271. * @param {Integer} data.id
  272. * @param {String} data.type
  273. * @param {String} data.secret
  274. * @return {Promise}
  275. */
  276. setPassword: (access, data) => {
  277. return access.can('users:password', data.id)
  278. .then(() => {
  279. return internalUser.get(access, {id: data.id});
  280. })
  281. .then(user => {
  282. if (user.id !== data.id) {
  283. // Sanity check that something crazy hasn't happened
  284. throw new error.InternalValidationError('User could not be updated, IDs do not match: ' + user.id + ' !== ' + data.id);
  285. }
  286. if (user.id === access.token.get('attrs').id) {
  287. // they're setting their own password. Make sure their current password is correct
  288. if (typeof data.current === 'undefined' || !data.current) {
  289. throw new error.ValidationError('Current password was not supplied');
  290. }
  291. return internalToken.getTokenFromEmail({
  292. identity: user.email,
  293. secret: data.current
  294. })
  295. .then(() => {
  296. return user;
  297. });
  298. }
  299. return user;
  300. })
  301. .then(user => {
  302. // Get auth, patch if it exists
  303. return authModel
  304. .query()
  305. .where('user_id', user.id)
  306. .andWhere('type', data.type)
  307. .first()
  308. .then(existing_auth => {
  309. if (existing_auth) {
  310. // patch
  311. return authModel
  312. .query()
  313. .where('user_id', user.id)
  314. .andWhere('type', data.type)
  315. .patch({
  316. type: data.type, // This is required for the model to encrypt on save
  317. secret: data.secret
  318. });
  319. } else {
  320. // insert
  321. return authModel
  322. .query()
  323. .insert({
  324. user_id: user.id,
  325. type: data.type,
  326. secret: data.secret,
  327. meta: {}
  328. });
  329. }
  330. });
  331. })
  332. .then(() => {
  333. return true;
  334. });
  335. },
  336. /**
  337. * @param {Access} access
  338. * @param {Object} data
  339. * @param {Integer} data.id
  340. */
  341. loginAs: (access, data) => {
  342. return access.can('users:loginas', data.id)
  343. .then(() => {
  344. return internalUser.get(access, data);
  345. })
  346. .then(user => {
  347. return internalToken.getTokenFromUser(user);
  348. });
  349. }
  350. };
  351. module.exports = internalUser;