- Asociar entornos locales con activos y conservar su trazabilidad. - Registrar la cuenta de acceso del mapeo con historial de cambios. - Calcular cobertura documental específica sin alterar la cobertura general. - Añadir migraciones, validaciones, endpoints protegidos, pruebas y documentación.
1003 lines
34 KiB
TypeScript
1003 lines
34 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { once } from "node:events";
|
|
import { test } from "node:test";
|
|
|
|
import type { AccessFieldsDTO } from "@metis/companies/common";
|
|
import type { Sequelize } from "sequelize";
|
|
import { QueryTypes } from "sequelize";
|
|
|
|
import { WorkstationUseCases } from "../../../modules/assets/src/api/application/workstation.use-cases";
|
|
import { SequelizeAssetRepository } from "../../../modules/assets/src/api/infrastructure/persistence/sequelize-asset.repository";
|
|
import { AccessUseCases } from "../../../modules/companies/src/api/application/access.use-cases";
|
|
import { FolderMappingUseCases } from "../../../modules/companies/src/api/application/folder-mapping.use-cases";
|
|
import { buildFolderResponse } from "../../../modules/companies/src/api/application/folder-mapping-response.builder";
|
|
import { WorkProfileUseCases } from "../../../modules/companies/src/api/application/work-profile.use-cases";
|
|
import { AccessKindSchema } from "../../../modules/companies/src/api/domain/access/access-definition";
|
|
import { registerFolderMappingRoutes } from "../../../modules/companies/src/api/infrastructure/http/folder-mapping.routes";
|
|
import {
|
|
AccessPersistenceMapper,
|
|
SequelizeAccessRepository,
|
|
} from "../../../modules/companies/src/api/infrastructure/persistence/access-persistence";
|
|
import { CompanyPersistenceMapper } from "../../../modules/companies/src/api/infrastructure/persistence/company-persistence.mapper";
|
|
import { migrateFolderMappings } from "../../../modules/companies/src/api/infrastructure/persistence/folder-mapping-migration";
|
|
import {
|
|
completeLocalIdentityAssetConstraint,
|
|
migrateLocalIdentityEnvironments,
|
|
} from "../../../modules/companies/src/api/infrastructure/persistence/local-identity-migration";
|
|
import { SequelizeCompanyRepository } from "../../../modules/companies/src/api/infrastructure/persistence/sequelize-company.repository";
|
|
import { SequelizeFolderMappingRepository } from "../../../modules/companies/src/api/infrastructure/persistence/sequelize-folder-mapping.repository";
|
|
import { SequelizeWorkProfileRepository } from "../../../modules/companies/src/api/infrastructure/persistence/sequelize-work-profile.repository";
|
|
import { migrateWorkProfiles } from "../../../modules/companies/src/api/infrastructure/persistence/work-profile-migration";
|
|
import { FolderMappingResponseSchema } from "../../../modules/companies/src/common/folder-mappings";
|
|
import { LocalIdentityPageSchema } from "../../../modules/companies/src/common/local-identity-environments";
|
|
import { UniqueID } from "../../../packages/rdx-ddd/src/index";
|
|
|
|
import {
|
|
cleanupMembershipFixture,
|
|
prepareMembershipFixture,
|
|
} from "./asset-directory-memberships.fixture";
|
|
import { createDatabase, readDatabaseEnvironment } from "./config/database";
|
|
|
|
test("MariaDB: identidades locales y cuenta documental de FolderMapping", async (t) => {
|
|
const env = readDatabaseEnvironment(process.env);
|
|
|
|
assert.equal(env.NODE_ENV, "test");
|
|
assert.equal(env.DB_NAME, "metis_desk_test");
|
|
const db = createDatabase(env);
|
|
const company = UniqueID.generateNewID().toPrimitive();
|
|
const otherCompany = UniqueID.generateNewID().toPrimitive();
|
|
const actor = UniqueID.generateNewID().toPrimitive();
|
|
const person = UniqueID.generateNewID().toPrimitive();
|
|
const otherPerson = UniqueID.generateNewID().toPrimitive();
|
|
const workstations = new WorkstationUseCases(new SequelizeAssetRepository(db));
|
|
let workstationId: string | undefined;
|
|
let ready = false;
|
|
|
|
try {
|
|
const f = await prepareMembershipFixture(db, "http://localhost:5173", actor, [
|
|
company,
|
|
otherCompany,
|
|
]);
|
|
|
|
ready = true;
|
|
await migrateWorkProfiles(db);
|
|
await migrateFolderMappings(db);
|
|
await migrateFolderMappings(db);
|
|
await migrateLocalIdentityEnvironments(db);
|
|
await completeLocalIdentityAssetConstraint(db);
|
|
const access = new AccessUseCases(
|
|
new SequelizeAccessRepository(
|
|
db,
|
|
new SequelizeCompanyRepository(db, new CompanyPersistenceMapper()),
|
|
new AccessPersistenceMapper()
|
|
)
|
|
);
|
|
const folders = new FolderMappingUseCases(
|
|
new SequelizeFolderMappingRepository(db, f.inventory)
|
|
);
|
|
const profiles = new WorkProfileUseCases(new SequelizeWorkProfileRepository(db, f.inventory));
|
|
|
|
registerFolderMappingRoutes(f.app, folders, f.authenticate);
|
|
for (const id of [person, otherPerson]) {
|
|
await db.query(
|
|
"INSERT INTO company_contacts (id,companyId,name,isContact,active) VALUES (:id,:company,'Local identity fixture',0,1)",
|
|
{ replacements: { id, company } }
|
|
);
|
|
}
|
|
|
|
const add = async (values: AccessFieldsDTO, scope = company) => {
|
|
const before = await access.get(scope);
|
|
|
|
assert.ok(before.isSuccess);
|
|
const ids = new Set(before.data.workspace.items.map((item) => item.id.toPrimitive()));
|
|
const saved = await access.save(scope, undefined, values, actor);
|
|
|
|
assert.ok(saved.isSuccess, saved.isSuccess ? "" : saved.error.code);
|
|
const item = saved.data.workspace.items.find((item) => !ids.has(item.id.toPrimitive()));
|
|
|
|
assert.ok(item);
|
|
|
|
return item;
|
|
};
|
|
|
|
const nas = await f.asset(company, "nas");
|
|
const nas2 = await f.asset(company, "nas");
|
|
const desktop = await f.asset(company, "desktop");
|
|
const laptop = await f.asset(company, "laptop");
|
|
const foreign = await f.asset(otherCompany, "nas");
|
|
const system = await add({ kind: "system", name: "Identidades explícitas", active: true });
|
|
|
|
const local = (input: unknown) => f.localIdentities.execute(company, actor, input);
|
|
|
|
const created = await local({
|
|
action: "create",
|
|
assetId: nas.id.toPrimitive(),
|
|
systemId: system.id.toPrimitive(),
|
|
name: "NAS identidades",
|
|
});
|
|
|
|
assert.ok(created.isSuccess);
|
|
const environmentId = created.data.toPrimitive().environmentId;
|
|
|
|
await t.test(
|
|
"vínculos NAS, PC y portátil, sin dominio y con dominio; cuentas homónimas diferenciadas",
|
|
async () => {
|
|
for (const [asset, name] of [
|
|
[nas2, "NAS segundo"],
|
|
[desktop, "PC"],
|
|
[laptop, "Portátil"],
|
|
] as const) {
|
|
assert.ok(
|
|
(
|
|
await local({
|
|
action: "create",
|
|
assetId: asset.id.toPrimitive(),
|
|
systemId: system.id.toPrimitive(),
|
|
name,
|
|
})
|
|
).isSuccess
|
|
);
|
|
}
|
|
const domain = await f.domain(company, `local-${company.slice(0, 8)}.local`);
|
|
|
|
assert.ok(
|
|
(
|
|
await f.membershipUseCases.execute(
|
|
company,
|
|
desktop.id.toPrimitive(),
|
|
{ action: "join", domainResourceId: domain.id.toPrimitive(), role: "member" },
|
|
actor
|
|
)
|
|
).isSuccess
|
|
);
|
|
const desktopLinks = await f.localIdentities.list(company, {
|
|
assetId: desktop.id.toPrimitive(),
|
|
});
|
|
|
|
assert.ok(desktopLinks.isSuccess);
|
|
assert.equal(desktopLinks.data.environments.length, 1);
|
|
const second = await f.localIdentities.list(company, { assetId: nas2.id.toPrimitive() });
|
|
|
|
assert.ok(second.isSuccess && second.data.environments[0]);
|
|
for (const environment of [environmentId, second.data.environments[0].environmentId]) {
|
|
await add({
|
|
kind: "account",
|
|
login: "juan",
|
|
systemId: system.id.toPrimitive(),
|
|
environmentId: environment,
|
|
usage: "shared",
|
|
active: true,
|
|
expiresOn: null,
|
|
notes: null,
|
|
});
|
|
}
|
|
const snapshot = await access.get(company);
|
|
|
|
assert.ok(snapshot.isSuccess);
|
|
assert.equal(
|
|
snapshot.data.workspace.items.filter(
|
|
(item) => item.values.data.kind === "account" && item.values.data.login === "juan"
|
|
).length,
|
|
2
|
|
);
|
|
}
|
|
);
|
|
|
|
await t.test(
|
|
"asociación idempotente, propietario inmutable, ámbitos y rechazo AD",
|
|
async () => {
|
|
const repeat = await local({
|
|
action: "associate",
|
|
environmentId,
|
|
assetId: nas.id.toPrimitive(),
|
|
});
|
|
|
|
assert.ok(repeat.isSuccess);
|
|
assert.equal(repeat.data.id.toPrimitive(), created.data.id.toPrimitive());
|
|
assert.equal(
|
|
(await local({ action: "associate", environmentId, assetId: nas2.id.toPrimitive() }))
|
|
.isSuccess,
|
|
false
|
|
);
|
|
assert.equal(
|
|
(await local({ action: "associate", environmentId, assetId: foreign.id.toPrimitive() }))
|
|
.isSuccess,
|
|
false
|
|
);
|
|
const domainRows = await db.query<{ environmentId: string; systemId: string }>(
|
|
"SELECT environmentId,systemId FROM access_directory_environments WHERE companyId=:company",
|
|
{ replacements: { company }, type: QueryTypes.SELECT }
|
|
);
|
|
|
|
assert.ok(domainRows[0]);
|
|
assert.equal(
|
|
(
|
|
await local({
|
|
action: "associate",
|
|
environmentId: domainRows[0].environmentId,
|
|
assetId: nas.id.toPrimitive(),
|
|
})
|
|
).isSuccess,
|
|
false
|
|
);
|
|
assert.equal(
|
|
(
|
|
await local({
|
|
action: "create",
|
|
systemId: domainRows[0].systemId,
|
|
assetId: nas.id.toPrimitive(),
|
|
name: "No clasificar AD como local",
|
|
})
|
|
).isSuccess,
|
|
false
|
|
);
|
|
assert.equal(
|
|
(
|
|
await local({
|
|
action: "associate",
|
|
environmentId: system.id.toPrimitive(),
|
|
assetId: nas.id.toPrimitive(),
|
|
})
|
|
).isSuccess,
|
|
false
|
|
);
|
|
const otherEnvironment = await add(
|
|
{
|
|
kind: "environment",
|
|
name: "Otra compañía",
|
|
systemId: (
|
|
await add({ kind: "system", name: "Otro", active: true }, otherCompany)
|
|
).id.toPrimitive(),
|
|
active: true,
|
|
},
|
|
otherCompany
|
|
);
|
|
|
|
assert.equal(
|
|
(
|
|
await local({
|
|
action: "associate",
|
|
environmentId: otherEnvironment.id.toPrimitive(),
|
|
assetId: nas.id.toPrimitive(),
|
|
})
|
|
).isSuccess,
|
|
false
|
|
);
|
|
}
|
|
);
|
|
|
|
await t.test(
|
|
"asocia un entorno existente sin reescribirlo y serializa altas duplicadas",
|
|
async () => {
|
|
const environment = await add({
|
|
kind: "environment",
|
|
name: "Entorno previamente creado",
|
|
systemId: system.id.toPrimitive(),
|
|
active: true,
|
|
});
|
|
const command = {
|
|
action: "associate",
|
|
environmentId: environment.id.toPrimitive(),
|
|
assetId: laptop.id.toPrimitive(),
|
|
};
|
|
const results = await Promise.all([local(command), local(command)]);
|
|
|
|
assert.ok(results[0]?.isSuccess && results[1]?.isSuccess);
|
|
assert.equal(results[0].data.id.toPrimitive(), results[1].data.id.toPrimitive());
|
|
const loaded = await access.get(company);
|
|
|
|
assert.ok(loaded.isSuccess);
|
|
const unchanged = loaded.data.workspace.items.find((item) =>
|
|
item.id.equals(environment.id)
|
|
);
|
|
|
|
assert.deepEqual(
|
|
unchanged?.history.map((item) => item.id.toPrimitive()),
|
|
environment.history.map((item) => item.id.toPrimitive())
|
|
);
|
|
}
|
|
);
|
|
|
|
await t.test(
|
|
"renombrado y desactivación conservan entorno, cuentas e historial; FK protege compañía y subtipo",
|
|
async () => {
|
|
assert.ok(
|
|
(
|
|
await f.assetUseCases.save(
|
|
company,
|
|
nas.id.toPrimitive(),
|
|
{ ...nas.values.toPrimitive(), name: "NAS renombrado", status: "retired" },
|
|
actor
|
|
)
|
|
).isSuccess
|
|
);
|
|
assert.equal(
|
|
(
|
|
await local({
|
|
action: "create",
|
|
assetId: nas.id.toPrimitive(),
|
|
systemId: system.id.toPrimitive(),
|
|
name: "Activo retirado",
|
|
})
|
|
).isSuccess,
|
|
false
|
|
);
|
|
const links = await f.localIdentities.list(company, { assetId: nas.id.toPrimitive() });
|
|
|
|
assert.ok(links.isSuccess);
|
|
assert.equal(links.data.environments[0]?.environmentId, environmentId);
|
|
const snapshot = await access.get(company);
|
|
|
|
assert.ok(snapshot.isSuccess);
|
|
assert.equal(
|
|
snapshot.data.workspace.items.find((item) => item.id.toPrimitive() === environmentId)
|
|
?.values.data.kind,
|
|
"environment"
|
|
);
|
|
const rows = await db.query<{ name: string; active: number }>(
|
|
"SELECT name,active FROM access_environment_values WHERE ownerId=:environmentId",
|
|
{ replacements: { environmentId }, type: QueryTypes.SELECT }
|
|
);
|
|
|
|
assert.equal(rows[0]?.name, "NAS identidades");
|
|
assert.equal(rows[0]?.active, 1);
|
|
assert.equal(
|
|
(await f.assetUseCases.remove(company, nas.id.toPrimitive())).isSuccess,
|
|
false
|
|
);
|
|
await assert.rejects(
|
|
db.query(
|
|
"UPDATE access_local_identity_environments SET assetId=:foreign WHERE environmentId=:environmentId",
|
|
{ replacements: { foreign: foreign.id.toPrimitive(), environmentId } }
|
|
)
|
|
);
|
|
await assert.rejects(
|
|
db.query("UPDATE access_items SET kind='group' WHERE id=:environmentId", {
|
|
replacements: { environmentId },
|
|
})
|
|
);
|
|
const retired = await local({ action: "retire", environmentId });
|
|
|
|
assert.ok(retired.isSuccess);
|
|
const repeat = await local({ action: "retire", environmentId });
|
|
|
|
assert.ok(repeat.isSuccess);
|
|
assert.deepEqual(repeat.data.toPrimitive(), retired.data.toPrimitive());
|
|
assert.equal(
|
|
(await f.assetUseCases.remove(company, nas.id.toPrimitive())).isSuccess,
|
|
false
|
|
);
|
|
assert.equal(
|
|
(await local({ action: "associate", environmentId, assetId: nas2.id.toPrimitive() }))
|
|
.isSuccess,
|
|
false
|
|
);
|
|
}
|
|
);
|
|
|
|
await t.test(
|
|
"rollback al crear vínculo revierte también el entorno y su revisión",
|
|
async () => {
|
|
const before = await access.get(company);
|
|
|
|
assert.ok(before.isSuccess);
|
|
const trigger = `local_fixture_${company.replaceAll("-", "")}`;
|
|
|
|
await db.query(
|
|
`CREATE TRIGGER ${trigger} BEFORE INSERT ON access_local_identity_environments FOR EACH ROW BEGIN IF NEW.companyId='${company}' THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='local fixture rollback'; END IF; END`
|
|
);
|
|
try {
|
|
await assert.rejects(
|
|
local({
|
|
action: "create",
|
|
assetId: desktop.id.toPrimitive(),
|
|
systemId: system.id.toPrimitive(),
|
|
name: "Rollback",
|
|
})
|
|
);
|
|
} finally {
|
|
await db.query(`DROP TRIGGER ${trigger}`);
|
|
}
|
|
const after = await access.get(company);
|
|
|
|
assert.ok(after.isSuccess);
|
|
assert.deepEqual(
|
|
after.data.workspace.items.map((item) => [
|
|
item.id.toPrimitive(),
|
|
item.history.map((revision) => revision.id.toPrimitive()),
|
|
]),
|
|
before.data.workspace.items.map((item) => [
|
|
item.id.toPrimitive(),
|
|
item.history.map((revision) => revision.id.toPrimitive()),
|
|
])
|
|
);
|
|
}
|
|
);
|
|
|
|
const resourceCatalog = await f.resourceUseCases.get(company);
|
|
|
|
assert.ok(resourceCatalog.isSuccess);
|
|
const template = resourceCatalog.data.catalog.definitions.find(
|
|
(item) => item.stockKey.getOrUndefined() === "template:shared_folder"
|
|
);
|
|
const unc = resourceCatalog.data.catalog.definitions.find(
|
|
(item) => item.stockKey.getOrUndefined() === "field:unc"
|
|
);
|
|
|
|
assert.ok(template && unc);
|
|
const shared = await f.resourceUseCases.save(
|
|
company,
|
|
undefined,
|
|
{
|
|
name: "Docs",
|
|
templateId: template.id.toPrimitive(),
|
|
active: true,
|
|
notes: "",
|
|
characteristics: { [unc.id.toPrimitive()]: String.raw`\\NAS\Docs` },
|
|
},
|
|
actor
|
|
);
|
|
|
|
assert.ok(shared.isSuccess);
|
|
const resourceId = shared.data.resource.id.toPrimitive();
|
|
const resourceValues = shared.data.resource.values.toPrimitive();
|
|
|
|
assert.ok(resourceValues.integration?.kind === "access");
|
|
const resourceTypeId = resourceValues.integration.resourceTypeId;
|
|
const accountBase = {
|
|
kind: "account" as const,
|
|
systemId: system.id.toPrimitive(),
|
|
environmentId,
|
|
usage: "shared" as const,
|
|
active: true,
|
|
expiresOn: null,
|
|
notes: null,
|
|
};
|
|
const firstAccount = await add({ ...accountBase, login: "perfil" });
|
|
const secondAccount = await add({ ...accountBase, login: "alternativa" });
|
|
const stranger = await add({ ...accountBase, login: "otra persona" });
|
|
const technical = await add({ ...accountBase, login: "servicio", usage: "technical" });
|
|
const period = { startDate: "2020-01-01", endDate: null };
|
|
const firstRelation = await add({
|
|
kind: "account_person",
|
|
accountId: firstAccount.id.toPrimitive(),
|
|
personId: person,
|
|
...period,
|
|
configuration: "configured",
|
|
});
|
|
const secondRelation = await add({
|
|
kind: "account_person",
|
|
accountId: secondAccount.id.toPrimitive(),
|
|
personId: person,
|
|
...period,
|
|
configuration: "configured",
|
|
});
|
|
|
|
await add({
|
|
kind: "account_person",
|
|
accountId: stranger.id.toPrimitive(),
|
|
personId: otherPerson,
|
|
...period,
|
|
configuration: "configured",
|
|
});
|
|
await add({
|
|
kind: "account_person",
|
|
accountId: technical.id.toPrimitive(),
|
|
personId: person,
|
|
...period,
|
|
configuration: null,
|
|
});
|
|
const permission = await add({
|
|
kind: "permission",
|
|
name: "Lectura",
|
|
active: true,
|
|
resourceTypeId,
|
|
description: null,
|
|
});
|
|
const need = await add({
|
|
kind: "need",
|
|
personId: person,
|
|
resourceId,
|
|
permissionIds: [permission.id.toPrimitive()],
|
|
...period,
|
|
notes: null,
|
|
});
|
|
const grant = await add({
|
|
kind: "grant",
|
|
accountId: firstAccount.id.toPrimitive(),
|
|
groupId: null,
|
|
permissionId: permission.id.toPrimitive(),
|
|
resourceId,
|
|
...period,
|
|
configuration: "configured",
|
|
});
|
|
|
|
await add({
|
|
kind: "coverage",
|
|
needId: need.id.toPrimitive(),
|
|
grantId: grant.id.toPrimitive(),
|
|
accountPersonId: firstRelation.id.toPrimitive(),
|
|
membershipId: null,
|
|
...period,
|
|
});
|
|
const station = await workstations.save(
|
|
company,
|
|
undefined,
|
|
{
|
|
name: "Puesto fixture",
|
|
active: true,
|
|
siteId: null,
|
|
personId: null,
|
|
computerAssetId: desktop.id.toPrimitive(),
|
|
assetIds: [desktop.id.toPrimitive()],
|
|
},
|
|
actor
|
|
);
|
|
|
|
assert.ok(station.isSuccess);
|
|
workstationId = station.data.workstation.id.toPrimitive();
|
|
const profile = await profiles.save(
|
|
company,
|
|
undefined,
|
|
{
|
|
name: "Perfil local fixture",
|
|
personId: person,
|
|
assetId: desktop.id.toPrimitive(),
|
|
accountId: firstAccount.id.toPrimitive(),
|
|
active: true,
|
|
notes: null,
|
|
},
|
|
actor
|
|
);
|
|
|
|
assert.ok(profile.isSuccess, profile.isSuccess ? "" : profile.error.code);
|
|
const folderInput = {
|
|
profileId: profile.data.profile.id.toPrimitive(),
|
|
needId: need.id.toPrimitive(),
|
|
path: String.raw`\\NAS\Docs`,
|
|
driveLetter: "F:",
|
|
state: "configured" as const,
|
|
notes: null,
|
|
};
|
|
const original = await folders.save(company, undefined, folderInput, actor);
|
|
|
|
assert.ok(original.isSuccess);
|
|
const mappingId = original.data.mapping.id.toPrimitive();
|
|
|
|
const read = async () => {
|
|
const snapshot = await folders.get(company);
|
|
|
|
assert.ok(snapshot.isSuccess);
|
|
const mapping = snapshot.data.mappings.find((item) => item.id.toPrimitive() === mappingId);
|
|
|
|
assert.ok(mapping);
|
|
|
|
return buildFolderResponse(mapping, snapshot.data);
|
|
};
|
|
|
|
await t.test(
|
|
"mapeo antiguo desconocido; cuenta del perfil y alternativa sin atribuir cobertura de otra cuenta",
|
|
async () => {
|
|
assert.equal((await read()).accessAccountCoverage, "unknown");
|
|
assert.ok(
|
|
(
|
|
await folders.save(
|
|
company,
|
|
mappingId,
|
|
{ ...folderInput, accessAccountId: firstAccount.id.toPrimitive() },
|
|
actor
|
|
)
|
|
).isSuccess
|
|
);
|
|
assert.equal((await read()).accessAccountCoverage, "covered");
|
|
assert.ok(
|
|
(
|
|
await folders.save(
|
|
company,
|
|
mappingId,
|
|
{ ...folderInput, accessAccountId: secondAccount.id.toPrimitive() },
|
|
actor
|
|
)
|
|
).isSuccess
|
|
);
|
|
const response = await read();
|
|
|
|
assert.equal(response.permissionsCovered, true);
|
|
assert.equal(response.accessAccountCoverage, "not-covered");
|
|
assert.equal(response.values.accessAccountId, secondAccount.id.toPrimitive());
|
|
assert.equal(response.history[0]?.values.accessAccountId, null);
|
|
assert.equal(response.history.length, 3);
|
|
assert.equal(response.history[0]?.id, original.data.mapping.history[0]?.id.toPrimitive());
|
|
}
|
|
);
|
|
|
|
await t.test(
|
|
"actualización ausente conserva, null retira; selecciones inválidas y cuentas técnicas documentales",
|
|
async () => {
|
|
assert.ok(
|
|
(await folders.save(company, mappingId, { ...folderInput, notes: "Solo notas" }, actor))
|
|
.isSuccess
|
|
);
|
|
assert.equal((await read()).values.accessAccountId, secondAccount.id.toPrimitive());
|
|
for (const id of [
|
|
stranger.id.toPrimitive(),
|
|
system.id.toPrimitive(),
|
|
UniqueID.generateNewID().toPrimitive(),
|
|
]) {
|
|
assert.equal(
|
|
(await folders.save(company, mappingId, { ...folderInput, accessAccountId: id }, actor))
|
|
.isSuccess,
|
|
false
|
|
);
|
|
}
|
|
const foreignSystem = await add(
|
|
{ kind: "system", name: "Sistema ajeno", active: true },
|
|
otherCompany
|
|
);
|
|
const foreignAccount = await add(
|
|
{
|
|
...accountBase,
|
|
systemId: foreignSystem.id.toPrimitive(),
|
|
environmentId: null,
|
|
login: "ajena",
|
|
},
|
|
otherCompany
|
|
);
|
|
|
|
assert.equal(
|
|
(
|
|
await folders.save(
|
|
company,
|
|
mappingId,
|
|
{ ...folderInput, accessAccountId: foreignAccount.id.toPrimitive() },
|
|
actor
|
|
)
|
|
).isSuccess,
|
|
false
|
|
);
|
|
assert.ok(
|
|
(
|
|
await folders.save(
|
|
company,
|
|
mappingId,
|
|
{ ...folderInput, accessAccountId: technical.id.toPrimitive() },
|
|
actor
|
|
)
|
|
).isSuccess
|
|
);
|
|
assert.equal((await read()).accessAccountCoverage, "not-covered");
|
|
assert.ok(
|
|
(await folders.save(company, mappingId, { ...folderInput, accessAccountId: null }, actor))
|
|
.isSuccess
|
|
);
|
|
assert.equal((await read()).accessAccountCoverage, "unknown");
|
|
assert.equal((await read()).values.accessAccountId, null);
|
|
const foreignWorkspace = await db.query<{ id: string }>(
|
|
"SELECT id FROM access_workspaces WHERE companyId=:otherCompany",
|
|
{ replacements: { otherCompany }, type: QueryTypes.SELECT }
|
|
);
|
|
|
|
await assert.rejects(
|
|
db.query(
|
|
"UPDATE folder_mappings SET accessAccountId=:id,accessAccountWorkspaceId=:workspace WHERE id=:mappingId",
|
|
{
|
|
replacements: {
|
|
id: foreignAccount.id.toPrimitive(),
|
|
workspace: foreignWorkspace[0]?.id,
|
|
mappingId,
|
|
},
|
|
}
|
|
)
|
|
);
|
|
const workspace = await db.query<{ id: string }>(
|
|
"SELECT id FROM access_workspaces WHERE companyId=:company",
|
|
{ replacements: { company }, type: QueryTypes.SELECT }
|
|
);
|
|
|
|
await assert.rejects(
|
|
db.query(
|
|
"UPDATE folder_mappings SET accessAccountId=:id,accessAccountWorkspaceId=:workspace WHERE id=:mappingId",
|
|
{
|
|
replacements: { id: system.id.toPrimitive(), workspace: workspace[0]?.id, mappingId },
|
|
}
|
|
)
|
|
);
|
|
}
|
|
);
|
|
|
|
await t.test("revisiones conservan ámbito y subtipo de cuenta en MariaDB", async () => {
|
|
const scope = await db.query<{ id: string }>(
|
|
"SELECT id FROM access_workspaces WHERE companyId=:company",
|
|
{ replacements: { company }, type: QueryTypes.SELECT }
|
|
);
|
|
const revision = original.data.mapping.history[0];
|
|
|
|
assert.ok(revision && scope[0]);
|
|
await assert.rejects(
|
|
db.query(
|
|
"UPDATE folder_mapping_revisions SET accessAccountId=:account,accessAccountWorkspaceId=:workspace WHERE id=:id",
|
|
{
|
|
replacements: {
|
|
account: system.id.toPrimitive(),
|
|
workspace: scope[0].id,
|
|
id: revision.id.toPrimitive(),
|
|
},
|
|
}
|
|
)
|
|
);
|
|
});
|
|
|
|
await t.test(
|
|
"cobertura de grupo solo por la cuenta seleccionada y pertenencia vigente; caducidad documental derivada",
|
|
async () => {
|
|
const group = await add({
|
|
kind: "group",
|
|
name: "Grupo NAS",
|
|
identifier: "nas",
|
|
systemId: system.id.toPrimitive(),
|
|
environmentId,
|
|
templateId: null,
|
|
description: null,
|
|
active: true,
|
|
});
|
|
|
|
assert.equal(
|
|
(
|
|
await folders.save(
|
|
company,
|
|
mappingId,
|
|
{ ...folderInput, accessAccountId: group.id.toPrimitive() },
|
|
actor
|
|
)
|
|
).isSuccess,
|
|
false
|
|
);
|
|
const membership = await add({
|
|
kind: "membership",
|
|
accountId: secondAccount.id.toPrimitive(),
|
|
groupId: group.id.toPrimitive(),
|
|
...period,
|
|
configuration: "configured",
|
|
});
|
|
const groupGrant = await add({
|
|
kind: "grant",
|
|
accountId: null,
|
|
groupId: group.id.toPrimitive(),
|
|
permissionId: permission.id.toPrimitive(),
|
|
resourceId,
|
|
...period,
|
|
configuration: "configured",
|
|
});
|
|
|
|
await add({
|
|
kind: "coverage",
|
|
needId: need.id.toPrimitive(),
|
|
grantId: groupGrant.id.toPrimitive(),
|
|
accountPersonId: secondRelation.id.toPrimitive(),
|
|
membershipId: membership.id.toPrimitive(),
|
|
...period,
|
|
});
|
|
assert.ok(
|
|
(
|
|
await folders.save(
|
|
company,
|
|
mappingId,
|
|
{ ...folderInput, accessAccountId: secondAccount.id.toPrimitive() },
|
|
actor
|
|
)
|
|
).isSuccess
|
|
);
|
|
assert.equal((await read()).accessAccountCoverage, "covered");
|
|
assert.ok(
|
|
(
|
|
await access.save(
|
|
company,
|
|
membership.id.toPrimitive(),
|
|
{
|
|
kind: "membership",
|
|
accountId: secondAccount.id.toPrimitive(),
|
|
groupId: group.id.toPrimitive(),
|
|
startDate: "2020-01-01",
|
|
endDate: "2021-01-01",
|
|
configuration: "removal_pending",
|
|
},
|
|
actor
|
|
)
|
|
).isSuccess
|
|
);
|
|
assert.equal((await read()).accessAccountCoverage, "not-covered");
|
|
assert.ok(
|
|
(
|
|
await access.save(
|
|
company,
|
|
secondAccount.id.toPrimitive(),
|
|
{ ...accountBase, login: "alternativa", active: false },
|
|
actor
|
|
)
|
|
).isSuccess
|
|
);
|
|
assert.equal((await read()).accessAccountCoverage, "not-covered");
|
|
const preserved = await folders.save(
|
|
company,
|
|
mappingId,
|
|
{ ...folderInput, notes: "Cuenta inactiva conservada" },
|
|
actor
|
|
);
|
|
|
|
assert.ok(preserved.isSuccess);
|
|
assert.equal((await read()).values.accessAccountId, secondAccount.id.toPrimitive());
|
|
assert.ok(
|
|
(await f.resourceUseCases.setActive(company, resourceId, false, actor)).isSuccess
|
|
);
|
|
assert.equal((await read()).accessAccountCoverage, "not-covered");
|
|
assert.equal((await read()).permissionsCovered, false);
|
|
}
|
|
);
|
|
|
|
await t.test(
|
|
"HTTP con sesión, CSRF, validación estricta, filtros y lectura de cuenta/cobertura",
|
|
async () => {
|
|
const http = f.app.listen(0, "127.0.0.1");
|
|
|
|
try {
|
|
await once(http, "listening");
|
|
const address = http.address();
|
|
|
|
assert.ok(address && typeof address !== "string");
|
|
const base = `http://127.0.0.1:${address.port}`;
|
|
const endpoint = `${base}/api/local-identity-environments/${company}`;
|
|
|
|
assert.equal((await fetch(endpoint)).status, 401);
|
|
const login = await fetch(`${base}/fixture-session`);
|
|
const cookie = login.headers.get("set-cookie")?.split(";")[0];
|
|
|
|
assert.ok(cookie);
|
|
const body = { action: "associate", assetId: desktop.id.toPrimitive(), environmentId };
|
|
|
|
const request = (
|
|
value: unknown,
|
|
token = "a".repeat(64),
|
|
origin = "http://localhost:5173"
|
|
) =>
|
|
fetch(endpoint, {
|
|
method: "POST",
|
|
headers: {
|
|
cookie,
|
|
origin,
|
|
"x-csrf-token": token,
|
|
"content-type": "application/json",
|
|
},
|
|
body: JSON.stringify(value),
|
|
});
|
|
|
|
assert.equal((await request(body, "invalid")).status, 403);
|
|
assert.equal((await request(body, "a".repeat(64), "https://wrong.invalid")).status, 403);
|
|
assert.equal((await request({ ...body, operatorId: actor })).status, 400);
|
|
assert.equal((await request(body)).status, 409);
|
|
assert.equal(
|
|
(
|
|
await fetch(
|
|
`${base}/api/local-identity-environments/${UniqueID.generateNewID().toPrimitive()}`,
|
|
{ headers: { cookie } }
|
|
)
|
|
).status,
|
|
404
|
|
);
|
|
const page = await fetch(`${endpoint}?pageSize=1&includeEnded=true`, {
|
|
headers: { cookie },
|
|
});
|
|
|
|
assert.equal(page.status, 200);
|
|
const data = LocalIdentityPageSchema.parse(await page.json());
|
|
|
|
assert.ok(data.nextAfterId);
|
|
const next = LocalIdentityPageSchema.parse(
|
|
await (
|
|
await fetch(`${endpoint}?pageSize=1&includeEnded=true&afterId=${data.nextAfterId}`, {
|
|
headers: { cookie },
|
|
})
|
|
).json()
|
|
);
|
|
|
|
assert.notEqual(data.environments[0]?.id, next.environments[0]?.id);
|
|
assert.equal(
|
|
(await fetch(`${endpoint}?operatorId=${actor}`, { headers: { cookie } })).status,
|
|
400
|
|
);
|
|
const update = await fetch(`${base}/api/folder-mappings/${company}/${mappingId}/update`, {
|
|
method: "POST",
|
|
headers: {
|
|
cookie,
|
|
origin: "http://localhost:5173",
|
|
"x-csrf-token": "a".repeat(64),
|
|
"content-type": "application/json",
|
|
},
|
|
body: JSON.stringify({ values: { ...folderInput, notes: "HTTP" } }),
|
|
});
|
|
|
|
assert.equal(update.status, 200);
|
|
const response = FolderMappingResponseSchema.parse(await update.json());
|
|
|
|
assert.equal(response.mapping.values.accessAccountId, secondAccount.id.toPrimitive());
|
|
assert.equal(response.mapping.accessAccountCoverage, "not-covered");
|
|
} finally {
|
|
await new Promise<void>((resolve, reject) =>
|
|
http.close((error) => (error ? reject(error) : resolve()))
|
|
);
|
|
}
|
|
}
|
|
);
|
|
} finally {
|
|
try {
|
|
if (ready) {
|
|
await cleanupLocalFixture(
|
|
db,
|
|
company,
|
|
otherCompany,
|
|
person,
|
|
otherPerson,
|
|
actor,
|
|
workstations,
|
|
workstationId
|
|
);
|
|
}
|
|
} finally {
|
|
await db.close();
|
|
}
|
|
}
|
|
});
|
|
|
|
async function cleanupLocalFixture(
|
|
db: Sequelize,
|
|
company: string,
|
|
otherCompany: string,
|
|
person: string,
|
|
otherPerson: string,
|
|
actor: string,
|
|
workstations: WorkstationUseCases,
|
|
workstationId: string | undefined
|
|
) {
|
|
for (const table of [
|
|
"folder_mapping_revisions",
|
|
"folder_mappings",
|
|
"work_profile_revisions",
|
|
"work_profiles",
|
|
]) {
|
|
const clause = table.endsWith("revisions")
|
|
? table === "folder_mapping_revisions"
|
|
? "mappingId IN (SELECT id FROM folder_mappings WHERE companyId=:company)"
|
|
: "profileId IN (SELECT id FROM work_profiles WHERE companyId=:company)"
|
|
: "companyId=:company";
|
|
|
|
await db.query(`DELETE FROM ${table} WHERE ${clause}`, { replacements: { company } });
|
|
}
|
|
if (workstationId) {
|
|
assert.ok((await workstations.remove(company, workstationId)).isSuccess);
|
|
}
|
|
await db.query(
|
|
"DELETE p FROM access_need_revision_permissions p JOIN access_revisions r ON r.id=p.ownerId JOIN access_items i ON i.id=r.itemId JOIN access_workspaces w ON w.id=i.workspaceId WHERE w.companyId IN (:companies)",
|
|
{ replacements: { companies: [company, otherCompany] } }
|
|
);
|
|
await db.query(
|
|
"DELETE p FROM access_need_permissions p JOIN access_items i ON i.id=p.ownerId JOIN access_workspaces w ON w.id=i.workspaceId WHERE w.companyId IN (:companies)",
|
|
{ replacements: { companies: [company, otherCompany] } }
|
|
);
|
|
for (const kind of AccessKindSchema.options) {
|
|
await db.query(
|
|
`DELETE v FROM access_${kind}_revision_values v JOIN access_revisions r ON r.id=v.ownerId JOIN access_items i ON i.id=r.itemId JOIN access_workspaces w ON w.id=i.workspaceId WHERE w.companyId IN (:companies)`,
|
|
{ replacements: { companies: [company, otherCompany] } }
|
|
);
|
|
await db.query(
|
|
`DELETE v FROM access_${kind}_values v JOIN access_items i ON i.id=v.ownerId JOIN access_workspaces w ON w.id=i.workspaceId WHERE w.companyId IN (:companies)`,
|
|
{ replacements: { companies: [company, otherCompany] } }
|
|
);
|
|
}
|
|
await db.query("DELETE FROM access_person_references WHERE personId IN (:people)", {
|
|
replacements: { people: [person, otherPerson] },
|
|
});
|
|
await db.query("DELETE FROM company_contacts WHERE id IN (:people)", {
|
|
replacements: { people: [person, otherPerson] },
|
|
});
|
|
await cleanupMembershipFixture(db, [company, otherCompany], actor);
|
|
}
|