MonkeysLegion Permissions v1
Enterprise RBAC, policy gates, composable rules, approval workflows, LDAP/SAML sync, and Apex AI fuzzy authorization for MonkeysLegion. Ground-up build for PHP 8.4 with property hooks, backed enums, and zero magic.
Features
FeatureStatusAuthorizer PipelineGate → PolicyResolver → RuleEngine → DecisionAttribute-First Security#[RequiresRole], #[RequiresPermission], #[Authorize], #[Gate]Policy SystemModel-to-policy mapping with before() / after() hooksComposable RulesPredicate engine with AND/OR/NOT/CUSTOM operators, priority-based executionDatabase StoreRole, Permission, UserRole, UserPermission, StoredRule entitiesApproval WorkflowsPermissionRequest → review → approve/deny with expiryCache LayerTransparent permission caching with TTL and tag-based invalidationLDAP/SAML SyncRoleSyncAdapterInterface with additive/replace/merge/intersect strategiesApex AI IntegrationLLM-backed fuzzy policy evaluation with confidence thresholdsPSR-15 MiddlewareAuthorizationMiddleware — attribute-aware request authorizationEvent System11 audit events for authorization, roles, permissions, and syncCLI Commandspermissions:install, permissions:entitiesPHP 8.4 NativeProperty hooks, backed enums, asymmetric visibility
Requirements
PHP 8.4 or higher
monkeyscloud/monkeyslegion-di(Container)monkeyscloud/monkeyslegion-events(Event dispatching)psr/http-message^2.0psr/http-server-middleware^1.0
Installation
composer require monkeyscloud/monkeyslegion-permissions
Quick Setup
# Full install: config + entities + migration
php ml permissions:installOr just copy entity stubs
php ml permissions:entities
php ml permissions:entities --force # overwrite existing
Architecture
┌─────────────────────────────────────────────────────────┐
│ HTTP Request │
└───────────────────────┬─────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ AuthorizationMiddleware (PSR-15) │
│ Reads: #[RequiresRole] #[RequiresPermission] #[Gate] │
└───────────────────────┬─────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ Authorizer │
│ ┌──────┐ ┌────────────┐ ┌────────────┐ ┌────────┐ │
│ │ Gate │→ │PolicyResolve│→ │ RuleEngine │→ │Decision│ │
│ └──────┘ └────────────┘ └────────────┘ └────────┘ │
└─────────────────────────────────────────────────────────┘
▼
┌──────────┐ ┌───────────┐ ┌──────────┐ ┌───────────┐
│ Database │ │ Cache │ │ Events │ │ Apex AI │
│ Store │ │ Layer │ │ Dispatch │ │ (opt.) │
└──────────┘ └───────────┘ └──────────┘ └───────────┘
The package is organized into clear namespaces:
Apex/: AI-powered fuzzy policy evaluation (ApexPolicyAdapter,ApexGateRegistrar)Attributes/: Controller/method attributes (#[RequiresRole],#[RequiresPermission],#[Authorize],#[Gate])Cache/: Transparent permission caching (CacheLayer)Contracts/: Core interfaces (AuthorizerInterface)Entity/: Database entities (Role, Permission, UserRole, UserPermission, StoredRule, PermissionRequest)Event/: 11 audit events for all authorization operationsExceptions/:AuthorizationExceptionGate/: Ability definitions and closuresMiddleware/: PSR-15AuthorizationMiddlewarePolicy/: Model-to-policy mapping (Policy,PolicyResolver)Provider/: DI service provider (PermissionsProvider)Rule/: Composable predicate engine (Rule,Predicate,RuleEngine)Store/: Database persistence (DatabaseStore,PermissionStoreInterface)Sync/: LDAP/SAML role synchronization (RoleSyncManager, adapters)Workflow/: Approval request pipeline (RequestManager)
Configuration
cp vendor/monkeyscloud/monkeyslegion-permissions/config/permissions.mlc config/permissions.mlc
permissions {
# Super-admin role (has all permissions)
super_admin_role = ${PERMISSIONS_SUPER_ADMIN:-super-admin}
# Table names
tables {
roles = ${PERMISSIONS_TABLE_ROLES:-perm_roles}
permissions = ${PERMISSIONS_TABLE_PERMISSIONS:-perm_permissions}
user_roles = ${PERMISSIONS_TABLE_USER_ROLES:-perm_user_roles}
user_permissions = ${PERMISSIONS_TABLE_USER_PERMISSIONS:-perm_user_permissions}
stored_rules = ${PERMISSIONS_TABLE_STORED_RULES:-perm_stored_rules}
requests = ${PERMISSIONS_TABLE_REQUESTS:-perm_permission_requests}
}
# Permission caching
cache {
enabled = ${PERMISSIONS_CACHE_ENABLED:-true}
ttl = ${PERMISSIONS_CACHE_TTL:-3600}
prefix = ${PERMISSIONS_CACHE_PREFIX:-perm:}
}
# Apex AI integration (optional)
apex {
confidence_threshold = ${PERMISSIONS_APEX_THRESHOLD:-0.7}
model = ${PERMISSIONS_APEX_MODEL:-}
}
}
Attribute-Based Authorization
use MonkeysLegion\Permissions\Attributes\RequiresRole;
use MonkeysLegion\Permissions\Attributes\RequiresPermission;
use MonkeysLegion\Permissions\Attributes\Authorize;
#[RequiresRole('admin')]
class AdminController
{
#[RequiresPermission('users.create')]
public function createUser(): Response
{
// Only accessible to admins with users.create permission
}
#[Authorize('update-user', subject: 'user')]
public function updateUser(int $id): Response
{
// Delegates to a policy: UserPolicy::update($currentUser, $user)
}
}
Gate (Ability Definitions)
use MonkeysLegion\Permissions\Gate;
$gate = new Gate();
// Define abilities with closures
$gate->define('edit-post', fn($user, $post) => $user->id === $post->author_id);
// Check
$gate->allows($user, 'edit-post', $post); // true/false
$gate->denies($user, 'edit-post', $post); // true/false
$gate->authorize($user, 'edit-post', $post); // throws AuthorizationException
Policy System
use MonkeysLegion\Permissions\Policy\Policy;
class PostPolicy extends Policy
{
public function update(object $user, object $post): bool
{
return $user->id === $post->author_id;
}
public function delete(object $user, object $post): bool
{
return $user->role === 'admin';
}
// Optional: runs before any check
public function before(object $user, string $ability): ?bool
{
// Super-admins can do anything
if ($user->role === 'super-admin') {
return true;
}
return null; // fall through to specific check
}
}
Composable Rules
use MonkeysLegion\Permissions\Rule\Rule;
use MonkeysLegion\Permissions\Rule\Predicate;
use MonkeysLegion\Permissions\Rule\PredicateType;
use MonkeysLegion\Permissions\Rule\RuleEngine;
// Build rules with predicates
$rule = new Rule(
name: 'business-hours-only',
key: 'business_hours',
predicates: [
new Predicate(PredicateType::Custom, fn() => date('H') >= 9 && date('H') < 17),
],
priority: 10,
);
$engine = new RuleEngine();
$engine->addRule($rule);
$result = $engine->evaluate($context);
// RuleResult: allowed, denied, or abstained
Approval Workflows
use MonkeysLegion\Permissions\Workflow\RequestManager;
$manager = $container->get(RequestManager::class);
// User requests a permission
$request = $manager->request(
userId: 'user-42',
targetKey: 'reports.export',
reason: 'Need to export Q4 reports for client presentation',
);
// Admin reviews
$manager->approve($request, reviewedBy: 'admin-1', note: 'Approved for Q4');
// or
$manager->deny($request, reviewedBy: 'admin-1', note: 'Use dashboard instead');
LDAP/SAML Sync
use MonkeysLegion\Permissions\Sync\RoleSyncManager;
use MonkeysLegion\Permissions\Sync\SyncStrategy;
$sync = $container->get(RoleSyncManager::class);
// Sync roles from external identity provider
$result = $sync->sync(
userId: 'user-42',
externalRoles: ['engineering', 'team-lead'],
strategy: SyncStrategy::Merge,
);
// SyncResult: added, removed, unchanged counts
Strategies:
StrategyBehaviorAdditiveOnly add new roles, never removeReplaceExternal roles replace all local rolesMergeUnion of external and local rolesIntersectLocalKeep only roles that exist both externally and locally
Apex AI Fuzzy Authorization
use MonkeysLegion\Permissions\Apex\ApexGateRegistrar;
$registrar = $container->get(ApexGateRegistrar::class);
// Register AI-powered policy
$registrar->register(
gateName: 'content-appropriate',
prompt: 'Is the following content appropriate for a professional workplace?',
confidenceThreshold: 0.8,
);
// Now use it like any other gate
$authorizer->authorize($user, 'content-appropriate', ['content' => $postBody]);
Database Entities
The package includes 8 entity stubs ready for database persistence:
EntityPurposeRoleRoles with key, name, level, metadataPermissionPermissions with key, name, groupUserRoleUser ↔ Role assignments with expiryUserPermissionDirect user ↔ permission grants with expiryStoredRulePersistent composable rules with predicatesPermissionRequestApproval workflow requestsRequestStatusEnum: pending, approved, denied, expiredRequestTypeEnum: permission, role
Events
EventWhen DispatchedAuthorizationCheckedAny authorization check completesAuthorizationDeniedAuthorization deniedPermissionGrantedPermission assigned to userPermissionRevokedPermission removed from userPermissionRequestedUser requests a permissionRequestApprovedRequest approved by reviewerRequestDeniedRequest denied by reviewerRoleAssignedRole assigned to userRoleRevokedRole removed from userRolesSyncedExternal role sync completedApexPolicyEvaluatedAI policy evaluation completedPolicyEvaluatedStandard policy evaluation completed
$dispatcher->listen(AuthorizationDenied::class, function (AuthorizationDenied $event) {
$logger->warning("Access denied: user {$event->userId} → {$event->ability}");
});
Middleware Setup
// In your middleware pipeline
$pipeline->pipe(AuthorizationMiddleware::class);
// The middleware:
// 1. Reads #[RequiresRole], #[RequiresPermission], #[Authorize], #[Gate] from the route
// 2. Checks permissions via Authorizer
// 3. Returns 403 on denial
// 4. Dispatches audit events
Cache Layer
use MonkeysLegion\Permissions\Cache\CacheLayer;
$cache = new CacheLayer($cacheStore, prefix: 'perm:', ttl: 3600);
// Permissions are cached transparently
$hasPermission = $cache->remember("user:42:posts.edit", function () use ($store) {
return $store->hasPermission('user-42', 'posts.edit');
});
// Invalidate on changes
$cache->forget("user:42:*");
Security Posture
Deny by default — no permission means no access
Super-admin bypass — configurable super-admin role
Expiring grants — UserRole and UserPermission support
expires_atAudit trail — 12 event types for full observability
AI confidence gates — configurable threshold prevents uncertain AI decisions
Cache invalidation — automatic cache flush on permission changes
Testing
composer test
composer phpstan
License
MIT © MonkeysCloud