UNPKG

forest-express

Version:

Official package for all Forest Express Lianas

497 lines (491 loc) 22.9 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } var _require = require('@forestadmin/context'), inject = _require.inject; var _require2 = require('../utils/string'), parameterize = _require2.parameterize; var Schemas = require('../generators/schemas'); var QueryDeserializer = require('../deserializers/query'); var RecordsGetter = require('../services/exposed/records-getter'); var _require3 = require('../utils/errors/unprocessable-error'), UnprocessableError = _require3["default"]; var errorHandler = require('../services/exposed/error-handler'); var RecordsCounter = require('../services/exposed/records-counter')["default"]; var PermissionMiddlewareCreator = /*#__PURE__*/function () { function PermissionMiddlewareCreator(collectionName) { (0, _classCallCheck2["default"])(this, PermissionMiddlewareCreator); this.collectionName = collectionName; var _inject = inject(), authorizationService = _inject.authorizationService, actionAuthorizationService = _inject.actionAuthorizationService, modelsManager = _inject.modelsManager, logger = _inject.logger; /** @private @readonly @type {import('../services/models-manager')} */ this.modelsManager = modelsManager; /** @private @readonly @type {import('../services/authorization/authorization').default} */ this.authorizationService = authorizationService; /** * @private @readonly @type {import('../services/authorization/action-authorization').default} * */ this.actionAuthorizationService = actionAuthorizationService; /** * @private @readonly} */ this.logger = logger; } (0, _createClass2["default"])(PermissionMiddlewareCreator, [{ key: "_getSmartActionName", value: function _getSmartActionName(request) { var smartActionEndpoint = "".concat(request.baseUrl).concat(request.path); var smartActionHTTPMethod = request.method; var smartAction = Schemas.schemas[this.collectionName].actions.find(function (action) { var endpoint = action.endpoint && !action.endpoint.startsWith('/') ? "/".concat(action.endpoint) : action.endpoint || "/forest/actions/".concat(parameterize(action.name)); var method = action.httpMethod || 'POST'; return endpoint === smartActionEndpoint && method === smartActionHTTPMethod; }); if (!smartAction) { throw new Error("Impossible to retrieve the smart action at endpoint ".concat(smartActionEndpoint, " and method ").concat(smartActionHTTPMethod)); } return smartAction.name; } // generate a middleware that will check that ids provided by the request exist // within the registered scope }, { key: "_ensureRecordIdsInScope", value: function _ensureRecordIdsInScope() { var _this = this; return /*#__PURE__*/function () { var _ref = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee(request, response, next) { var _Schemas$schemas$_thi, primaryKeys, isVirtual, hasBodyAttributes, attributes, filters, counter; return _regenerator["default"].wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: _context.prev = 0; _Schemas$schemas$_thi = Schemas.schemas[_this.collectionName], primaryKeys = _Schemas$schemas$_thi.primaryKeys, isVirtual = _Schemas$schemas$_thi.isVirtual; // Smart collections does not have the scope feature if (!isVirtual) { _context.next = 4; break; } return _context.abrupt("return", next()); case 4: // if performing a `selectAll` let the `getIdsFromRequest` handle the scopes hasBodyAttributes = request.body && request.body.data && request.body.data.attributes; attributes = hasBodyAttributes && new QueryDeserializer(request.body.data.attributes).perform(); if (!attributes.allRecords) { _context.next = 8; break; } return _context.abrupt("return", next()); case 8: // Otherwise, check that all records are within scope. filters = JSON.stringify(primaryKeys.length === 1 ? { field: primaryKeys[0], operator: 'in', value: attributes.ids } : { aggregator: 'or', conditions: attributes.ids.map(function (compositeId) { return { aggregator: 'and', conditions: compositeId.split('|').map(function (id, index) { return { field: primaryKeys[index], operator: 'equal', value: id }; }) }; }) }); // The implementation of ResourcesGetter uses the scopes ! counter = new RecordsCounter(_this.modelsManager.getModelByName(_this.collectionName), request.user, { filters: filters, timezone: request.query.timezone }); _context.next = 12; return counter.count(); case 12: _context.t0 = _context.sent; _context.t1 = attributes.ids.length; if (!(_context.t0 === _context.t1)) { _context.next = 16; break; } return _context.abrupt("return", next()); case 16: return _context.abrupt("return", response.status(400).send({ error: 'Smart Action: target records are out of scope' })); case 19: _context.prev = 19; _context.t2 = _context["catch"](0); return _context.abrupt("return", response.status(500).send({ error: 'Smart Action: failed to evaluate permissions' })); case 22: case "end": return _context.stop(); } }, _callee, null, [[0, 19]]); })); return function (_x, _x2, _x3) { return _ref.apply(this, arguments); }; }(); } }, { key: "list", value: function list() { var _this2 = this; return /*#__PURE__*/function () { var _ref2 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee2(request, response, next) { var _request$query, _request$query2, _request$query2$segme, segmentQuery; return _regenerator["default"].wrap(function _callee2$(_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: _context2.prev = 0; _request$query = request.query, _request$query2 = _request$query === void 0 ? {} : _request$query, _request$query2$segme = _request$query2.segmentQuery, segmentQuery = _request$query2$segme === void 0 ? null : _request$query2$segme; _context2.next = 4; return _this2.authorizationService.assertCanBrowse(request.user, _this2.collectionName, segmentQuery); case 4: next(); _context2.next = 10; break; case 7: _context2.prev = 7; _context2.t0 = _context2["catch"](0); next(_context2.t0); case 10: case "end": return _context2.stop(); } }, _callee2, null, [[0, 7]]); })); return function (_x4, _x5, _x6) { return _ref2.apply(this, arguments); }; }(); } }, { key: "export", value: function _export() { var _this3 = this; return /*#__PURE__*/function () { var _ref3 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee3(request, response, next) { return _regenerator["default"].wrap(function _callee3$(_context3) { while (1) switch (_context3.prev = _context3.next) { case 0: _context3.prev = 0; _context3.next = 3; return _this3.authorizationService.assertCanExport(request.user, _this3.collectionName); case 3: next(); _context3.next = 9; break; case 6: _context3.prev = 6; _context3.t0 = _context3["catch"](0); next(_context3.t0); case 9: case "end": return _context3.stop(); } }, _callee3, null, [[0, 6]]); })); return function (_x7, _x8, _x9) { return _ref3.apply(this, arguments); }; }(); } }, { key: "details", value: function details() { var _this4 = this; return /*#__PURE__*/function () { var _ref4 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee4(request, response, next) { return _regenerator["default"].wrap(function _callee4$(_context4) { while (1) switch (_context4.prev = _context4.next) { case 0: _context4.prev = 0; _context4.next = 3; return _this4.authorizationService.assertCanRead(request.user, _this4.collectionName); case 3: next(); _context4.next = 9; break; case 6: _context4.prev = 6; _context4.t0 = _context4["catch"](0); next(_context4.t0); case 9: case "end": return _context4.stop(); } }, _callee4, null, [[0, 6]]); })); return function (_x10, _x11, _x12) { return _ref4.apply(this, arguments); }; }(); } }, { key: "create", value: function create() { var _this5 = this; return /*#__PURE__*/function () { var _ref5 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee5(request, response, next) { return _regenerator["default"].wrap(function _callee5$(_context5) { while (1) switch (_context5.prev = _context5.next) { case 0: _context5.prev = 0; _context5.next = 3; return _this5.authorizationService.assertCanAdd(request.user, _this5.collectionName); case 3: next(); _context5.next = 9; break; case 6: _context5.prev = 6; _context5.t0 = _context5["catch"](0); next(_context5.t0); case 9: case "end": return _context5.stop(); } }, _callee5, null, [[0, 6]]); })); return function (_x13, _x14, _x15) { return _ref5.apply(this, arguments); }; }(); } }, { key: "update", value: function update() { var _this6 = this; return /*#__PURE__*/function () { var _ref6 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee6(request, response, next) { return _regenerator["default"].wrap(function _callee6$(_context6) { while (1) switch (_context6.prev = _context6.next) { case 0: _context6.prev = 0; _context6.next = 3; return _this6.authorizationService.assertCanEdit(request.user, _this6.collectionName); case 3: next(); _context6.next = 9; break; case 6: _context6.prev = 6; _context6.t0 = _context6["catch"](0); next(_context6.t0); case 9: case "end": return _context6.stop(); } }, _callee6, null, [[0, 6]]); })); return function (_x16, _x17, _x18) { return _ref6.apply(this, arguments); }; }(); } }, { key: "delete", value: function _delete() { var _this7 = this; return /*#__PURE__*/function () { var _ref7 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee7(request, response, next) { return _regenerator["default"].wrap(function _callee7$(_context7) { while (1) switch (_context7.prev = _context7.next) { case 0: _context7.prev = 0; _context7.next = 3; return _this7.authorizationService.assertCanDelete(request.user, _this7.collectionName); case 3: next(); _context7.next = 9; break; case 6: _context7.prev = 6; _context7.t0 = _context7["catch"](0); next(_context7.t0); case 9: case "end": return _context7.stop(); } }, _callee7, null, [[0, 6]]); })); return function (_x19, _x20, _x21) { return _ref7.apply(this, arguments); }; }(); } }, { key: "smartAction", value: function smartAction() { var _this8 = this; return [/*#__PURE__*/function () { var _ref8 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee8(request, response, next) { var _request$body$data, _request$body$data$at, _request$body, _request$body$data2, _request$body$data2$a; var signedParameters; return _regenerator["default"].wrap(function _callee8$(_context8) { while (1) switch (_context8.prev = _context8.next) { case 0: if (!((_request$body$data = request.body.data) !== null && _request$body$data !== void 0 && (_request$body$data$at = _request$body$data.attributes) !== null && _request$body$data$at !== void 0 && _request$body$data$at.requester_id)) { _context8.next = 2; break; } return _context8.abrupt("return", next(new UnprocessableError())); case 2: if ((_request$body = request.body) !== null && _request$body !== void 0 && (_request$body$data2 = _request$body.data) !== null && _request$body$data2 !== void 0 && (_request$body$data2$a = _request$body$data2.attributes) !== null && _request$body$data2$a !== void 0 && _request$body$data2$a.signed_approval_request) { signedParameters = _this8.actionAuthorizationService.verifySignedActionParameters(request.body.data.attributes.signed_approval_request); request.body = signedParameters; } return _context8.abrupt("return", next()); case 4: case "end": return _context8.stop(); } }, _callee8); })); return function (_x22, _x23, _x24) { return _ref8.apply(this, arguments); }; }(), /*#__PURE__*/function () { var _ref9 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee9(request, response, next) { var _request$body2, _request$body2$data, _request$body2$data$a, _Schemas$schemas$_thi2, primaryKeys, isVirtual, actionName, filters, model, getter, ids, canPerformCustomActionParams, _request$body3, _request$body3$data, _request$body3$data$a; return _regenerator["default"].wrap(function _callee9$(_context9) { while (1) switch (_context9.prev = _context9.next) { case 0: _context9.prev = 0; _Schemas$schemas$_thi2 = Schemas.schemas[_this8.collectionName], primaryKeys = _Schemas$schemas$_thi2.primaryKeys, isVirtual = _Schemas$schemas$_thi2.isVirtual; actionName = _this8._getSmartActionName(request); if (isVirtual) { _context9.next = 10; break; } model = _this8.modelsManager.getModelByName(_this8.collectionName); getter = new RecordsGetter(model, request.user, request.query); _context9.next = 8; return getter.getIdsFromRequest(request); case 8: ids = _context9.sent; filters = primaryKeys.length === 1 ? { field: primaryKeys[0], operator: 'in', value: ids } : { aggregator: 'or', conditions: ids.map(function (compositeId) { return { aggregator: 'and', /** * See line 108 src/services/exposed/records-getter.js * @fixme It could either be - or | */ conditions: compositeId.split(/-|\|/).map(function (id, index) { return { field: primaryKeys[index], operator: 'equal', value: id }; }) }; }) }; case 10: canPerformCustomActionParams = { user: request.user, customActionName: actionName, collectionName: _this8.collectionName, filterForCaller: filters, recordsCounterParams: { model: model, user: request.user, timezone: request.query.timezone } }; if (!((_request$body2 = request.body) !== null && _request$body2 !== void 0 && (_request$body2$data = _request$body2.data) !== null && _request$body2$data !== void 0 && (_request$body2$data$a = _request$body2$data.attributes) !== null && _request$body2$data$a !== void 0 && _request$body2$data$a.requester_id)) { _context9.next = 16; break; } _context9.next = 14; return _this8.actionAuthorizationService.assertCanApproveCustomAction(_objectSpread(_objectSpread({}, canPerformCustomActionParams), {}, { requesterId: (_request$body3 = request.body) === null || _request$body3 === void 0 ? void 0 : (_request$body3$data = _request$body3.data) === null || _request$body3$data === void 0 ? void 0 : (_request$body3$data$a = _request$body3$data.attributes) === null || _request$body3$data$a === void 0 ? void 0 : _request$body3$data$a.requester_id })); case 14: _context9.next = 18; break; case 16: _context9.next = 18; return _this8.actionAuthorizationService.assertCanTriggerCustomAction(canPerformCustomActionParams); case 18: next(); _context9.next = 24; break; case 21: _context9.prev = 21; _context9.t0 = _context9["catch"](0); next(_context9.t0); case 24: case "end": return _context9.stop(); } }, _callee9, null, [[0, 21]]); })); return function (_x25, _x26, _x27) { return _ref9.apply(this, arguments); }; }(), this._ensureRecordIdsInScope(), // Some old agents can have some code that is not correctly handling errors // To prevent this, we make sure that errors related to smart action rights // are correctly handled errorHandler({ logger: this.logger })]; } }, { key: "stats", value: function stats() { var _this9 = this; return /*#__PURE__*/function () { var _ref10 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee10(request, response, next) { return _regenerator["default"].wrap(function _callee10$(_context10) { while (1) switch (_context10.prev = _context10.next) { case 0: _context10.prev = 0; _context10.next = 3; return _this9.authorizationService.assertCanRetrieveChart({ user: request.user, chartRequest: request.body }); case 3: next(); _context10.next = 9; break; case 6: _context10.prev = 6; _context10.t0 = _context10["catch"](0); next(_context10.t0); case 9: case "end": return _context10.stop(); } }, _callee10, null, [[0, 6]]); })); return function (_x28, _x29, _x30) { return _ref10.apply(this, arguments); }; }(); } }]); return PermissionMiddlewareCreator; }(); module.exports = PermissionMiddlewareCreator;