@crowdin/app-project-module 0.61.0 → 0.63.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/out/types.d.ts CHANGED
@@ -364,6 +364,10 @@ export interface UiModule {
364
364
  * Module name
365
365
  */
366
366
  name?: string;
367
+ /**
368
+ * Temporary property. Indicates if passwords should be masked. Will be dropped when all existing apps will migrate.
369
+ */
370
+ maskPasswords?: boolean;
367
371
  }
368
372
  export interface ImagePath {
369
373
  /**
@@ -0,0 +1,6 @@
1
+ /// <reference types="qs" />
2
+ import { CrowdinClientRequest, UiModule } from '../types';
3
+ import { Request, Response } from 'express';
4
+ declare function postRequestCredentialsMasker(moduleConfig?: UiModule, credentialsExtractor?: Function): (req: CrowdinClientRequest | Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
5
+ declare function getRequestCredentialsMasker(moduleConfig?: UiModule): (req: Request | CrowdinClientRequest, res: Response, next: Function) => any;
6
+ export { getRequestCredentialsMasker, postRequestCredentialsMasker };
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.postRequestCredentialsMasker = exports.getRequestCredentialsMasker = void 0;
16
+ const lodash_1 = __importDefault(require("lodash"));
17
+ const index_1 = require("./index");
18
+ const index_2 = require("../index");
19
+ const crowdin_client_1 = require("../middlewares/crowdin-client");
20
+ const crowdin_apps_functions_1 = require("@crowdin/crowdin-apps-functions");
21
+ function maskKey(key) {
22
+ const maskWith = '*';
23
+ const unmaskedCharsAtEnd = 3;
24
+ const repeatCount = key.length > unmaskedCharsAtEnd ? key.length - unmaskedCharsAtEnd : 0;
25
+ return maskWith.repeat(repeatCount) + key.substring(key.length - 3);
26
+ }
27
+ function getMaskableFieldsKeys(moduleConfig) {
28
+ if (!moduleConfig.formUiSchema) {
29
+ return [];
30
+ }
31
+ return Object.keys(moduleConfig.formUiSchema).filter((fieldKey) => {
32
+ return lodash_1.default.get(moduleConfig, `formUiSchema[${fieldKey}]['ui:widget']`) === 'password';
33
+ });
34
+ }
35
+ function postRequestCredentialsMasker(moduleConfig, credentialsExtractor) {
36
+ return (0, index_1.runAsyncWrapper)((req, res, next) => __awaiter(this, void 0, void 0, function* () {
37
+ if (!moduleConfig || !moduleConfig.formSchema || !moduleConfig.formUiSchema) {
38
+ return next();
39
+ }
40
+ // temporary
41
+ if (!moduleConfig.maskPasswords) {
42
+ return next();
43
+ }
44
+ const fieldsKeysInRequest = Object.keys(req.body.data);
45
+ let unmaskedFields = {};
46
+ if (credentialsExtractor) {
47
+ unmaskedFields = yield credentialsExtractor(req, res);
48
+ }
49
+ else {
50
+ // most common way of storing data
51
+ const jwtToken = (0, crowdin_client_1.getToken)(req) || '';
52
+ const jwtPayload = yield (0, crowdin_apps_functions_1.validateJwtToken)(jwtToken, process.env.CROWDIN_CLIENT_SECRET || '');
53
+ const crowdinId = `${jwtPayload.context.organization_id}`;
54
+ unmaskedFields = yield index_2.metadataStore.getMetadata(`form-data-${crowdinId}`);
55
+ }
56
+ unmaskedFields = unmaskedFields || {};
57
+ const maskableFieldsKeys = getMaskableFieldsKeys(moduleConfig);
58
+ Object.keys(unmaskedFields).forEach((fieldKey) => {
59
+ if (!maskableFieldsKeys.includes(fieldKey) || !fieldsKeysInRequest.includes(fieldKey)) {
60
+ delete unmaskedFields[fieldKey];
61
+ }
62
+ });
63
+ req.body.data = Object.assign(Object.assign({}, req.body.data), Object.keys(unmaskedFields).reduce((acc, key) => {
64
+ if (maskKey(unmaskedFields[key]) === req.body.data[key]) {
65
+ acc[key] = unmaskedFields[key];
66
+ }
67
+ return acc;
68
+ }, {}));
69
+ next();
70
+ }));
71
+ }
72
+ exports.postRequestCredentialsMasker = postRequestCredentialsMasker;
73
+ function getRequestCredentialsMasker(moduleConfig) {
74
+ return function (req, res, next) {
75
+ // we can't find "password" fields without ui schema
76
+ if (!moduleConfig || !moduleConfig.formSchema || !moduleConfig.formUiSchema) {
77
+ return next();
78
+ }
79
+ // temporary
80
+ if (!moduleConfig.maskPasswords) {
81
+ return next();
82
+ }
83
+ const maskableFieldsKeys = getMaskableFieldsKeys(moduleConfig);
84
+ if (!maskableFieldsKeys.length) {
85
+ return next();
86
+ }
87
+ const originalSend = res.send;
88
+ res.send = function (body) {
89
+ if (body.formData) {
90
+ maskableFieldsKeys.forEach((fieldKey) => {
91
+ if (!body.formData[fieldKey]) {
92
+ return;
93
+ }
94
+ body.formData[fieldKey] = maskKey(body.formData[fieldKey]);
95
+ });
96
+ }
97
+ return originalSend.apply(res, [body]);
98
+ };
99
+ return next();
100
+ };
101
+ }
102
+ exports.getRequestCredentialsMasker = getRequestCredentialsMasker;
@@ -0,0 +1,3 @@
1
+ import { UiModule } from '../types';
2
+ export declare function getLowCodeUiConfigFromModuleConfig(moduleConfig: any): UiModule | null;
3
+ export declare function hasFormSchema(moduleConfig: any): boolean;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasFormSchema = exports.getLowCodeUiConfigFromModuleConfig = void 0;
4
+ function getLowCodeUiConfigFromModuleConfig(moduleConfig) {
5
+ var _a;
6
+ if (typeof moduleConfig !== 'object' || moduleConfig === null) {
7
+ return null;
8
+ }
9
+ if ((_a = moduleConfig.settingsUiModule) === null || _a === void 0 ? void 0 : _a.formSchema) {
10
+ return moduleConfig.settingsUiModule;
11
+ }
12
+ if (moduleConfig.formSchema) {
13
+ return moduleConfig;
14
+ }
15
+ return null;
16
+ }
17
+ exports.getLowCodeUiConfigFromModuleConfig = getLowCodeUiConfigFromModuleConfig;
18
+ function hasFormSchema(moduleConfig) {
19
+ return !!getLowCodeUiConfigFromModuleConfig(moduleConfig);
20
+ }
21
+ exports.hasFormSchema = hasFormSchema;
@@ -10,4 +10,3 @@ export declare function decryptData(config: Config, data: string): string;
10
10
  export declare function executeWithRetry<T>(func: () => Promise<T>, numOfRetries?: number): Promise<T>;
11
11
  export declare function getLogoUrl(moduleConfig?: ImagePath, modulePath?: string): string;
12
12
  export declare function isAuthorizedConfig(config: Config | UnauthorizedConfig): config is Config;
13
- export declare function hasFormSchema(moduleConfig: any): boolean;
package/out/util/index.js CHANGED
@@ -32,7 +32,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
32
32
  });
33
33
  };
34
34
  Object.defineProperty(exports, "__esModule", { value: true });
35
- exports.hasFormSchema = exports.isAuthorizedConfig = exports.getLogoUrl = exports.executeWithRetry = exports.decryptData = exports.encryptData = exports.runAsyncWrapper = exports.CodeError = void 0;
35
+ exports.isAuthorizedConfig = exports.getLogoUrl = exports.executeWithRetry = exports.decryptData = exports.encryptData = exports.runAsyncWrapper = exports.CodeError = void 0;
36
36
  const crypto = __importStar(require("crypto-js"));
37
37
  const storage_1 = require("../storage");
38
38
  const types_1 = require("../types");
@@ -118,11 +118,3 @@ function isAuthorizedConfig(config) {
118
118
  return !!config.clientId && !!config.clientSecret && config.authenticationType !== types_1.AuthenticationType.NONE;
119
119
  }
120
120
  exports.isAuthorizedConfig = isAuthorizedConfig;
121
- function hasFormSchema(moduleConfig) {
122
- var _a;
123
- if (typeof moduleConfig === 'object' && moduleConfig !== null) {
124
- return moduleConfig.formSchema || ((_a = moduleConfig.settingsUiModule) === null || _a === void 0 ? void 0 : _a.formSchema);
125
- }
126
- return false;
127
- }
128
- exports.hasFormSchema = hasFormSchema;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdin/app-project-module",
3
- "version": "0.61.0",
3
+ "version": "0.63.0",
4
4
  "description": "Module that generates for you all common endpoints for serving standalone Crowdin App",
5
5
  "main": "out/index.js",
6
6
  "types": "out/index.d.ts",