Commit Graph
1348 Commits
Author SHA1 Message Date
Nathan Vasse 5947ccd3ea (frontend) disable the new document entries without upload rights
The backend now rejects the creation, and the 403 is already toasted,
but letting the user name a document only to have it refused is a poor
way to learn about a quota. Surface the reason on the entries instead.

Entitlements are assumed permissive while the query is in flight, so
the menu does not grey out on every page load. "New folder" stays
enabled since folders are not quota gated.
2026-09-15 18:02:24 +02:00
Nathan Vasse 981e86a600 🐛(backend) gate item creation at the root on the upload entitlement
The can_upload entitlement was only checked when creating a child, so
creating a file through POST /items/ escaped it entirely. The "+ New"
button takes that path whenever the current folder is not writable,
which is the case on "My files", so an over quota user could still
create documents from there.

Template based creation made it worse: it stores the file and marks the
item READY itself, so upload-ended, the only other gate, never runs.

Move the check into a helper shared by both create paths so they cannot
drift apart again.
2026-09-15 18:02:24 +02:00
Julien Maupetit 432c075f17 👷(backend) simplify drive backend flow
Avoid code duplication using raw docker from one side and docker compose
on another.
2026-09-15 14:13:41 +02:00
rcutte df7431698e 🧑‍💻(project) install frontend dependencies via container in bootstrap
In the default compose setup, .
  /src/frontend/node_modules and
    ./src/frontend/apps/drive/node_modules are bind-mounted to the host
    filesystem. If these directories are empty, mounting them into the
    frontend-dev container masks the dependencies built into the Docker
    image, causing runtime failures (e.g. missing pdfjs-dist/package.json).

    This commit:
    - Uses the frontend-dev container (FRONTEND_YARN) to install
      dependencies via 'yarn install --frozen-lockfile' without requiring
      host Node.js or Yarn.
    - Adds the containerized yarn install step directly into the
      build-frontend target, ensuring host node_modules are populated during
      image build / make bootstrap.
    - Leaves frontend-development-install untouched for host-only developers.

Signed-off-by: rcutte <rcutte@student.42.fr>
2026-09-14 18:11:15 +02:00
Nathan Vasse 3d441eecc1 (frontend) cover region-less browser languages end to end
Every e2e project forces an en-US locale, so nothing exercised a browser
announcing a language without a region. The new test walks to the page a
public share link opens on an office file, where the regression showed.
2026-09-14 14:58:15 +02:00
Nathan Vasse 5a0fd58e23 🐛(frontend) resolve region-less browser languages
Browsers may announce a language without a region: Firefox in French
sends "fr" where Chrome sends "fr-FR". Our resources were keyed by bare
language and we declared no supported languages, so i18next kept "fr"
as-is. The design system only ships "fr-FR" and fell back to English,
which is why the intermediate page of an office file opened from a
public link was in English while the interface was in French.

The same mismatch prevented the language from being synced to the
backend, so the online editor opened in English too.

Key the resources the way Django does and declare them as supported, so
any incoming tag resolves to a language we actually ship.
2026-09-14 14:58:15 +02:00
Nathan Vasse f904a6e34d ♻️(frontend) move the language list to the i18n config
The list of languages we ship lived in the header component, forcing a
hook and the anonymous user menu to import from a component. Declaring
it next to the i18n configuration gives a single place to add or remove
a language.

Deutsch is dropped: we ship no German translations, so selecting it left
the interface in English with the design system in German.

The picker now reads the active language from i18next rather than from
the user record, which stays null until the language is synced.
2026-09-14 14:58:15 +02:00
Nicolas Clerc c12dcb986f 🐛(docker) pull minio images from quay.io
MinIO stopped publishing images on Docker Hub in October 2025, so
minio/mc and minio/minio can no longer be pulled and createbuckets
fails. The same images are still served from quay.io.
2026-09-12 16:10:01 +02:00
Bolaji AyodejiandNathan Vasse 1fb5f6bc7a ✏️(docs) Add DPG badge to README
Add DPG badge for LaSuite Drive
2026-09-11 10:35:22 +02:00
Maarten DraijerandNathan Vasse bb35341ae1 🐛(frontend) refresh the Recent view after item mutations
useGetQueryKeyToRefresh invalidated only ["items", "infinite"] (+ the folder
["items", <parentId>, "children"]). React Query invalidation is prefix-based,
so that matches the general list views (My files, Shared, Favorites) but not
the Recent view, whose key is ["items", "recent", "infinite"]
(useInfiniteRecentItems). As a result, a mutation performed while viewing
Recent (create, convert, delete, rename, upload) did not refresh the list
until a manual reload. Also invalidate the recent key.
2026-09-11 09:52:22 +02:00
Julien Maupetit 424823e75a 🎨(project) improve Makefile rules dependencies usage
Invoking `$(MAKE) target-rule` in a rule definition where it can be used
as a rule dependency seems less canonical and decrease readability.
Let's fix this!
2026-09-10 15:50:13 +02:00
Julien Maupetit 935143501e 🧑‍💻(project) improve e2e tests environment
Running end-to-end tests locally can be a burden as there are many
services to start and configure. It can be improved by avoiding to
restart the whole stack at every test run.

We've also added documentation and fix permission issues regarding
volumes and locally created files and directories.
2026-09-10 15:50:13 +02:00
Julien Maupetit 8391798dc1 🔨(project) remove unused DB_HOST and DB_PORT env vars
They are not used in the Makefile and injected in the running container,
there is no need to declare them explicitly.
2026-09-09 11:07:24 +02:00
Julien Maupetit d314565644 🔨(project) improve psql script
Make psql script more versatile and avoid unnecessary environment
variables parsing.
2026-09-09 11:07:24 +02:00
Nicolas Clerc 058b9f139c 🔖(minor) bump release version 0.22.0
Added

- 📈(backend) add a Sentry performance monitoring sample rate setting
- ♻️(backend) route permission decisions through a swappable backend
- (backend) add restricted access on folders, detached behind a restriction

Changed

- ♻️(frontend) migrate to the merged @gouvfr-lasuite/ui-components package

Fixed

- 🐛(frontend) render the JPEG 2000 layers of scanned PDFs in the preview
- 🐛(backend) prevent item deletion by a creator whose access was revoked
- 🐛(backend) resolve the direct parent by exact path after a move
v0.22.0-preprod v0.22.0
2026-09-09 08:38:28 +02:00
Nicolas Clerc bf5b3e38ca 📝(docs) document the permissions backend settings
PERMISSIONS_BACKEND and PERMISSIONS_BACKEND_PARAMETERS were missing from
the environment variables reference.
2026-09-09 08:38:06 +02:00
Nathan Vasse 1b8060ed91 ⬆️(frontend) bump ui-components to 1.1.2
1.1.2 takes every calendar hook from @react-aria/calendar instead of
splitting them between that package and the react-aria umbrella. The
WeakMap those hooks share is no longer cut in two when the tree carries
more than one copy, so the date filter stops crashing without us having
to pin that dependency cluster ourselves.
2026-09-07 12:29:18 +02:00
Nathan Vasse de979cc085 📝(changelog) add the ui-components migration entries
Required by the check-changelog CI job.
2026-09-07 12:29:18 +02:00
Nathan Vasse e2b6a088f9 🐛(frontend) serve the pdf.js decoders, fonts and cmaps
ui-components 1.1.1 looks for them at the site root, and pdf.js skips
a missing one with a warning rather than failing, so scanned PDFs were
drawn from their JBIG2 mask alone, without the JPEG 2000 layers.

Copying from the pdfjs-dist that react-pdf resolves keeps them on the
worker version, and 3.2 MB of generated data out of the repository.
2026-09-07 12:29:18 +02:00
Nathan Vasse 3975a4dc06 ♻️(frontend) migrate to @gouvfr-lasuite/ui-components
UI Kit and Cunningham are now published as a single package, with no
compatibility release under the former names. The import rewrite is
the output of `ui-codemod@1.0.0 migrate . --source all`.

Material Icons used to ride along with the ui-kit fonts bundle, whose
replacement ships only the text fonts, so it now needs an explicit
import; without it every icon renders as a blank glyph.
2026-09-07 12:29:17 +02:00
Nicolas Clerc fbb76dc3fa (backend) exclude restrictions from search, export and indexing
Restrictions are tree entries, not content: they never match a search,
never reach the search index, and leave no entry in an exported
archive. The target itself is indexed and exported through its own
root, so users excluded from a restricted folder cannot find its
content through search or an ancestor export.
2026-09-04 15:29:18 +02:00
Nicolas Clerc 6b076b1c9c (backend) trash the restriction along with its restricted folder
An explicit owner trashing a restricted folder leaves no entry
pointing into the trash. Restoring the folder brings the restriction
back, unless the containing subtree went to the trash meanwhile. Hard
deleting the folder takes its restriction along. A restriction trashed
with its container follows the container's restore, its target staying
untouched at the tree root.
2026-09-04 15:29:18 +02:00
Nicolas Clerc 5050a361e1 (backend) detach restricted folders by deleting their restriction
Deleting a restriction removes the entry from the containing folder
without trashing anything. An owner excluded from the target acts on
the container only and can neither destroy nor read it: the folder
keeps its accesses and surfaces in its members' top-level listing as
a regular root folder.
2026-09-04 15:29:18 +02:00
Nicolas Clerc 6e8abd2188 (backend) hide reachable restricted roots from the top-level listing
A restricted folder lives at the tree root but its members reach it
through the restriction when they can open the containing folder. The
listing hides the root in that case so the folder shows up in a
single location, and keeps it for members without container access.
2026-09-04 15:29:18 +02:00
Nicolas Clerc cd3e4e4a0b (backend) expose restriction targets in the items API
Restrictions expose their target's id, title and a can_access flag so
the frontend can grey out entries pointing to folders the user
cannot open. The children listing prefetches the viewer accesses to
keep the query count flat, and the tree includes restriction entries.
2026-09-04 15:29:18 +02:00
Nicolas Clerc 140cc80338 (backend) expose the restricted state in the items API
Owners activate and deactivate restriction on folders through a
dedicated restrict action, gated by the restrict ability. The
serializer keeps working on the instance returned by the toggle
since the item physically moves.
2026-09-04 15:29:18 +02:00
Nicolas Clerc 766c616aaa (backend) normalize explicit link reach on restriction deactivation
The reach kept from the restriction period is reset to inherit when
the reattached parent already grants as much or more. A more open
explicit reach survives, matching the role normalization rule.
2026-09-04 15:29:18 +02:00
Nicolas Clerc 755d4394b1 (backend) normalize explicit accesses on restriction deactivation
Explicit roles granted during restriction that are now covered by
inheritance are dropped, so the sharing screen does not keep dead
entries. Superior roles and roles without inherited counterpart stay.
2026-09-04 15:29:18 +02:00
Nicolas Clerc 6d9ea1d5ba (backend) deactivate restriction by reattaching the folder
The folder returns under its restriction's current parent and the
restriction disappears. Without a live restriction the folder stays a
detached root. Inheritance applies again through the tree structure.
2026-09-04 15:29:18 +02:00
Nicolas Clerc f4161047da (backend) activate restriction by moving the folder to the tree root
Restriction is structural: the folder physically leaves its parent
so inheritance stops applying without any query-level cut. A
restriction materializes its origin location. Explicit link reach is
kept and defaults to restricted only when it was inherited. The acting
owner gets an explicit owner access so the folder stays reachable once
cut from its parent. A restricted folder cannot be moved, nor a
restriction under its own target.
2026-09-04 15:29:18 +02:00
Nicolas Clerc f59e11f685 (backend) add restrict ability with activation and deactivation states
Only an explicit owner can toggle restriction on a folder. A folder
needs a parent to host its restriction on activation, while an already
restricted folder lives at the tree root and must stay deactivatable.
Listings annotate the restriction presence so the ability stays free
of extra queries.
2026-09-04 15:29:18 +02:00
Nicolas Clerc eefffbad94 (backend) add restriction item type targeting another item
A restriction materializes the original location of a restricted
folder moved to the tree root. The OneToOne target enforces a
single restriction per folder and a DB constraint ties the target
to the restriction type. A folder is restricted while a restriction
targets it. Restrictions cannot be created through the API: they only
appear by restricting a folder.
2026-09-04 14:35:33 +02:00
Nicolas Clerc 53f3892849 🐛(backend) override parent() to resolve it by exact path
TreeModel.parent() uses .last() which follows Meta.ordering
(created_at). After a move(), the grandparent created later
is returned instead of the direct parent. The parent path is
known statically, so look it up by equality instead of
scanning ancestors.
2026-09-04 14:35:33 +02:00
Nicolas Clerc 091d1a207f (backend) tighten exception tests around raising calls
The soft delete assertion sat inside the pytest.raises block and
never ran, hiding a wrong expected message. Indexer error tests
now keep a single raising invocation inside the block so the
failure source is unambiguous. Flagged by Sonar on PR #756.
2026-09-04 14:35:33 +02:00
Nicolas Clerc 63d5be266e 🚨(backend) extract link ancestors validation into a helper
Sonar flags the cognitive complexity of LinkItemSerializer.validate.
Move the checks against the ancestors' link definition to a helper so
validate only handles the required field; behavior is unchanged.
2026-09-04 14:35:33 +02:00
Nicolas Clerc d5d49c7e8f ♻️(backend) split abilities into one method per ability
Each ability now reads as a named rule on ItemAbilities, fixing
the Sonar S3776 complexity of the former monolithic function.
Per-action methods also sketch the vocabulary a future ABAC
engine will implement, one check per action.
2026-09-04 14:35:33 +02:00
Nicolas Clerc 1b4b756917 ♻️(backend) move abilities computation to the permissions backend
Pure move of Item.get_abilities into the role backend so every
permission decision sits behind the same facade.
2026-09-04 14:35:33 +02:00
Nicolas Clerc 6f5de716bc ♻️(backend) extract role resolution into a permissions backend
Move role and access resolution behind a backend resolved from
the new PERMISSIONS_BACKEND setting, following the entitlements
backend convention. Behavior is unchanged; this gives a single
decision point to swap for an ABAC engine later.
2026-09-04 14:35:32 +02:00
Nicolas Clerc 5f19d68de6 🔧(dev) align demo passwords with keycloak realm
Allow login with demo users in local development. Fix Paige Turner's
email and replace the Wade Wilson demo user with Mark Down so every
demo user exists in the realm.
2026-09-04 14:35:32 +02:00
Nathan VasseandNicolas Clerc ea45ff85a5 📝(changelog) add the load-testing suite entry
Required by the check-changelog CI job.
2026-09-03 13:53:36 +02:00
Nathan VasseandNicolas Clerc 8c9f2a497f (load-tests) add a read-heavy scenario variant
The mixed scenario writes on every session, while real drive traffic is
massively read-dominant. This variant keeps the same building blocks but
only UPLOAD_PCT percent of sessions (15 by default) go through the write
path; every session browses root items, recents, shared-with-me and
navigates into a randomly picked folder and subfolder.

To make reads meaningful despite empty fresh accounts, the first session
of each user always uploads the fixture folder tree and keeps it for the
whole run. Seed folders are left behind on purpose: the target instance
is reset by ops (database and bucket) between test campaigns.
2026-09-03 13:53:35 +02:00
Nathan VasseandNicolas Clerc 9084163ed9 (load-tests) add a JMeter basic user session scenario
We need to measure backend response times and error codes from 100 up to
100k simultaneous users, on any instance. The scenario simulates a full
user session (browse, recents, shared-with-me, folder creation, upload,
trash, hard-delete) with randomized think times, using the e2e auth
endpoint instead of the IDP, out of scope like the WOPI editors.

Everything is property-driven (BASE_URL, USERS, USER_OFFSET...) so the
same plan runs unchanged against any instance and across distributed
injectors, each with its own USER_OFFSET to keep generated user emails
globally unique. Uploads reproduce the frontend flow (item creation,
presigned PUT to the object storage, upload-ended) and randomly pick a
fixture entry; folder entries mirror the frontend folder upload by
materializing the hierarchy before uploading each file into its parent.
2026-09-03 13:53:35 +02:00
Nathan VasseandNicolas Clerc 1d8a6d6e52 (backend) add a LoadTest configuration for load testing
Load-test scenarios authenticate through the e2e user-auth endpoint, which
must be available on a production-like instance without enabling DEBUG.
LOAD_E2E_URLS is kept as a plain literal so no environment variable can
ever flip it, and post_setup refuses to start any configuration outside an
explicit allowlist that would expose it. Deploying with
DJANGO_CONFIGURATION=LoadTest becomes the only way to expose these
endpoints on a Production-derived setup, making it a deliberate and
reviewable infrastructure decision.
2026-09-03 13:53:35 +02:00
Nicolas Clerc 842624aa08 📈(backend) add Sentry performance monitoring sample rate
Expose SENTRY_TRACES_SAMPLE_RATE to enable Sentry tracing.
Defaults to 0 so existing deployments are unaffected; production
can opt in with a low sampling ratio to monitor performance.
2026-08-31 11:17:25 +02:00
Nathan Vasse 0e83bd53a7 (backend) align downgraded creator abilities with new delete rule
Since delete by the creator is now gated on holding editor access,
a creator downgraded to reader on the root loses destroy and
hard_delete on the sub items they created. Update the stale
expectations and fix ruff formatting on the factory call.
2026-08-31 10:17:48 +02:00
Nathan Vasse b1bcb1181d (backend) drop meaningless count assertion in delete test
The delete endpoint performs a soft delete, so Item.objects.count()
never changes whether the request succeeds or fails. The assertion
gave a false sense of protection; the deleted_at check is the one
that actually verifies the item was not touched.
2026-08-31 10:02:53 +02:00
Nathan Vasse ff8b8960bb 🐛(backend) block delete by creator without access
Deleting an item relied on the creator field alone, which is an
immutable historical reference. A user could still delete an item
they created inside a shared folder after their access was revoked,
wiping the whole subtree including the owner's content.

Gate the creator branch on holding editor access so the ability is
lost as soon as access is revoked, matching the update permission.
2026-08-28 12:22:31 +02:00
Nicolas Clerc 791ceca3de 🔖(patch) bump release version 0.21.2
Added

- 🔧(helm) schedule the malware detection reconciliation commands
- (admin) add actions to abandon malware analyses

Fixed

- 🐛(backend) fix malware analysis processing slots exhaustion
v0.21.2-preprod v0.21.2
2026-08-25 17:12:36 +02:00
Nicolas Clerc 036f41795c ⬆️(backend) update django-lasuite to 0.0.29
Exhausted polling retries and duplicate detection paths no longer
leak processing slots nor deadlock the analysis queue.
2026-08-25 16:31:17 +02:00
Nicolas Clerc 34e769eea3 (admin) add actions to abandon malware analyses
Let admins drop stuck detections and force their items to a final
upload state without a database shell.
2026-08-25 16:31:17 +02:00