Improve how users can access visible applications and files

Refactor `getVisibleAppsForUser` into a new file `appsVisibility.ts` and update existing imports. Add a new test case for app icon object authorization. Modify the `objectAuthz.ts` file to use a more precise JSONB path check for meeting attachments.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: d1d78e3b-ae14-4da2-b782-586269e0ef7e
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/jLdqQ2v
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
riyadhafraa
2026-05-13 10:14:35 +00:00
parent 0c8da09ea0
commit 745e503940
4 changed files with 194 additions and 92 deletions
@@ -363,6 +363,75 @@ test("J: GET /executive-meetings/pdf-archives by executive_viewer → 200", asyn
assert.ok(ours, "the seeded archive row must be visible to executive_viewer");
});
// ---- L: app-icon authz mirrors launcher RBAC ----------------------
test("L: app-icon object: user with app permission streams body, restricted user 404", async () => {
// Upload a real icon, attach it to a freshly-created app, then
// restrict the app via a unique permission. Only users granted
// that permission (here: admin) should see the icon stream.
const presignRes = await fetch(
`${API_BASE}/api/storage/uploads/request-url`,
{
method: "POST",
headers: { "Content-Type": "application/json", cookie: adminCookie },
body: JSON.stringify({
name: "app-icon.bin",
size: 8,
contentType: "application/octet-stream",
}),
},
);
assert.equal(presignRes.status, 200);
const { uploadURL, objectPath } = await presignRes.json();
const payload = "iconbyt";
const putRes = await fetch(uploadURL, {
method: "PUT",
headers: { "Content-Type": "application/octet-stream" },
body: payload,
});
assert.ok(putRes.status >= 200 && putRes.status < 300);
const appSlug = `obj_authz_app_${STAMP}`;
const permName = `obj_authz_perm_${STAMP}`;
const appRow = await pool.query(
`INSERT INTO apps (slug, name_en, name_ar, route, image_url)
VALUES ($1, $2, $3, $4, $5)
RETURNING id`,
[appSlug, appSlug, appSlug, `/${appSlug}`, objectPath],
);
const appId = appRow.rows[0].id;
const permRow = await pool.query(
`INSERT INTO permissions (name) VALUES ($1) RETURNING id`,
[permName],
);
const permId = permRow.rows[0].id;
await pool.query(
`INSERT INTO app_permissions (app_id, permission_id) VALUES ($1, $2)`,
[appId, permId],
);
try {
const tail = objectPath.replace("/objects/", "");
// Admin — sees every app via getVisibleAppsForUser short-circuit.
const allow = await fetch(`${API_BASE}/api/storage/objects/${tail}`, {
headers: { cookie: adminCookie },
});
assert.equal(allow.status, 200, `admin expected 200, got ${allow.status}`);
assert.equal(await allow.text(), payload, "admin should stream the icon bytes");
// Receiver — has no role granting permName, no group_apps row.
// Must be denied at authz → 404.
const deny = await fetch(`${API_BASE}/api/storage/objects/${tail}`, {
headers: { cookie: receiverCookie },
});
assert.equal(deny.status, 404, `receiver expected 404, got ${deny.status}`);
} finally {
await pool.query(`DELETE FROM app_permissions WHERE app_id = $1`, [appId]);
await pool.query(`DELETE FROM permissions WHERE id = $1`, [permId]);
await pool.query(`DELETE FROM apps WHERE id = $1`, [appId]);
}
});
// ---- K: positive + negative on the SAME real brand-logo object ----
test("K: brand-logo object: executive_viewer streams body 200, non-executive 404", async () => {
// Upload a real file as admin (any authed can presign), wire it to