From 6989b78aa3d6c0e755037bb5c6c2bbbe330a970b Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Tue, 15 Oct 2024 14:39:11 +0200 Subject: [PATCH] New rule for derived motions (#1026) --- internal/restrict/collection/motion.go | 61 +++++++++++++++---- internal/restrict/collection/motion_state.go | 37 ++++++++++- .../restrict/collection/motion_state_test.go | 26 ++++++++ internal/restrict/collection/motion_test.go | 42 +++++++++++++ 4 files changed, 153 insertions(+), 13 deletions(-) diff --git a/internal/restrict/collection/motion.go b/internal/restrict/collection/motion.go index bf97282d..66559484 100644 --- a/internal/restrict/collection/motion.go +++ b/internal/restrict/collection/motion.go @@ -14,21 +14,27 @@ import ( // // The user can see a motion if: // -// The user has motion.can_see in the meeting, and -// For one `restriction` in the motion's state `state/restriction` field: -// If: `restriction` is `is_submitter`: The user needs to be a submitter of the motion -// Else: (a permission string): The user needs the permission -// And - for amendments (lead_motion_id != null) - the user can also see the lead motion. +// The user has motion.can_see in the meeting or +// for one of the motions in `motion/all_derived_motion_ids` the user has motion.can_see_origin in the corresponding meeting // -// Mode A: The user can see the motion or can see a referenced motion in motion/all_origin_ids and motion/all_derived_motion_ids. +// and for one `restriction` in the motion's state `state/restriction` field: +// If: `restriction` is `is_submitter`: The user needs to be a submitter of the motion +// Else: (a permission string): The user needs the permission // -// Mode B: The user has the permission motion.can_manage_metadata in the motion's meeting. +// and - for amendments (lead_motion_id != null) - the user can also see the lead motion. +// +// Mode A: The user can see the motion or can see a referenced motion in +// motion/all_origin_ids and motion/all_derived_motion_ids. +// +// Mode B: The user has the permission motion.can_manage_metadata in the +// motion's meeting. // // Mode C: The user can see the motion. // // Mode D: Never published to any user. // -// Mode E: If the motion states is_internal is true the user needs the permission motion.can_manage_metadata otherwise same as Mode C +// Mode E: If the motion states is_internal is true the user needs the +// permission motion.can_manage_metadata otherwise same as Mode C type Motion struct{} // Name returns the collection name. @@ -76,11 +82,44 @@ func (m Motion) see(ctx context.Context, ds *dsfetch.Fetch, motionIDs ...int) ([ return nil, fmt.Errorf("getting permissions: %w", err) } - if !perms.Has(perm.MotionCanSee) { - return nil, nil + step1Allowed, err := eachCondition(ids, func(id int) (bool, error) { + if perms.Has(perm.MotionCanSee) { + return true, nil + } + + derivedIDs, err := ds.Motion_AllDerivedMotionIDs(id).Value(ctx) + if err != nil { + return false, fmt.Errorf("fetching all_derived_ids: %w", err) + } + + meetingIDs := make([]int, len(derivedIDs)) + for i, derivedID := range derivedIDs { + ds.Motion_MeetingID(derivedID).Lazy(&meetingIDs[i]) + } + + if err := ds.Execute(ctx); err != nil { + return false, fmt.Errorf("fetching meeting ids from derived motions: %w", err) + } + + for _, meetingID := range meetingIDs { + perms, err := perm.FromContext(ctx, meetingID) + if err != nil { + return false, fmt.Errorf("getting permission from derived motion meeting: %w", err) + } + + if perms.Has(perm.MotionCanSeeOrigin) { + return true, nil + } + } + + return false, nil + + }) + if err != nil { + return nil, fmt.Errorf("checking condition 1: %w", err) } - return eachRelationField(ctx, ds.Motion_StateID, ids, func(stateID int, ids []int) ([]int, error) { + return eachRelationField(ctx, ds.Motion_StateID, step1Allowed, func(stateID int, ids []int) ([]int, error) { restrictions, err := ds.MotionState_Restrictions(stateID).Value(ctx) if err != nil { return nil, fmt.Errorf("getting restrictions: %w", err) diff --git a/internal/restrict/collection/motion_state.go b/internal/restrict/collection/motion_state.go index a1737d66..65949e91 100644 --- a/internal/restrict/collection/motion_state.go +++ b/internal/restrict/collection/motion_state.go @@ -10,7 +10,8 @@ import ( // MotionState handels restrictions of the collection motion_state. // -// The user can see a motion state if the user has motion.can_see. +// The user can see a motion state if the user has motion.can_see or see a +// motion in motion_state/motion_ids. // // Mode A: The user can see the motion state. type MotionState struct{} @@ -40,5 +41,37 @@ func (m MotionState) Modes(mode string) FieldRestricter { } func (m MotionState) see(ctx context.Context, ds *dsfetch.Fetch, motionStateIDs ...int) ([]int, error) { - return meetingPerm(ctx, ds, m, motionStateIDs, perm.MotionCanSee) + return eachMeeting(ctx, ds, m, motionStateIDs, func(meetingID int, motionStateIDs []int) ([]int, error) { + perms, err := perm.FromContext(ctx, meetingID) + if err != nil { + return nil, fmt.Errorf("getting permission: %w", err) + } + + if perms.Has(perm.MotionCanSee) { + return motionStateIDs, nil + } + + motionIDsList := make([][]int, len(motionStateIDs)) + for i, id := range motionStateIDs { + ds.MotionState_MotionIDs(id).Lazy(&motionIDsList[i]) + } + if err := ds.Execute(ctx); err != nil { + return nil, fmt.Errorf("fetching motion ids: %w", err) + } + + allowed := make([]int, 0, len(motionStateIDs)) + for i, motionIDs := range motionIDsList { + allowedMotions, err := Collection(ctx, Motion{}.Name()).Modes("C")(ctx, ds, motionIDs...) + if err != nil { + return nil, fmt.Errorf("check restriction of motions: %w", err) + } + + if len(allowedMotions) > 0 { + allowed = append(allowed, motionStateIDs[i]) + } + } + + return allowed, nil + }) + } diff --git a/internal/restrict/collection/motion_state_test.go b/internal/restrict/collection/motion_state_test.go index 3395cefe..6bdf7c5e 100644 --- a/internal/restrict/collection/motion_state_test.go +++ b/internal/restrict/collection/motion_state_test.go @@ -26,4 +26,30 @@ func TestMotionStateModeA(t *testing.T) { "motion_state/1/meeting_id: 30", withPerms(30, perm.MotionCanSee), ) + + testCase( + "can see motion", + t, + f, + true, + `--- + motion: + 1: + meeting_id: 30 + state_id: 3 + all_derived_motion_ids: [10] + + 10: + meeting_id: 31 + state_id: 7 + + motion_state/3: + meeting_id: 30 + motion_ids: [1] + + motion_state/7/id: 7 + `, + withPerms(31, perm.MotionCanSeeOrigin), + withElementID(3), + ) } diff --git a/internal/restrict/collection/motion_test.go b/internal/restrict/collection/motion_test.go index 8e18c2ea..45235194 100644 --- a/internal/restrict/collection/motion_test.go +++ b/internal/restrict/collection/motion_test.go @@ -37,6 +37,48 @@ func TestMotionModeC(t *testing.T) { withPerms(30, perm.MotionCanSee), ) + testCase( + "motion/all_derived_motion_ids but not motion.can_see_origin", + t, + f, + false, + `--- + motion: + 1: + meeting_id: 30 + state_id: 3 + all_derived_motion_ids: [10] + + 10: + meeting_id: 31 + state_id: 3 + + motion_state/3/id: 3 + `, + withPerms(31, perm.MotionCanSee), + ) + + testCase( + "motion/all_derived_motion_ids with motion.can_see_origin", + t, + f, + true, + `--- + motion: + 1: + meeting_id: 30 + state_id: 3 + all_derived_motion_ids: [10] + + 10: + meeting_id: 31 + state_id: 3 + + motion_state/3/id: 3 + `, + withPerms(31, perm.MotionCanSeeOrigin), + ) + testCase( "motion.can_see with restrict is_submitter", t,