Configure Juicebox enterprise role-based access control. Use when implementing team permissions, configuring access policies, or setting up enterprise security controls. Trigger with phrases like "juicebox RBAC", "juicebox permissions", "juicebox access control", "juicebox enterprise security".
/plugin marketplace add jeremylongshore/claude-code-plugins-plus-skills/plugin install juicebox-pack@claude-code-plugins-plusThis skill is limited to using the following tools:
Implement enterprise-grade role-based access control for Juicebox integrations.
Admin
├── Manager
│ ├── Senior Recruiter
│ │ └── Recruiter
│ └── Hiring Manager
├── Analyst (read-only)
└── API Service Account
// lib/rbac/permissions.ts
export enum Permission {
// Search permissions
SEARCH_READ = 'search:read',
SEARCH_ADVANCED = 'search:advanced',
SEARCH_EXPORT = 'search:export',
// Profile permissions
PROFILE_READ = 'profile:read',
PROFILE_ENRICH = 'profile:enrich',
PROFILE_CONTACT = 'profile:contact',
PROFILE_NOTES = 'profile:notes',
// Team permissions
TEAM_VIEW = 'team:view',
TEAM_MANAGE = 'team:manage',
// Admin permissions
ADMIN_SETTINGS = 'admin:settings',
ADMIN_BILLING = 'admin:billing',
ADMIN_AUDIT = 'admin:audit'
}
export enum Role {
ADMIN = 'admin',
MANAGER = 'manager',
SENIOR_RECRUITER = 'senior_recruiter',
RECRUITER = 'recruiter',
HIRING_MANAGER = 'hiring_manager',
ANALYST = 'analyst',
SERVICE_ACCOUNT = 'service_account'
}
export const rolePermissions: Record<Role, Permission[]> = {
[Role.ADMIN]: Object.values(Permission), // All permissions
[Role.MANAGER]: [
Permission.SEARCH_READ,
Permission.SEARCH_ADVANCED,
Permission.SEARCH_EXPORT,
Permission.PROFILE_READ,
Permission.PROFILE_ENRICH,
Permission.PROFILE_CONTACT,
Permission.PROFILE_NOTES,
Permission.TEAM_VIEW,
Permission.TEAM_MANAGE
],
[Role.SENIOR_RECRUITER]: [
Permission.SEARCH_READ,
Permission.SEARCH_ADVANCED,
Permission.SEARCH_EXPORT,
Permission.PROFILE_READ,
Permission.PROFILE_ENRICH,
Permission.PROFILE_CONTACT,
Permission.PROFILE_NOTES,
Permission.TEAM_VIEW
],
[Role.RECRUITER]: [
Permission.SEARCH_READ,
Permission.PROFILE_READ,
Permission.PROFILE_CONTACT,
Permission.PROFILE_NOTES
],
[Role.HIRING_MANAGER]: [
Permission.SEARCH_READ,
Permission.PROFILE_READ,
Permission.PROFILE_NOTES,
Permission.TEAM_VIEW
],
[Role.ANALYST]: [
Permission.SEARCH_READ,
Permission.PROFILE_READ,
Permission.TEAM_VIEW
],
[Role.SERVICE_ACCOUNT]: [
Permission.SEARCH_READ,
Permission.PROFILE_READ,
Permission.PROFILE_ENRICH
]
};
// lib/rbac/permission-checker.ts
export class PermissionChecker {
constructor(private user: User) {}
hasPermission(permission: Permission): boolean {
const userPermissions = this.getUserPermissions();
return userPermissions.includes(permission);
}
hasAnyPermission(permissions: Permission[]): boolean {
return permissions.some(p => this.hasPermission(p));
}
hasAllPermissions(permissions: Permission[]): boolean {
return permissions.every(p => this.hasPermission(p));
}
private getUserPermissions(): Permission[] {
const role = this.user.role as Role;
const basePermissions = rolePermissions[role] || [];
// Add any custom permissions assigned to user
const customPermissions = this.user.customPermissions || [];
return [...new Set([...basePermissions, ...customPermissions])];
}
// Check permission with data-level access
async canAccessProfile(profileId: string): Promise<boolean> {
if (!this.hasPermission(Permission.PROFILE_READ)) {
return false;
}
// Check team-level access
if (this.user.teamRestrictions?.length > 0) {
const profile = await db.profiles.findUnique({
where: { id: profileId },
select: { ownedByTeam: true }
});
return this.user.teamRestrictions.includes(profile?.ownedByTeam);
}
return true;
}
}
// middleware/authorization.ts
import { Permission } from '../lib/rbac/permissions';
import { PermissionChecker } from '../lib/rbac/permission-checker';
export function requirePermission(...permissions: Permission[]) {
return async (req: Request, res: Response, next: NextFunction) => {
const user = req.user;
if (!user) {
return res.status(401).json({ error: 'Authentication required' });
}
const checker = new PermissionChecker(user);
if (!checker.hasAllPermissions(permissions)) {
await logAccessDenied(user, permissions, req);
return res.status(403).json({
error: 'Insufficient permissions',
required: permissions
});
}
next();
};
}
export function requireAnyPermission(...permissions: Permission[]) {
return async (req: Request, res: Response, next: NextFunction) => {
const user = req.user;
if (!user) {
return res.status(401).json({ error: 'Authentication required' });
}
const checker = new PermissionChecker(user);
if (!checker.hasAnyPermission(permissions)) {
await logAccessDenied(user, permissions, req);
return res.status(403).json({
error: 'Insufficient permissions',
required: permissions
});
}
next();
};
}
// Usage in routes
app.get('/api/search',
requirePermission(Permission.SEARCH_READ),
searchController.search
);
app.post('/api/profiles/:id/enrich',
requirePermission(Permission.PROFILE_READ, Permission.PROFILE_ENRICH),
profileController.enrich
);
app.get('/api/profiles/:id/contact',
requirePermission(Permission.PROFILE_CONTACT),
profileController.getContact
);
// lib/rbac/team-access.ts
export class TeamAccessControl {
constructor(private db: Database) {}
async canAccessTeamData(userId: string, teamId: string): Promise<boolean> {
const membership = await this.db.teamMemberships.findFirst({
where: {
userId,
teamId,
active: true
}
});
return !!membership;
}
async filterByTeamAccess<T extends { teamId: string }>(
userId: string,
items: T[]
): Promise<T[]> {
const userTeams = await this.getUserTeams(userId);
return items.filter(item => userTeams.includes(item.teamId));
}
async getUserTeams(userId: string): Promise<string[]> {
const memberships = await this.db.teamMemberships.findMany({
where: { userId, active: true },
select: { teamId: true }
});
return memberships.map(m => m.teamId);
}
}
// lib/rbac/audit.ts
export class RBACauditLog {
async logAccess(event: {
userId: string;
action: string;
resource: string;
resourceId?: string;
granted: boolean;
permissions: Permission[];
}): Promise<void> {
await db.rbacAuditLog.create({
data: {
...event,
timestamp: new Date(),
ip: getCurrentIP(),
userAgent: getCurrentUserAgent()
}
});
// Alert on suspicious patterns
if (!event.granted) {
await this.checkSuspiciousActivity(event.userId);
}
}
private async checkSuspiciousActivity(userId: string): Promise<void> {
const recentDenials = await db.rbacAuditLog.count({
where: {
userId,
granted: false,
timestamp: { gte: new Date(Date.now() - 3600000) } // Last hour
}
});
if (recentDenials > 10) {
await alertService.send({
type: 'security',
message: `User ${userId} has ${recentDenials} access denials in last hour`,
severity: 'high'
});
}
}
}
// For service accounts, use scoped API keys
const serviceAccountKey = await juicebox.apiKeys.create({
name: 'integration-service',
scopes: [
'search:read',
'profiles:read',
'profiles:enrich'
],
expiresAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000), // 90 days
ipAllowlist: ['10.0.0.0/8']
});
## Enterprise RBAC Setup
### Role Definition
- [ ] Roles mapped to business functions
- [ ] Permissions granular and well-defined
- [ ] Role hierarchy documented
- [ ] Service account roles separate
### Implementation
- [ ] Permission checks on all endpoints
- [ ] Team-level access enforced
- [ ] Audit logging enabled
- [ ] Suspicious activity alerts
### Integration
- [ ] SSO/SAML configured
- [ ] Group sync from IdP
- [ ] JIT provisioning enabled
- [ ] Offboarding automation
After RBAC setup, see juicebox-migration-deep-dive for advanced migrations.
This skill should be used when the user asks to "create a hookify rule", "write a hook rule", "configure hookify", "add a hookify rule", or needs guidance on hookify rule syntax and patterns.
Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.