Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- /**
- * EspoCRM Access Control Overview for ISO27001 Audit
- * run via CLI, output to HTML and JSON
- *
- * This script generates a report with
- * - Users and their roles
- * - Teams and memberships
- * - Role permissions (entity and field level)
- * - Access control matrix
- */
- require_once 'bootstrap.php';
- class EspoACLAuditor
- {
- private $entityManager;
- private $config;
- private $metadata;
- public function __construct()
- {
- $app = new \Espo\Core\Application();
- $this->entityManager = $app->getContainer()->get('entityManager');
- $this->config = $app->getContainer()->get('config');
- $this->metadata = $app->getContainer()->get('metadata');
- }
- public function generateAuditReport()
- {
- $report = [
- 'generated_at' => date('Y-m-d H:i:s'),
- 'system_info' => $this->getSystemInfo(),
- 'users' => $this->getUsersOverview(),
- 'roles' => $this->getRolesOverview(),
- 'teams' => $this->getTeamsOverview(),
- 'access_matrix' => $this->generateAccessMatrix(),
- 'field_level_security' => $this->getFieldLevelSecurity(),
- 'audit_findings' => $this->performAuditChecks()
- ];
- return $report;
- }
- private function getSystemInfo()
- {
- return [
- 'espo_version' => $this->config->get('version'),
- 'site_url' => $this->config->get('siteUrl'),
- 'total_users' => $this->entityManager->getRepository('User')->count(),
- 'active_users' => $this->entityManager->getRepository('User')
- ->where(['isActive' => true])->count(),
- 'total_roles' => $this->entityManager->getRepository('Role')->count(),
- 'total_teams' => $this->entityManager->getRepository('Team')->count()
- ];
- }
- private function getUsersOverview()
- {
- $users = $this->entityManager->getRepository('User')
- ->find(['orderBy' => 'userName']);
- $userList = [];
- foreach ($users as $user) {
- $teams = $this->entityManager->getRepository('User')
- ->getRelation($user, 'teams')->find();
- $roles = $this->entityManager->getRepository('User')
- ->getRelation($user, 'roles')->find();
- $userList[] = [
- 'id' => $user->get('id'),
- 'username' => $user->get('userName'),
- 'name' => $user->get('name'),
- 'email' => $user->get('emailAddress'),
- 'is_active' => $user->get('isActive'),
- 'is_admin' => $user->get('isAdmin'),
- 'type' => $user->get('type'),
- 'created_at' => $user->get('createdAt'),
- 'last_access' => $user->get('lastAccess'),
- 'teams' => array_map(function($team) {
- return [
- 'id' => $team->get('id'),
- 'name' => $team->get('name')
- ];
- }, iterator_to_array($teams)),
- 'roles' => array_map(function($role) {
- return [
- 'id' => $role->get('id'),
- 'name' => $role->get('name')
- ];
- }, iterator_to_array($roles))
- ];
- }
- return $userList;
- }
- private function getRolesOverview()
- {
- $roles = $this->entityManager->getRepository('Role')->find();
- $roleList = [];
- foreach ($roles as $role) {
- $data = $role->get('data');
- $roleList[] = [
- 'id' => $role->get('id'),
- 'name' => $role->get('name'),
- 'assignment_permission' => $role->get('assignmentPermission'),
- 'user_permission' => $role->get('userPermission'),
- 'portal_permission' => $role->get('portalPermission'),
- 'data_privacy_permission' => $role->get('dataPrivacyPermission'),
- 'scope_level_permissions' => $this->parseScopePermissions($data),
- 'field_level_permissions' => $this->parseFieldPermissions($data)
- ];
- }
- return $roleList;
- }
- private function parseScopePermissions($data)
- {
- $permissions = [];
- if (!$data || !is_object($data)) {
- return $permissions;
- }
- foreach (get_object_vars($data) as $scope => $scopeData) {
- if (is_object($scopeData) && !isset($scopeData->fields)) {
- $permissions[$scope] = [
- 'create' => $scopeData->create ?? 'no',
- 'read' => $scopeData->read ?? 'no',
- 'edit' => $scopeData->edit ?? 'no',
- 'delete' => $scopeData->delete ?? 'no',
- 'stream' => $scopeData->stream ?? 'no'
- ];
- }
- }
- return $permissions;
- }
- private function parseFieldPermissions($data)
- {
- $permissions = [];
- if (!$data || !is_object($data)) {
- return $permissions;
- }
- foreach (get_object_vars($data) as $scope => $scopeData) {
- if (is_object($scopeData) && isset($scopeData->fields)) {
- $permissions[$scope] = [];
- foreach (get_object_vars($scopeData->fields) as $field => $fieldData) {
- $permissions[$scope][$field] = [
- 'read' => $fieldData->read ?? 'yes',
- 'edit' => $fieldData->edit ?? 'yes'
- ];
- }
- }
- }
- return $permissions;
- }
- private function getTeamsOverview()
- {
- $teams = $this->entityManager->getRepository('Team')->find();
- $teamList = [];
- foreach ($teams as $team) {
- $users = $this->entityManager->getRepository('Team')
- ->getRelation($team, 'users')->find();
- $teamList[] = [
- 'id' => $team->get('id'),
- 'name' => $team->get('name'),
- 'created_at' => $team->get('createdAt'),
- 'users' => array_map(function($user) {
- return [
- 'id' => $user->get('id'),
- 'username' => $user->get('userName'),
- 'name' => $user->get('name')
- ];
- }, iterator_to_array($users)),
- 'user_count' => count(iterator_to_array($users))
- ];
- }
- return $teamList;
- }
- private function generateAccessMatrix()
- {
- $entities = $this->metadata->get(['entityDefs']);
- $roles = $this->entityManager->getRepository('Role')->find();
- $matrix = [];
- foreach ($roles as $role) {
- $data = $role->get('data');
- $roleName = $role->get('name');
- foreach (array_keys($entities) as $entityType) {
- if (!isset($matrix[$entityType])) {
- $matrix[$entityType] = [];
- }
- $permissions = 'no';
- if ($data && is_object($data) && isset($data->$entityType)) {
- $scopeData = $data->$entityType;
- $permissions = sprintf(
- 'C:%s R:%s E:%s D:%s',
- $scopeData->create ?? 'no',
- $scopeData->read ?? 'no',
- $scopeData->edit ?? 'no',
- $scopeData->delete ?? 'no'
- );
- }
- $matrix[$entityType][$roleName] = $permissions;
- }
- }
- return $matrix;
- }
- private function getFieldLevelSecurity()
- {
- $roles = $this->entityManager->getRepository('Role')->find();
- $fieldSecurity = [];
- foreach ($roles as $role) {
- $data = $role->get('data');
- $roleName = $role->get('name');
- if (!$data || !is_object($data)) {
- continue;
- }
- foreach (get_object_vars($data) as $scope => $scopeData) {
- if (is_object($scopeData) && isset($scopeData->fields)) {
- if (!isset($fieldSecurity[$scope])) {
- $fieldSecurity[$scope] = [];
- }
- foreach (get_object_vars($scopeData->fields) as $field => $fieldData) {
- if (!isset($fieldSecurity[$scope][$field])) {
- $fieldSecurity[$scope][$field] = [];
- }
- $fieldSecurity[$scope][$field][$roleName] = [
- 'read' => $fieldData->read ?? 'yes',
- 'edit' => $fieldData->edit ?? 'yes'
- ];
- }
- }
- }
- }
- return $fieldSecurity;
- }
- private function performAuditChecks()
- {
- $findings = [];
- // Check for users without roles
- $usersWithoutRoles = $this->entityManager->getRepository('User')
- ->where(['isActive' => true, 'type' => 'regular'])
- ->find();
- foreach ($usersWithoutRoles as $user) {
- $roles = $this->entityManager->getRepository('User')
- ->getRelation($user, 'roles')->find();
- if (count(iterator_to_array($roles)) === 0 && !$user->get('isAdmin')) {
- $findings[] = [
- 'severity' => 'HIGH',
- 'type' => 'USER_WITHOUT_ROLE',
- 'description' => "User '{$user->get('userName')}' has no assigned roles",
- 'user_id' => $user->get('id')
- ];
- }
- }
- // Check for inactive users with recent access
- $inactiveUsers = $this->entityManager->getRepository('User')
- ->where(['isActive' => false])
- ->find();
- foreach ($inactiveUsers as $user) {
- $lastAccess = $user->get('lastAccess');
- if ($lastAccess && strtotime($lastAccess) > strtotime('-30 days')) {
- $findings[] = [
- 'severity' => 'MEDIUM',
- 'type' => 'INACTIVE_USER_RECENT_ACCESS',
- 'description' => "Inactive user '{$user->get('userName')}' has recent access",
- 'last_access' => $lastAccess
- ];
- }
- }
- // Check for admin users
- $adminUsers = $this->entityManager->getRepository('User')
- ->where(['isAdmin' => true, 'isActive' => true])
- ->find();
- $findings[] = [
- 'severity' => 'INFO',
- 'type' => 'ADMIN_USERS_COUNT',
- 'description' => "Total active admin users: " . count(iterator_to_array($adminUsers)),
- 'count' => count(iterator_to_array($adminUsers))
- ];
- // Check for users without teams
- $usersWithoutTeams = $this->entityManager->getRepository('User')
- ->where(['isActive' => true, 'type' => 'regular'])
- ->find();
- foreach ($usersWithoutTeams as $user) {
- $teams = $this->entityManager->getRepository('User')
- ->getRelation($user, 'teams')->find();
- if (count(iterator_to_array($teams)) === 0) {
- $findings[] = [
- 'severity' => 'LOW',
- 'type' => 'USER_WITHOUT_TEAM',
- 'description' => "User '{$user->get('userName')}' is not assigned to any team",
- 'user_id' => $user->get('id')
- ];
- }
- }
- return $findings;
- }
- public function exportToJSON($filename = 'espo_acl_audit_report.json')
- {
- $report = $this->generateAuditReport();
- file_put_contents($filename, json_encode($report, JSON_PRETTY_PRINT));
- return $filename;
- }
- public function exportToHTML($filename = 'espo_acl_audit_report.html')
- {
- $report = $this->generateAuditReport();
- $html = $this->generateHTMLReport($report);
- file_put_contents($filename, $html);
- return $filename;
- }
- private function generateHTMLReport($report)
- {
- ob_start();
- ?>
- <!DOCTYPE html>
- <html>
- <head>
- <title>EspoCRM Access Control Audit Report - ISO27001</title>
- <style>
- body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }
- .container { max-width: 1400px; margin: 0 auto; background: white; padding: 30px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }
- h1 { color: #2c3e50; border-bottom: 3px solid #3498db; padding-bottom: 10px; }
- h2 { color: #34495e; margin-top: 30px; border-bottom: 2px solid #ecf0f1; padding-bottom: 8px; }
- h3 { color: #7f8c8d; }
- table { width: 100%; border-collapse: collapse; margin: 20px 0; }
- th { background: #3498db; color: white; padding: 12px; text-align: left; }
- td { padding: 10px; border: 1px solid #ddd; }
- tr:nth-child(even) { background: #f9f9f9; }
- .info-box { background: #e8f4f8; padding: 15px; margin: 15px 0; border-left: 4px solid #3498db; }
- .severity-HIGH { color: #e74c3c; font-weight: bold; }
- .severity-MEDIUM { color: #f39c12; font-weight: bold; }
- .severity-LOW { color: #95a5a6; }
- .severity-INFO { color: #3498db; }
- .badge { display: inline-block; padding: 3px 8px; border-radius: 3px; font-size: 12px; }
- .badge-active { background: #2ecc71; color: white; }
- .badge-inactive { background: #e74c3c; color: white; }
- .badge-admin { background: #9b59b6; color: white; }
- .matrix-table { font-size: 11px; }
- .matrix-table td { padding: 5px; }
- .permission { font-family: monospace; font-size: 10px; }
- </style>
- </head>
- <body>
- <div class="container">
- <h1>EspoCRM Access Control Audit Report</h1>
- <p><strong>Generated:</strong> <?= htmlspecialchars($report['generated_at']) ?></p>
- <p><strong>Purpose:</strong> ISO27001 Internal Audit - Access Control Review</p>
- <h2>1. System Overview</h2>
- <div class="info-box">
- <p><strong>EspoCRM Version:</strong> <?= htmlspecialchars($report['system_info']['espo_version']) ?></p>
- <p><strong>Site URL:</strong> <?= htmlspecialchars($report['system_info']['site_url']) ?></p>
- <p><strong>Total Users:</strong> <?= $report['system_info']['total_users'] ?> (Active: <?= $report['system_info']['active_users'] ?>)</p>
- <p><strong>Total Roles:</strong> <?= $report['system_info']['total_roles'] ?></p>
- <p><strong>Total Teams:</strong> <?= $report['system_info']['total_teams'] ?></p>
- </div>
- <h2>2. Audit Findings</h2>
- <table>
- <tr>
- <th>Severity</th>
- <th>Type</th>
- <th>Description</th>
- </tr>
- <?php foreach ($report['audit_findings'] as $finding): ?>
- <tr>
- <td class="severity-<?= $finding['severity'] ?>"><?= $finding['severity'] ?></td>
- <td><?= htmlspecialchars($finding['type']) ?></td>
- <td><?= htmlspecialchars($finding['description']) ?></td>
- </tr>
- <?php endforeach; ?>
- </table>
- <h2>3. Users Overview</h2>
- <table>
- <tr>
- <th>Username</th>
- <th>Name</th>
- <th>Status</th>
- <th>Type</th>
- <th>Roles</th>
- <th>Teams</th>
- <th>Last Access</th>
- </tr>
- <?php foreach ($report['users'] as $user): ?>
- <tr>
- <td><?= htmlspecialchars($user['username']) ?></td>
- <td><?= htmlspecialchars($user['name']) ?></td>
- <td>
- <?php if ($user['is_active']): ?>
- <span class="badge badge-active">Active</span>
- <?php else: ?>
- <span class="badge badge-inactive">Inactive</span>
- <?php endif; ?>
- <?php if ($user['is_admin']): ?>
- <span class="badge badge-admin">Admin</span>
- <?php endif; ?>
- </td>
- <td><?= htmlspecialchars($user['type']) ?></td>
- <td><?= implode(', ', array_column($user['roles'], 'name')) ?></td>
- <td><?= implode(', ', array_column($user['teams'], 'name')) ?></td>
- <td><?= htmlspecialchars($user['last_access'] ?? 'Never') ?></td>
- </tr>
- <?php endforeach; ?>
- </table>
- <h2>4. Roles Overview</h2>
- <?php foreach ($report['roles'] as $role): ?>
- <h3><?= htmlspecialchars($role['name']) ?></h3>
- <div class="info-box">
- <p><strong>Assignment Permission:</strong> <?= htmlspecialchars($role['assignment_permission'] ?? 'N/A') ?></p>
- <p><strong>User Permission:</strong> <?= htmlspecialchars($role['user_permission'] ?? 'N/A') ?></p>
- </div>
- <?php if (!empty($role['scope_level_permissions'])): ?>
- <h4>Entity Permissions</h4>
- <table>
- <tr>
- <th>Entity</th>
- <th>Create</th>
- <th>Read</th>
- <th>Edit</th>
- <th>Delete</th>
- <th>Stream</th>
- </tr>
- <?php foreach ($role['scope_level_permissions'] as $entity => $perms): ?>
- <tr>
- <td><?= htmlspecialchars($entity) ?></td>
- <td><?= htmlspecialchars($perms['create']) ?></td>
- <td><?= htmlspecialchars($perms['read']) ?></td>
- <td><?= htmlspecialchars($perms['edit']) ?></td>
- <td><?= htmlspecialchars($perms['delete']) ?></td>
- <td><?= htmlspecialchars($perms['stream']) ?></td>
- </tr>
- <?php endforeach; ?>
- </table>
- <?php endif; ?>
- <?php endforeach; ?>
- <h2>5. Teams Overview</h2>
- <table>
- <tr>
- <th>Team Name</th>
- <th>User Count</th>
- <th>Members</th>
- <th>Created</th>
- </tr>
- <?php foreach ($report['teams'] as $team): ?>
- <tr>
- <td><?= htmlspecialchars($team['name']) ?></td>
- <td><?= $team['user_count'] ?></td>
- <td><?= implode(', ', array_column($team['users'], 'username')) ?></td>
- <td><?= htmlspecialchars($team['created_at']) ?></td>
- </tr>
- <?php endforeach; ?>
- </table>
- <h2>6. Access Control Matrix</h2>
- <p><em>Format: C=Create, R=Read, E=Edit, D=Delete</em></p>
- <table class="matrix-table">
- <tr>
- <th>Entity</th>
- <?php foreach ($report['roles'] as $role): ?>
- <th><?= htmlspecialchars($role['name']) ?></th>
- <?php endforeach; ?>
- </tr>
- <?php foreach ($report['access_matrix'] as $entity => $rolePerms): ?>
- <tr>
- <td><strong><?= htmlspecialchars($entity) ?></strong></td>
- <?php foreach ($report['roles'] as $role): ?>
- <td class="permission"><?= htmlspecialchars($rolePerms[$role['name']] ?? 'N/A') ?></td>
- <?php endforeach; ?>
- </tr>
- <?php endforeach; ?>
- </table>
- <h2>7. ISO27001 Compliance Notes</h2>
- <div class="info-box">
- <p><strong>A.9.2.1 User Registration:</strong> Review user creation dates and approval process</p>
- <p><strong>A.9.2.2 User Access Provisioning:</strong> Verify role assignments match job functions</p>
- <p><strong>A.9.2.3 Management of Privileged Access:</strong> Review admin user list and justification</p>
- <p><strong>A.9.2.5 Review of User Access Rights:</strong> Conduct periodic access reviews</p>
- <p><strong>A.9.2.6 Removal of Access Rights:</strong> Verify inactive users have no recent access</p>
- </div>
- </div>
- </body>
- </html>
- <?php
- return ob_get_clean();
- }
- }
- // Usage
- try {
- $auditor = new EspoACLAuditor();
- // Generate JSON report
- $jsonFile = $auditor->exportToJSON();
- echo "JSON report generated: $jsonFile\n";
- // Generate HTML report
- $htmlFile = $auditor->exportToHTML();
- echo "HTML report generated: $htmlFile\n";
- } catch (Exception $e) {
- echo "Error: " . $e->getMessage() . "\n";
- }
Advertisement
Add Comment
Please, Sign In to add comment