Files
objectstorage/test/test.enterprise-hardening.test.ts
T

147 lines
6.0 KiB
TypeScript
Raw Normal View History

import { assertEquals, assertRejects } from 'jsr:@std/assert';
import { describe, it } from 'jsr:@std/testing/bdd';
import { TypedRequest } from '@api.global/typedrequest';
import {
createTestContainer,
getTestPorts,
loginAndGetIdentity,
} from './helpers/server.helper.ts';
import { ObjectStorageContainer } from '../ts/index.ts';
import type { IReq_AdminLogout, IReq_VerifyIdentity } from '../ts_interfaces/requests/admin.ts';
import type { IReq_CreateBucket } from '../ts_interfaces/requests/buckets.ts';
import type { IReq_AddCredential } from '../ts_interfaces/requests/credentials.ts';
import type { IReq_ListAuditEntries } from '../ts_interfaces/requests/audit.ts';
const PORT_INDEX = 12;
const ports = getTestPorts(PORT_INDEX);
const url = `http://localhost:${ports.uiPort}/typedrequest`;
const storageDirectory = `.nogit/testdata-${PORT_INDEX}`;
const cleanupStorageDirectory = async () => {
try {
await Deno.remove(storageDirectory, { recursive: true });
} catch (error) {
if (!(error instanceof Deno.errors.NotFound)) {
throw error;
}
}
};
describe('Enterprise hardening', { sanitizeResources: false, sanitizeOps: false }, () => {
it('refuses default admin credentials on persistent production storage', async () => {
const previousAllowInsecureDefaults = Deno.env.get('OBJST_ALLOW_INSECURE_DEFAULTS');
const previousAccessKey = Deno.env.get('OBJST_ACCESS_KEY');
const previousSecretKey = Deno.env.get('OBJST_SECRET_KEY');
const previousAdminPassword = Deno.env.get('OBJST_ADMIN_PASSWORD');
const previousStorageDir = Deno.env.get('OBJST_STORAGE_DIR');
try {
Deno.env.delete('OBJST_ALLOW_INSECURE_DEFAULTS');
Deno.env.delete('OBJST_ACCESS_KEY');
Deno.env.delete('OBJST_SECRET_KEY');
Deno.env.delete('OBJST_ADMIN_PASSWORD');
Deno.env.delete('OBJST_STORAGE_DIR');
const container = new ObjectStorageContainer();
await assertRejects(() => container.start(), Error, 'Refusing to start with default admin credentials');
} finally {
if (previousAllowInsecureDefaults === undefined) {
Deno.env.delete('OBJST_ALLOW_INSECURE_DEFAULTS');
} else {
Deno.env.set('OBJST_ALLOW_INSECURE_DEFAULTS', previousAllowInsecureDefaults);
}
if (previousAccessKey === undefined) Deno.env.delete('OBJST_ACCESS_KEY');
else Deno.env.set('OBJST_ACCESS_KEY', previousAccessKey);
if (previousSecretKey === undefined) Deno.env.delete('OBJST_SECRET_KEY');
else Deno.env.set('OBJST_SECRET_KEY', previousSecretKey);
if (previousAdminPassword === undefined) Deno.env.delete('OBJST_ADMIN_PASSWORD');
else Deno.env.set('OBJST_ADMIN_PASSWORD', previousAdminPassword);
if (previousStorageDir === undefined) Deno.env.delete('OBJST_STORAGE_DIR');
else Deno.env.set('OBJST_STORAGE_DIR', previousStorageDir);
}
});
it('exposes health endpoints and writes audit entries for privileged actions', async () => {
await cleanupStorageDirectory();
let container: ObjectStorageContainer | null = null;
try {
container = createTestContainer(PORT_INDEX, { storageDirectory });
await container.start();
const live = await fetch(`http://localhost:${ports.uiPort}/livez`);
assertEquals(live.status, 200);
assertEquals((await live.json()).status, 'alive');
const ready = await fetch(`http://localhost:${ports.uiPort}/readyz`);
assertEquals(ready.status, 200);
assertEquals((await ready.json()).status, 'ready');
const health = await fetch(`http://localhost:${ports.uiPort}/healthz`);
assertEquals(health.status, 200);
assertEquals((await health.json()).ok, true);
const metrics = await fetch(`http://localhost:${ports.uiPort}/metrics`);
assertEquals(metrics.status, 200);
assertEquals((await metrics.text()).includes('objectstorage_ready 1'), true);
const identity = await loginAndGetIdentity(ports.uiPort);
const createBucket = new TypedRequest<IReq_CreateBucket>(url, 'createBucket');
await createBucket.fire({ identity, bucketName: 'enterprise-audit-bucket' });
const addCredential = new TypedRequest<IReq_AddCredential>(url, 'addCredential');
await addCredential.fire({
identity,
accessKeyId: 'enterprise-key',
secretAccessKey: 'enterprise-secret',
});
const adminConfigInfo = await Deno.stat(
`${storageDirectory}/.objectstorage/admin-config.json`,
);
if (adminConfigInfo.mode !== null) {
assertEquals(adminConfigInfo.mode & 0o777, 0o600);
}
const listAuditEntries = new TypedRequest<IReq_ListAuditEntries>(url, 'listAuditEntries');
const auditResponse = await listAuditEntries.fire({ identity, limit: 10 });
const actions = auditResponse.entries.map((entry) => entry.action);
assertEquals(actions.includes('admin.login'), true);
assertEquals(actions.includes('bucket.create'), true);
assertEquals(actions.includes('credential.add'), true);
} finally {
if (container) {
await container.stop();
}
await cleanupStorageDirectory();
}
});
it('revokes admin identities on logout', async () => {
await cleanupStorageDirectory();
let container: ObjectStorageContainer | null = null;
try {
container = createTestContainer(PORT_INDEX, { storageDirectory });
await container.start();
const identity = await loginAndGetIdentity(ports.uiPort);
const logout = new TypedRequest<IReq_AdminLogout>(url, 'adminLogout');
await logout.fire({ identity });
const verifyIdentity = new TypedRequest<IReq_VerifyIdentity>(url, 'verifyIdentity');
const verification = await verifyIdentity.fire({ identity });
assertEquals(verification.valid, false);
const createBucket = new TypedRequest<IReq_CreateBucket>(url, 'createBucket');
await assertRejects(() =>
createBucket.fire({ identity, bucketName: 'revoked-token-bucket' })
);
} finally {
if (container) {
await container.stop();
}
await cleanupStorageDirectory();
}
});
});