@oondemand/oon-core-back 0.4.1 → 0.4.3
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/package.json +1 -1
- package/src/api/activation.routes.js +66 -27
- package/src/api/auth.routes.js +32 -2
- package/src/core/middlewares/error.js +9 -1
- package/src/core/models/InstanciaEcossistema.js +7 -0
- package/src/core/rbac/identityProvider.js +5 -3
- package/src/core/utils/instanceActivation.js +22 -0
- package/src/manifest/appManifest.js +47 -5
package/package.json
CHANGED
|
@@ -18,6 +18,7 @@ const {
|
|
|
18
18
|
validateRootCredentials,
|
|
19
19
|
encrypt,
|
|
20
20
|
instanceHeaders,
|
|
21
|
+
operationalIdentity,
|
|
21
22
|
validateParams,
|
|
22
23
|
providerPost,
|
|
23
24
|
providerPayload,
|
|
@@ -83,13 +84,11 @@ function safeContext(data) {
|
|
|
83
84
|
|
|
84
85
|
function requireRootBootstrapResult(result) {
|
|
85
86
|
const usuarioId = result?.usuario?.id || result?.usuario?._id;
|
|
86
|
-
const tenantId = result?.tenant?.id || result?.tenant?._id;
|
|
87
87
|
const appId = result?.app?.id || result?.app?._id;
|
|
88
|
-
const licencaId = result?.licenca?.id || result?.licenca?._id;
|
|
89
88
|
|
|
90
|
-
if (!usuarioId || !
|
|
89
|
+
if (!usuarioId || !appId) {
|
|
91
90
|
throw new GenericError(
|
|
92
|
-
"O bootstrap root não retornou
|
|
91
|
+
"O bootstrap root não retornou Usuario e App de sistema.",
|
|
93
92
|
{ statusCode: 500 },
|
|
94
93
|
);
|
|
95
94
|
}
|
|
@@ -106,9 +105,8 @@ function requireRootBootstrapResult(result) {
|
|
|
106
105
|
|
|
107
106
|
return {
|
|
108
107
|
usuarioId: String(usuarioId),
|
|
109
|
-
tenantId: String(tenantId),
|
|
110
108
|
appId: String(appId),
|
|
111
|
-
|
|
109
|
+
systemAccessId: idOf(result?.systemAccess) ? String(idOf(result.systemAccess)) : undefined,
|
|
112
110
|
};
|
|
113
111
|
}
|
|
114
112
|
|
|
@@ -141,11 +139,17 @@ function idOf(value) {
|
|
|
141
139
|
return value?.id || value?._id || value;
|
|
142
140
|
}
|
|
143
141
|
|
|
144
|
-
function
|
|
142
|
+
function summarizeOperationalIdentity(data, appCode, { allowSynthetic = false } = {}) {
|
|
145
143
|
const appId = idOf(data?.app);
|
|
144
|
+
const deploymentId = idOf(data?.deployment);
|
|
145
|
+
const instanceId = idOf(data?.instancia || data?.instance);
|
|
146
146
|
return {
|
|
147
|
-
instanceId: `dev-local:${appId || appCode}
|
|
148
|
-
instanceCode: "dev-local",
|
|
147
|
+
instanceId: instanceId ? String(instanceId) : (allowSynthetic ? `dev-local:${appId || appCode}` : undefined),
|
|
148
|
+
instanceCode: data?.instancia?.codigo || data?.instance?.codigo || (allowSynthetic ? "dev-local" : undefined),
|
|
149
|
+
deploymentId: deploymentId ? String(deploymentId) : undefined,
|
|
150
|
+
bindingId: idOf(data?.binding) ? String(idOf(data.binding)) : undefined,
|
|
151
|
+
entitlementId: idOf(data?.entitlement) ? String(idOf(data.entitlement)) : undefined,
|
|
152
|
+
credentialType: deploymentId ? "deployment" : "instance",
|
|
149
153
|
appId: appId ? String(appId) : undefined,
|
|
150
154
|
tenantId: idOf(data?.tenant) ? String(idOf(data.tenant)) : undefined,
|
|
151
155
|
licencaId: idOf(data?.licenca) ? String(idOf(data.licenca)) : undefined,
|
|
@@ -171,7 +175,7 @@ router.get(
|
|
|
171
175
|
const canActivateMember =
|
|
172
176
|
instance.ecosystemRole === "member" &&
|
|
173
177
|
["nao_ativada", "erro"].includes(instance.status) &&
|
|
174
|
-
!instance
|
|
178
|
+
!operationalIdentity(instance);
|
|
175
179
|
|
|
176
180
|
sendResponse({
|
|
177
181
|
res,
|
|
@@ -182,7 +186,7 @@ router.get(
|
|
|
182
186
|
podeAtivar: canActivateRoot || canActivateMember,
|
|
183
187
|
podeTentarNovamente:
|
|
184
188
|
instance.ecosystemRole === "member" &&
|
|
185
|
-
Boolean(instance
|
|
189
|
+
Boolean(operationalIdentity(instance)) &&
|
|
186
190
|
["configurando", "erro"].includes(instance.status),
|
|
187
191
|
motivoSuspensao: instance.motivoSuspensao,
|
|
188
192
|
motivoRevogacao: instance.motivoRevogacao,
|
|
@@ -284,14 +288,14 @@ router.post(
|
|
|
284
288
|
instanceCode: result.instanceCode || "root",
|
|
285
289
|
appCode: getAppCode(),
|
|
286
290
|
appId: ids.appId,
|
|
287
|
-
tenantId:
|
|
288
|
-
licencaId:
|
|
291
|
+
tenantId: undefined,
|
|
292
|
+
licencaId: undefined,
|
|
289
293
|
ambiente: ambiente(),
|
|
290
294
|
nomeInstancia: ctx.config.name || ctx.config.slug || getAppCode(),
|
|
291
295
|
publicUrl: publicUrl(req),
|
|
292
|
-
tenant: result.tenant,
|
|
293
296
|
app: result.app,
|
|
294
|
-
|
|
297
|
+
tenant: undefined,
|
|
298
|
+
licenca: undefined,
|
|
295
299
|
parametrosGlobais: {},
|
|
296
300
|
parametrosAplicacao: {},
|
|
297
301
|
configurationVersion: config.configurationVersion,
|
|
@@ -389,6 +393,13 @@ router.post(
|
|
|
389
393
|
branch: req.body?.branch,
|
|
390
394
|
nomeUsuario: req.body?.nomeUsuario,
|
|
391
395
|
nomeTenant: req.body?.nomeTenant,
|
|
396
|
+
ecosystemRole: ctx.config.ecosystem?.role,
|
|
397
|
+
tenancyModel: ctx.config.tenancyModel || ctx.config.ecosystem?.tenancyModel,
|
|
398
|
+
availability: ctx.config.availability || ctx.config.ecosystem?.availability,
|
|
399
|
+
accessMode: ctx.config.accessMode || ctx.config.ecosystem?.accessMode,
|
|
400
|
+
authorizationMode:
|
|
401
|
+
ctx.config.authorizationMode || ctx.config.ecosystem?.authorizationMode,
|
|
402
|
+
deploymentMode: ctx.config.deploymentMode || ctx.config.ecosystem?.deploymentMode,
|
|
392
403
|
},
|
|
393
404
|
{ Authorization: authorization },
|
|
394
405
|
);
|
|
@@ -399,7 +410,8 @@ router.post(
|
|
|
399
410
|
const resolvedAppCode = String(
|
|
400
411
|
data.app?.codigo || data.appCode || appCode,
|
|
401
412
|
).toLowerCase();
|
|
402
|
-
const ids =
|
|
413
|
+
const ids = summarizeOperationalIdentity(data, resolvedAppCode, { allowSynthetic: true });
|
|
414
|
+
const operationalToken = data?.credencial?.token || data?.deploymentCredential?.token;
|
|
403
415
|
const now = new Date();
|
|
404
416
|
|
|
405
417
|
Object.assign(instance, {
|
|
@@ -407,6 +419,10 @@ router.post(
|
|
|
407
419
|
ecosystemRole: "member",
|
|
408
420
|
instanceId: ids.instanceId,
|
|
409
421
|
instanceCode: ids.instanceCode,
|
|
422
|
+
deploymentId: ids.deploymentId,
|
|
423
|
+
bindingId: ids.bindingId,
|
|
424
|
+
entitlementId: ids.entitlementId,
|
|
425
|
+
credentialType: ids.credentialType,
|
|
410
426
|
appCode: resolvedAppCode,
|
|
411
427
|
appId: ids.appId,
|
|
412
428
|
tenantId: ids.tenantId,
|
|
@@ -417,13 +433,22 @@ router.post(
|
|
|
417
433
|
publicUrl: urlPublica,
|
|
418
434
|
coreVersion: process.env.npm_package_version || "0.0.0",
|
|
419
435
|
appVersion: process.env.SERVICE_VERSION || "0.1.0",
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
436
|
+
...(operationalToken ? encrypt(operationalToken) : {
|
|
437
|
+
credentialEncrypted: undefined,
|
|
438
|
+
credentialIv: undefined,
|
|
439
|
+
credentialAuthTag: undefined,
|
|
440
|
+
}),
|
|
441
|
+
credentialVersion:
|
|
442
|
+
data?.credencial?.versao ||
|
|
443
|
+
data?.credencial?.version ||
|
|
444
|
+
data?.deploymentCredential?.versao ||
|
|
445
|
+
data?.deploymentCredential?.version,
|
|
424
446
|
tenant: data.tenant,
|
|
425
447
|
app: data.app,
|
|
426
448
|
licenca: data.licenca,
|
|
449
|
+
deployment: data.deployment,
|
|
450
|
+
binding: data.binding,
|
|
451
|
+
entitlement: data.entitlement,
|
|
427
452
|
parametrosGlobais: {
|
|
428
453
|
...globalDefaults,
|
|
429
454
|
nomeInstancia: nomeCentral,
|
|
@@ -455,6 +480,7 @@ router.post(
|
|
|
455
480
|
sucesso: true,
|
|
456
481
|
status: "ativa",
|
|
457
482
|
instanceId: instance.instanceId,
|
|
483
|
+
deploymentId: instance.deploymentId,
|
|
458
484
|
});
|
|
459
485
|
} catch (error) {
|
|
460
486
|
instance.status = "erro";
|
|
@@ -550,7 +576,7 @@ router.post(
|
|
|
550
576
|
const instance = await ensureInstance();
|
|
551
577
|
|
|
552
578
|
if (
|
|
553
|
-
instance
|
|
579
|
+
operationalIdentity(instance) &&
|
|
554
580
|
["configurando", "erro"].includes(instance.status)
|
|
555
581
|
) {
|
|
556
582
|
return res.redirect(307, "/ativacao/tentar-novamente");
|
|
@@ -589,8 +615,9 @@ router.post(
|
|
|
589
615
|
);
|
|
590
616
|
assertApp(activated.data);
|
|
591
617
|
|
|
592
|
-
const token = activated.data?.credencial?.token;
|
|
593
|
-
|
|
618
|
+
const token = activated.data?.credencial?.token || activated.data?.deploymentCredential?.token;
|
|
619
|
+
const ids = summarizeOperationalIdentity(activated.data, getAppCode());
|
|
620
|
+
if (!(ids.deploymentId || ids.instanceId) || !token) {
|
|
594
621
|
throw new GenericError("Resposta de ativação inválida.", {
|
|
595
622
|
statusCode: 502,
|
|
596
623
|
});
|
|
@@ -598,9 +625,17 @@ router.post(
|
|
|
598
625
|
|
|
599
626
|
Object.assign(instance, encrypt(token), {
|
|
600
627
|
status: "configurando",
|
|
601
|
-
instanceId:
|
|
602
|
-
instanceCode:
|
|
603
|
-
|
|
628
|
+
instanceId: ids.instanceId,
|
|
629
|
+
instanceCode: ids.instanceCode,
|
|
630
|
+
deploymentId: ids.deploymentId,
|
|
631
|
+
bindingId: ids.bindingId,
|
|
632
|
+
entitlementId: ids.entitlementId,
|
|
633
|
+
credentialType: ids.credentialType,
|
|
634
|
+
credentialVersion:
|
|
635
|
+
activated.data.credencial?.versao ||
|
|
636
|
+
activated.data.credencial?.version ||
|
|
637
|
+
activated.data.deploymentCredential?.versao ||
|
|
638
|
+
activated.data.deploymentCredential?.version,
|
|
604
639
|
appCode: getAppCode(),
|
|
605
640
|
appId: activated.data.app?.id || preview.data?.app?.id,
|
|
606
641
|
tenantId: activated.data.tenant?.id || preview.data?.tenant?.id,
|
|
@@ -611,6 +646,9 @@ router.post(
|
|
|
611
646
|
tenant: activated.data.tenant || preview.data?.tenant,
|
|
612
647
|
app: activated.data.app || preview.data?.app,
|
|
613
648
|
licenca: activated.data.licenca || preview.data?.licenca,
|
|
649
|
+
deployment: activated.data.deployment || preview.data?.deployment,
|
|
650
|
+
binding: activated.data.binding || preview.data?.binding,
|
|
651
|
+
entitlement: activated.data.entitlement || preview.data?.entitlement,
|
|
614
652
|
parametrosGlobais,
|
|
615
653
|
parametrosAplicacao,
|
|
616
654
|
configurationVersion: config.configurationVersion,
|
|
@@ -629,6 +667,7 @@ router.post(
|
|
|
629
667
|
sucesso: true,
|
|
630
668
|
status: "ativa",
|
|
631
669
|
instanceId: instance.instanceId,
|
|
670
|
+
deploymentId: instance.deploymentId,
|
|
632
671
|
});
|
|
633
672
|
}),
|
|
634
673
|
);
|
|
@@ -687,7 +726,7 @@ router.post(
|
|
|
687
726
|
const config = activationConfig();
|
|
688
727
|
|
|
689
728
|
if (
|
|
690
|
-
!instance
|
|
729
|
+
!operationalIdentity(instance) ||
|
|
691
730
|
!["configurando", "erro"].includes(instance.status)
|
|
692
731
|
) {
|
|
693
732
|
throw new GenericError(
|
package/src/api/auth.routes.js
CHANGED
|
@@ -15,6 +15,7 @@ const {
|
|
|
15
15
|
} = require("../core/utils/activationProvider");
|
|
16
16
|
|
|
17
17
|
const DEV_ACTIVATION_PATH = "/integracao/dev-local/ativar-central";
|
|
18
|
+
const TENANT_HEADER = "x-oon-tenant-id";
|
|
18
19
|
const router = express.Router();
|
|
19
20
|
|
|
20
21
|
function readBasicAuthorization(req) {
|
|
@@ -35,10 +36,30 @@ function translateProviderError(error) {
|
|
|
35
36
|
if (error instanceof GenericError) return error;
|
|
36
37
|
const timedOut = error.code === "ECONNABORTED";
|
|
37
38
|
const statusCode = timedOut ? 504 : error.response?.status || 502;
|
|
38
|
-
const
|
|
39
|
+
const providerError = error.response?.data?.error;
|
|
40
|
+
const envelope = providerError && typeof providerError === "object"
|
|
41
|
+
? providerError
|
|
42
|
+
: {};
|
|
43
|
+
const message = envelope.message || error.response?.data?.message || (timedOut
|
|
39
44
|
? "A Central de Ativações não respondeu dentro do prazo."
|
|
40
45
|
: "Não foi possível autenticar o usuário na Central de Ativações.");
|
|
41
|
-
return new GenericError(message, {
|
|
46
|
+
return new GenericError(message, {
|
|
47
|
+
statusCode,
|
|
48
|
+
code: envelope.code || error.response?.data?.code,
|
|
49
|
+
details: envelope.details ?? error.response?.data?.details ?? null,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function tenantForLogin(req, instance) {
|
|
54
|
+
const tenancyModel =
|
|
55
|
+
ctx.config.tenancyModel || ctx.config.ecosystem?.tenancyModel;
|
|
56
|
+
if (tenancyModel === "multi_tenant") {
|
|
57
|
+
// Uma instância compartilhada não pertence ao tenant que a ativou
|
|
58
|
+
// originalmente. Sem seleção explícita, a Central de Ativações resolve o
|
|
59
|
+
// único tenant elegível ou devolve as opções disponíveis.
|
|
60
|
+
return req.headers[TENANT_HEADER];
|
|
61
|
+
}
|
|
62
|
+
return instance.tenantId;
|
|
42
63
|
}
|
|
43
64
|
|
|
44
65
|
async function authenticateRoot(req, res, authorization, instance) {
|
|
@@ -62,6 +83,7 @@ router.post(
|
|
|
62
83
|
if (await authenticateRoot(req, res, authorization, instance)) return;
|
|
63
84
|
const baseUrl = getBaseUrl(req);
|
|
64
85
|
const appCode = getAppCode();
|
|
86
|
+
const tenantId = tenantForLogin(req, instance);
|
|
65
87
|
try {
|
|
66
88
|
const response = await axios.post(
|
|
67
89
|
`${baseUrl}${LOGIN_PATH}`,
|
|
@@ -73,6 +95,7 @@ router.post(
|
|
|
73
95
|
Authorization: authorization,
|
|
74
96
|
"Content-Type": "application/json",
|
|
75
97
|
"x-app-code": appCode,
|
|
98
|
+
...(tenantId ? { [TENANT_HEADER]: tenantId } : {}),
|
|
76
99
|
},
|
|
77
100
|
},
|
|
78
101
|
);
|
|
@@ -104,6 +127,13 @@ router.post(
|
|
|
104
127
|
branch: req.body?.branch,
|
|
105
128
|
nomeUsuario: req.body?.nomeUsuario,
|
|
106
129
|
nomeTenant: req.body?.nomeTenant,
|
|
130
|
+
ecosystemRole: ctx.config.ecosystem?.role,
|
|
131
|
+
tenancyModel: ctx.config.tenancyModel || ctx.config.ecosystem?.tenancyModel,
|
|
132
|
+
availability: ctx.config.availability || ctx.config.ecosystem?.availability,
|
|
133
|
+
accessMode: ctx.config.accessMode || ctx.config.ecosystem?.accessMode,
|
|
134
|
+
authorizationMode:
|
|
135
|
+
ctx.config.authorizationMode || ctx.config.ecosystem?.authorizationMode,
|
|
136
|
+
deploymentMode: ctx.config.deploymentMode || ctx.config.ecosystem?.deploymentMode,
|
|
107
137
|
},
|
|
108
138
|
{
|
|
109
139
|
timeout: getTimeout(),
|
|
@@ -58,10 +58,18 @@ const errorMiddleware = (error, req, res, next) => {
|
|
|
58
58
|
const details = error.details && typeof error.details === "object" && !Array.isArray(error.details)
|
|
59
59
|
? { ...error.details, ...(requestId ? { requestId } : {}) }
|
|
60
60
|
: error.details;
|
|
61
|
+
const envelope = error.code
|
|
62
|
+
? {
|
|
63
|
+
code: error.code,
|
|
64
|
+
message: error.message,
|
|
65
|
+
...(details !== null && details !== undefined ? { details } : {}),
|
|
66
|
+
...(requestId ? { requestId } : {}),
|
|
67
|
+
}
|
|
68
|
+
: details;
|
|
61
69
|
return helpers.sendErrorResponse({
|
|
62
70
|
res,
|
|
63
71
|
statusCode: error.statusCode,
|
|
64
|
-
error:
|
|
72
|
+
error: envelope,
|
|
65
73
|
message: error.message,
|
|
66
74
|
});
|
|
67
75
|
}
|
|
@@ -10,6 +10,10 @@ const schema = new mongoose.Schema(
|
|
|
10
10
|
ecosystemRole: { type: String, enum: roles, default: "member", index: true },
|
|
11
11
|
instanceId: { type: String, index: true },
|
|
12
12
|
instanceCode: String,
|
|
13
|
+
deploymentId: { type: String, index: true },
|
|
14
|
+
bindingId: String,
|
|
15
|
+
entitlementId: String,
|
|
16
|
+
credentialType: { type: String, enum: ["instance", "deployment"] },
|
|
13
17
|
appCode: { type: String, index: true },
|
|
14
18
|
appId: String,
|
|
15
19
|
tenantId: String,
|
|
@@ -30,6 +34,9 @@ const schema = new mongoose.Schema(
|
|
|
30
34
|
parametrosAplicacao: { type: mongoose.Schema.Types.Mixed, default: {} },
|
|
31
35
|
tenant: { type: mongoose.Schema.Types.Mixed, default: {} },
|
|
32
36
|
app: { type: mongoose.Schema.Types.Mixed, default: {} },
|
|
37
|
+
deployment: { type: mongoose.Schema.Types.Mixed, default: {} },
|
|
38
|
+
binding: { type: mongoose.Schema.Types.Mixed, default: {} },
|
|
39
|
+
entitlement: { type: mongoose.Schema.Types.Mixed, default: {} },
|
|
33
40
|
licenca: { type: mongoose.Schema.Types.Mixed, default: {} },
|
|
34
41
|
ativadaEm: Date,
|
|
35
42
|
concluidaEm: Date,
|
|
@@ -5,6 +5,7 @@ const GenericError = require("../errors/GenericError");
|
|
|
5
5
|
const {
|
|
6
6
|
ensureInstance,
|
|
7
7
|
instanceHeaders,
|
|
8
|
+
operationalIdentity,
|
|
8
9
|
providerPost,
|
|
9
10
|
} = require("../utils/instanceActivation");
|
|
10
11
|
|
|
@@ -15,10 +16,10 @@ const IDENTITY_PATHS = Object.freeze({
|
|
|
15
16
|
|
|
16
17
|
async function defaultProviderCall(req, path, payload) {
|
|
17
18
|
const instance = await ensureInstance();
|
|
18
|
-
if (!instance
|
|
19
|
-
throw new GenericError("
|
|
19
|
+
if (!operationalIdentity(instance) || !instance?.credentialEncrypted) {
|
|
20
|
+
throw new GenericError("O deployment precisa estar ativo para gerenciar identidades.", {
|
|
20
21
|
statusCode: 423,
|
|
21
|
-
code: "
|
|
22
|
+
code: "RBAC_OPERATIONAL_CREDENTIAL_REQUIRED",
|
|
22
23
|
});
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -43,6 +44,7 @@ async function provisionInvitation({ req, invitation, password }) {
|
|
|
43
44
|
return defaultProviderCall(req, IDENTITY_PATHS.provisionInvitation, {
|
|
44
45
|
email: invitation.email,
|
|
45
46
|
nome: invitation.name,
|
|
47
|
+
roleCode: invitation.roleCode,
|
|
46
48
|
password,
|
|
47
49
|
invitationFingerprint: invitation.tokenFingerprint,
|
|
48
50
|
});
|
|
@@ -217,6 +217,10 @@ async function domainHasActiveRootApp() {
|
|
|
217
217
|
function clearInstanceIdentity(instance) {
|
|
218
218
|
instance.instanceId = undefined;
|
|
219
219
|
instance.instanceCode = undefined;
|
|
220
|
+
instance.deploymentId = undefined;
|
|
221
|
+
instance.bindingId = undefined;
|
|
222
|
+
instance.entitlementId = undefined;
|
|
223
|
+
instance.credentialType = undefined;
|
|
220
224
|
instance.appId = undefined;
|
|
221
225
|
instance.tenantId = undefined;
|
|
222
226
|
instance.licencaId = undefined;
|
|
@@ -393,6 +397,14 @@ function decrypt(instance) {
|
|
|
393
397
|
}
|
|
394
398
|
|
|
395
399
|
function instanceHeaders(instance) {
|
|
400
|
+
if (instance.deploymentId) {
|
|
401
|
+
return {
|
|
402
|
+
"x-oon-deployment-id": instance.deploymentId,
|
|
403
|
+
"x-oon-deployment-token": decrypt(instance),
|
|
404
|
+
"x-app-code": getAppCode(),
|
|
405
|
+
...(instance.tenantId ? { "x-oon-tenant-id": instance.tenantId } : {}),
|
|
406
|
+
};
|
|
407
|
+
}
|
|
396
408
|
return {
|
|
397
409
|
"x-oon-instance-id": instance.instanceId,
|
|
398
410
|
"x-oon-instance-token": decrypt(instance),
|
|
@@ -400,6 +412,10 @@ function instanceHeaders(instance) {
|
|
|
400
412
|
};
|
|
401
413
|
}
|
|
402
414
|
|
|
415
|
+
function operationalIdentity(instance) {
|
|
416
|
+
return instance?.deploymentId || instance?.instanceId;
|
|
417
|
+
}
|
|
418
|
+
|
|
403
419
|
function validateUrl(value) {
|
|
404
420
|
const url = new URL(value);
|
|
405
421
|
if (!["http:", "https:"].includes(url.protocol)) throw new Error();
|
|
@@ -468,6 +484,11 @@ function providerPayload(req, codigoAtivacao, extra = {}) {
|
|
|
468
484
|
coreVersion: version(),
|
|
469
485
|
appVersion: version(),
|
|
470
486
|
fingerprint: fingerprint(),
|
|
487
|
+
tenancyModel: ctx.config.tenancyModel || ctx.config.ecosystem?.tenancyModel,
|
|
488
|
+
availability: ctx.config.availability || ctx.config.ecosystem?.availability,
|
|
489
|
+
accessMode: ctx.config.accessMode || ctx.config.ecosystem?.accessMode,
|
|
490
|
+
authorizationMode: ctx.config.authorizationMode || ctx.config.ecosystem?.authorizationMode,
|
|
491
|
+
deploymentMode: ctx.config.deploymentMode || ctx.config.ecosystem?.deploymentMode,
|
|
471
492
|
...extra,
|
|
472
493
|
};
|
|
473
494
|
}
|
|
@@ -491,6 +512,7 @@ module.exports = {
|
|
|
491
512
|
encrypt,
|
|
492
513
|
decrypt,
|
|
493
514
|
instanceHeaders,
|
|
515
|
+
operationalIdentity,
|
|
494
516
|
validateParams,
|
|
495
517
|
providerPost,
|
|
496
518
|
providerPayload,
|
|
@@ -14,6 +14,9 @@ const SUPPORTED_MODULES = Object.freeze([
|
|
|
14
14
|
]);
|
|
15
15
|
const SUPPORTED_TENANCY_MODELS = Object.freeze(["none", "single_tenant", "multi_tenant"]);
|
|
16
16
|
const SUPPORTED_AVAILABILITIES = Object.freeze(["system", "common", "licensed"]);
|
|
17
|
+
const SUPPORTED_ACCESS_MODES = Object.freeze(["tenant_members", "explicit_users"]);
|
|
18
|
+
const SUPPORTED_AUTHORIZATION_MODES = Object.freeze(["none", "rbac"]);
|
|
19
|
+
// Compatibilidade de leitura para manifestos anteriores à separação das duas dimensões.
|
|
17
20
|
const SUPPORTED_ACCESS_POLICIES = Object.freeze(["tenant_members", "explicit_users", "rbac"]);
|
|
18
21
|
const SUPPORTED_DEPLOYMENT_MODES = Object.freeze(["singleton", "shared", "dedicated", "hybrid"]);
|
|
19
22
|
const APP_KIND_ROLE = Object.freeze({
|
|
@@ -25,19 +28,22 @@ const APP_KIND_DIMENSIONS = Object.freeze({
|
|
|
25
28
|
"root-central": Object.freeze({
|
|
26
29
|
tenancyModel: "none",
|
|
27
30
|
availability: "system",
|
|
28
|
-
|
|
31
|
+
accessMode: "explicit_users",
|
|
32
|
+
authorizationMode: "rbac",
|
|
29
33
|
deploymentMode: "singleton",
|
|
30
34
|
}),
|
|
31
35
|
"member-central": Object.freeze({
|
|
32
36
|
tenancyModel: "single_tenant",
|
|
33
37
|
availability: "licensed",
|
|
34
|
-
|
|
38
|
+
accessMode: "explicit_users",
|
|
39
|
+
authorizationMode: "rbac",
|
|
35
40
|
deploymentMode: "dedicated",
|
|
36
41
|
}),
|
|
37
42
|
"portal-cockpit": Object.freeze({
|
|
38
43
|
tenancyModel: "multi_tenant",
|
|
39
44
|
availability: "common",
|
|
40
|
-
|
|
45
|
+
accessMode: "tenant_members",
|
|
46
|
+
authorizationMode: "none",
|
|
41
47
|
deploymentMode: "shared",
|
|
42
48
|
}),
|
|
43
49
|
});
|
|
@@ -136,12 +142,22 @@ function validateDimension(value, field, supported, issues) {
|
|
|
136
142
|
}
|
|
137
143
|
}
|
|
138
144
|
|
|
145
|
+
function legacyAccessDimensions(accessPolicy) {
|
|
146
|
+
return {
|
|
147
|
+
tenant_members: { accessMode: "tenant_members", authorizationMode: "none" },
|
|
148
|
+
explicit_users: { accessMode: "explicit_users", authorizationMode: "none" },
|
|
149
|
+
rbac: { accessMode: "explicit_users", authorizationMode: "rbac" },
|
|
150
|
+
}[accessPolicy];
|
|
151
|
+
}
|
|
152
|
+
|
|
139
153
|
function dimensionsFor(manifest) {
|
|
140
154
|
const defaults = APP_KIND_DIMENSIONS[manifest.appKind] || APP_KIND_DIMENSIONS["member-central"];
|
|
155
|
+
const legacy = legacyAccessDimensions(manifest.accessPolicy) || {};
|
|
141
156
|
return {
|
|
142
157
|
tenancyModel: manifest.tenancyModel || defaults.tenancyModel,
|
|
143
158
|
availability: manifest.availability || defaults.availability,
|
|
144
|
-
|
|
159
|
+
accessMode: manifest.accessMode || legacy.accessMode || defaults.accessMode,
|
|
160
|
+
authorizationMode: manifest.authorizationMode || legacy.authorizationMode || defaults.authorizationMode,
|
|
145
161
|
deploymentMode: manifest.deploymentMode || defaults.deploymentMode,
|
|
146
162
|
};
|
|
147
163
|
}
|
|
@@ -153,7 +169,7 @@ function validateAppManifest(input, { coreVersion = corePackage.version } = {})
|
|
|
153
169
|
}
|
|
154
170
|
const allowedTopLevel = new Set([
|
|
155
171
|
"$schema", "schemaVersion", "id", "name", "slug", "appKind",
|
|
156
|
-
"tenancyModel", "availability", "accessPolicy", "deploymentMode",
|
|
172
|
+
"tenancyModel", "availability", "accessMode", "authorizationMode", "accessPolicy", "deploymentMode",
|
|
157
173
|
"modules", "capabilities", "compatibility", "activation", "rbac",
|
|
158
174
|
]);
|
|
159
175
|
for (const key of Object.keys(input)) {
|
|
@@ -174,8 +190,30 @@ function validateAppManifest(input, { coreVersion = corePackage.version } = {})
|
|
|
174
190
|
}
|
|
175
191
|
validateDimension(input.tenancyModel, "tenancyModel", SUPPORTED_TENANCY_MODELS, issues);
|
|
176
192
|
validateDimension(input.availability, "availability", SUPPORTED_AVAILABILITIES, issues);
|
|
193
|
+
validateDimension(input.accessMode, "accessMode", SUPPORTED_ACCESS_MODES, issues);
|
|
194
|
+
validateDimension(input.authorizationMode, "authorizationMode", SUPPORTED_AUTHORIZATION_MODES, issues);
|
|
177
195
|
validateDimension(input.accessPolicy, "accessPolicy", SUPPORTED_ACCESS_POLICIES, issues);
|
|
178
196
|
validateDimension(input.deploymentMode, "deploymentMode", SUPPORTED_DEPLOYMENT_MODES, issues);
|
|
197
|
+
if (input.accessPolicy !== undefined && (input.accessMode !== undefined || input.authorizationMode !== undefined)) {
|
|
198
|
+
addIssue(issues, "accessPolicy", "é legado e não pode ser combinado com accessMode/authorizationMode.");
|
|
199
|
+
}
|
|
200
|
+
const dimensions = dimensionsFor(input);
|
|
201
|
+
const role = APP_KIND_ROLE[input.appKind];
|
|
202
|
+
if (role === "root" && (
|
|
203
|
+
dimensions.tenancyModel !== "none" ||
|
|
204
|
+
dimensions.availability !== "system" ||
|
|
205
|
+
dimensions.accessMode !== "explicit_users" ||
|
|
206
|
+
dimensions.authorizationMode !== "rbac" ||
|
|
207
|
+
dimensions.deploymentMode !== "singleton"
|
|
208
|
+
)) {
|
|
209
|
+
addIssue(issues, "appKind", "root-central exige none/system/explicit_users/rbac/singleton.");
|
|
210
|
+
}
|
|
211
|
+
if (role === "member" && dimensions.availability === "system") {
|
|
212
|
+
addIssue(issues, "availability", "Apps member não podem usar availability=system.");
|
|
213
|
+
}
|
|
214
|
+
if (dimensions.tenancyModel === "none" && dimensions.accessMode === "tenant_members") {
|
|
215
|
+
addIssue(issues, "accessMode", "tenant_members exige uma aplicação tenant-aware.");
|
|
216
|
+
}
|
|
179
217
|
if (!isPlainObject(input.modules)) {
|
|
180
218
|
addIssue(issues, "modules", "deve ser um objeto.");
|
|
181
219
|
} else {
|
|
@@ -218,6 +256,7 @@ function appManifestToConfig(manifest, manifestPath) {
|
|
|
218
256
|
slug: manifest.slug,
|
|
219
257
|
appCode: manifest.id,
|
|
220
258
|
appKind: manifest.appKind,
|
|
259
|
+
...dimensions,
|
|
221
260
|
ecosystem: {
|
|
222
261
|
role: APP_KIND_ROLE[manifest.appKind],
|
|
223
262
|
...dimensions,
|
|
@@ -260,12 +299,15 @@ module.exports = {
|
|
|
260
299
|
SUPPORTED_MODULES,
|
|
261
300
|
SUPPORTED_TENANCY_MODELS,
|
|
262
301
|
SUPPORTED_AVAILABILITIES,
|
|
302
|
+
SUPPORTED_ACCESS_MODES,
|
|
303
|
+
SUPPORTED_AUTHORIZATION_MODES,
|
|
263
304
|
SUPPORTED_ACCESS_POLICIES,
|
|
264
305
|
SUPPORTED_DEPLOYMENT_MODES,
|
|
265
306
|
APP_KIND_DIMENSIONS,
|
|
266
307
|
AppManifestError,
|
|
267
308
|
parseVersion,
|
|
268
309
|
compareVersions,
|
|
310
|
+
legacyAccessDimensions,
|
|
269
311
|
dimensionsFor,
|
|
270
312
|
validateAppManifest,
|
|
271
313
|
resolveAppManifestPath,
|