forest-express
Version:
Official package for all Forest Express Lianas
497 lines (491 loc) • 22.9 kB
JavaScript
"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;