71 lines
3.4 KiB
JavaScript
71 lines
3.4 KiB
JavaScript
|
|
// smoke: permission admin endpoints (Bearer + Casbin RBAC required)
|
||
|
|
//
|
||
|
|
// Goal: verify each route is wired and rejects a non-admin caller. Full happy-
|
||
|
|
// path admin testing lives in journeys/rbac_admin.js (requires seeded admin).
|
||
|
|
//
|
||
|
|
// Covers (12 endpoints, mostly negative since the test user has no admin role):
|
||
|
|
// GET /api/v1/permissions/roles
|
||
|
|
// POST /api/v1/permissions/roles
|
||
|
|
// PATCH /api/v1/permissions/roles/:id
|
||
|
|
// DELETE /api/v1/permissions/roles/:id
|
||
|
|
// GET /api/v1/permissions/roles/:id/permissions
|
||
|
|
// PUT /api/v1/permissions/roles/:id/permissions
|
||
|
|
// GET /api/v1/permissions/users/:uid/roles
|
||
|
|
// POST /api/v1/permissions/users/:uid/roles
|
||
|
|
// DELETE /api/v1/permissions/users/:uid/roles/:role_id
|
||
|
|
// GET /api/v1/permissions/role-mappings
|
||
|
|
// PUT /api/v1/permissions/role-mappings
|
||
|
|
// DELETE /api/v1/permissions/role-mappings
|
||
|
|
// POST /api/v1/permissions/policy/reload
|
||
|
|
//
|
||
|
|
// Each call is expected to return 403 (RBAC) — we just need to confirm the
|
||
|
|
// status is non-2xx and the route exists (no 404).
|
||
|
|
import { get, post, put, patch, del } from '../lib/http.js';
|
||
|
|
import { check } from 'k6';
|
||
|
|
import { registerAndConfirm } from '../lib/auth.js';
|
||
|
|
|
||
|
|
export const options = {
|
||
|
|
vus: 1,
|
||
|
|
iterations: 1,
|
||
|
|
thresholds: { checks: ['rate==1.0'] },
|
||
|
|
};
|
||
|
|
|
||
|
|
function assertForbidden(res, label) {
|
||
|
|
// Casbin reject → 403 (31507000 forbidden). We tolerate 401/403 here since
|
||
|
|
// the exact code may vary across edge cases (missing role vs RBAC denied).
|
||
|
|
const ok = check(res, {
|
||
|
|
[`${label}: route exists (not 404)`]: (r) => r.status !== 404,
|
||
|
|
[`${label}: non-2xx (RBAC blocks)`]: (r) => r.status >= 400 && r.status < 500,
|
||
|
|
});
|
||
|
|
if (!ok) {
|
||
|
|
throw new Error(`${label}: unexpected status ${res.status} body=${res.body}`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export default function () {
|
||
|
|
const { tokens } = registerAndConfirm();
|
||
|
|
const bearer = { Authorization: `Bearer ${tokens.access_token}` };
|
||
|
|
const fakeID = '000000000000000000000000';
|
||
|
|
|
||
|
|
assertForbidden(get('/api/v1/permissions/roles', bearer), 'GET /roles');
|
||
|
|
assertForbidden(post('/api/v1/permissions/roles', { key: 'smoke_role' }, bearer), 'POST /roles');
|
||
|
|
assertForbidden(patch(`/api/v1/permissions/roles/${fakeID}`, { display_name: 'x' }, bearer), 'PATCH /roles/:id');
|
||
|
|
assertForbidden(del(`/api/v1/permissions/roles/${fakeID}`, null, bearer), 'DELETE /roles/:id');
|
||
|
|
assertForbidden(get(`/api/v1/permissions/roles/${fakeID}/permissions`, bearer), 'GET /roles/:id/permissions');
|
||
|
|
assertForbidden(put(`/api/v1/permissions/roles/${fakeID}/permissions`, { permission_ids: [] }, bearer), 'PUT /roles/:id/permissions');
|
||
|
|
assertForbidden(get(`/api/v1/permissions/users/${tokens.uid}/roles`, bearer), 'GET /users/:uid/roles');
|
||
|
|
assertForbidden(post(`/api/v1/permissions/users/${tokens.uid}/roles`, { role_id: fakeID }, bearer), 'POST /users/:uid/roles');
|
||
|
|
assertForbidden(del(`/api/v1/permissions/users/${tokens.uid}/roles/${fakeID}`, null, bearer), 'DELETE /users/:uid/roles/:role_id');
|
||
|
|
assertForbidden(get('/api/v1/permissions/role-mappings', bearer), 'GET /role-mappings');
|
||
|
|
assertForbidden(put('/api/v1/permissions/role-mappings', {
|
||
|
|
external_source: 'zitadel',
|
||
|
|
external_key: 'smoke',
|
||
|
|
internal_role_key: 'platform_admin',
|
||
|
|
}, bearer), 'PUT /role-mappings');
|
||
|
|
assertForbidden(del('/api/v1/permissions/role-mappings', {
|
||
|
|
external_source: 'zitadel',
|
||
|
|
external_key: 'smoke',
|
||
|
|
}, bearer), 'DELETE /role-mappings');
|
||
|
|
assertForbidden(post('/api/v1/permissions/policy/reload', {}, bearer), 'POST /policy/reload');
|
||
|
|
}
|