@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 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 integrationCredentials = yield storage.getStorage().getIntegrationConfig(clientId);
109
- if (integrationCredentials === null || integrationCredentials === void 0 ? void 0 : integrationCredentials.config) {
110
- return JSON.parse(integrationCredentials.config);
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
- const clientId = req.crowdinContext.clientId;
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
- const integrationCredentials = yield (0, storage_1.getStorage)().getIntegrationCredentials(clientId);
21
- const integrationConfig = yield (0, storage_1.getStorage)().getIntegrationConfig(clientId);
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
- if (optional) {
66
+ const owners = yield getIntegrationManagedBy(ownerIds, req);
67
+ if (optional && !owners.length) {
24
68
  return next();
25
69
  }
26
- (0, logger_1.temporaryErrorDebug)('Access denied: integration-credentials', req);
27
- return res.status(403).send({ error: 'Access denied' });
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, url, webhook, }) {
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
  }
@@ -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) {
@@ -59,6 +59,10 @@
59
59
  margin-left: 8px;
60
60
  }
61
61
 
62
+ .mt-2 {
63
+ margin-top: 16px;
64
+ }
65
+
62
66
  .m-0 {
63
67
  margin: 0;
64
68
  }
@@ -254,4 +258,4 @@
254
258
  form > .MuiBox-root {
255
259
  display: none;
256
260
  }
257
- }
261
+ }