fix(control-panel): sign strict goal approvals via oidc
This commit is contained in:
@@ -10,7 +10,7 @@ const projectAdmin = { actor: 'alice', role: 'project-admin', project: 'default'
|
||||
const approver = { actor: 'bob', role: 'approver', project: 'default', tenant: 'default' };
|
||||
const orgAdmin = { actor: 'root', role: 'org-admin', project: 'default', tenant: 'default' };
|
||||
|
||||
function withTempGovernance(fn: (paths: { inbox: string; store: string }) => void) {
|
||||
async function withTempGovernance(fn: (paths: { inbox: string; store: string }) => Promise<void> | void) {
|
||||
const prevInbox = process.env.CASAN_APPROVAL_INBOX_FILE;
|
||||
const prevStrict = process.env.CASAN_APPROVAL_STRICT;
|
||||
const prevStore = process.env.CASAN_CP_STORE_FILE;
|
||||
@@ -24,7 +24,7 @@ function withTempGovernance(fn: (paths: { inbox: string; store: string }) => voi
|
||||
process.env.CASAN_CP_KEY_DIR = join(work, 'keys');
|
||||
process.env.CASAN_CP_PUB = join(work, 'cp.pub');
|
||||
try {
|
||||
fn({ inbox: process.env.CASAN_APPROVAL_INBOX_FILE, store: process.env.CASAN_CP_STORE_FILE });
|
||||
await fn({ inbox: process.env.CASAN_APPROVAL_INBOX_FILE, store: process.env.CASAN_CP_STORE_FILE });
|
||||
} finally {
|
||||
if (prevInbox === undefined) delete process.env.CASAN_APPROVAL_INBOX_FILE;
|
||||
else process.env.CASAN_APPROVAL_INBOX_FILE = prevInbox;
|
||||
@@ -41,8 +41,8 @@ function withTempGovernance(fn: (paths: { inbox: string; store: string }) => voi
|
||||
}
|
||||
}
|
||||
|
||||
test('approval inbox submit -> approve applies governed setting and writes oversight', () => {
|
||||
withTempGovernance(({ inbox, store }) => {
|
||||
test('approval inbox submit -> approve applies governed setting and writes oversight', async () => {
|
||||
await withTempGovernance(async ({ inbox, store }) => {
|
||||
const svc = new ApprovalsService();
|
||||
const submitted = svc.submit({
|
||||
action: 'settings.write',
|
||||
@@ -55,7 +55,7 @@ test('approval inbox submit -> approve applies governed setting and writes overs
|
||||
assert.equal(submitted.proposal.status, 'pending');
|
||||
assert.equal(submitted.proposal.delegation.requires_approval, true);
|
||||
|
||||
const decided = svc.decide({ id: submitted.proposal.id, decision: 'approve', reason: 'approved by reviewer' }, approver) as any;
|
||||
const decided = await svc.decide({ id: submitted.proposal.id, decision: 'approve', reason: 'approved by reviewer' }, approver) as any;
|
||||
assert.equal(decided.proposal.status, 'approved');
|
||||
assert.equal(decided.applied.value, true);
|
||||
|
||||
@@ -69,8 +69,8 @@ test('approval inbox submit -> approve applies governed setting and writes overs
|
||||
});
|
||||
});
|
||||
|
||||
test('approval inbox denies forged JWT in strict mode without deciding proposal', () => {
|
||||
withTempGovernance(({ inbox }) => {
|
||||
test('approval inbox denies forged JWT in strict mode without deciding proposal', async () => {
|
||||
await withTempGovernance(async ({ inbox }) => {
|
||||
const svc = new ApprovalsService();
|
||||
const submitted = svc.submit({
|
||||
action: 'settings.write',
|
||||
@@ -81,8 +81,8 @@ test('approval inbox denies forged JWT in strict mode without deciding proposal'
|
||||
payload: { key: 'security.strict', value: true },
|
||||
}, projectAdmin) as any;
|
||||
process.env.CASAN_APPROVAL_STRICT = '1';
|
||||
assert.throws(
|
||||
() => svc.decide({ id: submitted.proposal.id, decision: 'approve', reason: 'forged jwt', approvalJwt: 'fake.jwt.token' }, approver),
|
||||
await assert.rejects(
|
||||
svc.decide({ id: submitted.proposal.id, decision: 'approve', reason: 'forged jwt', approvalJwt: 'fake.jwt.token' }, approver),
|
||||
ForbiddenException,
|
||||
);
|
||||
const inboxRaw = JSON.parse(readFileSync(inbox, 'utf8'));
|
||||
@@ -91,8 +91,8 @@ test('approval inbox denies forged JWT in strict mode without deciding proposal'
|
||||
});
|
||||
});
|
||||
|
||||
test('approval inbox enforces separation of duties', () => {
|
||||
withTempGovernance(() => {
|
||||
test('approval inbox enforces separation of duties', async () => {
|
||||
await withTempGovernance(async () => {
|
||||
const svc = new ApprovalsService();
|
||||
const submitted = svc.submit({
|
||||
action: 'settings.write',
|
||||
@@ -101,8 +101,8 @@ test('approval inbox enforces separation of duties', () => {
|
||||
reason: 'change by root',
|
||||
payload: { key: 'compression.enabled', value: true },
|
||||
}, orgAdmin) as any;
|
||||
assert.throws(
|
||||
() => svc.decide({ id: submitted.proposal.id, decision: 'approve', reason: 'self approve' }, orgAdmin),
|
||||
await assert.rejects(
|
||||
svc.decide({ id: submitted.proposal.id, decision: 'approve', reason: 'self approve' }, orgAdmin),
|
||||
ForbiddenException,
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user