When debugging or auditing GraphQL APIs — especially in Hasura —
the key question is often:
“Which roles can access which fields on which types?”
Answering this by hand is slow, error-prone, and unscalable.
This guide shows how to auto-generate a Role × Field access matrix, export it as CSV/HTML, and visualize it interactively.
1. Objective: Matrix Output
Example table:
Role | Table | Field | SELECT | INSERT | UPDATE | DELETE |
---|---|---|---|---|---|---|
user | users | id | ✅ | ❌ | ❌ | ❌ |
user | users | ✅ | ❌ | ✅ | ❌ | |
public | posts | title | ✅ | ✅ | ❌ | ❌ |
admin | users | password_hash | ✅ | ✅ | ✅ | ✅ |
2. Data Source: Hasura Metadata
Permissions live in:
metadata/
├── tables/
│ ├── users.yaml
│ ├── posts.yaml
Each file contains permissions by role:
select_permissions:
- role: user
permission:
columns: [id, email]
filter: { id: { _eq: X-Hasura-User-Id } }
insert_permissions:
- role: public
permission:
columns: [title, content]
3. CLI Generator Script (Node.js)
Install dependencies:
npm install yaml glob chalk fs
Script outline:
// rbac-matrix.js
import fs from 'fs';
import yaml from 'yaml';
import glob from 'glob';
const matrix = [];
const files = glob.sync('./metadata/tables/*.yaml');
for (const file of files) {
const table = file.split('/').pop().replace('.yaml', '');
const doc = yaml.parse(fs.readFileSync(file, 'utf8'));
['select', 'insert', 'update', 'delete'].forEach(action => {
const perms = doc[`${action}_permissions`] || [];
perms.forEach(p => {
const role = p.role;
const cols = p.permission?.columns || [];
cols.forEach(col => {
const row = matrix.find(r => r.role === role && r.table === table && r.field === col);
if (row) {
row[action.toUpperCase()] = '✅';
} else {
matrix.push({
role,
table,
field: col,
SELECT: '',
INSERT: '',
UPDATE: '',
DELETE: '',
[action.toUpperCase()]: '✅'
});
}
});
});
});
}
const csv = [
['Role', 'Table', 'Field', 'SELECT', 'INSERT', 'UPDATE', 'DELETE'],
...matrix.map(r => [r.role, r.table, r.field, r.SELECT, r.INSERT, r.UPDATE, r.DELETE])
].map(row => row.join(',')).join('\n');
fs.writeFileSync('rbac-matrix.csv', csv);
console.log('✅ RBAC matrix written to rbac-matrix.csv');
4. Optional: HTML Table Renderer
Convert CSV → HTML table with filters (use DataTables.js or React):
$('#rbac').DataTable();
Use for:
- Visual review by security teams
- Review in PRs or audit sessions
5. CI Integration
Add as a script in package.json
:
"scripts": {
"rbac:matrix": "node rbac-matrix.js"
}
Then include in CI for drift detection / snapshot comparison.
Final Thoughts
Field-level RBAC is a security asset — but only if it’s visible.
This matrix gives devs, security, and auditors a shared source of truth.
What’s visible becomes improvable.
In future posts:
- Visual diff between dev/prod RBAC matrices
- Mapping role → field → query impact
- Live explorer for GraphQL access previews
Render the matrix. Detect the drift. Own the access graph.