@crowdin/app-project-module 0.70.2 → 0.71.1
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/index.js +3 -3
- package/out/middlewares/integration-credentials.js +104 -6
- package/out/modules/integration/handlers/integration-logout.js +28 -0
- package/out/modules/integration/handlers/main.js +26 -0
- package/out/modules/integration/util/defaults.js +35 -0
- package/out/modules/integration/util/webhooks.js +1 -7
- package/out/modules/uninstall.js +4 -0
- package/out/static/css/styles.css +5 -1
- package/out/static/js/form.js +8 -8
- package/out/util/connection.js +2 -0
- package/out/views/error.handlebars +28 -5
- package/out/views/main.handlebars +9 -2
- package/package.json +10 -10
package/out/index.js
CHANGED
|
@@ -105,9 +105,9 @@ exports.metadataStore = {
|
|
|
105
105
|
return storage.getStorage().deleteMetadata(id);
|
|
106
106
|
},
|
|
107
107
|
getUserSettings: (clientId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
108
|
-
const
|
|
109
|
-
if (
|
|
110
|
-
return JSON.parse(
|
|
108
|
+
const integrationConfig = yield storage.getStorage().getIntegrationConfig(clientId);
|
|
109
|
+
if (integrationConfig === null || integrationConfig === void 0 ? void 0 : integrationConfig.config) {
|
|
110
|
+
return JSON.parse(integrationConfig.config);
|
|
111
111
|
}
|
|
112
112
|
}),
|
|
113
113
|
};
|
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
26
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
27
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -13,19 +36,54 @@ const storage_1 = require("../storage");
|
|
|
13
36
|
const util_1 = require("../util");
|
|
14
37
|
const connection_1 = require("../util/connection");
|
|
15
38
|
const logger_1 = require("../util/logger");
|
|
39
|
+
const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions"));
|
|
16
40
|
function handle(config, integration, optional = false) {
|
|
17
41
|
return (0, util_1.runAsyncWrapper)((req, res, next) => __awaiter(this, void 0, void 0, function* () {
|
|
18
|
-
|
|
42
|
+
let clientId = req.crowdinContext.clientId;
|
|
43
|
+
const { organization, projectId, userId } = crowdinAppFunctions.parseCrowdinId(clientId);
|
|
19
44
|
req.logInfo(`Loading integration credentials for client ${clientId}`);
|
|
20
|
-
|
|
21
|
-
const
|
|
45
|
+
let integrationCredentials = yield (0, storage_1.getStorage)().getIntegrationCredentials(clientId);
|
|
46
|
+
const ownerIds = [];
|
|
47
|
+
// check if user has access to integration in settings(managers)
|
|
48
|
+
if (!integrationCredentials) {
|
|
49
|
+
const projectIntegrationCredentials = (yield (0, storage_1.getStorage)().getAllIntegrationCredentials(organization)).filter((item) => {
|
|
50
|
+
const { organization: itemOrganization, projectId: itemProjectId } = crowdinAppFunctions.parseCrowdinId(item.id);
|
|
51
|
+
return itemOrganization === organization && itemProjectId === projectId;
|
|
52
|
+
});
|
|
53
|
+
if (projectIntegrationCredentials.length) {
|
|
54
|
+
for (const credentials of projectIntegrationCredentials) {
|
|
55
|
+
ownerIds.push(crowdinAppFunctions.parseCrowdinId(credentials.id).userId);
|
|
56
|
+
if (yield checkUserAccessToIntegration(credentials, `${userId}`)) {
|
|
57
|
+
integrationCredentials = credentials;
|
|
58
|
+
clientId = credentials.id;
|
|
59
|
+
req.crowdinContext.clientId = clientId;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
22
65
|
if (!integrationCredentials) {
|
|
23
|
-
|
|
66
|
+
const owners = yield getIntegrationManagedBy(ownerIds, req);
|
|
67
|
+
if (optional && !owners.length) {
|
|
24
68
|
return next();
|
|
25
69
|
}
|
|
26
|
-
|
|
27
|
-
|
|
70
|
+
const errorOptions = {
|
|
71
|
+
code: 403,
|
|
72
|
+
message: 'Access denied',
|
|
73
|
+
owners: null,
|
|
74
|
+
hideActions: false,
|
|
75
|
+
};
|
|
76
|
+
if (owners) {
|
|
77
|
+
errorOptions.message = 'Looks like you don’t have access';
|
|
78
|
+
errorOptions.hideActions = true;
|
|
79
|
+
errorOptions.owners = owners;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
(0, logger_1.temporaryErrorDebug)('Access denied: integration-credentials', req);
|
|
83
|
+
}
|
|
84
|
+
return res.render('error', errorOptions);
|
|
28
85
|
}
|
|
86
|
+
const integrationConfig = yield (0, storage_1.getStorage)().getIntegrationConfig(clientId);
|
|
29
87
|
if (integrationConfig === null || integrationConfig === void 0 ? void 0 : integrationConfig.config) {
|
|
30
88
|
req.integrationSettings = JSON.parse(integrationConfig.config);
|
|
31
89
|
}
|
|
@@ -40,3 +98,43 @@ function handle(config, integration, optional = false) {
|
|
|
40
98
|
}));
|
|
41
99
|
}
|
|
42
100
|
exports.default = handle;
|
|
101
|
+
function checkUserAccessToIntegration(integrationCredentials, userId) {
|
|
102
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
103
|
+
const projectIntegrationConfig = yield (0, storage_1.getStorage)().getIntegrationConfig(integrationCredentials.id);
|
|
104
|
+
if (projectIntegrationConfig === null || projectIntegrationConfig === void 0 ? void 0 : projectIntegrationConfig.config) {
|
|
105
|
+
const appSettings = JSON.parse(projectIntegrationConfig.config);
|
|
106
|
+
return ((appSettings === null || appSettings === void 0 ? void 0 : appSettings.managers) || []).includes(userId);
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
function getIntegrationManagedBy(ownerIds, req) {
|
|
112
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
113
|
+
if (!ownerIds.length) {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
const projectId = crowdinAppFunctions.getProjectId(req.crowdinContext.clientId);
|
|
117
|
+
let owners = [];
|
|
118
|
+
try {
|
|
119
|
+
owners =
|
|
120
|
+
ownerIds.length > 1
|
|
121
|
+
? (yield req.crowdinApiClient.usersApi.listProjectMembers(projectId)).data.filter((member) => ownerIds.includes(member.data.id))
|
|
122
|
+
: [yield req.crowdinApiClient.usersApi.getProjectMemberPermissions(projectId, ownerIds[0])];
|
|
123
|
+
}
|
|
124
|
+
catch (e) {
|
|
125
|
+
console.warn('Failed to get project members', e);
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
return owners.map((owner) => {
|
|
129
|
+
const ownerFullName = 'fullName' in owner.data
|
|
130
|
+
? owner.data.fullName
|
|
131
|
+
: `${owner.data.firstName || ''} ${owner.data.lastName || ''}`.trim();
|
|
132
|
+
return {
|
|
133
|
+
id: owner.data.id,
|
|
134
|
+
name: !!ownerFullName && owner.data.username !== ownerFullName
|
|
135
|
+
? `${ownerFullName} (${owner.data.username})`
|
|
136
|
+
: owner.data.username,
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
}
|
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
26
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
27
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -14,8 +37,13 @@ const util_1 = require("../../../util");
|
|
|
14
37
|
const logger_1 = require("../../../util/logger");
|
|
15
38
|
const subscription_1 = require("../../../util/subscription");
|
|
16
39
|
const webhooks_1 = require("../util/webhooks");
|
|
40
|
+
const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions"));
|
|
17
41
|
function handle(config, integration) {
|
|
18
42
|
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
43
|
+
const { userId } = crowdinAppFunctions.parseCrowdinId(req.crowdinContext.clientId);
|
|
44
|
+
if (+userId !== +req.crowdinContext.jwtPayload.sub) {
|
|
45
|
+
return res.status(403).send({ error: 'Access denied' });
|
|
46
|
+
}
|
|
19
47
|
req.logInfo('Received integration logout request');
|
|
20
48
|
if (integration.onLogout) {
|
|
21
49
|
try {
|
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
26
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
27
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -13,6 +36,7 @@ const util_1 = require("../../../util");
|
|
|
13
36
|
const logger_1 = require("../../../util/logger");
|
|
14
37
|
const subscription_1 = require("../../../util/subscription");
|
|
15
38
|
const defaults_1 = require("../util/defaults");
|
|
39
|
+
const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions"));
|
|
16
40
|
function handle(config, integration) {
|
|
17
41
|
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
18
42
|
var _a, _b;
|
|
@@ -41,6 +65,8 @@ function handle(config, integration) {
|
|
|
41
65
|
}
|
|
42
66
|
}
|
|
43
67
|
else if (integration.getConfiguration) {
|
|
68
|
+
const { userId } = crowdinAppFunctions.parseCrowdinId(req.crowdinContext.clientId);
|
|
69
|
+
options.isOwner = +req.crowdinContext.jwtPayload.sub === userId;
|
|
44
70
|
const configurationFields = yield integration.getConfiguration(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, req.integrationCredentials);
|
|
45
71
|
options.configurationFields = configurationFields;
|
|
46
72
|
options.config = JSON.stringify(req.integrationSettings || {});
|
|
@@ -147,6 +147,27 @@ function applyIntegrationModuleDefaults(config, integration) {
|
|
|
147
147
|
fields = yield getUserSettings(projectId, crowdinClient, integrationCredentials);
|
|
148
148
|
}
|
|
149
149
|
const defaultSettings = [];
|
|
150
|
+
const mangers = yield getManagers(projectId, crowdinClient, integrationCredentials);
|
|
151
|
+
if (mangers.length) {
|
|
152
|
+
defaultSettings.push({
|
|
153
|
+
key: 'managers',
|
|
154
|
+
label: 'Managers',
|
|
155
|
+
type: 'select',
|
|
156
|
+
isMulti: true,
|
|
157
|
+
isSearchable: true,
|
|
158
|
+
options: mangers.map((manager) => {
|
|
159
|
+
const ownerFullName = 'fullName' in manager
|
|
160
|
+
? manager.fullName
|
|
161
|
+
: ((manager.firstName || '') + ' ' + (manager.lastName || '')).trim();
|
|
162
|
+
return {
|
|
163
|
+
value: manager.id.toString(),
|
|
164
|
+
label: !!ownerFullName && (manager === null || manager === void 0 ? void 0 : manager.username) !== ownerFullName
|
|
165
|
+
? `${ownerFullName} (${manager === null || manager === void 0 ? void 0 : manager.username})`
|
|
166
|
+
: manager === null || manager === void 0 ? void 0 : manager.username,
|
|
167
|
+
};
|
|
168
|
+
}),
|
|
169
|
+
});
|
|
170
|
+
}
|
|
150
171
|
if (project.data.inContext) {
|
|
151
172
|
defaultSettings.push({
|
|
152
173
|
key: 'inContext',
|
|
@@ -299,3 +320,17 @@ function getOAuthLoginFormId(clientId) {
|
|
|
299
320
|
return `oauth_form_${clientId}`;
|
|
300
321
|
}
|
|
301
322
|
exports.getOAuthLoginFormId = getOAuthLoginFormId;
|
|
323
|
+
function getManagers(projectId, client, integrationCredentials) {
|
|
324
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
325
|
+
const managers = [];
|
|
326
|
+
if (client.organization) {
|
|
327
|
+
const admins = (yield client.usersApi.listUsers()).data.filter((user) => user.data.isAdmin);
|
|
328
|
+
managers.push(...admins.map((admin) => admin.data));
|
|
329
|
+
}
|
|
330
|
+
const projectMembers = (yield client.usersApi.listProjectMembers(projectId)).data.filter((user) => 'role' in user.data ? ['owner', 'manager'].includes(user.data.role) : user.data.isManager);
|
|
331
|
+
managers.push(...projectMembers.map((members) => members.data));
|
|
332
|
+
return managers
|
|
333
|
+
.filter((manager) => manager.id !== +integrationCredentials.ownerId)
|
|
334
|
+
.filter((manager, index, self) => self.findIndex((m) => m.id === manager.id) === index);
|
|
335
|
+
});
|
|
336
|
+
}
|
|
@@ -110,7 +110,6 @@ function registerWebhooks({ config, apiCredentials, appSettings, client, crowdin
|
|
|
110
110
|
projectId,
|
|
111
111
|
webhook,
|
|
112
112
|
events,
|
|
113
|
-
url: webhookUrl,
|
|
114
113
|
});
|
|
115
114
|
}
|
|
116
115
|
else if (isWebhookSync && !webhook) {
|
|
@@ -198,7 +197,7 @@ function registerCrowdinWebhook({ client, config, crowdinContext, events, url, }
|
|
|
198
197
|
});
|
|
199
198
|
});
|
|
200
199
|
}
|
|
201
|
-
function updateCrowdinWebhooks({ client, events, projectId,
|
|
200
|
+
function updateCrowdinWebhooks({ client, events, projectId, webhook, }) {
|
|
202
201
|
return __awaiter(this, void 0, void 0, function* () {
|
|
203
202
|
const payload = createPayload(events);
|
|
204
203
|
yield client.webhooksApi.editWebhook(projectId, webhook.id, [
|
|
@@ -212,11 +211,6 @@ function updateCrowdinWebhooks({ client, events, projectId, url, webhook, }) {
|
|
|
212
211
|
op: 'replace',
|
|
213
212
|
path: '/payload',
|
|
214
213
|
},
|
|
215
|
-
{
|
|
216
|
-
value: url,
|
|
217
|
-
op: 'replace',
|
|
218
|
-
path: '/url',
|
|
219
|
-
},
|
|
220
214
|
]);
|
|
221
215
|
});
|
|
222
216
|
}
|
package/out/modules/uninstall.js
CHANGED
|
@@ -20,6 +20,10 @@ function handle(config) {
|
|
|
20
20
|
(0, logger_1.log)(`Received uninstall request ${JSON.stringify(event, null, 2)}`);
|
|
21
21
|
const projectIntegration = config.projectIntegration;
|
|
22
22
|
const organization = (event.domain || event.organizationId).toString();
|
|
23
|
+
const crowdinCredential = yield (0, storage_1.getStorage)().getCrowdinCredentials(organization);
|
|
24
|
+
if ((crowdinCredential === null || crowdinCredential === void 0 ? void 0 : crowdinCredential.appSecret) !== event.appSecret) {
|
|
25
|
+
return res.status(403).send({ error: 'Access denied' });
|
|
26
|
+
}
|
|
23
27
|
if (config.onUninstall) {
|
|
24
28
|
let allCredentials = [];
|
|
25
29
|
if (projectIntegration) {
|