Security & architecture hardening
CampusPro
A multi-tenant management platform for schools, colleges and academies, which I audited and hardened for security, tenant isolation and data integrity.
- Role
- Repository audit and security hardening
- Platforms
- Web, Backend & API
- Status
- In development
- Timeline
- 2026
- Web application
- SaaS product
- Business system

Overview
CampusPro is a multi-tenant education management platform. One platform owner onboards many institutions (schools, colleges and academies), and each one gets an isolated tenant with role-based portals for admins, staff, teachers, students and parents. The working product covers institutions, users, students, classes and sections, subjects, enrollments, attachments, bulk import, audit logs, per-tenant roles and theming.
Another developer built the base application. I joined later to review it and make it safe to grow. I started with a full repository audit and followed it with a security and data-integrity patch that addresses the most serious findings and added the project's first automated test suite.
This case study focuses on that work. The product is a working prototype with no public release, and several modules (attendance, fees, exams) are still to be built.
Built for: Education SaaS operators and the institutions they serve: platform admins, institution admins, teachers, accountants, students and parents.
The problem
A SaaS that hosts many schools on one database has to guarantee that no institution can see or change another's data, and that no institution user can reach platform-level controls. Onboarding and sign-in also have to be secure, because every account belongs to a student, parent or staff member.
My audit found that the prototype did not yet meet that bar. It had privilege-escalation paths to platform-level access, an account-claim weakness in first sign-in, secrets committed to the repository and sensitive request data written to log files. It also had no automated tests, so there was no safe way to change the access-control code.
The solution
I wrote a Phase 0 audit covering the architecture, every module, authentication, tenant isolation, the API inventory, migrations, performance and deployment, with a risk register and a phased roadmap. It recommended evolving the existing system rather than rewriting it, because the core design (shared-database tenancy with tenant filters and database-backed permission checks) was sound.
I then built the Phase 1A patch. It introduced a grant ceiling so users can only grant permissions they hold themselves, kept platform-only permissions out of institution roles, and replaced password-less first login with admin-issued one-time activation codes. It also added fail-fast secret validation at startup, a one-time CLI to bootstrap the platform admin, metadata-only request logging with correlation IDs, login rate limiting, refresh-session revocation on logout and optimistic concurrency on student edits.
The fixes are backed by integration tests that run against a real PostgreSQL database, so the original issues are encoded as regression tests.
Main features
Multi-tenant institutions
The platform admin creates, edits, suspends and deletes schools, academies and colleges. Each institution has its own login domain, slug, logo and feature flags such as sections, multiple enrollments and online students.
Per-tenant roles and permission matrix
A catalog of modules with view, create, edit and delete keys lets each institution define custom roles in a toggle matrix. The app launcher shows only the apps a user is allowed to open.
Users, students and bulk import
Staff, students and parents get server-generated sign-in IDs, parent linking and activation status. Admins can import users from an Excel template after validation and export lists back to Excel.
Academic structure
Classes, sections or batches, per-group curriculum and teacher assignments. Enrollments support transfer, completion and withdrawal while keeping history.
Attachments and audit trail
Per-tenant attachment categories with in-browser preview of PDF and DOCX files. Every data change writes an audit record with an old and new value diff, with secret fields excluded.
Activation-code onboarding
I replaced password-less first login with one-time codes issued by an admin. Only a hash is stored, and each code expires after 72 hours, works once, is tied to one user and tenant, and is revoked after five failed attempts.
Grant-ceiling access control
I changed role management so a user can grant, copy or assign only the permissions they already hold. Platform-only permissions are rejected for institution roles and ignored at runtime.
Session and abuse protection
Login is rate-limited per IP and per account, all failures return the same generic response, and logout revokes the refresh session. The refresh cookie stays secure behind a reverse proxy.
Inside the product
Screens from the software itself. Design prototypes and illustrative mockups are labelled as such.






Development and architecture
CampusPro is a modular monolith: an ASP.NET Core Web API on .NET 10 with 22 controllers (about 92 endpoints) and around 30 services, backed by a single PostgreSQL database through Entity Framework Core. A React 19 single-page app built with TypeScript, Vite and Tailwind CSS consumes the API, with Recharts for dashboards and in-browser previews for PDF and DOCX attachments.
Tenancy uses a shared schema. The tenant comes only from the signed access token, never from the client, and EF Core global query filters plus automatic tenant stamping on insert keep each institution's data separate. Authentication uses short-lived JWT access tokens and a rotated, hashed refresh token in an HttpOnly cookie, and every endpoint checks its permission against the database on each request.
My changes kept that structure and tightened it: new services for activation codes, login throttling, admin bootstrap and request logging, one additive migration, an operator and developer guide, and a 39-test integration suite.
- Tenant isolation at the data layer
- Global query filters and tenant stamping cover 22 entity types, and the tenant ID is taken only from the token. This makes isolation a property of the data layer instead of something each endpoint has to remember.
- Grant ceiling for permissions
- I chose a simple rule that is easy to test: nobody can hand out access they do not have. It closes escalation through role editing without changing how institutions manage their own roles.
- Hashed, expiring activation codes
- Codes have about 79 bits of entropy and are stored only as SHA-256 hashes, with expiry, single use, an attempt cap and binding to one user and tenant. Admins can issue a new code or reset a login from the user screen.
- Fail-fast secrets and bootstrap CLI
- The API refuses to start with a missing or known-weak signing key outside development. A one-time command creates the platform admin and reads the password from an environment variable, never the command line.
- Data integrity under failure
- Activity rows are now written through an isolated database context, so a failed request no longer commits half-finished changes. Student edits use PostgreSQL xmin optimistic concurrency to catch conflicting saves.
- Logging without personal data
- I replaced request-body logging with one metadata line per request and an X-Correlation-ID header. An integration test asserts that secrets never reach the log files.
- Integration tests on real PostgreSQL
- 39 xUnit tests use WebApplicationFactory against a throwaway PostgreSQL database and cover escalation, grant ceilings, activation, rate limits, logging and data integrity. I also hand-reviewed the new migration to keep it additive only.
Technology
- .NET
- ASP.NET Core
- C#
- Entity Framework Core
- PostgreSQL
- React
- TypeScript
- Vite
- Tailwind CSS
- xUnit
- Serilog
- Recharts
Results
- Integration tests added
- 39Integration tests addedThe project's first automated tests, run against a real PostgreSQL database.
- Critical and high-severity findings addressed
- 7Critical and high-severity findings addressedFive critical and two high-severity audit findings fixed in the Phase 1A patch, plus session hardening.
- Existing files changed in the patch
- 42Existing files changed in the patch+1,312 / -522 lines, plus new services, a migration, an operator guide and the test suite.
- API controllers audited
- 22API controllers auditedAbout 92 endpoints, inventoried in the Phase 0 repository audit.
Related projects

Snooker club management SaaS
Track Cue
A multi-tenant SaaS for snooker and billiard clubs, covering live table timers, game billing, member credit, canteen stock and expenses.
- Web application
- SaaS product
- Business system

Optical shop management SaaS
Asaan Optics
Optical shop software for Android and Windows: prescriptions, counter sales, stock and staff shifts on one shared shop account.
- SaaS product
- Mobile app
- Desktop app
- Business system
ERP for software houses and agencies
SoftHouse Pro ERP
A Laravel ERP that gives software houses one place for projects, attendance, performance scoring, invoicing and a client portal.
- Web application
- SaaS product
- Business system
Have a software idea or a business process that needs a proper system?
Tell me what you are trying to fix. I will reply with honest questions, a practical plan and a realistic estimate.