user.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  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. ssl_passthrough_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. .omit(omissions())
  128. .patchAndFetchById(user.id, data)
  129. .then((saved_user) => {
  130. return _.omit(saved_user, omissions());
  131. });
  132. })
  133. .then(() => {
  134. return internalUser.get(access, {id: data.id});
  135. })
  136. .then((user) => {
  137. // Add to audit log
  138. return internalAuditLog.add(access, {
  139. action: 'updated',
  140. object_type: 'user',
  141. object_id: user.id,
  142. meta: data
  143. })
  144. .then(() => {
  145. return user;
  146. });
  147. });
  148. },
  149. /**
  150. * @param {Access} access
  151. * @param {Object} [data]
  152. * @param {Integer} [data.id] Defaults to the token user
  153. * @param {Array} [data.expand]
  154. * @param {Array} [data.omit]
  155. * @return {Promise}
  156. */
  157. get: (access, data) => {
  158. if (typeof data === 'undefined') {
  159. data = {};
  160. }
  161. if (typeof data.id === 'undefined' || !data.id) {
  162. data.id = access.token.getUserId(0);
  163. }
  164. return access.can('users:get', data.id)
  165. .then(() => {
  166. let query = userModel
  167. .query()
  168. .where('is_deleted', 0)
  169. .andWhere('id', data.id)
  170. .allowEager('[permissions]')
  171. .first();
  172. // Custom omissions
  173. if (typeof data.omit !== 'undefined' && data.omit !== null) {
  174. query.omit(data.omit);
  175. }
  176. if (typeof data.expand !== 'undefined' && data.expand !== null) {
  177. query.eager('[' + data.expand.join(', ') + ']');
  178. }
  179. return query;
  180. })
  181. .then((row) => {
  182. if (row) {
  183. return _.omit(row, omissions());
  184. } else {
  185. throw new error.ItemNotFoundError(data.id);
  186. }
  187. });
  188. },
  189. /**
  190. * Checks if an email address is available, but if a user_id is supplied, it will ignore checking
  191. * against that user.
  192. *
  193. * @param email
  194. * @param user_id
  195. */
  196. isEmailAvailable: (email, user_id) => {
  197. let query = userModel
  198. .query()
  199. .where('email', '=', email.toLowerCase().trim())
  200. .where('is_deleted', 0)
  201. .first();
  202. if (typeof user_id !== 'undefined') {
  203. query.where('id', '!=', user_id);
  204. }
  205. return query
  206. .then((user) => {
  207. return !user;
  208. });
  209. },
  210. /**
  211. * @param {Access} access
  212. * @param {Object} data
  213. * @param {Integer} data.id
  214. * @param {String} [data.reason]
  215. * @returns {Promise}
  216. */
  217. delete: (access, data) => {
  218. return access.can('users:delete', data.id)
  219. .then(() => {
  220. return internalUser.get(access, {id: data.id});
  221. })
  222. .then((user) => {
  223. if (!user) {
  224. throw new error.ItemNotFoundError(data.id);
  225. }
  226. // Make sure user can't delete themselves
  227. if (user.id === access.token.getUserId(0)) {
  228. throw new error.PermissionError('You cannot delete yourself.');
  229. }
  230. return userModel
  231. .query()
  232. .where('id', user.id)
  233. .patch({
  234. is_deleted: 1
  235. })
  236. .then(() => {
  237. // Add to audit log
  238. return internalAuditLog.add(access, {
  239. action: 'deleted',
  240. object_type: 'user',
  241. object_id: user.id,
  242. meta: _.omit(user, omissions())
  243. });
  244. });
  245. })
  246. .then(() => {
  247. return true;
  248. });
  249. },
  250. /**
  251. * This will only count the users
  252. *
  253. * @param {Access} access
  254. * @param {String} [search_query]
  255. * @returns {*}
  256. */
  257. getCount: (access, search_query) => {
  258. return access.can('users:list')
  259. .then(() => {
  260. let query = userModel
  261. .query()
  262. .count('id as count')
  263. .where('is_deleted', 0)
  264. .first();
  265. // Query is used for searching
  266. if (typeof search_query === 'string') {
  267. query.where(function () {
  268. this.where('user.name', 'like', '%' + search_query + '%')
  269. .orWhere('user.email', 'like', '%' + search_query + '%');
  270. });
  271. }
  272. return query;
  273. })
  274. .then((row) => {
  275. return parseInt(row.count, 10);
  276. });
  277. },
  278. /**
  279. * All users
  280. *
  281. * @param {Access} access
  282. * @param {Array} [expand]
  283. * @param {String} [search_query]
  284. * @returns {Promise}
  285. */
  286. getAll: (access, expand, search_query) => {
  287. return access.can('users:list')
  288. .then(() => {
  289. let query = userModel
  290. .query()
  291. .where('is_deleted', 0)
  292. .groupBy('id')
  293. .omit(['is_deleted'])
  294. .allowEager('[permissions]')
  295. .orderBy('name', 'ASC');
  296. // Query is used for searching
  297. if (typeof search_query === 'string') {
  298. query.where(function () {
  299. this.where('name', 'like', '%' + search_query + '%')
  300. .orWhere('email', 'like', '%' + search_query + '%');
  301. });
  302. }
  303. if (typeof expand !== 'undefined' && expand !== null) {
  304. query.eager('[' + expand.join(', ') + ']');
  305. }
  306. return query;
  307. });
  308. },
  309. /**
  310. * @param {Access} access
  311. * @param {Integer} [id_requested]
  312. * @returns {[String]}
  313. */
  314. getUserOmisionsByAccess: (access, id_requested) => {
  315. let response = []; // Admin response
  316. if (!access.token.hasScope('admin') && access.token.getUserId(0) !== id_requested) {
  317. response = ['roles', 'is_deleted']; // Restricted response
  318. }
  319. return response;
  320. },
  321. /**
  322. * @param {Access} access
  323. * @param {Object} data
  324. * @param {Integer} data.id
  325. * @param {String} data.type
  326. * @param {String} data.secret
  327. * @return {Promise}
  328. */
  329. setPassword: (access, data) => {
  330. return access.can('users:password', data.id)
  331. .then(() => {
  332. return internalUser.get(access, {id: data.id});
  333. })
  334. .then((user) => {
  335. if (user.id !== data.id) {
  336. // Sanity check that something crazy hasn't happened
  337. throw new error.InternalValidationError('User could not be updated, IDs do not match: ' + user.id + ' !== ' + data.id);
  338. }
  339. if (user.id === access.token.getUserId(0)) {
  340. // they're setting their own password. Make sure their current password is correct
  341. if (typeof data.current === 'undefined' || !data.current) {
  342. throw new error.ValidationError('Current password was not supplied');
  343. }
  344. return internalToken.getTokenFromEmail({
  345. identity: user.email,
  346. secret: data.current
  347. })
  348. .then(() => {
  349. return user;
  350. });
  351. }
  352. return user;
  353. })
  354. .then((user) => {
  355. // Get auth, patch if it exists
  356. return authModel
  357. .query()
  358. .where('user_id', user.id)
  359. .andWhere('type', data.type)
  360. .first()
  361. .then((existing_auth) => {
  362. if (existing_auth) {
  363. // patch
  364. return authModel
  365. .query()
  366. .where('user_id', user.id)
  367. .andWhere('type', data.type)
  368. .patch({
  369. type: data.type, // This is required for the model to encrypt on save
  370. secret: data.secret
  371. });
  372. } else {
  373. // insert
  374. return authModel
  375. .query()
  376. .insert({
  377. user_id: user.id,
  378. type: data.type,
  379. secret: data.secret,
  380. meta: {}
  381. });
  382. }
  383. })
  384. .then(() => {
  385. // Add to Audit Log
  386. return internalAuditLog.add(access, {
  387. action: 'updated',
  388. object_type: 'user',
  389. object_id: user.id,
  390. meta: {
  391. name: user.name,
  392. password_changed: true,
  393. auth_type: data.type
  394. }
  395. });
  396. });
  397. })
  398. .then(() => {
  399. return true;
  400. });
  401. },
  402. /**
  403. * @param {Access} access
  404. * @param {Object} data
  405. * @return {Promise}
  406. */
  407. setPermissions: (access, data) => {
  408. return access.can('users:permissions', data.id)
  409. .then(() => {
  410. return internalUser.get(access, {id: data.id});
  411. })
  412. .then((user) => {
  413. if (user.id !== data.id) {
  414. // Sanity check that something crazy hasn't happened
  415. throw new error.InternalValidationError('User could not be updated, IDs do not match: ' + user.id + ' !== ' + data.id);
  416. }
  417. return user;
  418. })
  419. .then((user) => {
  420. // Get perms row, patch if it exists
  421. return userPermissionModel
  422. .query()
  423. .where('user_id', user.id)
  424. .first()
  425. .then((existing_auth) => {
  426. if (existing_auth) {
  427. // patch
  428. return userPermissionModel
  429. .query()
  430. .where('user_id', user.id)
  431. .patchAndFetchById(existing_auth.id, _.assign({user_id: user.id}, data));
  432. } else {
  433. // insert
  434. return userPermissionModel
  435. .query()
  436. .insertAndFetch(_.assign({user_id: user.id}, data));
  437. }
  438. })
  439. .then((permissions) => {
  440. // Add to Audit Log
  441. return internalAuditLog.add(access, {
  442. action: 'updated',
  443. object_type: 'user',
  444. object_id: user.id,
  445. meta: {
  446. name: user.name,
  447. permissions: permissions
  448. }
  449. });
  450. });
  451. })
  452. .then(() => {
  453. return true;
  454. });
  455. },
  456. /**
  457. * @param {Access} access
  458. * @param {Object} data
  459. * @param {Integer} data.id
  460. */
  461. loginAs: (access, data) => {
  462. return access.can('users:loginas', data.id)
  463. .then(() => {
  464. return internalUser.get(access, data);
  465. })
  466. .then((user) => {
  467. return internalToken.getTokenFromUser(user);
  468. });
  469. }
  470. };
  471. module.exports = internalUser;