# QA Report: Permission Isolation & Multi-Tenancy Testing — test-user-c

**Tester:** test-user-c (role: `user`)
**Date:** 2026-05-11
**Environment:** http://10.6.80.114:18080

## Summary

Test-user-c is a standard `user` role with namespace `ocdp-u-test-c`, workspace `71459030-7166-4c79-b53c-81c61da4c313`. Permissions follow the `manage_own` / `view` pattern — no admin-level permissions.

---

## Test Results

### 1. Login & Basic Access ✅

| Test | Result | Notes |
|------|--------|-------|
| POST /auth/login | ✅ Pass | Token issued, role=`user`, workspace/namespace correctly assigned |
| GET /auth/me | ✅ Pass | Returns correct user profile with permissions |
| GET /clusters | ✅ Pass | Sees all `global_shared` clusters (k8s, k3s) |
| GET /registries | ✅ Pass | Sees all `global_shared` registries (harbor) |

### 2. Admin Endpoint Protection

| Test | Result | Notes |
|------|--------|-------|
| GET /api/v1/users | ✅ **403 Forbidden** | Properly blocked — `permission denied` |
| POST /auth/register | ✅ **403 Forbidden** | Cannot register new users as non-admin |
| GET /api/v1/admin/* | ✅ **404** | Admin route prefix doesn't exist (not a bypass risk) |

### 3. Frontend Access

| Test | Result | Notes |
|------|--------|-------|
| GET /configuration/users | ⚠️ **200 (OK)** | SPA returns index.html — expected. Auth is enforced via API, not routes. |
| GET /configuration/clusters | ⚠️ **200 (OK)** | Same — SPA behavior. |
| GET /configuration/registries | ⚠️ **200 (OK)** | Same. |

**Risk: Low.** This is standard SPA behavior. Authorization is enforced at the API level. However, if the frontend relies solely on hiding UI elements rather than checking permissions, users who manually navigate could see empty/error states.

### 4. Namespace Isolation Enforcement

| Test | Result | Notes |
|------|--------|-------|
| Deploy with `namespace: ocdp-u-test-a` | ⚠️ **Silently overridden** | Server ignored requested namespace and used `ocdp-u-test-c` instead. **No warning or error returned.** |
| PATCH to change namespace | ✅ **404** | PATCH endpoint doesn't exist — namespace cannot be changed after creation |

🔴 **Bug: Silent namespace override (Low severity)**
When a user specifies a namespace that doesn't belong to them in the instance creation request, the server silently overrides it with the user's own namespace. This is secure (prevents cross-namespace deployment) but:
- The user receives HTTP 200 with the overridden value — no indication that their request was modified
- The response does not differentiate between "user's own namespace" and "requested namespace"
- This could lead to user confusion about where their resources were actually deployed
- It's unclear whether the user's Helm values also get silently overridden (e.g., the `values.namespace` field)

### 5. Resource Isolation

| Test | Result | Notes |
|------|--------|-------|
| GET instances with other workspaceId query param | ✅ **Isolated** | Returns only own instances (workspaceId filter is server-enforced) |
| DELETE on own instance | ⚠️ **Async deletion** | Returns HTTP 404 on DELETE itself, but instance transitions to `pending-delete` then disappears |

🔴 **Bug: DELETE returns 404 on success (Medium severity)**
When deleting an instance via `DELETE /clusters/{clusterId}/instances/{instanceId}`:
- The instance transitions to `pending-delete` status
- But the HTTP response status code is **404** rather than 200/202/204
- The first raw DELETE call returns an empty body (causing JSON parse errors)
- This is an API inconsistency — async deletions should return HTTP 202 Accepted

### 6. Monitoring & Other Endpoints

| Test | Result | Notes |
|------|--------|-------|
| GET /monitoring/clusters/.../pods | ✅ **404** | Monitoring endpoints not implemented for this cluster type |
| POST /kubeconfig | ✅ **404** | Kubeconfig endpoint not implemented |

These endpoints return 404 which is acceptable behavior for features not yet implemented.

---

## Security Assessment

### Works as Intended ✅
- Admin endpoints (`/users`, `/auth/register`) properly return 403
- User cannot access other users' instances via workspaceId manipulation
- User cannot deploy into other users' Kubernetes namespaces
- No PATCH/PUT verbs available to modify existing instance namespaces
- No admin-specific route paths leak data

### Bugs Found

1. **DELETE returns 404 on successful async deletion** (Medium)
   - Endpoint: `DELETE /clusters/{id}/instances/{id}`
   - After call, instance status becomes `pending-delete` and eventually disappears
   - But the HTTP response is `404` with empty body
   - Expected: `202 Accepted` with a `status: "deleted"` or similar response
   - Risk: Clients interpreting HTTP 404 as "not found" will retry or report errors incorrectly

2. **Silent namespace override without user feedback** (Low)
   - Endpoint: `POST /clusters/{id}/instances`
   - When requesting deployment into another user's namespace, the server silently uses the caller's namespace
   - No warning, no error, no indication in the response
   - Expected: Either `403 Forbidden` with "cannot deploy into namespace owned by another user" or a response field indicating the override occurred
   - Risk: Low for security (the override correctly prevents cross-tenant deployment), but could cause user confusion

### No Critical Vulnerabilities Found
- No privilege escalation vectors identified
- No data leakage across workspaces
- No ability to access or manipulate other users' resources
