@crowdin/app-project-module
Version:
Module that generates for you all common endpoints for serving standalone Crowdin App
111 lines (110 loc) • 5.14 kB
JavaScript
;
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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.maskKey = exports.postRequestCredentialsMasker = exports.getRequestCredentialsMasker = void 0;
const lodash_get_1 = __importDefault(require("lodash.get"));
const index_1 = require("./index");
const index_2 = require("../index");
const crowdin_client_1 = require("../middlewares/crowdin-client");
const crowdin_apps_functions_1 = require("@crowdin/crowdin-apps-functions");
function maskKey(key) {
const maskWith = '*';
const unmaskedCharsAtEnd = 3;
const repeatCount = key.length > unmaskedCharsAtEnd ? key.length - unmaskedCharsAtEnd : 0;
return maskWith.repeat(repeatCount) + key.substring(key.length - 3);
}
exports.maskKey = maskKey;
function getMaskableFieldsKeys(moduleConfig) {
if (!moduleConfig.formUiSchema) {
return [];
}
return Object.keys(moduleConfig.formUiSchema).filter((fieldKey) => {
return (0, lodash_get_1.default)(moduleConfig, `formUiSchema[${fieldKey}]['ui:widget']`) === 'password';
});
}
function getRequestCredentialsMasker({ moduleConfig, dataPath = 'formData', }) {
return function (req, res, next) {
// we can't find "password" fields without ui schema
if (!moduleConfig || !moduleConfig.formSchema || !moduleConfig.formUiSchema) {
return next();
}
// temporary
if (!moduleConfig.maskPasswords) {
return next();
}
const maskableFieldsKeys = getMaskableFieldsKeys(moduleConfig);
if (!maskableFieldsKeys.length) {
return next();
}
const originalSend = res.send;
res.send = function (body) {
const maskFields = (target) => {
maskableFieldsKeys.forEach((fieldKey) => {
if (target[fieldKey]) {
target[fieldKey] = maskKey(target[fieldKey]);
}
});
};
if (body[dataPath]) {
maskFields(body[dataPath]);
}
if (dataPath === '') {
maskFields(body);
}
return originalSend.apply(res, [body]);
};
return next();
};
}
exports.getRequestCredentialsMasker = getRequestCredentialsMasker;
function postRequestCredentialsMasker(moduleConfig, credentialsExtractor) {
return (0, index_1.runAsyncWrapper)((req, res, next) => __awaiter(this, void 0, void 0, function* () {
var _a;
if (!moduleConfig || !moduleConfig.formSchema || !moduleConfig.formUiSchema) {
return next();
}
// temporary
if (!moduleConfig.maskPasswords) {
return next();
}
const fieldsKeysInRequest = Object.keys(((_a = req.body) === null || _a === void 0 ? void 0 : _a.data) || []);
let unmaskedFields = {};
if (credentialsExtractor) {
unmaskedFields = yield credentialsExtractor(req, res);
}
else {
// most common way of storing data
const jwtToken = (0, crowdin_client_1.getToken)(req) || '';
const jwtPayload = yield (0, crowdin_apps_functions_1.validateJwtToken)(jwtToken, process.env.CROWDIN_CLIENT_SECRET || '');
const crowdinId = `${jwtPayload.context.organization_id}`;
unmaskedFields = yield index_2.metadataStore.getMetadata(`form-data-${crowdinId}`);
}
unmaskedFields = unmaskedFields || {};
const maskableFieldsKeys = getMaskableFieldsKeys(moduleConfig);
Object.keys(unmaskedFields).forEach((fieldKey) => {
if (!maskableFieldsKeys.includes(fieldKey) || !fieldsKeysInRequest.includes(fieldKey)) {
delete unmaskedFields[fieldKey];
}
});
req.body.data = Object.assign(Object.assign({}, req.body.data), Object.keys(unmaskedFields).reduce((acc, key) => {
if (maskKey(unmaskedFields[key]) === req.body.data[key]) {
acc[key] = unmaskedFields[key];
}
return acc;
}, {}));
// run getRequestCredentialsMasker to mask fields that can be in response
return getRequestCredentialsMasker({ moduleConfig })(req, res, next);
}));
}
exports.postRequestCredentialsMasker = postRequestCredentialsMasker;