hoangsonww / hoangsonww/Learning-Management-System-Fullstack

Multi-Tenant Orgs, Fine-Grained RBAC & Audit Logging

Open
#16 0 comments 0 reactions 1 assignee Claimed by @hoangsonww View on GitHub
bug documentation enhancement good first issue help wanted question
Dominant language
HCL
Stars
39
Forks
24
PR merge metrics
No merged PRs in 30d

Description

# Feature: **Multi-Tenant Orgs, Fine-Grained RBAC & Audit Logging**

**Summary**
Add native multi-tenancy (organizations, sub-orgs/programs), role-based access control with fine-grained permissions (resource-level & action-level), and a tamper-evident audit log across LMS entities. This enables schools/companies to isolate data, delegate administration, and meet compliance needs.

---

## Motivation

* Current model assumes a single global tenant; enterprise users need **org isolation** and delegated admin.
* Instructors need scoped capabilities (course owner vs. grader vs. TA).
* Compliance (FERPA/GDPR/ISO) requires **who did what, when, to which resource**.

---

## Goals

1. **Organizations & Scoping**

* Users belong to one or more Organizations (and optional SubOrgs/Departments).
* All core entities (Course, Lesson, Quiz, Enrollment, Progress, Notification) are tenant-scoped.
2. **RBAC**

* System roles: `platform_admin`, `org_admin`, `instructor`, `grader`, `ta`, `student`, `viewer`.
* Resource permissions: `read`, `create`, `update`, `delete`, `grade`, `publish`, `enroll_manage`.
* Policy resolution: role → permissions + optional per-resource overrides (ACLs).
3. **Audit Logging**

* Append-only events for CRUD & auth actions with actor, resource, before/after snapshot hashes.
* Queryable with filters (date, actor, org, resource).
4. **Admin UX**

* Angular admin screens for Org management, Role assignment, and Audit log views.

---

## Non-Goals

* SSO/SCIM (can be a follow-up).
* Full ABAC with attribute policies (keep RBAC + simple ACL for now).

---

## Proposed Design

### Data Model (MongoDB)

Collections (new or extended):

* `organizations`: `{ _id, name, slug, parentOrgId?, createdAt }`
* `user_org_memberships`: `{ userId, orgId, roles: [string], createdAt }`
* `acl_overrides`: `{ resourceType, resourceId, orgId, subjectType: 'user'|'role', subjectId, permissions: [string] }`
* Extend each resource with `orgId` (and optional `subOrgId`).
* `audit_events`:

```json
{
"ts": "2025-10-16T12:00:00Z",
"actorId": "user...",
"orgId": "org...",
"action": "COURSE_UPDATE",
"resource": {"type": "course", "id": "..." },
"beforeHash": "sha256:...",
"afterHash": "sha256:...",
"meta": {"ip":"...", "ua":"...", "reason":"..."}
}
```

### Backend (Django + DRF)

* **Middleware**

* `OrgContextMiddleware`: resolves active `orgId` from JWT claim, header (`X-Org-Id`), or membership default; validates access.
* `AuditMiddleware`: captures request/response, calculates content hashes, emits `audit_events`.
* **Permission Classes**

* `IsOrgMember`, `HasRole(role)`, `HasPermission(resource, action)`, `HasAclOverride`.
* **Decorators/Helpers**

* `@tenant_scoped(model_field="orgId")` to auto-filter queries.
* `check_perm(user, orgId, resource, action)` resolver with cache.
* **Endpoints (new)**

* `POST /api/orgs` (platform_admin)
* `GET /api/orgs/:id/members` | `POST /api/orgs/:id/members` assign roles
* `GET /api/audit?orgId=&actor=&resourceType=&from=&to=`
* `POST /api/acl` create/update per-resource overrides
* **Caching**

* Redis cache for `user→org→roles/permissions` (TTL 5–15 min) with bust on membership/ACL change.

### Frontend (Angular)

* **Org Switcher** in header for users with multiple orgs.
* **Admin → Organizations**: CRUD, member list, role assignment.
* **Admin → Roles & Permissions**: read-only matrix view (initially, roles fixed in code/config).
* **Admin → Audit Log**: filterable table with export (CSV/JSON).
* **Guards & Directives**:

* `canPerm="course.update"` directive to conditionally render actions (publish, edit, grade).
* Route guards enforcing tenant scope.

### Security & Compliance

* All queries must include `orgId` filters (enforced via decorator/mixins).
* Audit entries are append-only; use hash chaining (`prevHash`) for tamper evidence (optional Phase 2).
* PII minimization in audit payload; store diffs as hashes, not raw content where possible.

---

## Acceptance Criteria

* [ ] Every core entity has `orgId`, and all CRUD is tenant-scoped by default.
* [ ] RBAC resolver returns correct decision for at least 12 representative scenarios (unit tests).
* [ ] Per-resource ACL can grant a TA `grade` on a single course without `update` rights.
* [ ] Audit entries created for CREATE/UPDATE/DELETE + auth events (login/logout), viewable via API/UI.
* [ ] Org switcher affects data scope across the app; unauthorized access returns 403.
* [ ] Performance: permission check p95 < 5ms (cached), audit write p95 < 15ms.
* [ ] Documentation updated (README, ARCHITECTURE.md, API docs).

---

## Rollout Plan

1. **Phase 0**: Hidden behind `FEATURE_MULTI_TENANT=false` flag.
2. **Phase 1**: Enable for internal test orgs; migrate seed data with default org.
3. **Phase 2**: Enable ACL UI + Audit Log UI for all admins.

---

## Migration Notes

* Backfill script to set a default `orgId` on existing docs.
* Create a default Organization and assign all users.
* Re-index collections on `(orgId, ...)` compound keys for common queries.

---

## Tasks

* [ ] Schema updates & backfill scripts.
* [ ] DRF middleware, permission classes, helpers, tests.
* [ ] Redis caching for policy resolution.
* [ ] New endpoints (orgs, members, ACL, audit) + Swagger updates.
* [ ] Angular: Org switcher, Admin pages, guards/directives.
* [ ] Observability: counters for allow/deny, audit write failures.
* [ ] Docs & runbooks.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.