Skip to main content

girder-collection-review

Share a Girder collection with anonymous peer reviewers via a single revocable access key.

Reviewers never create Girder accounts, cannot change anything, and lose access the moment the collection owner ends the review.

Workflow

  1. The collection owner (ADMIN on the collection) opens Actions → Manage review on the collection page and opens a review round. The server provisions a throwaway read-only account plus an API key, and shows the key once.
  2. The owner passes the key to the journal editor, who distributes it to reviewers.
  3. A reviewer visits #review, pastes the key, and gets the collection hierarchy on a page with no navigation chrome — read only, with downloads working.
  4. The owner ends the review. The key stops working immediately, live reviewer sessions die, every ACL entry for the reviewer is stripped, and the throwaway account is deleted.

Install

pip install -e .
cd girder_collection_review/web_client && npm ci && npm run build

The web client bundle must be built before starting Girder: registerPluginStaticContent hashes the files in web_client/dist/ at plugin load time, so a missing dist/ fails server startup.

Develop

tox -e lint      # ruff check
tox -e pytest    # pytest with coverage, 4-way xdist
tox -e format    # ruff format + ruff check --fix

Tests live in girder_collection_review/tests/ and use the pytest-girder fixtures (db, server, admin, user, fsAssetstore); plugin-specific fixtures are in tests/conftest.py. Every test module needs pytestmark = pytest.mark.plugin('collection_review') — pytest only honors module- and class-level pytestmark, so it cannot be hoisted into the conftest.

Browser end-to-end checks live in girder_collection_review/tests/e2e/ and are run by hand with ./run.sh — they need a built web client, a running Girder, a droppable Mongo and a Chrome, so they are not part of tox -e pytest. See that directory's README.md.

JS, pug and stylus linting is self-contained — it does not need a girder checkout:

npm ci
npm run lint      # eslint + pug-lint + stylelint
npm run format    # eslint --fix

The rules are the same ones girder core applies to plugin web clients (@girder/eslint-config and @girder/pug-lint-config, plus stylelint-stylus/standard), reproduced in this repo's root package.json so they run standalone and in CI.

Trust model

Several choices here are deliberate and non-obvious. Changing them will quietly weaken the plugin, so the reasoning is recorded next to each one in the source, and enumerated in CLAUDE.md.

The reviewer account cannot be logged into. It is created passwordless (salt = None), which User().authenticate() refuses outright, and its email is on the RFC 2606 reserved .invalid domain. The domain matters: PUT /user/password/temporary is a public route that works on passwordless accounts, so a routable reviewer mailbox would let whoever controls it upgrade the account to a full USER_AUTH session. status is also set to disabled, but only as an operator-visible marker — neither getCurrentUser nor ApiKey().createToken consults verifyLogin.

Read-only is enforced by a request guard, not by token scope. The review key is scoped [core.data.read, core.user_info.read], but scope alone is not sufficient: PUT /collection/:id is declared @access.user(scope=TokenScope.DATA_READ) in core, and plugins declare worse. A review token is therefore more capable than an anonymous visitor. lib/guard.py binds auth.user.get and rejects any non-safe HTTP method from a review session, which closes the whole class of mis-scoped routes at once.

Ending a review revokes the key, not just the review document. POST /api_key/token is public and never sees the review record, so a reviewer who kept the key could keep minting tokens. Closing a review removes the API key, which makes Token().clearForApiKey drop every derived token. The key's tokenDuration is also set from the review's duration, so it caps token lifetime independently.

An auth cookie is set. Download links in the hierarchy widget are plain <a href> navigations that carry no Girder-Token header. Every cookie=True route in core is read-only, and the guard rejects non-safe methods for the session, so the cookie adds no write surface. Note that the cookie is path=/ and replaces any existing girderToken in that browser; the reviewer page warns before starting a session if somebody is already signed in.

Known limitations

  • The key is not truly single-view. ApiKey exposes key at READ level, so a site admin can recover it from GET /api_key?userId=.... The UI shows it once as a convention, not a guarantee.
  • The site-wide core.api_keys switch is bypassed. Only the core REST layer checks it; the model methods this plugin uses do not.
  • Reviewer search returns nothing. GET /resource/search declares no scope, so a review token is treated as anonymous inside that handler.
  • A deleted collection can orphan a review. DELETE /collection/:id dispatches the removal to a Celery worker where this plugin is not loaded, so no event fires. Read paths auto-close such reviews, and a site admin can list and close them with GET /review and DELETE /review/:id.
  • No throttle on key submission. Keys are 40 characters from a CSPRNG, and the core POST /api_key/token route is unthrottled anyway, so a throttle here would buy nothing. The real risk is key leakage in transit; use a short duration and one key per reviewer where practical.
  • No maximum review duration. duration is only validated as positive.

Settings

Key Meaning
collection_review.default_duration Days a review key stays valid when the owner does not specify a duration. Default 90.

Endpoints

Route Purpose
POST /review Open a review round. Returns the review plus its key.
GET /review List reviews for a collection (ADMIN), or all of them (site admin, for cleaning up orphans).
GET /review/:id Read one review.
DELETE /review/:id Close a review and revoke access. Idempotent.
POST /review/session Reviewer-facing: exchange a key for a read-only session.
GET /review/session Reviewer-facing: current session, for reload and end-of-review detection.
DELETE /review/session Reviewer-facing: end the session.

License

BSD 3-Clause. See LICENSE.

Metadata

Release files for girder-collection-review 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for girder-collection-review 1.0.0
File Size Uploaded
girder_collection_review-1.0.0.tar.gz 71.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for girder-collection-review 1.0.0
File Interpreter ABI Platform
girder_collection_review-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 97.8 kB

Release files / girder_collection_review-1.0.0.tar.gz

Download URL girder_collection_review-1.0.0.tar.gz
Size 71.9 kB
Tags Source
SHA-256 checksum
How to use checksums
586f8a3ea853df719c135718e7f682ac00f0cb07c88f7cbc881b37faef48689f
BLAKE2b-256 checksum
How to use checksums
fe63139cb47398ad75009232f5097b01bc2fe2f1e8b70ffbf8cfdab186071b5c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 25, 2026.

Transparency log

Release files / girder_collection_review-1.0.0-py3-none-any.whl

Download URL girder_collection_review-1.0.0-py3-none-any.whl
Size 25.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
20ebebaa2259ed34a2ced81fa39d447f9c4f69bc31349789f26eed2d55e317b7
BLAKE2b-256 checksum
How to use checksums
293a7c2d4935d46cb9d368d794e7716562333f44699ea41f93bf6d19c3312a05
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page