We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/andychoi/mcp-strapi'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
#!/usr/bin/env node
/**
* Test 10: Secure Document ABAC Enforcement
*
* Tests ABAC policy enforcement across users with different attributes:
* - Classification-based access (clearance levels)
* - Department-based access (allowed_departments)
* - Tenant isolation (tenantId)
* - Contractor exclusion (excluded_employmentTypes)
* - Cross-user visibility (admin sees all, author sees subset)
*
* Uses the three demo users:
* - admin@example.com — clearance 3, all departments, employee
* - editor@example.com — clearance 2, engineering, employee
* - author@example.com — clearance 1, engineering, contractor
*/
import {
requireStrapi,
section,
pass,
fail,
skip,
assert,
assertEqual,
assertStatus,
summary,
adminLogin,
STRAPI_URL,
fetchJSON,
ADMIN_EMAIL,
ADMIN_PASSWORD,
} from './helpers.js';
const DOCS_URL = `${STRAPI_URL}/api/secure-documents/documents`;
// Demo users from bootstrap seed
const USERS = {
admin: { email: 'admin@example.com', password: 'Admin1234!' },
editor: { email: 'editor@example.com', password: 'Admin1234!' },
author: { email: 'author@example.com', password: 'Admin1234!' },
};
/**
* Upload a text document with a given policy.
*/
async function uploadDoc(token, title, policy) {
const boundary = '----ABACTest' + Date.now() + Math.random().toString(36).slice(2);
const fileContent = `Test document: ${title}\nCreated for ABAC testing.`;
const body = [
`--${boundary}`,
`Content-Disposition: form-data; name="file"; filename="${title.replace(/\s/g, '-').toLowerCase()}.txt"`,
'Content-Type: text/plain',
'',
fileContent,
`--${boundary}`,
'Content-Disposition: form-data; name="policy"',
'',
JSON.stringify(policy),
`--${boundary}`,
'Content-Disposition: form-data; name="metadata"',
'',
JSON.stringify({ title, description: `ABAC test: ${title}` }),
`--${boundary}--`,
].join('\r\n');
const resp = await fetch(DOCS_URL, {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': `multipart/form-data; boundary=${boundary}`,
},
body,
});
const data = await resp.json().catch(() => null);
return { status: resp.status, data, documentId: data?.data?.documentId };
}
/**
* Delete a document by documentId.
*/
async function deleteDoc(token, documentId) {
await fetch(`${DOCS_URL}/${documentId}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${token}` },
});
}
async function main() {
console.log('\x1b[1m=== Test 10: Secure Document ABAC Enforcement ===\x1b[0m');
await requireStrapi();
// ── Setup: Login all users ─────────────────────────────────────────
section('Setup');
const tokens = {};
for (const [role, creds] of Object.entries(USERS)) {
const result = await adminLogin(creds.email, creds.password);
if (result?.token) {
tokens[role] = result.token;
pass(`Login ${role} (${creds.email})`);
} else {
fail(`Login ${role} (${creds.email})`, 'Login failed');
}
}
if (!tokens.admin) {
fail('Admin login required', 'Cannot proceed without admin');
summary();
process.exit(1);
}
// Track created docs for cleanup
const createdDocs = [];
try {
// ── Create test documents with varying policies ──────────────────
section('Create Test Documents');
// Public doc — anyone can access
const publicDoc = await uploadDoc(tokens.admin, 'ABAC Public Doc', {
classification: 'public',
min_clearance: 0,
});
if (publicDoc.status === 201) {
createdDocs.push(publicDoc.documentId);
pass('Create public document');
} else {
fail('Create public document', `HTTP ${publicDoc.status}`);
}
// Internal engineering doc — clearance 1, engineering only
const internalDoc = await uploadDoc(tokens.admin, 'ABAC Internal Engineering', {
classification: 'internal',
min_clearance: 1,
allowed_departments: ['engineering'],
});
if (internalDoc.status === 201) {
createdDocs.push(internalDoc.documentId);
pass('Create internal engineering document');
} else {
fail('Create internal engineering document', `HTTP ${internalDoc.status}`);
}
// Confidential doc — clearance 2, no contractors
const confidentialDoc = await uploadDoc(tokens.admin, 'ABAC Confidential No Contractors', {
classification: 'confidential',
min_clearance: 2,
excluded_employmentTypes: ['contractor'],
});
if (confidentialDoc.status === 201) {
createdDocs.push(confidentialDoc.documentId);
pass('Create confidential document (no contractors)');
} else {
fail('Create confidential document', `HTTP ${confidentialDoc.status}`);
}
// Restricted doc — clearance 3, admin only
const restrictedDoc = await uploadDoc(tokens.admin, 'ABAC Restricted Admin Only', {
classification: 'restricted',
min_clearance: 3,
});
if (restrictedDoc.status === 201) {
createdDocs.push(restrictedDoc.documentId);
pass('Create restricted document');
} else {
fail('Create restricted document', `HTTP ${restrictedDoc.status}`);
}
// Finance-only doc — department locked
const financeDoc = await uploadDoc(tokens.admin, 'ABAC Finance Only', {
classification: 'internal',
min_clearance: 1,
allowed_departments: ['finance'],
});
if (financeDoc.status === 201) {
createdDocs.push(financeDoc.documentId);
pass('Create finance-only document');
} else {
fail('Create finance-only document', `HTTP ${financeDoc.status}`);
}
// ── Admin access (clearance 3, all departments) ──────────────────
section('Admin Access (clearance 3, full access)');
if (tokens.admin) {
// Admin should see all documents
const { status, data } = await fetchJSON(DOCS_URL, {
headers: { Authorization: `Bearer ${tokens.admin}` },
});
if (assertStatus(status, 200, 'Admin: list documents')) {
const titles = data.data.map((d) => d.title);
for (const doc of ['ABAC Public Doc', 'ABAC Internal Engineering', 'ABAC Confidential No Contractors', 'ABAC Restricted Admin Only', 'ABAC Finance Only']) {
assert(titles.includes(doc), `Admin sees: ${doc}`, `Not found in list: ${titles.join(', ')}`);
}
}
// Admin can access restricted doc directly
if (restrictedDoc.documentId) {
const { status: s } = await fetchJSON(`${DOCS_URL}/${restrictedDoc.documentId}`, {
headers: { Authorization: `Bearer ${tokens.admin}` },
});
assertStatus(s, 200, 'Admin: access restricted document');
}
}
// ── Editor access (clearance 2, engineering, employee) ───────────
section('Editor Access (clearance 2, engineering, employee)');
if (tokens.editor) {
const { status, data } = await fetchJSON(DOCS_URL, {
headers: { Authorization: `Bearer ${tokens.editor}` },
});
if (assertStatus(status, 200, 'Editor: list documents')) {
const titles = data.data.map((d) => d.title);
// Editor should see public docs
assert(titles.includes('ABAC Public Doc'), 'Editor sees public doc');
// Editor has engineering + clearance 2, should see internal engineering
// Note: depends on how user attributes are configured in bootstrap
// The test verifies the ABAC filter is applied
const docCount = data.data.length;
assert(docCount > 0, `Editor sees ${docCount} documents`, 'Editor sees no documents');
}
// Editor should NOT see restricted doc (clearance 3 required)
if (restrictedDoc.documentId) {
const { status: s } = await fetchJSON(`${DOCS_URL}/${restrictedDoc.documentId}`, {
headers: { Authorization: `Bearer ${tokens.editor}` },
});
assert(s === 403 || s === 404, 'Editor: denied restricted document', `Expected 403/404, got ${s}`);
}
}
// ── Author access (clearance 1, engineering, contractor) ─────────
section('Author Access (clearance 1, engineering, contractor)');
if (tokens.author) {
const { status, data } = await fetchJSON(DOCS_URL, {
headers: { Authorization: `Bearer ${tokens.author}` },
});
if (assertStatus(status, 200, 'Author: list documents')) {
const titles = data.data.map((d) => d.title);
// Author should see public docs
assert(titles.includes('ABAC Public Doc'), 'Author sees public doc');
// Author should NOT see confidential (clearance 2, + no contractors)
assert(!titles.includes('ABAC Confidential No Contractors'), 'Author: denied confidential (contractor excluded)', `Found in list unexpectedly`);
// Author should NOT see restricted (clearance 3)
assert(!titles.includes('ABAC Restricted Admin Only'), 'Author: denied restricted document');
}
// Author direct access to confidential → 403
if (confidentialDoc.documentId) {
const { status: s } = await fetchJSON(`${DOCS_URL}/${confidentialDoc.documentId}`, {
headers: { Authorization: `Bearer ${tokens.author}` },
});
assert(s === 403, 'Author: 403 on confidential doc', `Expected 403, got ${s}`);
}
// Author direct access to finance-only → 403 (wrong department)
if (financeDoc.documentId) {
const { status: s } = await fetchJSON(`${DOCS_URL}/${financeDoc.documentId}`, {
headers: { Authorization: `Bearer ${tokens.author}` },
});
assert(s === 403, 'Author: 403 on finance-only doc', `Expected 403, got ${s}`);
}
}
// ── Cross-check: update denied by ABAC ───────────────────────────
section('ABAC on Update/Delete');
if (tokens.author && restrictedDoc.documentId) {
const { status } = await fetchJSON(`${DOCS_URL}/${restrictedDoc.documentId}`, {
method: 'PUT',
headers: { Authorization: `Bearer ${tokens.author}` },
body: JSON.stringify({ title: 'Hacked' }),
});
assert(status === 403, 'Author: cannot update restricted doc', `Expected 403, got ${status}`);
}
if (tokens.author && restrictedDoc.documentId) {
const resp = await fetch(`${DOCS_URL}/${restrictedDoc.documentId}`, {
method: 'DELETE',
headers: {
Authorization: `Bearer ${tokens.author}`,
'Content-Type': 'application/json',
},
});
assert(resp.status === 403, 'Author: cannot delete restricted doc', `Expected 403, got ${resp.status}`);
}
} finally {
// ── Cleanup ──────────────────────────────────────────────────────
section('Cleanup');
for (const docId of createdDocs) {
if (docId) {
try {
await deleteDoc(tokens.admin, docId);
} catch {
// Ignore cleanup errors
}
}
}
pass(`Cleaned up ${createdDocs.length} test documents`);
}
// ── Summary ────────────────────────────────────────────────────────
section('Summary');
const results = summary();
process.exit(results.failed > 0 ? 1 : 0);
}
main().catch((err) => {
console.error('Fatal error:', err);
process.exit(1);
});