fix(authz): tighten policy scope enforcement
This commit is contained in:
@@ -37,6 +37,10 @@ test('GET /readyz reports database not configured when no database env is presen
|
||||
assert.equal(res.body.status, 'DATABASE_NOT_CONFIGURED');
|
||||
});
|
||||
|
||||
test.afterEach(() => {
|
||||
delete process.env.AUTH_BYPASS_CONTEXT;
|
||||
});
|
||||
|
||||
test('createApp fails fast in protected env when auth bypass is enabled', async () => {
|
||||
process.env.APP_ENV = 'staging';
|
||||
process.env.AUTH_BYPASS = 'true';
|
||||
@@ -134,3 +138,28 @@ test('GET /query/tenants/:tenantId/businesses/:businessId/favorite-staff validat
|
||||
assert.equal(res.status, 200);
|
||||
assert.equal(res.body.items[0].staffId, staffId);
|
||||
});
|
||||
|
||||
test('GET /query/tenants/:tenantId/orders denies mismatched tenant scope before handler execution', async () => {
|
||||
process.env.AUTH_BYPASS_CONTEXT = JSON.stringify({
|
||||
user: { userId: 'test-user' },
|
||||
tenant: { tenantId: '99999999-9999-4999-8999-999999999999', role: 'MANAGER' },
|
||||
business: { businessId },
|
||||
});
|
||||
|
||||
const app = createApp({
|
||||
queryService: {
|
||||
listOrders: async () => assert.fail('listOrders should not be called'),
|
||||
getOrderDetail: async () => assert.fail('getOrderDetail should not be called'),
|
||||
listFavoriteStaff: async () => assert.fail('listFavoriteStaff should not be called'),
|
||||
getStaffReviewSummary: async () => assert.fail('getStaffReviewSummary should not be called'),
|
||||
getAssignmentAttendance: async () => assert.fail('getAssignmentAttendance should not be called'),
|
||||
},
|
||||
});
|
||||
|
||||
const res = await request(app)
|
||||
.get(`/query/tenants/${tenantId}/orders`)
|
||||
.set('Authorization', 'Bearer test-token');
|
||||
|
||||
assert.equal(res.status, 403);
|
||||
assert.equal(res.body.code, 'FORBIDDEN');
|
||||
});
|
||||
|
||||
86
backend/query-api/test/policy.test.js
Normal file
86
backend/query-api/test/policy.test.js
Normal file
@@ -0,0 +1,86 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { can } from '../src/services/policy.js';
|
||||
|
||||
test('orders.read requires client scope and matching tenant/business scope', async () => {
|
||||
const allowed = await can(
|
||||
'orders.read',
|
||||
'order',
|
||||
{
|
||||
uid: 'user-1',
|
||||
policyContext: {
|
||||
user: { userId: 'user-1' },
|
||||
tenant: { tenantId: 'tenant-1', role: 'MANAGER' },
|
||||
business: { businessId: 'business-1' },
|
||||
},
|
||||
},
|
||||
{ params: { tenantId: 'tenant-1' }, query: { businessId: 'business-1' } }
|
||||
);
|
||||
|
||||
const denied = await can(
|
||||
'orders.read',
|
||||
'order',
|
||||
{
|
||||
uid: 'user-1',
|
||||
policyContext: {
|
||||
user: { userId: 'user-1' },
|
||||
tenant: { tenantId: 'tenant-1', role: 'MANAGER' },
|
||||
business: { businessId: 'business-1' },
|
||||
},
|
||||
},
|
||||
{ params: { tenantId: 'tenant-2' }, query: { businessId: 'business-1' } }
|
||||
);
|
||||
|
||||
assert.equal(allowed, true);
|
||||
assert.equal(denied, false);
|
||||
});
|
||||
|
||||
test('shifts.read requires staff scope', async () => {
|
||||
const allowed = await can(
|
||||
'shifts.read',
|
||||
'shift',
|
||||
{
|
||||
uid: 'user-1',
|
||||
policyContext: {
|
||||
user: { userId: 'user-1' },
|
||||
tenant: { tenantId: 'tenant-1' },
|
||||
staff: { staffId: 'staff-1' },
|
||||
},
|
||||
},
|
||||
{ params: {} }
|
||||
);
|
||||
|
||||
const denied = await can(
|
||||
'shifts.read',
|
||||
'shift',
|
||||
{
|
||||
uid: 'user-1',
|
||||
policyContext: {
|
||||
user: { userId: 'user-1' },
|
||||
tenant: { tenantId: 'tenant-1' },
|
||||
business: { businessId: 'business-1' },
|
||||
},
|
||||
},
|
||||
{ params: {} }
|
||||
);
|
||||
|
||||
assert.equal(allowed, true);
|
||||
assert.equal(denied, false);
|
||||
});
|
||||
|
||||
test('attendance.read allows tenant-scoped actor', async () => {
|
||||
const allowed = await can(
|
||||
'attendance.read',
|
||||
'attendance',
|
||||
{
|
||||
uid: 'user-1',
|
||||
policyContext: {
|
||||
user: { userId: 'user-1' },
|
||||
tenant: { tenantId: 'tenant-1' },
|
||||
},
|
||||
},
|
||||
{ params: { tenantId: 'tenant-1' } }
|
||||
);
|
||||
|
||||
assert.equal(allowed, true);
|
||||
});
|
||||
Reference in New Issue
Block a user