agentsclimarketplace

Maintainx enterprise rbac

Skill jeremylongshore/claude-code-plugins-plus-skills/plugins/saas-packs/maintainx-pack/skills/maintainx-enterprise-rbac

425 plugins, 2,810 skills, 200 agents for Claude Code. Open-source marketplace at tonsofskills.com with the ccpi CLI package manager.

Install
npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill maintainx-enterprise-rbac

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its author says it does

Copied from the file, not written here

'Configure enterprise role-based access control for MaintainX integrations.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

8.0 KB, as published. Nobody here has run it

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

IssueCauseSolution
403 on valid userMissing permission for actionCheck ROLE_PERMISSIONS mapping
Empty results for managerLocation scope filter too narrowVerify locationIds in auth context
Scoped key missingEnvironment variable not setAdd MAINTAINX_API_KEY_{ROLE} to env
Audit log gapsMiddleware not applied to routeEnsure authorize() 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

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.