UNPKG

@tableau/taco-toolkit

Version:
230 lines 17.2 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.logForwardedRequest = exports.isValidForwardedRequest = exports.handleRequestForward = void 0; var http_status_codes_1 = require("http-status-codes"); var error_codes_1 = require("../../../../shared/errors/error-codes"); var error_source_1 = require("../../../../shared/errors/error-source"); var status_code_1 = require("../../../../shared/errors/status-code"); var permission_check_1 = require("../../../../shared/utils/permission-check"); var eps_logger_1 = require("../../../logging/eps-logger"); var request_utils_1 = require("../../../utils/request-utils"); function handleRequestForward(networkAdapter, forwardedRequest, apiPermission) { return __awaiter(this, void 0, void 0, function () { var method, url, message, fetchOptions, response, status, ajaxResponse; var _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: method = forwardedRequest.method, url = forwardedRequest.url; if (!(0, permission_check_1.checkApiPermission)(apiPermission, url, method)) { message = "This app does not have permission to make ".concat(method, " request to ").concat(url); eps_logger_1.Logger.logEPSError(status_code_1.StatusCode.PermissionDenied, error_source_1.ErrorSources.Configuration, error_codes_1.ErrorCodes.XQJT9XXC, message); return [2 /*return*/, { state: 'error', statusCode: http_status_codes_1.StatusCodes.UNAUTHORIZED, error: { message: message }, }]; } fetchOptions = getFetchOption(forwardedRequest); return [4 /*yield*/, networkAdapter.fetch(url, fetchOptions)]; case 1: response = _c.sent(); status = response.status; if ((0, request_utils_1.isRedirectCode)(status)) { throw new Error("Forwarded request currently does not support redirect. Status: ".concat(response.status, ", StatusText: ").concat(response.statusText)); } if (!!response.ok) return [3 /*break*/, 3]; _a = { state: 'error', statusCode: status }; _b = { statusText: response.statusText }; return [4 /*yield*/, response.text()]; case 2: return [2 /*return*/, (_a.error = (_b.message = _c.sent(), _b), _a)]; case 3: return [4 /*yield*/, composeAjaxResponse(response)]; case 4: ajaxResponse = _c.sent(); return [2 /*return*/, { state: 'success', statusCode: response.status, response: ajaxResponse, }]; } }); }); } exports.handleRequestForward = handleRequestForward; function getFetchOption(forwardedRequest) { var method = forwardedRequest.method, headers = forwardedRequest.headers; if (method === 'GET') { return { method: 'GET', headers: headers }; } var body = forwardedRequest.body; return (0, request_utils_1.composeFetchOption)(method, headers, body); } /** * Compose AjaxResponse object based on node-fetch response. * Note: this function is designated to forwarded request, so do not use it anywhere else. * * Since the request forwarding adds an abstraction layer on top of the original response of * the forwarded request, the content needs to be decoded before sent to the client. (The browser * won't decode it due to the abstraction layer) */ function composeAjaxResponse(fetchResponse) { return __awaiter(this, void 0, void 0, function () { var contentTypeHeader, contentType, commonProps, _a, text, text, body, _b; var _c, _d; return __generator(this, function (_e) { switch (_e.label) { case 0: contentTypeHeader = fetchResponse.headers.get('Content-Type'); contentType = contentTypeHeader ? (0, request_utils_1.getContentType)(contentTypeHeader) : 'text/plain'; commonProps = { headers: Object.fromEntries(fetchResponse.headers), ok: fetchResponse.ok, status: fetchResponse.status, type: contentType, }; if (contentType.startsWith('multipart/')) { throw new Error('Forwarded request does not support multipart content'); } if (!(contentType.endsWith('/xml') || contentType.startsWith('text/'))) return [3 /*break*/, 2]; _a = [__assign({}, commonProps)]; _c = { body: null }; return [4 /*yield*/, fetchResponse.text()]; case 1: return [2 /*return*/, __assign.apply(void 0, _a.concat([(_c.text = _e.sent(), _c)]))]; case 2: if (!(contentType === 'application/x-www-form-urlencoded')) return [3 /*break*/, 4]; return [4 /*yield*/, fetchResponse.text()]; case 3: text = _e.sent(); return [2 /*return*/, __assign(__assign({}, commonProps), { body: (0, request_utils_1.parseFormData)(text), text: text })]; case 4: if (!(contentType === 'application/json')) return [3 /*break*/, 6]; return [4 /*yield*/, fetchResponse.text()]; case 5: text = _e.sent(); body = null; try { // Note: it is observed that fetchResponse.clone().json() gets stuck in some cases. // Parse the body syncly with JSON.parse for now. body = JSON.parse(text); } catch (e) { // no-op, let body be null } return [2 /*return*/, __assign(__assign({}, commonProps), { body: body, text: text })]; case 6: _b = [__assign({}, commonProps)]; _d = { text: '' }; return [4 /*yield*/, fetchResponse.arrayBuffer()]; case 7: // by default, treats the content as binary return [2 /*return*/, __assign.apply(void 0, _b.concat([(_d.body = _e.sent(), _d)]))]; } }); }); } function isValidForwardedRequest(value) { if (!value || typeof value !== 'object') { return false; } var forwardRequest = value; var url = forwardRequest.url, method = forwardRequest.method, headers = forwardRequest.headers; if (method !== 'GET' && method !== 'POST') { return false; } if (headers !== undefined && typeof headers !== 'object') { return false; } return typeof url === 'string'; } exports.isValidForwardedRequest = isValidForwardedRequest; /** * Log forwarded request infos. * * Forwarded requests are used by connectors to talk to external resources, so * the requests may contain user sensitive infos and credentials. The function * makes sure we only log insensitive infos for debugging purpose. */ function logForwardedRequest(forwardedRequest) { var method = forwardedRequest.method, url = forwardedRequest.url, headers = forwardedRequest.headers; var logEntry = { type: 'forwarded-request', method: method, endpoint: getEndpointFromUrl(url), }; if (headers) { // only log header names, since values may have creds logEntry['headers'] = Object.keys(headers); } eps_logger_1.Logger.debug(logEntry); } exports.logForwardedRequest = logForwardedRequest; /** * Get the hostname and path of a URL for logging. * It strips out query strings which may contains user sensitive data. */ function getEndpointFromUrl(urlString) { try { var url = new URL(urlString); url.search = ''; return url.toString(); } catch (e) { // no-op, return the message for logging return 'Invalid URL string'; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9yd2FyZC1yZXF1ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2Vwcy9tYWluL2FwaS9lcHMtaGFuZGxlcnMvZm9yd2FyZC1yZXF1ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsdURBQStDO0FBSS9DLHFFQUFrRTtBQUNsRSx1RUFBcUU7QUFDckUscUVBQWtFO0FBR2xFLDhFQUE4RTtBQUM5RSwwREFBb0Q7QUFFcEQsOERBQWdIO0FBZ0JoSCxTQUFzQixvQkFBb0IsQ0FDeEMsY0FBOEIsRUFDOUIsZ0JBQWtDLEVBQ2xDLGFBQTRCOzs7Ozs7O29CQUVwQixNQUFNLEdBQVUsZ0JBQWdCLE9BQTFCLEVBQUUsR0FBRyxHQUFLLGdCQUFnQixJQUFyQixDQUFxQjtvQkFDeEMsSUFBSSxDQUFDLElBQUEscUNBQWtCLEVBQUMsYUFBYSxFQUFFLEdBQUcsRUFBRSxNQUFNLENBQUMsRUFBRTt3QkFFN0MsT0FBTyxHQUFHLG9EQUE2QyxNQUFNLHlCQUFlLEdBQUcsQ0FBRSxDQUFBO3dCQUN2RixtQkFBTSxDQUFDLFdBQVcsQ0FBQyx3QkFBVSxDQUFDLGdCQUFnQixFQUFFLDJCQUFZLENBQUMsYUFBYSxFQUFFLHdCQUFVLENBQUMsUUFBUSxFQUFFLE9BQU8sQ0FBQyxDQUFBO3dCQUV6RyxzQkFBTztnQ0FDTCxLQUFLLEVBQUUsT0FBTztnQ0FDZCxVQUFVLEVBQUUsK0JBQVcsQ0FBQyxZQUFZO2dDQUNwQyxLQUFLLEVBQUUsRUFBRSxPQUFPLFNBQUEsRUFBRTs2QkFDbkIsRUFBQTtxQkFDRjtvQkFFSyxZQUFZLEdBQUcsY0FBYyxDQUFDLGdCQUFnQixDQUFDLENBQUE7b0JBQ3BDLHFCQUFNLGNBQWMsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLFlBQVksQ0FBQyxFQUFBOztvQkFBeEQsUUFBUSxHQUFHLFNBQTZDO29C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