Skip to content
Farhad Nazir

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
CampusPro Super Admin dashboard with institution, people, active and deep-tracking tiles, an institution growth chart, an institutions-by-type donut, people per institution and recent activity

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

What is built so far, taken from the code.
  • 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.

Screens from the software itself. Design prototypes and illustrative mockups are labelled as such.

CampusPro institutions table listing schools, academies and colleges with user counts, created dates, active or suspended status and deep-tracking toggles
Institution management for the platform owner (UI with demo data).
CampusPro roles and permissions matrix with view, create, edit and delete toggles for dashboard, users, students, staff, classes and subjects
Per-tenant roles, now limited by a grant ceiling (UI with demo data).
CampusPro student roster with total, active, online and in-person counts, class and section, guardian, fee and status columns
Student roster split into in-person and online learners (UI with demo data).
CampusPro users directory in grid view with generated sign-in IDs, active and pending status, and bulk import, export and add user actions
Users directory with Excel bulk import and export (UI with demo data).
CampusPro Super Admin dashboard in dark mode with institution KPIs, growth chart and recent activity
The same dashboard in dark mode, one of about a dozen built-in themes (UI with demo data).
CampusPro institution admin dashboard on a mobile viewport with students, in-person, online and classes tiles
The responsive institution dashboard on a phone-sized screen (UI with demo data).

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.
Track Cue monitoring grid with a running game on Table No 1 showing a live timer, game type, cost, rate, start time and a Stop Game button, next to two idle snooker table cards
In development

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
Asaan Optics Desktop Stock Control Center showing stock value at cost and retail, potential profit, stock by category, last 30 days of sales and low-stock alerts

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
SoftHouse Pro ERP Executive Hub showing collected revenue, outstanding invoices, active projects, team online percentage, a project distribution donut chart, recent invoices and a live operations feed

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.