Skip to main content

Access Control

Access Control provides enterprise-grade permissions management through integration with Nextcloud RBAC (Role-Based Access Control) and Keycloak.

Overview

The access control system integrates with:

  • ADFS (Active Directory Federation Services) for user and group management via Keycloak
  • Nextcloud RBAC for role-based permissions
  • FCS (Federal Cloud Services) compliance requirements
  • Verwerkingen registers for process tracking

Permission Levels

Access can be controlled at multiple levels:

  • Register level - Control access to entire registers
  • Schema level - Manage permissions for specific register/schema combinations
  • Object level - Set permissions on individual objects
  • Property level - Fine-grained control over specific object properties

Permission Types

Permissions are granted through:

  1. User Rights

    • CRUD (Create, Read, Update, Delete) operations
    • Inherited from ADFS groups via Keycloak
    • Role-based access control through Nextcloud
  2. Contract Rights

    • Application-level permissions
    • Process-specific authorizations
    • Compliance with FCS requirements
    • Integration with verwerkingen registers

Implementation

Access control is implemented through:

  1. User Authentication

    • Direct integration with Keycloak for identity management
    • ADFS synchronization for user and group information
    • Single Sign-On (SSO) capabilities
  2. Permission Management

    • CRUD-level permissions for all system entities
    • Hierarchical permission inheritance
    • Fine-grained access control at multiple levels
  3. Process Integration

    • Compliance with FCS guidelines
    • Integration with verwerkingen registers for process tracking
    • Application-specific permission contracts

Technical Implementation

Architecture Overview

Authorization Flow

Schema Authorization Configuration

Authorization Exception System

RBAC Query Filtering Process

Database Schema

Authorization Exception Table: oc_openregister_authorization_exceptions

ColumnTypeDescription
idINTEGERPrimary key
uuidVARCHAR(36)Unique identifier
typeVARCHAR(20)Exception type: inclusion or exclusion
subject_typeVARCHAR(20)Subject type: user or group
subject_idVARCHAR(255)User ID or Group ID
schema_uuidVARCHAR(36)Schema UUID (nullable for global)
register_uuidVARCHAR(36)Register UUID (nullable)
organization_uuidVARCHAR(36)Organisation UUID (nullable)
actionVARCHAR(20)CRUD action: create, read, update, delete
priorityINTEGERPriority for resolution (higher = more important)
activeBOOLEANWhether exception is active
descriptionTEXTHuman-readable description
created_byVARCHAR(255)User who created the exception
created_atDATETIMECreation timestamp
updated_atDATETIMELast update timestamp

Schema Authorization Field:

  • Stored in oc_openregister_schemas.authorization (JSON)
  • Format:
{
"create": ["admin", "editors"],
"read": ["admin", "editors", "viewers", "public"],
"update": ["admin", "editors"],
"delete": ["admin"]
}

Object Authorization Field:

  • Stored in oc_openregister_objects.authorization (JSON)
  • Inherits from schema but can be overridden per-object
  • Same format as schema authorization

Permission Resolution Algorithm

Code Examples

Schema Authorization Configuration

// Setting authorization on a schema
$schema->setAuthorization([
'create' => ['admin', 'editors'],
'read' => ['admin', 'editors', 'viewers', 'public'],
'update' => ['admin', 'editors'],
'delete' => ['admin']
]);

// Checking if a user has permission
$hasPermission = $schema->hasPermission('read', $userGroups);

Creating Authorization Exceptions

use OCA\OpenRegister\Db\AuthorizationException;

// Create an inclusion exception (grant extra permission)
$inclusion = new AuthorizationException();
$inclusion->setType(AuthorizationException::TYPE_INCLUSION);
$inclusion->setSubjectType(AuthorizationException::SUBJECT_TYPE_USER);
$inclusion->setSubjectId('user123');
$inclusion->setSchemaUuid($schemaUuid);
$inclusion->setAction(AuthorizationException::ACTION_UPDATE);
$inclusion->setPriority(10);
$inclusion->setActive(true);
$inclusion->setDescription('Allow user123 to update objects in this schema');

// Create an exclusion exception (deny permission)
$exclusion = new AuthorizationException();
$exclusion->setType(AuthorizationException::TYPE_EXCLUSION);
$exclusion->setSubjectType(AuthorizationException::SUBJECT_TYPE_GROUP);
$exclusion->setSubjectId('restricted_group');
$exclusion->setRegisterUuid($registerUuid);
$exclusion->setAction(AuthorizationException::ACTION_DELETE);
$exclusion->setPriority(20);
$exclusion->setActive(true);
$exclusion->setDescription('Prevent restricted_group from deleting objects in this register');

// Check if an exception matches criteria
$matches = $exception->matches(
subjectType: 'user',
subjectId: 'user123',
action: 'update',
schemaUuid: $schemaUuid
);

RBAC Query Filtering (MagicMapper)

use OCA\OpenRegister\Service\MagicMapperHandlers\MagicRbacHandler;

// Apply RBAC filters to a dynamic table query
$rbacHandler->applyRbacFilters(
qb: $queryBuilder,
register: $register,
schema: $schema,
tableAlias: 't',
userId: $currentUserId,
rbac: true
);

// Check if current user is admin
$isAdmin = $rbacHandler->isCurrentUserAdmin();

// Get current user's groups
$userGroups = $rbacHandler->getCurrentUserGroups();

Object-Level Authorization

// Get object authorization (inherits from schema if not set)
$objectAuth = $object->getAuthorization();

// Override schema authorization for specific object
$object->setAuthorization([
'read' => ['admin', 'special_viewers'],
'update' => ['admin']
]);

RBAC Configuration

RBAC can be configured in Nextcloud app settings:

{
"enabled": true,
"adminOverride": true
}
  • enabled: Master switch for RBAC system
  • adminOverride: Allow users in 'admin' group to bypass all RBAC checks

Performance Optimizations

  1. Lazy Group Loading

    • User groups are only fetched when RBAC is enabled
    • Results are cached within the request lifecycle
  2. Admin Fast Path

    • Admin users bypass permission checks entirely when override is enabled
    • Reduces database queries for administrative operations
  3. Query-Level Filtering

    • RBAC filters are applied at the database query level
    • Prevents loading unauthorized objects into memory
  4. Authorization Config Caching

    • Schema authorization configs are cached with schema entities
    • Reduces redundant JSON parsing
  5. Exception Priority Indexing

    • Database index on priority field for fast exception sorting
    • Composite index on (subject_type, subject_id, action, active) for fast matching

Integration Points

1. ObjectEntityMapper

  • Applies RBAC filters to all object queries via findAll(), find(), count()
  • Respects rbac parameter (default: true)

2. MagicMapper (Dynamic Tables)

  • Uses MagicRbacHandler for schema-specific table filtering
  • Consistent security across schema-generated tables
  • RBAC filtering applied via applyAdditionalFilters() in GuzzleSolrService
  • Currently logs RBAC application but full implementation pending

4. API Controllers

  • RBAC checks in ObjectsController, RegistersController, SchemasController
  • Validates permissions before CRUD operations

Best Practices

  1. Use Schema-Level Authorization

    • Define authorization at the schema level for consistency
    • Only override at object level when necessary
  2. Leverage Group-Based Permissions

    • Use Nextcloud groups for role management
    • Avoid user-specific permissions unless absolutely required
  3. Authorization Exceptions as Last Resort

    • Use exceptions sparingly for edge cases
    • Document the reason for each exception
    • Set appropriate priorities to avoid conflicts
  4. Test Permission Scenarios

    • Test unauthenticated access
    • Test group membership changes
    • Test admin override behavior
    • Test exception priority resolution
  5. Monitor Authorization Exceptions

    • Regularly audit active exceptions
    • Deactivate or delete obsolete exceptions
    • Review exception conflicts (overlapping priorities)

Debugging & Monitoring

Enable Debug Logging

// In GuzzleSolrService
$this->logger->debug('[SOLR] RBAC filtering applied');

// In MagicRbacHandler
$this->logger->debug('Applying RBAC filters', [
'user_id' => $userId,
'user_groups' => $userGroups,
'schema_uuid' => $schema->getUuid()
]);

Check Authorization Config

# Query schema authorization
docker exec -u 33 master-nextcloud-1 php -r "
\$schema = \OC::$server->get(\OCA\OpenRegister\Db\SchemaMapper::class)->find(1);
var_dump(\$schema->getAuthorization());
"

Query Authorization Exceptions

# List active exceptions
docker exec -it master-database-mysql-1 mysql -u nextcloud -pnextcloud nextcloud -e "
SELECT type, subject_type, subject_id, action, priority, description
FROM oc_openregister_authorization_exceptions
WHERE active = 1
ORDER BY priority DESC;
"

Security Considerations

  1. Default Deny

    • When authorization is configured, default behavior is to deny access
    • Explicitly configure 'public' in read permissions for public access
  2. Admin Override

    • Admin override can be disabled for high-security environments
    • When disabled, even admins must have explicit permissions
  3. Authorization Inheritance

    • Objects inherit authorization from schemas
    • Object-level overrides take precedence
  4. Exception Priority

    • Exclusions should have higher priority than inclusions to ensure security
    • Use priority > 50 for security-critical exclusions
  5. Unauthenticated Access

    • Unauthenticated users only see objects with 'public' in read permissions
    • Fallback to published object filtering can be enabled (currently disabled)

Authorization Exceptions

The Authorization Exception System provides a flexible way to override the standard Role-Based Access Control (RBAC) system. It allows for fine-grained control over permissions by defining specific inclusions and exclusions that take precedence over normal authorization rules.

Exception Types

  1. Inclusions: Grant additional permissions to users or groups that they wouldn't normally have
  2. Exclusions: Deny permissions to users or groups even if they would normally have access through RBAC

Subject Types

  • User: Exceptions that apply to specific individual users
  • Group: Exceptions that apply to all members of a specific group

Priority System

Authorization exceptions use a priority system to resolve conflicts:

  1. Exclusions (highest priority) - Always deny access if applicable
  2. Inclusions (medium priority) - Grant access if no exclusions apply
  3. Normal RBAC (lowest priority) - Default system behavior

Within each type, exceptions with higher numerical priority values take precedence.

Database Schema

The oc_openregister_authorization_exceptions table stores authorization exceptions with the following key fields:

  • type: 'inclusion' or 'exclusion'
  • subject_type: 'user' or 'group'
  • subject_id: The actual user ID or group ID
  • action: CRUD operation ('create', 'read', 'update', 'delete')
  • schema_uuid: Optional - limits exception to specific schema
  • register_uuid: Optional - limits exception to specific register
  • organization_uuid: Optional - limits exception to specific organization
  • priority: Integer priority for conflict resolution
  • active: Boolean to enable/disable exceptions
  • description: Human-readable description

Usage Examples

Example 1: Group Cross-Organization Access

Allow users in the 'ambtenaar' group to read 'gebruik' objects from all organizations:

$exception = new AuthorizationException();
$exception->setType(AuthorizationException::TYPE_INCLUSION);
$exception->setSubjectType(AuthorizationException::SUBJECT_TYPE_GROUP);
$exception->setSubjectId('ambtenaar');
$exception->setAction(AuthorizationException::ACTION_READ);
$exception->setSchemaUuid('gebruik-schema-uuid');
$exception->setPriority(20); // High priority to override multi-tenancy
$exception->setDescription('Allow ambtenaar group to read gebruik objects from all organizations');

$authService->createException(/* parameters */);

Example 2: User Exclusion

Deny a specific user update access despite group membership:

$exception = new AuthorizationException();
$exception->setType(AuthorizationException::TYPE_EXCLUSION);
$exception->setSubjectType(AuthorizationException::SUBJECT_TYPE_USER);
$exception->setSubjectId('problematic-user');
$exception->setAction(AuthorizationException::ACTION_UPDATE);
$exception->setSchemaUuid('sensitive-schema-uuid');
$exception->setPriority(15);
$exception->setDescription('Deny user update access due to security concerns');

API Usage

Creating Exceptions

# Create user inclusion
curl -X POST http://localhost/index.php/apps/openregister/api/authorization-exceptions \
-u 'admin:admin' \
-H 'Content-Type: application/json' \
-H 'OCS-APIREQUEST: true' \
-d '{
"type": "inclusion",
"subject_type": "user",
"subject_id": "special-user",
"action": "read",
"schema_uuid": "confidential-schema-uuid",
"priority": 10,
"description": "Allow special user to read confidential data"
}'

Listing Exceptions

# List all exceptions
curl http://localhost/index.php/apps/openregister/api/authorization-exceptions \
-u 'admin:admin' \
-H 'OCS-APIREQUEST: true'

# Filter by criteria
curl 'http://localhost/index.php/apps/openregister/api/authorization-exceptions?type=inclusion&active=true' \
-u 'admin:admin' \
-H 'OCS-APIREQUEST: true'

Integration with RBAC

The authorization exception system integrates seamlessly with the existing RBAC system:

  1. Query Level: The ObjectEntityMapper::applyRbacFilters() method checks for exceptions before applying normal RBAC rules
  2. Object Level: The ObjectEntityMapper::checkObjectPermission() method evaluates exceptions first, then falls back to standard permission checks
  3. Evaluation Order: Exclusions → Inclusions → Normal RBAC → Object ownership → Publication status

Best Practices

1. Use Specific Scope

Always limit exceptions to the most specific scope possible:

// Good - specific to schema and organization
$exception->setSchemaUuid('specific-schema');
$exception->setOrganizationUuid('specific-org');

// Avoid - too broad, affects everything
// (leaving schema_uuid and organization_uuid as null)

2. Set Appropriate Priorities

Use priority levels consistently:

  • 1-10: Low priority inclusions
  • 11-20: Medium priority inclusions
  • 21-30: High priority inclusions
  • 31-40: Low priority exclusions
  • 41-50: Medium priority exclusions
  • 51+: High priority exclusions

3. Document Exceptions

Always provide clear descriptions explaining why the exception exists:

$exception->setDescription('Allow support team to read customer data for troubleshooting purposes - ticket #12345');

4. Regular Audits

Regularly review authorization exceptions to ensure they're still needed:

// Find old exceptions
$oldExceptions = $mapper->findByCriteria([
'created_at' => '<' . (new DateTime('-6 months'))->format('Y-m-d'),
'active' => true
]);

Troubleshooting

Exception Not Working

  1. Check if exception is active:

    $exception = $mapper->findByUuid($uuid);
    var_dump($exception->getActive());
  2. Verify priority is high enough:

    $exceptions = $service->getUserExceptions($userId);
    usort($exceptions, fn($a, $b) => $b->getPriority() <=> $a->getPriority());
  3. Check scope matching:

    $result = $exception->matches($subjectType, $subjectId, $action, $schemaUuid);

Security Considerations

  1. Audit Trail: All exception creation/modification is logged with user information
  2. Admin Only: Only administrators should create/modify authorization exceptions
  3. Regular Review: Exceptions should be reviewed quarterly to ensure they're still appropriate
  4. Principle of Least Privilege: Use the most restrictive scope possible for each exception

Future Enhancements

  1. Field-Level Authorization

    • Control access to specific object properties
    • Redact sensitive fields based on user permissions
  2. Time-Based Permissions

    • Temporary permission grants with expiration
    • Scheduled permission changes
  3. Audit Logging

    • Log all authorization decisions
    • Track permission changes over time
  4. Permission Testing Tool

    • UI for testing user permissions
    • Visualize effective permissions for users/groups
  5. RBAC Analytics

    • Permission usage statistics
    • Identify over-privileged users
    • Suggest permission optimizations