user.js 12 KB

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