# Access Governance & Least Privilege Policy

**Version:** 1.0  
**Effective Date:** 2026-03-30  
**Owner:** Engineering + Security Team

---

## 1. Principle: Least Privilege

**Core Tenet:** Every user, service account, and admin has the minimum access needed to perform their role. No excess permissions.

**Application:**
- Development team: Code + staging only; no production database access
- Staging environment: Full access for testing
- Production environment: Minimal access; Super Admin approval required for any add-itions
- Schools: Access only to their own student data; superadmins see (audit-only) across schools

---

## 2. Role-Based Access Control (RBAC)

### Application Roles (In-App Access)

| Role | System Access | Data Access | Can Grant Roles | Typical User |
|---|---|---|---|---|
| **student** | Read own spelling progress, attempt exercises | Own user/spellings records only | No | K-12 student |
| **user** | Read own spelling progress, attempt exercises | Own user/spellings records only | No | K-12 student (alias) |
| **admin** | Manage all users/classes/reports within school | All data for their school only | Yes (create schooladmin) | District IT / Principal IT |
| **schooladmin** | Manage all users/classes/reports within school | All data for their school only | No (admins only) | School IT coordinator / teacher |
| **superadmin** | Audit any school; manage system settings; manage admins | All data (read-only audit); create admins | Yes (create schooladmin/admin for any school) | Spelling Mastery employee |

### Inheritance & Privilege Escalation Prevention

```
superadmin (Spelling Mastery)
    ├─ Can create: admin, schooladmin
    ├─ Can audit: Any school's data
    └─ Cannot: View plaintext passwords (hashed only)

admin (School)
    ├─ Can create: schooladmin, user (student accounts)
    ├─ Can manage: All users within their school
    ├─ Cannot: Create other admins (Spelling Mastery only)
    └─ Cannot: Access other schools' data

schooladmin (School)
    ├─ Can create: user (student accounts), read reports
    ├─ Can manage: All users within their school
    ├─ Cannot: Create admins
    └─ Cannot: Access other schools' data

user/student (Student)
    ├─ Can: Read own progress, make attempts
    ├─ Cannot: Access other students' data
    ├─ Cannot: Modify own role/school (immutable)
    └─ Cannot: Create accounts
```

### Reference Implementation (Firestore Rules)

```firestore
function hasRole(role) {
  return isAuthenticated() && request.auth.token.role == role;
}

function hasAnyRole(roles) {
  return isSuperadmin() || (isAuthenticated() && request.auth.token.role in roles);
}

match /users/{school}/{role}/{userId} {
  allow read: if isOwner(userId) 
              || (isInSchool(school) && hasAnyRole(['admin', 'schooladmin', 'superadmin']));
  
  allow update: if (
    // User updating self, but cannot escalate role/school
    isOwner(userId) && !('role' in request.resource.data) && !('school' in request.resource.data)
  ) || (
    // Admin/schooladmin updating within school
    isInSchool(school) && hasAnyRole(['admin', 'schooladmin', 'superadmin'])
  );
}
```

---

## 3. Environment Access Policy

### Development Environment (`localhost:3000`, `dev branch`)

**Restrictions:**
- No production data
- Test data only (manually created or faker.js generated)
- Full access for dev team (no restrictions)
- Shared password: [encrypted in `.env.local`]

**Access Grant:** By manager approval (onboarding)  
**Revocation:** Automatic on employee offboarding

### Staging Environment (`staging.spellingmastery.com`)

**Restrictions:**
- Copy of production schema (Firestore backup)
- No real student PII (data anonymized OR one-way hashed names)
- Full auth simulation (tokens, rules, etc.)
- Suitable for security testing

**Access:** Dev + QA team + select schools for UAT  
**Credentials:** Personal Firebase ID required; tracked in Firestore access logs

### Production Environment (`spellingmastery.com`, `production` branch)

**Restrictions:**
- Real student data; FERPA/COPPA protected
- Schools: read your own data only
- Spelling Mastery staff: minimal access via API

**Access Hierarchy:**

```
Tier 1: No direct database access (Least Privilege)
  └─ School admins: Via UI dashboard ONLY (Firestore rules enforce isolation)

Tier 2: Backend service accounts (Google Cloud)
  └─ Netlify functions: Can read/write per function role (scoped in Firestore rules)

Tier 3: Spelling Mastery support (Justification required)
  ├─ SRE / Data analyst: Read-only access to audit logs & system metrics
  ├─ Approval: Security Lead sign-off
  ├─ Audit trail: Every query logged & timestamped
  ├─ Revocation: SLA 24 hours after role change
  └─ Tools: Google Cloud console (with VPN) or Firestore UI (browser)

Tier 4: Executive / Founder (Emergency only)
  ├─ Full production access
  ├─ Approval: CEO decision journal entry
  ├─ Revocation: Automatic after incident resolution
  └─ Audit: Logged explicitly; reviewed monthly
```

### API & Service Accounts

**Service Account:** `spelling-mastery-functions@[project-id].iam.gserviceaccount.com`

**Permissions:**
- Can read/write Firestore within scope defined by security rules
- Cannot escalate permissions (no grantIamRoles)
- Cannot access other GCP projects
- Key rotation: Every 90 days (automated by Secrets Manager)

**Credentials Storage:**
- [ ] Private key encrypted at rest (Google Secret Manager)
- [ ] Accessible only by Netlify functions via environment variable injection
- [ ] Never logged or committed to Git
- [ ] Rotated automatically; old key revoked after 7 days

---

## 4. Admin Credential Management

### Initial Setup (Onboarding)

**Step 1: Create Superadmin Account**
```
Done by: Spelling Mastery co-founder/CEO (manual, one-time)
Method: Firebase console (direct UID claim setting)
Temp password: Generated + stored in secure vault (1Password, LastPass)
```

**Step 2: Create School Admin Account**
```
Done by: Superadmin via create-school-admin function
Request: Superadmin enters school name + admin email + desired password
Constraints:
  - Password: 12+ chars, uppercase, lowercase, number, symbol (checked by function)
  - Email: Verified (not instant; school receives welcome email with temp reset link)
  - Account: Disabled until first login (email verification)
```

**Step 3: Admin First Login**
```
Flow:
  1. School admin clicks email link
  2. Redirected to login with pre-filled email
  3. Enters temporary password
  4. Prompted: "Set new password" (same constraints)
  5. Login successful; account active
  6. Audit log: 'admin_first_login', school=X, admin_email=Y, timestamp=Z
```

### Password Policy

| Aspect | Requirement | Enforced |
|---|---|---|
| Length | 12-128 characters | Function validation + Firebase Auth |
| Complexity | Upper + lower + number + symbol | Function validation |
| Reuse | Cannot reuse last 5 passwords | Firebase Auth (if enabled) |
| Expiration | 90 days (optional per school) | Manual reminder, not enforced by app |
| Reset | Self-service via "Forgot Password" | Firebase Auth OOB email |
| History | Stored (hashed in old system); Bcrypt if new | Firebase Auth native |

### Multi-Factor Authentication (MFA)

**Status:** Optional (Schools can enable via Firebase console)  
**Methods Supported:**
- SMS/Text message (Firebase built-in)
- Authenticator app (Google Authenticator, Authy)
- Backup codes (generated during MFA setup)

**Recommended:** Yes, for Superadmins and admin accounts (COPPA/FERPA best practice)

---

## 5. Access Review & Audit

### Monthly Access Review

**Owner:** Security Lead / SRE team  
**Frequency:** Monthly (1st week of month)  
**Queries:**

```firestore
# Query 1: All active admin accounts
db.collection('users')
  .where('role', 'in', ['admin', 'schooladmin'])
  .get()

# Query 2: Recent admin creation
db.collection('audit_logs')
  .where('action', '==', 'create_admin')
  .where('timestamp', '>', now - 30days)
  .get()

# Query 3: Unusual access patterns
db.collection('audit_logs')
  .where('result', '==', 'failure')
  .where('timestamp', '>', now - 7days)
  .count() # Alert if > 100
```

**Review Checklist:**
- [ ] All active admins legitimate? (Cross-reference with school employee list)
- [ ] Any admins no longer at school? (Flag for deactivation)
- [ ] Any suspicious role changes? (unauthorized escalation attempt)
- [ ] Any unusual data access patterns? (outside business hours, large exports)

**Output:** Monthly access report (email to CEO + Security Lead)

### Annual Access Audit (SOC 2 / GDPR DPA compliance)

**Scope:** Complete access control matrix review

**Procedure:**
1. Document all users: name, role, school, date_created, date_last_accessed
2. Cross-check with payroll: Any "ghost" accounts?
3. Verify rule enforcement: Can role XYZ access data outside their school? (negative test)
4. Verify MFA status: How many admins have MFA enabled? (target: 100%)
5. Verify key rotation: Last Firebase key rotation date? (max 90 days old)
6. Evidence collected: Screenshots of audit logs, access matrix CSV, rule test results
7. Report filed: SOC 2 audit binder OR GDPR DPA evidence file

---

## 6. Onboarding & Offboarding Procedures

### New Employee (Spelling Mastery)

**Role: Developer**
- [ ] Grant: Read access to staging Firestore + non-prod code
- [ ] Grant: Netlify build previews
- [ ] Revoke: None (fresh account)
- [ ] Tools: GitHub access to private repo
- [ ] End date: Day 1

**Role: SRE / DevOps**
- [ ] Grant: Read-only production metrics (Google Cloud console)
- [ ] Grant: Staging Firestore full access
- [ ] Grant: Netlify function logs (read-only)
- [ ] Approval: VP Engineering sign-off before prod access
- [ ] End date: Day 1-3

**Role: Support / Customer Success**
- [ ] Grant: Read-only production logs (school-scoped queries only)
- [ ] Approval: Security Lead approval required
- [ ] Training: FERPA/COPPA overview (internal wiki)
- [ ] End date: Day 5 (after training attestation)

### Departing Employee

**Process:**
1. Manager notifies HR + Security Lead: Employee name, last day, role
2. Security Lead flags access revocation: 1 day before last day
3. Revocation occurs: EOD on last day
   - [ ] GitHub repo access revoked
   - [ ] Google Cloud IAM removed
   - [ ] Netlify API key revoked
   - [ ] VPN certificate revoked
   - [ ] Email forwarded to manager (2 weeks)
   - [ ] Password manager (1Password, LastPass) shared vaults updated
4. Verification: Run access report verify removal (next month)
5. Documentation: Log offboarding action + timestamp in compliance tracker

### Contractor / Consultant

**Special Rules:**
- [ ] Temporary account: 3-month max (extendable 1x)
- [ ] Limited scope: Staging only (not production)
- [ ] Approval: CEO approval required; reviewed monthly
- [ ] Audit: All access queries logged + reviewed quarterly
- [ ] Termination: Automatic on contract end; no data export after exit

---

## 7. Third-Party / Vendor Management

### Approved Vendors (Data Processors)

| Vendor | Service | Data Access | Security Attestation | Review Freq |
|---|---|---|---|---|
| **Google Cloud (Firebase)** | Database, Storage, Auth | Student data | SOC 2 Type II, GDPR DPA | Annual |
| **OpenAI** | Text-to-Speech API | Spelling words (no metadata) | SOC 2 Type II | Annual |
| **Google Cloud TTS** | Text-to-Speech API | Spelling words (no metadata) | SOC 2 Type II, GDPR DPA | Annual |
| **Netlify** | Hosting, Functions | Request logs (no PII) | SOC 2 Type II | Annual |

### Vendor Data Processing Agreement (DPA) Template

**Every vendor must sign:**
1. Data Processing Agreement (if in EU or processing EU data)
2. Security questionnaire (ISO 27001 controls, MFA, encryption, incident response)
3. Privacy policy attestation (no data selling, list of subprocessors)

**Annually:**
- [ ] Verify security cert current (SOC 2, ISO 27001, etc.)
- [ ] Confirm no data breaches reported
- [ ] Review subprocessor list (any changes?)
- [ ] Escalate SLA: If vendor does not meet commitments, evaluate replacement

### Incident: Vendor Breach

**Protocol:**
1. Vendor notifies us of breach (or we discover it)
2. Security Lead contacts vendor: time, scope, data impact
3. Evaluate: Is student data affected? (Usually NO for Firebase/SaaS vendors)
4. If YES: Proceed per Incident Response Runbook (Section 5)
5. Document: Vendor breach log; notify schools if impact confirmed

---

## 8. Security Monitoring & Logging

### Audit Log Queries (For Access Verification)

**Query 1: List all admin accounts and last access**
```firestore
db.collection('audit_logs')
  .where('action', '==', 'login_success')
  .where('role', '==', 'admin')
  .orderBy('timestamp', 'desc')
  .limit(1000)
  .get()
  // Extract UID + last login timestamp per admin
```

**Query 2: Detect privilege escalation attempts**
```firestore
db.collection('audit_logs')
  .where('action', '==', 'set_admin_claims')
  .where('result', '==', 'success')
  .where('timestamp', '>', now - 7days)
  .get()
  // Verify each escalation was authorized (compare with approval logs)
```

**Query 3: Cross-school access attempts**
```firestore
db.collection('audit_logs')
  .where('action', 'in', ['read_users', 'read_sessions'])
  .where('school', '!=', resource.data.school)  // Mismatch!
  .get()
  // Should return ZERO (if any exist, it's a bug or unauthorized access)
```

---

## 9. Approval & Attestation

**This policy approved by:**

- Chief Information Security Officer: ____________________________
- VP Engineering: ____________________________
- HR Director: ____________________________

**All Spelling Mastery employees must attest to:**

"I acknowledge that I understand and agree to follow Spelling Mastery's Access Governance Policy. I understand that:
- I will access only the data necessary for my role
- I will not share credentials or API keys
- I will report any suspected unauthorized access immediately
- I am subject to audit and review of my access"

Signed: [Employee name] ____________________________  
Date: ____________________________

---

**APPROVED FOR K-12 SALES**

This policy is effective immediately and is mandatory for all employees, contractors, and vendors.
