Claude-code-plugins-plus maintainx-enterprise-rbac
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/maintainx-pack/skills/maintainx-enterprise-rbac" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-maintainx-enterprise-rbac && rm -rf "$T"
manifest:
plugins/saas-packs/maintainx-pack/skills/maintainx-enterprise-rbac/SKILL.mdsource content
MaintainX Enterprise RBAC
Overview
Configure enterprise role-based access control for MaintainX integrations with role definitions, location-scoped permissions, and audit logging.
Prerequisites
- MaintainX Enterprise plan
- Understanding of RBAC concepts
- Node.js 18+
MaintainX Role Hierarchy
Organization Admin ├── can manage all locations, users, teams, and settings ├── Full API access │ Location Manager ├── can manage work orders, assets at assigned locations ├── API: filtered by locationId │ Technician ├── can view/update assigned work orders ├── API: filtered by assigneeId │ Viewer (Read-Only) └── can view work orders, assets, locations └── API: GET endpoints only
Instructions
Step 1: Role Definitions
// src/rbac/roles.ts export type Role = 'admin' | 'manager' | 'technician' | 'viewer'; interface Permission { resource: string; actions: Array<'create' | 'read' | 'update' | 'delete'>; scope?: 'all' | 'location' | 'assigned'; } export const ROLE_PERMISSIONS: Record<Role, Permission[]> = { admin: [ { resource: 'workorders', actions: ['create', 'read', 'update', 'delete'], scope: 'all' }, { resource: 'assets', actions: ['create', 'read', 'update', 'delete'], scope: 'all' }, { resource: 'locations', actions: ['create', 'read', 'update', 'delete'], scope: 'all' }, { resource: 'users', actions: ['create', 'read', 'update', 'delete'], scope: 'all' }, { resource: 'teams', actions: ['create', 'read', 'update', 'delete'], scope: 'all' }, ], manager: [ { resource: 'workorders', actions: ['create', 'read', 'update'], scope: 'location' }, { resource: 'assets', actions: ['create', 'read', 'update'], scope: 'location' }, { resource: 'locations', actions: ['read'], scope: 'location' }, { resource: 'users', actions: ['read'], scope: 'all' }, { resource: 'teams', actions: ['read'], scope: 'all' }, ], technician: [ { resource: 'workorders', actions: ['read', 'update'], scope: 'assigned' }, { resource: 'assets', actions: ['read'], scope: 'location' }, { resource: 'locations', actions: ['read'], scope: 'location' }, ], viewer: [ { resource: 'workorders', actions: ['read'], scope: 'all' }, { resource: 'assets', actions: ['read'], scope: 'all' }, { resource: 'locations', actions: ['read'], scope: 'all' }, ], };
Step 2: Permission Middleware
// src/rbac/middleware.ts import express from 'express'; interface AuthContext { userId: number; role: Role; locationIds: number[]; // Locations this user can access } function authorize(resource: string, action: 'create' | 'read' | 'update' | 'delete') { return (req: express.Request, res: express.Response, next: express.NextFunction) => { const auth = req.user as AuthContext; if (!auth) return res.status(401).json({ error: 'Not authenticated' }); const perms = ROLE_PERMISSIONS[auth.role]; const match = perms.find( (p) => p.resource === resource && p.actions.includes(action), ); if (!match) { return res.status(403).json({ error: 'Insufficient permissions', required: { resource, action }, role: auth.role, }); } // Apply scope filtering if (match.scope === 'location') { req.query.locationId = auth.locationIds.join(','); } else if (match.scope === 'assigned') { req.query.assigneeId = String(auth.userId); } next(); }; } // Usage in routes const router = express.Router(); router.get('/api/workorders', authorize('workorders', 'read'), async (req, res) => { const client = new MaintainXClient(); const { data } = await client.getWorkOrders(req.query as any); res.json(data); }); router.post('/api/workorders', authorize('workorders', 'create'), async (req, res) => { const client = new MaintainXClient(); const { data } = await client.createWorkOrder(req.body); res.json(data); });
Step 3: Scoped API Keys
Create separate API keys per role to enforce server-side access control:
// src/rbac/scoped-clients.ts const scopedClients: Record<Role, MaintainXClient> = { admin: new MaintainXClient(process.env.MAINTAINX_API_KEY_ADMIN), manager: new MaintainXClient(process.env.MAINTAINX_API_KEY_MANAGER), technician: new MaintainXClient(process.env.MAINTAINX_API_KEY_TECH), viewer: new MaintainXClient(process.env.MAINTAINX_API_KEY_VIEWER), }; function getClientForRole(role: Role): MaintainXClient { const client = scopedClients[role]; if (!client) throw new Error(`No client configured for role: ${role}`); return client; }
Step 4: User and Team Management
// Fetch all users and their roles async function listUsersWithRoles(client: MaintainXClient) { const { data } = await client.getUsers({ limit: 100 }); const { data: teams } = await client.request('GET', '/teams'); const userTeams = new Map<number, string[]>(); for (const team of teams.teams) { for (const member of team.members || []) { const existing = userTeams.get(member.id) || []; existing.push(team.name); userTeams.set(member.id, existing); } } for (const user of data.users) { const teamList = userTeams.get(user.id) || []; console.log(` ${user.firstName} ${user.lastName} (${user.email})`); console.log(` Role: ${user.role || 'N/A'} | Teams: ${teamList.join(', ') || 'None'}`); } }
Step 5: Audit Logging
// src/rbac/audit.ts function logAccess(auth: AuthContext, resource: string, action: string, result: 'allow' | 'deny') { const entry = { timestamp: new Date().toISOString(), type: 'access_control', userId: auth.userId, role: auth.role, resource, action, result, locationScope: auth.locationIds, }; // Send to your log aggregation (CloudWatch, Datadog, etc.) console.log(JSON.stringify(entry)); }
Output
- Role definitions mapping roles to resource permissions and scopes
- Express middleware enforcing RBAC on all API proxy routes
- Location-scoped and assignee-scoped query filtering
- Scoped API keys per role for defense in depth
- Audit logging for all access control decisions
Error Handling
| Issue | Cause | Solution |
|---|---|---|
| 403 on valid user | Missing permission for action | Check mapping |
| Empty results for manager | Location scope filter too narrow | Verify in auth context |
| Scoped key missing | Environment variable not set | Add to env |
| Audit log gaps | Middleware not applied to route | Ensure is on all routes |
Resources
Next Steps
For complete platform migration, see
maintainx-migration-deep-dive.
Examples
Check if a user can perform an action:
function canPerform(role: Role, resource: string, action: string): boolean { const perms = ROLE_PERMISSIONS[role]; return perms.some((p) => p.resource === resource && p.actions.includes(action as any)); } console.log(canPerform('technician', 'workorders', 'update')); // true console.log(canPerform('technician', 'workorders', 'delete')); // false console.log(canPerform('viewer', 'workorders', 'read')); // true