Feat/auth image collections [OWTR #1] - #548
Open
JohanHjelsethStorstad wants to merge 87 commits into
Open
Conversation
…geUploadData for reuse
…tter reflect that they are not cms but implementation, just like /services folder does
…alCollections from a seperate file
…er should control this field never an outside user
Member
Author
|
I suggest someone to user-test this that the visibility authoring makes sense |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Image system rewrite: special/dynamic collections + double-level visibility
This is PR # 1 in the one week till realese series. These should be merged sequentially
Rewrites the image service from a single flat "images" service into a
sub-service with per-domain implementations, and puts every dynamic image
collection behind a two-level visibility system (who may see it, who may
administrate it) with a full admin UI.
main..HEADis 82 commits / 374 files. This is a large refactor — the sectionsbelow are ordered roughly by how much reviewer attention they need.
1. Image service: one service → sub-service + implementations
The old
src/services/images/{actions,operations,schemas,auth,types,collections/}is deleted and replaced by:
images/subservice/uploadImage,updateCollection,destroyCollection,readPageOfImagesInCollection, …)images/subservice/special/implement.tsimplementSpecialCollection()— how a domain service claims oneSpecialCollectionand gets typed operations for itimages/dynamic/images/standard/STANDARDIMAGEScollection +readStandardImage/ regeneration-from-configimages/specialPanels/Domain services now own their own image collection rather than reaching into a
shared one —
users(PROFILE_IMAGES),ombul(OMBULCOVERS),committees(COMMITTEELOGOS),
flairs(FLAIRIMAGES).Schema:
Flair.imageandCommittee.logoImagenow point atImageinsteadof
CmsImage.Article/ArticleCategory/NewsArticlemoved out of the CMSschema file to reflect that they are implementations, not CMS primitives.
Standard images are no longer seeded rows that can drift: each is declared in
StandardImageConfigwith a source file in the new top-levelstandard_store/,and
readStandardImageregenerates it from config if it is missing or hasescaped the standard collection. The
SpecialCmsImageenum shrank accordingly(
FRONTPAGE_LOGO,NOT_FOUND,LOADER_IMAGE, nav/footer buttons … are gone —those are standard images now).
Store (
src/services/store/→src/lib/store/) became a factory,implementStore(), so each service gets a namespaced store with its own allowedextensions — and deleting an image now actually deletes the files.
2. Double-level visibility
Visibilityis attached twice to everyImageCollection(
visibilityRegularId/visibilityAdminId).implementDoubleLevelVisibilityOperations()gives an owning service areadDoubleLevelMatrixplusupdateRegularLevel/updateAdminLevel, each withits own authorizer and an ownership check that a passed
visibilityIdreally isthat owner's level.
New authorizer
RequireLevelFromDoubleLevelVisibility(level: REGULAR | ADMIN,with an optional bypass permission —
IMAGE_ADMINfor images).Invariant: the admin level must always be a sub-visibility of the regular
level; an administrator who cannot see what they administrate is a broken state.
Enforced via
isSubVisibilityon the matrix the update would produce, so it ischecked before anything is written and either level can still be updated alone.
beforeRun(framework): enforcing this needed a hook, so.implement()gainedan optional
beforeRun?: BeforeRun<…>taking the same args asauthorizer/ownershipCheck. It runs after auth and ownership, before the operation, andthrows to abort. This is the general place for implementer invariants a
sub-operation cannot state on its own —
ownershipCheckstays for "does thisimplementer own the resource".
Bug fix:
visibilityOperations.updatecalledomegaOrderOperations.readCurrent({})without bypassing auth. That order is only a placeholder stored on
ACTIVEconditions, but requiring the caller to also hold
OMEGA_ORDER_READ— not adefault or membership permission — meant essentially nobody could save a
visibility change. Now
{ bypassAuth: true }, matching every other internalreadCurrentcall.3. Frontend
VisibilityAdmin— new editor for one matrix: requirements (ANDed) eachholding conditions (ORed),
ACTIVEvsORDERper condition, group/order pickers.CollectionAdminmounts it twice, once per level.DoubleLevelVisibilityDescription— human-readable "Kan se / Kan administrere",shown on the collection page. Generic over any double-level service, grouped with
VisibilityAdminunder_components/Visibility/.CollectionAdminruns one authorizer per action (upload-one, upload-many,update, destroy, update-regular, update-admin) instead of gating the whole panel
on
updateCollection. The count of auth checks now matches the count of actions.page.tsxand threaded down. Ifthat read fails it becomes
nulland the visibility button is simply hidden(rather than 404-ing the page); the remaining checks fall closed against an
unsatisfiable placeholder, so the
IMAGE_ADMINbypass still works.ImageList→ImagePanel, which serves both special and dynamic collections;CollectionCardno longer forces being a link (newCollectionCardLink);new
StandardImageServer/StandardImageClient; newClientDataproviderreplaces
DefaultPermissionsand the image-selection contexts.ImageUploaderis now just aForm. Callers decide on popup vs inline —committee logos and ombul covers render it beside the current image,
EditOverlayis no longer used for special-collection uploads, and
titleis caller-supplied.Flaircomponent into a button inthe
/admin/flairslist. (Flairhad briefly become a client component receivinga
Sessionclass instance as a prop, which crashes RSC serialization.)secondarymatched thepage background), and collection cards collapsing horizontally in the CMS image
editor (missing
flex-shrink: 0).4. Shared utils
Pulled out of services into
src/lib/groups/so they can be reused and so none ofthem throw:
inferGroupName,checkGroupValidity,groupOptions(orderOptions/findGroup).checkGroupValidityno longer throws — it returns{ valid: true, group } | { valid: false }. The throwing behaviour lives inassertGroupValidityin the service layer, which all 5 existing call sites use.orderOptionsdeduplicates the identical order-range logic thatVisibilityAdminand
UserListeach had.describeMatrixmoved toauth/visibility/next tocheckVisibility/isSubVisibility.5. Seeding
Migrated to
defineSeedOperation(context-aware, so seeders stop hand-threadingprisma/session). Fixes a standard-image race on seed, stops logging expected
NOT-FOUNDs during upsert, and makes the OmegaWeb migration use the new image system.
6. Tests
New
tests/services/visibility.test.ts(29 tests) andtests/services/dynamicImages.test.ts(21 tests) — 50 total, all passing.checkVisibility(AND across requirements, OR within one,ACTIVE vs ORDER, empty = everyone) and
isSubVisibility.visibilityOperationscreate/update/destroy: update replaces rather thanappends, ORDER conditions keep their order, cascade on destroy.
implementDoubleLevelVisibilityOperationswith no owning domain model — itsimplementationParams are just the two visibility ids. Covers both level updates,
the per-level authorizers, the ownership check (wrong level / another owner's
visibility →
DISSALLOWED), and the sub/super invariant in both directions.gates updating/destroying,
IMAGE_ADMINbypasses both, the paging filter hidescollections the session may not see,
showOnlyCollectionsSessionAdministratesfilters on the admin level, and special collections stay unreachable through the
dynamic service.