លំហសិក្សាកម្ពុជា
V1.0
**ExamFlow** is a web-based examination platform developed with **Python, Flask, and PostgreSQL**. The system supports exam creation, question management, timed assessments, automated grading, and performance tracking. Its main focus is the implementation of a **Composed Role-Based Access Control (RBAC)** model, which combines direct role permissions with reusable **Permission Bundles**. This approach improves permission reuse, reduces duplication, and provides a centralized and maintainable method for managing access across students, instructors, and administrators.
Advanced Object-Oriented Programming
R&D Project Report
Secure Online Examination System
with Composed Role-Based Access Control (RBAC)
( ExamFlow )
Prepared by: Sareth Pich, Men Sereyyuth, Veasna Panhareach
Lecturer: SEK SOCHEAT
Submission Date: September 25, 2026
Educational institutions rely heavily on digital platforms to create, deliver, and grade examinations. As these platforms scale, restricting user actions becomes critical: instructors must author and publish tests, students should only see assigned assessments, and administrators need system-level oversight without unnecessary access to course content. Role-Based Access Control (RBAC) solves this by grouping permissions into roles and assigning those roles to users, eliminating the need to manage individual user rights directly.
This project, ExamFlow, is a web-based examination platform developed using Python, Flask, and PostgreSQL. Beyond managing core testing workflows—exam authoring, question banks, timed submissions, automated grading, and score analytics—the project's main focus is designing and evaluating a Composed RBAC model. In this setup, a role's effective permissions combine rights assigned directly to the role with reusable, named Permission Bundles.
Standard RBAC setups usually map roles directly to permissions through a flat many-to-many relationship. While functional, this approach presents two main drawbacks in practice:
Permission duplication: when multiple roles share common responsibilities (such as administrators and auditors needing read-only report access), individual permissions must be attached separately to each role, raising the chance of configuration drift over time.
Limited grouping capabilities: flat models lack a direct mechanism to package and reuse related permissions (like an "exam authoring" set) across different roles.
ExamFlow addresses these issues by adding a composition layer—the Permission Bundle—between roles and individual permissions, structured and tested using object-oriented principles.
Design and implement a normalized database schema supporting both direct role to permission assignments and bundle-based permission groups.
Build a centralized authorization service that determines effective user permissions by combining direct and bundle grants.
Enforce route-level access control on all server endpoints, ensuring security is independent of client-side UI state.
To provide a complete administrative interface allowing permissions, roles, and permission bundles to be created, configured, and safely deleted at runtime, without requiring code changes or redeployment.
Validate security controls through automated integration tests and unauthorized access attempts, documenting issues found during development.
This project focuses primarily on the RBAC architecture: schema design, object-oriented structure, and authorization enforcement. The examination features (test authoring, question banks, timed attempts, automatic grading, and performance dashboards) serve as a realistic environment to demonstrate and evaluate access control. Exam proctoring, plagiarism monitoring, and manual essay grading are outside the project's scope.
Implementing and testing Composed RBAC within a multi-role application highlights core software design techniques: relational normalization, association tables, object composition, and decoupling authorization from application logic. The development history in Chapter 5 also documents the iterative testing and defect resolution process used throughout the project.
Existing educational platforms handle access control in various ways, but few offer administrator-configurable permission bundles:
Flask was selected for its minimal, unopinionated design, allowing the project to be structured into clear layers (routes, services, and repositories). This architecture keeps components loosely coupled, enabling the authorization module (Section 3.7) to be tested independently of web routing or database details.
PostgreSQL serves as the primary database due to its support for relational constraints, composite keys in association tables, and custom CHECK constraints. These features let us enforce key data rules—such as valid exam statuses and positive question point values—directly at the database level rather than depending entirely on application code.
Bcrypt handles password hashing using a salted, key-stretching design that resists brute-force attacks. While slower than basic hashing algorithms, it provides appropriate security for authentication without affecting overall system responsiveness.
Encapsulation: domain logic resides within model objects—for instance, Exam.publish() verifies that an exam is in draft state and contains at least one question before publishing, rather than relying on caller checks.
Composition over inheritance: permissions are linked via object composition rather than deep role hierarchies—a Role combines individual Permission objects directly or aggregates them through PermissionBundle instances (Section 3.5).
Polymorphism (Strategy Pattern): grading logic uses a QuestionGrader interface with distinct implementations for single-choice, true/false, and multiple-choice questions. A GraderFactory selects the appropriate grader at runtime, allowing new question types without modifying existing grading code.
Layered architecture: web routes manage HTTP requests, services handle business rules, and repositories encapsulate database queries, as shown in Figure 3.4.
The database schema is fully normalized. The access-control layer uses four association tables (user_roles, role_permissions, bundle_permissions, and role_bundles) to allow roles to gather permissions through direct assignment or bundle inheritance. The testing domain (subjects, exams, questions, choices, attempts, and answers) is similarly structured, using CHECK constraints to enforce valid status values, positive timer values, and non-negative scores directly in the database.
Figure 3.1: Entity-Relationship Diagram
Figure 3.2 details the core access-control design. AuthorizationService.get_user_permissions() calculates a user's active permissions by combining two paths: direct grants (Role → Permission via role_permissions) and bundle grants (Role → PermissionBundle → Permission via role_bundles and bundle_permissions). Roles can use either path or combine both as needed.
Figure 3.2: Composed RBAC Class Diagram
The grading subsystem uses the Strategy and Factory design patterns. Each supported question format (single choice, true/false, multiple choice) implements a shared QuestionGrader interface, retrieved dynamically via GraderFactory. This structure simplifies extending the platform with new question types without altering existing grading routines.
Figure 3.3: Examination Domain and Grading Strategy Class Diagram
The application uses a standard layered layout where components only interact with the layer directly below them. Routes delegate logic to services, and services use repositories for data access. This structure keeps AuthorizationService independent of HTTP contexts, making unit testing straightforward.
Figure 3.4: Layered System Architecture
The codebase follows the layered architecture described in Section 3.7. Templates are further split by role (admin, teacher, student) to keep each portal's presentation logic isolated.
Table 4.1 summarizes the primary service and repository classes referenced in the class diagrams of Section 3.5 and 3.6. Full method-level detail is provided in Appendix A.
The administrator portal provides full lifecycle management for permissions, roles, and permission bundles, directly exercising the Composed RBAC design from Chapter 3.
Figure 4.1: Permission Registry (create, edit, guarded delete)
Figure 4.2: Role Management (direct permissions and attached bundles)
Figure 4.3: Permission Bundles (composed capability groups)
The teacher dashboard presents authored-exam KPIs and candidate performance analytics, backed by TeacherDashboardService.
Figure 4.4: Instructor Dashboard
The student portal lists available examinations (filtered by AuthorizationService independent business rules: publication status, attempt window, and remaining attempts) and presents results and progress analytics.
Figure 4.5: Available Examinations
Figure 4.6: My Results Dashboard
System testing combined three approaches: (1) automated unit and integration tests using Python's unittest module to check business logic and endpoint permissions; (2) manual browser checks on a local development server to test template rendering and CSRF protection (which are disabled during automated test runs); and (3) exploratory testing across user roles to uncover edge cases missed by automated scripts (Section 5.4).
The test suite grew iteratively alongside defect discovery rather than being written upfront and left static, as shown in Table 5.1.
The following examples illustrate the testing and debugging process. Each issue was investigated to determine the root cause, resolved in code, and followed by a regression test to prevent recurrence.
In addition to functional testing, the authorization layer underwent unauthorized access testing to verify endpoint protections directly. Since hiding UI elements is insufficient for security, these tests sent direct HTTP requests to verify that backend endpoints properly block unauthorized actions regardless of client interface controls.
Development highlighted two important testing observations. First, automated suites can pass completely while user-facing bugs remain present (as seen in Defects 1 and 4). These gaps stemmed from test environment settings, such as checking response status codes instead of page content or disabling CSRF validation in test runs. To address this, later test updates focused on rendered output assertions and CSRF-enabled integration runs. Second, Defect 5 showed that perceived bugs can sometimes stem from unexpected user interaction behavior rather than application logic, proving the value of reproducing exact browser steps before modifying code.
Password storage uses bcrypt with per-password salting; raw passwords are never logged or persisted.
Login is rate-limited by email and client IP to mitigate brute-force credential attacks.
All state-changing requests (POST) are protected by Flask-WTF CSRF tokens, verified across all three admin RBAC management views following Defect 4.
Permission and role changes take effect on the very next request with no caching layer, eliminating a class of "stale privilege" bugs common in systems that cache authorization state in the session.
Database-level CHECK constraints (e.g. on exam status, attempt count, and score ranges) provide a defense-in-depth layer independent of application-level validation.
End-to-end functionality was verified across all three user roles on a live development instance. As Administrator, custom permissions, roles, and bundles were created, edited, and removed, while system-default entities remained protected against deletion. As Teacher, an examination was created, populated with questions, published, and evaluated via score reports. As Student, published tests were taken within time limits, with results accurately recorded on the student dashboard.
This project implemented a Composed RBAC authorization framework within an online examination system. The platform resolves user privileges by combining direct permission grants with reusable Permission Bundles, enforced through a normalized database schema and a centralized AuthorizationService. Endpoint testing confirmed server-side enforcement across all 14 system permissions, with boundary tests showing no unauthorized access leaks. The development process documented in Chapter 5 helped refine both application features and system security controls.
The composition model supports permission bundling but does not implement role inheritance (a role extending another role's full permission set); this was a deliberate scope decision to keep the authorization model focused and defensible rather than open-ended.
Resource-level ownership (for example, confirming a specific exam belongs to the requesting teacher) is enforced procedurally within service methods rather than being modeled as part of the authorization layer itself — a deliberate separation-of-concerns decision, but one that means ownership rules are not centrally auditable the way role/permission rules are.
There is no persistent audit trail (database-backed log) of who modified which role, permission, or bundle and when; changes are currently logged only to the application's runtime log.
The system currently supports only objective question types (single-choice, true/false, multiple-choice); free-text or essay questions requiring manual grading are not supported.
A key takeaway from development is that a fully passing test suite does not guarantee the absence of bugs. Early automated tests missed UI and configuration issues because they checked HTTP status codes rather than page contents and ran with CSRF protections disabled. Updating integration tests to validate rendered template output and test with active security settings proved essential for catching subtle interface and request handling issues.
Implement role inheritance (e.g. a role that extends another role's full permission and bundle set) to further reduce configuration duplication for closely related roles.
Introduce a persistent, queryable audit log for all RBAC mutations (permission, role, and bundle changes), recording the acting administrator, timestamp, and before/after state.
Extend the authorization layer to support resource-scoped permission checks natively (e.g. has_permission_on_resource), rather than handling ownership procedurally in each service.
Support additional question types requiring manual or semi-automated grading (short answer, essay), extending the existing QuestionGrader strategy hierarchy.
Add proctoring and integrity features (e.g. tab-switch detection, timed lockdown) which were explicitly excluded from this project's scope.
មតិយោបល់ 0