Found while implementing #59 (document email invites), then confirmed against the live API.
The bug
CollaboratorRole in feature/documents/.../domain/Collaborator.kt sends viewer / editor /
admin:
enum class CollaboratorRole(val apiValue: String, val label: String) {
VIEWER("viewer", "Viewer"),
EDITOR("editor", "Editor"),
ADMIN("admin", "Admin");
The server's vocabulary is watcher / collaborator / manager. From the live API, on
POST /api/lists/{id}/invites:
{"error":"Invalid role. Must be watcher, collaborator, or manager","code":"bad_request"}
— returned for role: "viewer" as well as for a nonsense value.
Why it is worse than a rejected request
POST /api/documents/{id}/collaborators does not validate the role. A live POST with
role: "zzz" returned 201 {"collaborating": true}, and the resulting collaborator came back from
GET /api/documents/{id}/collaborators as:
{ "role": "watcher", "user": { "username": "…" } }
So an unrecognised role silently falls back to watcher, the read-only level.
Consequence: every collaborator added from the Android app is granted read-only access no
matter which role the user picked. Choosing "Editor" or "Admin" appears to work — the request
succeeds, the row appears — and the person silently cannot edit. There is no error to notice.
The read path masks it too: CollaboratorRole.fromApi maps anything unrecognised to VIEWER, so a
real collaborator or manager role coming back from the server also renders as "Viewer".
Fix
- Change the
apiValues to watcher / collaborator / manager.
- Update
fromApi to map the real values (keeping a tolerant fallback is fine, but it must
recognise the three real ones first).
- Check
PUT /api/documents/{id}/collaborators/{userId} for the same problem.
- Check whether
:feature:lists contributors have the equivalent issue.
- Add a test that pins the three wire values, so this cannot regress silently.
/help/api/documents and /help/api/sharing both document watcher/collaborator/manager.
#59 uses the correct values for its separate InviteRole; the collaborator path was left alone as
out of scope for that issue.
Note
Confirming this required one live write (adding a collaborator to a document on the test account).
It was removed immediately afterwards and the collaborator list verified back to empty.
Found while implementing #59 (document email invites), then confirmed against the live API.
The bug
CollaboratorRoleinfeature/documents/.../domain/Collaborator.ktsendsviewer/editor/admin:The server's vocabulary is
watcher/collaborator/manager. From the live API, onPOST /api/lists/{id}/invites:— returned for
role: "viewer"as well as for a nonsense value.Why it is worse than a rejected request
POST /api/documents/{id}/collaboratorsdoes not validate the role. A live POST withrole: "zzz"returned201 {"collaborating": true}, and the resulting collaborator came back fromGET /api/documents/{id}/collaboratorsas:{ "role": "watcher", "user": { "username": "…" } }So an unrecognised role silently falls back to
watcher, the read-only level.Consequence: every collaborator added from the Android app is granted read-only access no
matter which role the user picked. Choosing "Editor" or "Admin" appears to work — the request
succeeds, the row appears — and the person silently cannot edit. There is no error to notice.
The read path masks it too:
CollaboratorRole.fromApimaps anything unrecognised toVIEWER, so areal
collaboratorormanagerrole coming back from the server also renders as "Viewer".Fix
apiValues towatcher/collaborator/manager.fromApito map the real values (keeping a tolerant fallback is fine, but it mustrecognise the three real ones first).
PUT /api/documents/{id}/collaborators/{userId}for the same problem.:feature:listscontributors have the equivalent issue./help/api/documentsand/help/api/sharingboth documentwatcher/collaborator/manager.#59 uses the correct values for its separate
InviteRole; the collaborator path was left alone asout of scope for that issue.
Note
Confirming this required one live write (adding a collaborator to a document on the test account).
It was removed immediately afterwards and the collaborator list verified back to empty.