user.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. const _ = require('lodash');
  2. const error = require('../lib/error');
  3. const userModel = require('../models/user');
  4. const userPermissionModel = require('../models/user_permission');
  5. const authModel = require('../models/auth');
  6. const gravatar = require('gravatar');
  7. const internalToken = require('./token');
  8. const internalAuditLog = require('./audit-log');
  9. function omissions () {
  10. return ['is_deleted'];
  11. }
  12. const internalUser = {
  13. /**
  14. * @param {Access} access
  15. * @param {Object} data
  16. * @returns {Promise}
  17. */
  18. create: (access, data) => {
  19. let auth = data.auth || null;
  20. delete data.auth;
  21. data.avatar = data.avatar || '';
  22. data.roles = data.roles || [];
  23. if (typeof data.is_disabled !== 'undefined') {
  24. data.is_disabled = data.is_disabled ? 1 : 0;
  25. }
  26. return access.can('users:create', data)
  27. .then(() => {
  28. data.avatar = gravatar.url(data.email, {default: 'mm'});
  29. return userModel
  30. .query()
  31. .omit(omissions())
  32. .insertAndFetch(data);
  33. })
  34. .then((user) => {
  35. if (auth) {
  36. return authModel
  37. .query()
  38. .insert({
  39. user_id: user.id,
  40. type: auth.type,
  41. secret: auth.secret,
  42. meta: {}
  43. })
  44. .then(() => {
  45. return user;
  46. });
  47. } else {
  48. return user;
  49. }
  50. })
  51. .then((user) => {
  52. // Create permissions row as well
  53. let is_admin = data.roles.indexOf('admin') !== -1;
  54. return userPermissionModel
  55. .query()
  56. .insert({
  57. user_id: user.id,
  58. visibility: is_admin ? 'all' : 'user',
  59. proxy_hosts: 'manage',
  60. redirection_hosts: 'manage',
  61. dead_hosts: 'manage',
  62. streams: 'manage',
  63. access_lists: 'manage',
  64. certificates: 'manage'
  65. })
  66. .then(() => {
  67. return internalUser.get(access, {id: user.id, expand: ['permissions']});
  68. });
  69. })
  70. .then((user) => {
  71. // Add to audit log
  72. return internalAuditLog.add(access, {
  73. action: 'created',
  74. object_type: 'user',
  75. object_id: user.id,
  76. meta: user
  77. })
  78. .then(() => {
  79. return user;
  80. });
  81. });
  82. },
  83. /**
  84. * @param {Access} access
  85. * @param {Object} data
  86. * @param {Integer} data.id
  87. * @param {String} [data.email]
  88. * @param {String} [data.name]
  89. * @return {Promise}
  90. */
  91. update: (access, data) => {
  92. if (typeof data.is_disabled !== 'undefined') {
  93. data.is_disabled = data.is_disabled ? 1 : 0;
  94. }
  95. return access.can('users:update', data.id)
  96. .then(() => {
  97. // Make sure that the user being updated doesn't change their email to another user that is already using it
  98. // 1. get user we want to update
  99. return internalUser.get(access, {id: data.id})
  100. .then((user) => {
  101. // 2. if email is to be changed, find other users with that email
  102. if (typeof data.email !== 'undefined') {
  103. data.email = data.email.toLowerCase().trim();
  104. if (user.email !== data.email) {
  105. return internalUser.isEmailAvailable(data.email, data.id)
  106. .then((available) => {
  107. if (!available) {
  108. throw new error.ValidationError('Email address already in use - ' + data.email);
  109. }
  110. return user;
  111. });
  112. }
  113. }
  114. // No change to email:
  115. return user;
  116. });
  117. })
  118. .then((user) => {
  119. if (user.id !== data.id) {
  120. // Sanity check that something crazy hasn't happened
  121. throw new error.InternalValidationError('User could not be updated, IDs do not match: ' + user.id + ' !== ' + data.id);
  122. }
  123. data.avatar = gravatar.url(data.email || user.email, {default: 'mm'});
  124. return userModel
  125. .query()
  126. .omit(omissions())
  127. .patchAndFetchById(user.id, data)
  128. .then((saved_user) => {
  129. return _.omit(saved_user, omissions());
  130. });
  131. })
  132. .then(() => {
  133. return internalUser.get(access, {id: data.id});
  134. })
  135. .then((user) => {
  136. // Add to audit log
  137. return internalAuditLog.add(access, {
  138. action: 'updated',
  139. object_type: 'user',
  140. object_id: user.id,
  141. meta: data
  142. })
  143. .then(() => {
  144. return user;
  145. });
  146. });
  147. },
  148. /**
  149. * @param {Access} access
  150. * @param {Object} [data]
  151. * @param {Integer} [data.id] Defaults to the token user
  152. * @param {Array} [data.expand]
  153. * @param {Array} [data.omit]
  154. * @return {Promise}
  155. */
  156. get: (access, data) => {
  157. if (typeof data === 'undefined') {
  158. data = {};
  159. }
  160. if (typeof data.id === 'undefined' || !data.id) {
  161. data.id = access.token.getUserId(0);
  162. }
  163. return access.can('users:get', data.id)
  164. .then(() => {
  165. let query = userModel
  166. .query()
  167. .where('is_deleted', 0)
  168. .andWhere('id', data.id)
  169. .allowEager('[permissions]')
  170. .first();
  171. // Custom omissions
  172. if (typeof data.omit !== 'undefined' && data.omit !== null) {
  173. query.omit(data.omit);
  174. }
  175. if (typeof data.expand !== 'undefined' && data.expand !== null) {
  176. query.eager('[' + data.expand.join(', ') + ']');
  177. }
  178. return query;
  179. })
  180. .then((row) => {
  181. if (row) {
  182. return _.omit(row, omissions());
  183. } else {
  184. throw new error.ItemNotFoundError(data.id);
  185. }
  186. });
  187. },
  188. /**
  189. * Checks if an email address is available, but if a user_id is supplied, it will ignore checking
  190. * against that user.
  191. *
  192. * @param email
  193. * @param user_id
  194. */
  195. isEmailAvailable: (email, user_id) => {
  196. let query = userModel
  197. .query()
  198. .where('email', '=', email.toLowerCase().trim())
  199. .where('is_deleted', 0)
  200. .first();
  201. if (typeof user_id !== 'undefined') {
  202. query.where('id', '!=', user_id);
  203. }
  204. return query
  205. .then((user) => {
  206. return !user;
  207. });
  208. },
  209. /**
  210. * @param {Access} access
  211. * @param {Object} data
  212. * @param {Integer} data.id
  213. * @param {String} [data.reason]
  214. * @returns {Promise}
  215. */
  216. delete: (access, data) => {
  217. return access.can('users:delete', data.id)
  218. .then(() => {
  219. return internalUser.get(access, {id: data.id});
  220. })
  221. .then((user) => {
  222. if (!user) {
  223. throw new error.ItemNotFoundError(data.id);
  224. }
  225. // Make sure user can't delete themselves
  226. if (user.id === access.token.getUserId(0)) {
  227. throw new error.PermissionError('You cannot delete yourself.');
  228. }
  229. return userModel
  230. .query()
  231. .where('id', user.id)
  232. .patch({
  233. is_deleted: 1
  234. })
  235. .then(() => {
  236. // Add to audit log
  237. return internalAuditLog.add(access, {
  238. action: 'deleted',
  239. object_type: 'user',
  240. object_id: user.id,
  241. meta: _.omit(user, omissions())
  242. });
  243. });
  244. })
  245. .then(() => {
  246. return true;
  247. });
  248. },
  249. /**
  250. * This will only count the users
  251. *
  252. * @param {Access} access
  253. * @param {String} [search_query]
  254. * @returns {*}
  255. */
  256. getCount: (access, search_query) => {
  257. return access.can('users:list')
  258. .then(() => {
  259. let query = userModel
  260. .query()
  261. .count('id as count')
  262. .where('is_deleted', 0)
  263. .first();
  264. // Query is used for searching
  265. if (typeof search_query === 'string') {
  266. query.where(function () {
  267. this.where('user.name', 'like', '%' + search_query + '%')
  268. .orWhere('user.email', 'like', '%' + search_query + '%');
  269. });
  270. }
  271. return query;
  272. })
  273. .then((row) => {
  274. return parseInt(row.count, 10);
  275. });
  276. },
  277. /**
  278. * All users
  279. *
  280. * @param {Access} access
  281. * @param {Array} [expand]
  282. * @param {String} [search_query]
  283. * @returns {Promise}
  284. */
  285. getAll: (access, expand, search_query) => {
  286. return access.can('users:list')
  287. .then(() => {
  288. let query = userModel
  289. .query()
  290. .where('is_deleted', 0)
  291. .groupBy('id')
  292. .omit(['is_deleted'])
  293. .allowEager('[permissions]')
  294. .orderBy('name', 'ASC');
  295. // Query is used for searching
  296. if (typeof search_query === 'string') {
  297. query.where(function () {
  298. this.where('name', 'like', '%' + search_query + '%')
  299. .orWhere('email', 'like', '%' + search_query + '%');
  300. });
  301. }
  302. if (typeof expand !== 'undefined' && expand !== null) {
  303. query.eager('[' + expand.join(', ') + ']');
  304. }
  305. return query;
  306. });
  307. },
  308. /**
  309. * @param {Access} access
  310. * @param {Integer} [id_requested]
  311. * @returns {[String]}
  312. */
  313. getUserOmisionsByAccess: (access, id_requested) => {
  314. let response = []; // Admin response
  315. if (!access.token.hasScope('admin') && access.token.getUserId(0) !== id_requested) {
  316. response = ['roles', 'is_deleted']; // Restricted response
  317. }
  318. return response;
  319. },
  320. /**
  321. * @param {Access} access
  322. * @param {Object} data
  323. * @param {Integer} data.id
  324. * @param {String} data.type
  325. * @param {String} data.secret
  326. * @return {Promise}
  327. */
  328. setPassword: (access, data) => {
  329. return access.can('users:password', data.id)
  330. .then(() => {
  331. return internalUser.get(access, {id: data.id});
  332. })
  333. .then((user) => {
  334. if (user.id !== data.id) {
  335. // Sanity check that something crazy hasn't happened
  336. throw new error.InternalValidationError('User could not be updated, IDs do not match: ' + user.id + ' !== ' + data.id);
  337. }
  338. if (user.id === access.token.getUserId(0)) {
  339. // they're setting their own password. Make sure their current password is correct
  340. if (typeof data.current === 'undefined' || !data.current) {
  341. throw new error.ValidationError('Current password was not supplied');
  342. }
  343. return internalToken.getTokenFromEmail({
  344. identity: user.email,
  345. secret: data.current
  346. })
  347. .then(() => {
  348. return user;
  349. });
  350. }
  351. return user;
  352. })
  353. .then((user) => {
  354. // Get auth, patch if it exists
  355. return authModel
  356. .query()
  357. .where('user_id', user.id)
  358. .andWhere('type', data.type)
  359. .first()
  360. .then((existing_auth) => {
  361. if (existing_auth) {
  362. // patch
  363. return authModel
  364. .query()
  365. .where('user_id', user.id)
  366. .andWhere('type', data.type)
  367. .patch({
  368. type: data.type, // This is required for the model to encrypt on save
  369. secret: data.secret
  370. });
  371. } else {
  372. // insert
  373. return authModel
  374. .query()
  375. .insert({
  376. user_id: user.id,
  377. type: data.type,
  378. secret: data.secret,
  379. meta: {}
  380. });
  381. }
  382. })
  383. .then(() => {
  384. // Add to Audit Log
  385. return internalAuditLog.add(access, {
  386. action: 'updated',
  387. object_type: 'user',
  388. object_id: user.id,
  389. meta: {
  390. name: user.name,
  391. password_changed: true,
  392. auth_type: data.type
  393. }
  394. });
  395. });
  396. })
  397. .then(() => {
  398. return true;
  399. });
  400. },
  401. /**
  402. * @param {Access} access
  403. * @param {Object} data
  404. * @return {Promise}
  405. */
  406. setPermissions: (access, data) => {
  407. return access.can('users:permissions', data.id)
  408. .then(() => {
  409. return internalUser.get(access, {id: data.id});
  410. })
  411. .then((user) => {
  412. if (user.id !== data.id) {
  413. // Sanity check that something crazy hasn't happened
  414. throw new error.InternalValidationError('User could not be updated, IDs do not match: ' + user.id + ' !== ' + data.id);
  415. }
  416. return user;
  417. })
  418. .then((user) => {
  419. // Get perms row, patch if it exists
  420. return userPermissionModel
  421. .query()
  422. .where('user_id', user.id)
  423. .first()
  424. .then((existing_auth) => {
  425. if (existing_auth) {
  426. // patch
  427. return userPermissionModel
  428. .query()
  429. .where('user_id', user.id)
  430. .patchAndFetchById(existing_auth.id, _.assign({user_id: user.id}, data));
  431. } else {
  432. // insert
  433. return userPermissionModel
  434. .query()
  435. .insertAndFetch(_.assign({user_id: user.id}, data));
  436. }
  437. })
  438. .then((permissions) => {
  439. // Add to Audit Log
  440. return internalAuditLog.add(access, {
  441. action: 'updated',
  442. object_type: 'user',
  443. object_id: user.id,
  444. meta: {
  445. name: user.name,
  446. permissions: permissions
  447. }
  448. });
  449. });
  450. })
  451. .then(() => {
  452. return true;
  453. });
  454. },
  455. /**
  456. * @param {Access} access
  457. * @param {Object} data
  458. * @param {Integer} data.id
  459. */
  460. loginAs: (access, data) => {
  461. return access.can('users:loginas', data.id)
  462. .then(() => {
  463. return internalUser.get(access, data);
  464. })
  465. .then((user) => {
  466. return internalToken.getTokenFromUser(user);
  467. });
  468. }
  469. };
  470. module.exports = internalUser;