| name | maintainx-enterprise-rbac |
| description | Configure enterprise role-based access control for MaintainX integrations.
Use when implementing SSO, managing organization-level permissions,
or setting up enterprise access controls with MaintainX.
Trigger with phrases like "maintainx rbac", "maintainx sso",
"maintainx enterprise", "maintainx permissions", "maintainx roles".
|
| allowed-tools | Read, Write, Edit, Bash(npm:*) |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
MaintainX Enterprise RBAC
Overview
Configure enterprise-grade role-based access control for MaintainX integrations, including SSO integration, permission management, and audit logging.
Prerequisites
- MaintainX Enterprise plan
- Identity Provider (IdP) with SAML/OIDC
- Understanding of RBAC concepts
MaintainX Role Hierarchy
┌─────────────────────────────────────────────────────────────────────┐
│ MaintainX Role Hierarchy │
│ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ ORGANIZATION ADMIN │ │
│ │ Full access to all features, users, and settings │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────────────┼────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ ADMIN │ │ SUPERVISOR │ │ REQUESTER │ │
│ │ │ │ │ │ │ │
│ │ Manage users │ │ Manage work │ │ Submit │ │
│ │ Manage assets │ │ Assign tasks │ │ requests only │ │
│ │ Full WO access│ │ View reports │ │ │ │
│ └───────────────┘ └───────┬───────┘ └───────────────┘ │
│ │ │
│ ┌──────────────┴──────────────┐ │
│ ▼ ▼ │
│ ┌───────────────┐ ┌───────────────┐ │
│ │ TECHNICIAN │ │ VIEWER │ │
│ │ │ │ │ │
│ │ Execute work │ │ Read-only │ │
│ │ Update status │ │ View reports │ │
│ └───────────────┘ └───────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
Instructions
Step 1: Role Definition
enum MaintainXRole {
OrganizationAdmin = 'ORGANIZATION_ADMIN',
Admin = 'ADMIN',
Supervisor = 'SUPERVISOR',
Technician = 'TECHNICIAN',
Requester = 'REQUESTER',
Viewer = 'VIEWER',
}
interface Permission {
resource: string;
actions: ('create' | 'read' | 'update' | 'delete' | 'assign')[];
}
const rolePermissions: Record<MaintainXRole, Permission[]> = {
[MaintainXRole.OrganizationAdmin]: [
{ resource: '*', actions: ['create', 'read', 'update', 'delete', 'assign'] },
],
[MaintainXRole.Admin]: [
{ resource: 'workorders', actions: ['create', 'read', 'update', 'delete', 'assign'] },
{ resource: 'assets', : [, , , ] },
{ : , : [, , , ] },
{ : , : [, , ] },
],
[.]: [
{ : , : [, , , ] },
{ : , : [] },
{ : , : [] },
{ : , : [] },
{ : , : [] },
],
[.]: [
{ : , : [, ] },
{ : , : [] },
{ : , : [] },
],
[.]: [
{ : , : [, ] },
{ : , : [] },
],
[.]: [
{ : , : [] },
{ : , : [] },
{ : , : [] },
{ : , : [] },
],
};
(): {
permissions = rolePermissions[role];
permissions.( {
resourceMatch = p. === || p. === resource;
actionMatch = p..(action );
resourceMatch && actionMatch;
});
}
{ , hasPermission, rolePermissions };
Step 2: SAML SSO Integration
import { Strategy as SamlStrategy } from 'passport-saml';
import passport from 'passport';
interface SamlConfig {
entryPoint: string;
issuer: string;
cert: string;
callbackUrl: string;
}
const samlConfig: SamlConfig = {
entryPoint: process.env.SAML_ENTRY_POINT!,
issuer: process.env.SAML_ISSUER!,
cert: process.env.SAML_CERT!,
callbackUrl: `${process.env.APP_URL}/auth/saml/callback`,
};
const groupRoleMapping: Record<string, MaintainXRole> = {
'MaintainX-Admins': MaintainXRole.Admin,
'MaintainX-Supervisors': MaintainXRole.Supervisor,
: .,
: .,
: .,
};
passport.( (
{
...samlConfig,
: ,
},
(req, profile, done) => {
{
email = profile. || profile.;
groups = profile. || [];
role = .;
( [group, mappedRole] .(groupRoleMapping)) {
(groups.(group)) {
role = mappedRole;
;
}
}
user = ({
email,
: profile.,
: profile.,
role,
: profile.,
});
(, user);
} (error) {
(error);
}
}
));
app.(, passport.());
app.(,
passport.(, { : }),
{
res.();
}
);
Step 3: Permission Middleware
import { Request, Response, NextFunction } from 'express';
interface AuthenticatedRequest extends Request {
user?: {
id: string;
email: string;
role: MaintainXRole;
};
}
function requirePermission(resource: string, action: string) {
return (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
if (!req.user) {
return res.status(401).json({ error: 'Unauthorized' });
}
if (!hasPermission(req.user.role, resource, action)) {
auditLogger.log({
type: 'ACCESS_DENIED',
userId: req.user.id,
resource,
action,
: req.,
});
res.().({
: ,
: ,
});
}
();
};
}
() {
{
(!req.) {
res.().({ : });
}
(!allowedRoles.(req..)) {
res.().({
: ,
: ,
});
}
();
};
}
app.(,
(, ),
getWorkOrders
);
app.(,
(, ),
createWorkOrder
);
app.(,
(, ),
deleteWorkOrder
);
app.(,
(., .),
getUsers
);
Step 4: Location-Based Access Control
interface LocationAccess {
userId: string;
locationIds: string[];
includeChildren: boolean;
}
class LocationAccessControl {
private accessRules: Map<string, LocationAccess> = new Map();
setAccess(userId: string, locationIds: string[], includeChildren = true) {
this.accessRules.set(userId, {
userId,
locationIds,
includeChildren,
});
}
async canAccessWorkOrder(userId: string, workOrder: WorkOrder): Promise<boolean> {
const access = this.accessRules.get(userId);
if (!access || access.locationIds.length === 0) {
return true;
}
if (!workOrder.locationId) {
;
}
(access..(workOrder.)) {
;
}
(access.) {
childLocations = .(access.);
childLocations.(workOrder.);
}
;
}
(: []): <[]> {
: [] = [];
locations = maintainxClient.();
() {
locations.
.( l. === parentId)
.( {
children.(l.);
(l.);
});
}
parentIds.(findChildren);
children;
}
}
(): <[]> {
lac = ();
filtered = [];
( wo workOrders) {
( lac.(userId, wo)) {
filtered.(wo);
}
}
filtered;
}
Step 5: Audit Logging
interface AuditEntry {
timestamp: Date;
type: 'ACCESS_GRANTED' | 'ACCESS_DENIED' | 'DATA_MODIFIED' | 'LOGIN' | 'LOGOUT';
userId: string;
userEmail?: string;
userRole?: MaintainXRole;
resource?: string;
resourceId?: string;
action?: string;
ip: string;
userAgent?: string;
details?: any;
}
class AuditLogger {
private store: AuditStore;
async log(entry: Omit<AuditEntry, 'timestamp'>): Promise<void> {
const fullEntry: AuditEntry = {
...entry,
timestamp: new Date(),
};
await this.store.insert(fullEntry);
.();
(entry. === ) {
.(entry.);
}
}
(: ): <> {
recentDenials = ..({
userId,
: ,
: { : (.() - * * ) },
});
(recentDenials > ) {
({
: ,
userId,
: ,
});
}
}
(: , : ): <> {
entries = ..({
: { : startDate, : endDate },
});
{
: { : startDate, : endDate },
: entries.,
: .(entries, ),
: .(entries, ),
: entries.( e. === ),
: entries.( e. === ),
};
}
}
auditLogger = ();
{ auditLogger };
Step 6: API Key Scoping
interface ScopedApiKey {
id: string;
name: string;
keyHash: string;
permissions: Permission[];
locationRestrictions?: string[];
createdBy: string;
createdAt: Date;
expiresAt?: Date;
lastUsedAt?: Date;
}
class ApiKeyManager {
async createScopedKey(
name: string,
permissions: Permission[],
options?: {
locationRestrictions?: string[];
expiresIn?: number;
}
): Promise<{ key: string; id: string }> {
const rawKey = generateSecureToken(32);
const keyHash = hashApiKey(rawKey);
const apiKey: ScopedApiKey = {
id: generateId(),
name,
keyHash,
permissions,
locationRestrictions: options?.,
: (),
: (),
: options?.
? (.() + options. * * * * )
: ,
};
..(apiKey);
{
: rawKey,
: apiKey.,
};
}
(: ): < | > {
keyHash = (rawKey);
apiKey = ..({ keyHash });
(!apiKey) ;
(apiKey. && apiKey. < ()) {
;
}
..(
{ : apiKey. },
{ : { : () } }
);
apiKey;
}
}
Output
- Role definitions implemented
- SAML SSO integration
- Permission middleware
- Location-based access control
- Audit logging
- Scoped API keys
Enterprise Security Checklist
Resources
Next Steps
For complete platform migration, see maintainx-migration-deep-dive.