Close the gaps an audit of courses, tasks, files and grades turned up
Every area — courses, rooms, boards, topics, tasks, files, quizzes, teams,
groups, submissions, grades — was checked for data the instance has and the
tools did not show.
Grades and feedback. A teacher's /homework page is a different page from a
student's: grade and comment live in the grading form, one block per
submission, so a teacher account reported every graded submission as having
neither. parseTeacherGrading reads the form, and list_submissions can now
include the written feedback and who handed the work in.
Names. /api/v1 is partly served: courses, users and classes survive in the
deployment's ingress table, and users/{id} is the only route from an id to a
name. Submitters, file creators and course teachers resolve through it, and
degrade to "not visible to this account" where a student may not read them.
Courses, rooms and classes. get_course adds the description, teachers,
member count and weekly timetable from /api/v1/courses. list_classes is new.
get_room reports what the account may do — allowedOperations is an object of
booleans, not the list it was typed as — and applicants and invitation links
where it may manage them.
Board and topic content. Link descriptions, image alt text, drawing and
video-conference titles, the ids behind external tools and H5P content (the
only thing resembling a quiz), and what a deleted element used to be. Topic
Etherpad pads are read like board pads, and htmlToText keeps table columns
apart and drops template indentation.
Files. A scan with no text layer falls back to the preview endpoint, whose
width and outputFormat are undocumented enums, so Claude gets a picture of
the page; list_files reports counts and sizes. Teams stay documented as
unreadable at any API version; their files come later.
What the crawl missed. Tasks attached to topics (18 of 60 on the live
account), each course's own file area, and — behind INDEX_PERSONAL_FILES —
personal files and submissions with their grade comments, so search and
what_changed cover grading. A submission hit points at get_task.
The local instance's preview profile gets an ImageMagick policy that allows
the coders its 7.1.2 build needs; the image's own denies them all.
110 tests.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -252,6 +252,17 @@ plain HTTP locally.
|
||||
|
||||
**A file uploads but will not download.** See the `av` note above.
|
||||
|
||||
**Previews never appear, and `/api/v3/file/preview/...` answers 404
|
||||
PREVIEW_NOT_POSSIBLE.** Two causes, both local. First, with no virus scanner
|
||||
(see the `av` note) every upload stays `securityCheck.status=pending`, and a
|
||||
record that has not been scanned reports `previewStatus: awaiting_scan_status`
|
||||
— previews are gated on the scan. Second, the `file-preview` image ships an
|
||||
ImageMagick policy written for an older ImageMagick than the 7.1.2 it actually
|
||||
contains, so every coder it needs is denied and each attempt fails with
|
||||
*"attempt to perform an operation not authorized by the security policy"* —
|
||||
which the API surfaces as a 404. `file-preview/policy.xml` is mounted over the
|
||||
image's own to fix the second; the first is inherent to running without `av`.
|
||||
|
||||
**H5P element stays empty.** `docker compose --profile tools run --rm
|
||||
h5p-libraries` and watch it finish; the editor has nothing to offer until the
|
||||
content types are in the bucket.
|
||||
|
||||
@@ -160,6 +160,10 @@ services:
|
||||
image: quay.io/schulcloudverbund/file-storage:file-preview-${SC_VERSION:-33.40}
|
||||
profiles: ["preview"]
|
||||
env_file: [env/shared.env, env/jwt.env, env/file-storage.env]
|
||||
volumes:
|
||||
# The image's own ImageMagick policy denies every coder it needs; see the
|
||||
# comment in the file. Without this the profile runs but produces nothing.
|
||||
- ./file-preview/policy.xml:/etc/ImageMagick-7/policy.xml:ro
|
||||
depends_on:
|
||||
rabbitmq: {condition: service_healthy}
|
||||
minio: {condition: service_healthy}
|
||||
|
||||
63
local-instance/file-preview/policy.xml
Normal file
63
local-instance/file-preview/policy.xml
Normal file
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE policymap [
|
||||
<!ELEMENT policymap (policy)*>
|
||||
<!ATTLIST policymap xmlns CDATA #FIXED "">
|
||||
<!ELEMENT policy EMPTY>
|
||||
<!ATTLIST policy xmlns CDATA #FIXED "">
|
||||
<!ATTLIST policy domain NMTOKEN #REQUIRED>
|
||||
<!ATTLIST policy name NMTOKEN #IMPLIED>
|
||||
<!ATTLIST policy pattern CDATA #IMPLIED>
|
||||
<!ATTLIST policy rights NMTOKEN #IMPLIED>
|
||||
<!ATTLIST policy stealth NMTOKEN #IMPLIED>
|
||||
<!ATTLIST policy value CDATA #IMPLIED>
|
||||
]>
|
||||
<policymap>
|
||||
<!-- Resource limits to prevent OOM based on 4000 MB memory from AMQP_FILE_PREVIEW_MEMORY_LIMITS used in
|
||||
https://github.com/hpi-schul-cloud/file-storage/blob/main/ansible/roles/file-storage/templates/preview-generator-deployment.yml.j2#L64-L68 -->
|
||||
<policy domain="resource" name="memory" value="3.5GiB"/>
|
||||
<policy domain="resource" name="map" value="3.5GiB"/>
|
||||
<policy domain="resource" name="area" value="1GB"/>
|
||||
<policy domain="resource" name="disk" value="2GiB"/>
|
||||
<policy domain="resource" name="width" value="16KP"/>
|
||||
<policy domain="resource" name="height" value="16KP"/>
|
||||
<policy domain="resource" name="time" value="60"/>
|
||||
<policy domain="resource" name="list-length" value="1024"/>
|
||||
<policy domain="resource" name="thread" value="4"/>
|
||||
|
||||
<!-- Security: Disable dangerous format handlers -->
|
||||
<policy domain="coder" rights="none" pattern="EPHEMERAL"/>
|
||||
<policy domain="coder" rights="none" pattern="URL"/>
|
||||
<policy domain="coder" rights="none" pattern="HTTPS"/>
|
||||
<policy domain="coder" rights="none" pattern="MVG"/>
|
||||
<policy domain="coder" rights="none" pattern="MSL"/>
|
||||
<policy domain="coder" rights="none" pattern="PS"/>
|
||||
<policy domain="coder" rights="none" pattern="EPS"/>
|
||||
<policy domain="coder" rights="none" pattern="LABEL"/>
|
||||
<policy domain="coder" rights="none" pattern="CAPTION"/>
|
||||
<policy domain="coder" rights="none" pattern="TEXT"/>
|
||||
<policy domain="coder" rights="none" pattern="DOT"/>
|
||||
<policy domain="coder" rights="none" pattern="PLT"/>
|
||||
<policy domain="coder" rights="none" pattern="HPGL"/>
|
||||
<policy domain="coder" rights="none" pattern="PCL"/>
|
||||
<policy domain="coder" rights="none" pattern="XPS"/>
|
||||
<policy domain="coder" rights="none" pattern="FIG"/>
|
||||
|
||||
<!-- Input formats.
|
||||
Upstream ships these as rights="read", which ImageMagick 7.1.2 — the
|
||||
version in this image — rejects at IsCoderAuthorized: every preview
|
||||
fails with "attempt to perform an operation not authorized by the
|
||||
security policy `PNG'" (or `PDF'), the record is flagged
|
||||
previewGenerationFailed, and /api/v3/file/preview answers 404
|
||||
PREVIEW_NOT_POSSIBLE even while the file record still reports
|
||||
previewStatus: preview_possible. Granting write as well is what makes
|
||||
the preview profile do anything at all here. -->
|
||||
<policy domain="coder" rights="read|write" pattern="JPEG"/>
|
||||
<policy domain="coder" rights="read|write" pattern="PNG"/>
|
||||
<policy domain="coder" rights="read|write" pattern="TIFF"/>
|
||||
<policy domain="coder" rights="read|write" pattern="HEIC"/>
|
||||
<policy domain="coder" rights="read|write" pattern="PDF"/>
|
||||
<policy domain="coder" rights="read|write" pattern="SVG"/>
|
||||
|
||||
<!-- Output format: READ + WRITE -->
|
||||
<policy domain="coder" rights="read|write" pattern="WEBP"/>
|
||||
</policymap>
|
||||
Reference in New Issue
Block a user