fix(course-edit): additional permission checks wrt allocations
This commit is contained in:
parent
248482b1bb
commit
fca5caaa31
@ -172,6 +172,10 @@ CourseLecturerAlreadyAdded email@UserEmail: Es gibt bereits einen Kursverwalter
|
|||||||
CourseRegistrationEndMustBeAfterStart: Ende des Anmeldezeitraums muss nach dem Anfang liegen
|
CourseRegistrationEndMustBeAfterStart: Ende des Anmeldezeitraums muss nach dem Anfang liegen
|
||||||
CourseDeregistrationEndMustBeAfterStart: Ende des Abmeldezeitraums muss nach dem Anfang des Anmeldezeitraums liegen
|
CourseDeregistrationEndMustBeAfterStart: Ende des Abmeldezeitraums muss nach dem Anfang des Anmeldezeitraums liegen
|
||||||
CourseUserMustBeLecturer: Aktueller Benutzer muss als Kursverwalter eingetragen sein
|
CourseUserMustBeLecturer: Aktueller Benutzer muss als Kursverwalter eingetragen sein
|
||||||
|
CourseAllocationRequiresCapacity: Bei Teilnahme an einer Zentralanmeldung muss eine Kurskapazität angegeben werden
|
||||||
|
CourseAllocationTermMustMatch: Kurs-Semester muss mit Semester der Zentralanmeldung übereinstimmen
|
||||||
|
CourseAllocationCapacityMayNotBeChanged: Kapazität eines Kurses, der an einer Zentralanmeldung teilnimmt, darf nicht nachträglich verändert werden
|
||||||
|
|
||||||
CourseLecturerRightsIdentical: Alle Sorten von Kursverwalter haben identische Rechte.
|
CourseLecturerRightsIdentical: Alle Sorten von Kursverwalter haben identische Rechte.
|
||||||
|
|
||||||
NoSuchTerm tid@TermId: Semester #{tid} gibt es nicht.
|
NoSuchTerm tid@TermId: Semester #{tid} gibt es nicht.
|
||||||
@ -341,6 +345,7 @@ UnauthorizedPasswordResetToken: Dieses Authorisierungs-Token kann nicht mehr zum
|
|||||||
|
|
||||||
UnauthorizedAllocatedCourseRegister: Direkte Anmeldungen zum Kurs sind aufgrund einer Zentralanmeldung aktuell nicht gestattet
|
UnauthorizedAllocatedCourseRegister: Direkte Anmeldungen zum Kurs sind aufgrund einer Zentralanmeldung aktuell nicht gestattet
|
||||||
UnauthorizedAllocatedCourseDeregister: Abmeldungen vom Kurs sind aufgrund einer Zentralanmeldung aktuell nicht gestattet
|
UnauthorizedAllocatedCourseDeregister: Abmeldungen vom Kurs sind aufgrund einer Zentralanmeldung aktuell nicht gestattet
|
||||||
|
UnauthorizedAllocatedCourseDelete: Kurse, die an einer Zentralanmeldung teilnehmen, dürfen nicht gelöscht werden
|
||||||
|
|
||||||
EMail: E-Mail
|
EMail: E-Mail
|
||||||
EMailUnknown email@UserEmail: E-Mail #{email} gehört zu keinem bekannten Benutzer.
|
EMailUnknown email@UserEmail: E-Mail #{email} gehört zu keinem bekannten Benutzer.
|
||||||
|
|||||||
2
routes
2
routes
@ -89,7 +89,7 @@
|
|||||||
/register CRegisterR GET POST !timeANDcapacityANDallocation-time !lecturerANDallocation-time
|
/register CRegisterR GET POST !timeANDcapacityANDallocation-time !lecturerANDallocation-time
|
||||||
/edit CEditR GET POST
|
/edit CEditR GET POST
|
||||||
/lecturer-invite CLecInviteR GET POST
|
/lecturer-invite CLecInviteR GET POST
|
||||||
/delete CDeleteR GET POST !lecturerANDempty
|
/delete CDeleteR GET POST !lecturerANDemptyANDallocation-time
|
||||||
/users CUsersR GET POST
|
/users CUsersR GET POST
|
||||||
!/users/new CAddUserR GET POST !lecturerANDallocation-time
|
!/users/new CAddUserR GET POST !lecturerANDallocation-time
|
||||||
!/users/invite CInviteR GET POST
|
!/users/invite CInviteR GET POST
|
||||||
|
|||||||
@ -820,10 +820,20 @@ tagAccessPredicate AuthAllocationTime = APDB $ \mAuthId route _ -> case route of
|
|||||||
mba <- mbAllocation tid ssh csh
|
mba <- mbAllocation tid ssh csh
|
||||||
case mba of
|
case mba of
|
||||||
Just (_, Allocation{..})
|
Just (_, Allocation{..})
|
||||||
| NTop allocationRegisterByStaffTo <= NTop (Just now)
|
| NTop allocationStaffRegisterTo <= NTop (Just now)
|
||||||
, NTop allocationRegisterByStaffFrom >= NTop (Just now)
|
|| NTop allocationStaffRegisterFrom >= NTop (Just now)
|
||||||
-> unauthorizedI MsgUnauthorizedAllocatedCourseRegister
|
-> unauthorizedI MsgUnauthorizedAllocatedCourseRegister
|
||||||
_other -> return Authorized
|
_other -> return Authorized
|
||||||
|
|
||||||
|
CourseR tid ssh csh CDeleteR -> do
|
||||||
|
now <- liftIO getCurrentTime
|
||||||
|
mba <- mbAllocation tid ssh csh
|
||||||
|
case mba of
|
||||||
|
Just (_, Allocation{..})
|
||||||
|
| NTop allocationRegisterByStaffTo <= NTop (Just now)
|
||||||
|
|| NTop allocationRegisterByStaffFrom >= NTop (Just now)
|
||||||
|
-> unauthorizedI MsgUnauthorizedAllocatedCourseDelete
|
||||||
|
_other -> return Authorized
|
||||||
|
|
||||||
r -> $unsupportedAuthPredicate AuthAllocationTime r
|
r -> $unsupportedAuthPredicate AuthAllocationTime r
|
||||||
where
|
where
|
||||||
|
|||||||
@ -51,12 +51,12 @@ data CourseForm = CourseForm
|
|||||||
|
|
||||||
data AllocationCourseForm = AllocationCourseForm
|
data AllocationCourseForm = AllocationCourseForm
|
||||||
{ acfAllocation :: AllocationId
|
{ acfAllocation :: AllocationId
|
||||||
, acfMinCapacity :: Int
|
|
||||||
, acfInstructions :: Maybe Html
|
, acfInstructions :: Maybe Html
|
||||||
, acfFiles :: Maybe (Source Handler (Either FileId File))
|
, acfFiles :: Maybe (Source Handler (Either FileId File))
|
||||||
, acfApplicationText :: Bool
|
, acfApplicationText :: Bool
|
||||||
, acfApplicationFiles :: UploadMode
|
, acfApplicationFiles :: UploadMode
|
||||||
, acfApplicationRatingsVisible :: Bool
|
, acfApplicationRatingsVisible :: Bool
|
||||||
|
, acfMinCapacity :: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
courseToForm :: Entity Course -> [Lecturer] -> [(UserEmail, InvitationDBData Lecturer)] -> Maybe (Entity AllocationCourse) -> CourseForm
|
courseToForm :: Entity Course -> [Lecturer] -> [(UserEmail, InvitationDBData Lecturer)] -> Maybe (Entity AllocationCourse) -> CourseForm
|
||||||
@ -223,12 +223,12 @@ makeCourseForm miButtonAction template = identifyForm FIDcourse $ \html -> do
|
|||||||
let
|
let
|
||||||
allocationForm' = AllocationCourseForm
|
allocationForm' = AllocationCourseForm
|
||||||
<$> apreq (selectField' Nothing $ return allocationOptions) (fslI MsgCourseAllocation) (fmap acfAllocation $ template >>= cfAllocation)
|
<$> apreq (selectField' Nothing $ return allocationOptions) (fslI MsgCourseAllocation) (fmap acfAllocation $ template >>= cfAllocation)
|
||||||
<*> apreq (natFieldI MsgCourseAllocationMinCapacityMustBeNonNegative) (fslI MsgCourseAllocationMinCapacity & setTooltip MsgCourseAllocationMinCapacityTip) (fmap acfMinCapacity $ template >>= cfAllocation)
|
|
||||||
<*> (assertM (not . null . renderHtml) <$> aopt htmlField (fslI MsgCourseAllocationInstructions & setTooltip MsgCourseAllocationInstructionsTip) (fmap acfInstructions $ template >>= cfAllocation))
|
<*> (assertM (not . null . renderHtml) <$> aopt htmlField (fslI MsgCourseAllocationInstructions & setTooltip MsgCourseAllocationInstructionsTip) (fmap acfInstructions $ template >>= cfAllocation))
|
||||||
<*> aopt (multiFileField . return $ fromMaybe Set.empty oldFileIds) (fslI MsgCourseAllocationApplicationTemplate) (fmap acfFiles $ template >>= cfAllocation)
|
<*> aopt (multiFileField . return $ fromMaybe Set.empty oldFileIds) (fslI MsgCourseAllocationApplicationTemplate) (fmap acfFiles $ template >>= cfAllocation)
|
||||||
<*> apopt checkBoxField (fslI MsgCourseAllocationApplicationText & setTooltip MsgCourseAllocationApplicationTextTip) (fmap acfApplicationText $ template >>= cfAllocation)
|
<*> apopt checkBoxField (fslI MsgCourseAllocationApplicationText & setTooltip MsgCourseAllocationApplicationTextTip) (fmap acfApplicationText $ template >>= cfAllocation)
|
||||||
<*> uploadModeForm (fmap acfApplicationFiles $ template >>= cfAllocation)
|
<*> uploadModeForm (fmap acfApplicationFiles $ template >>= cfAllocation)
|
||||||
<*> apopt checkBoxField (fslI MsgCourseAllocationApplicationRatingsVisible & setTooltip MsgCourseAllocationApplicationRatingsVisibleTip) (fmap acfApplicationRatingsVisible $ template >>= cfAllocation)
|
<*> apopt checkBoxField (fslI MsgCourseAllocationApplicationRatingsVisible & setTooltip MsgCourseAllocationApplicationRatingsVisibleTip) (fmap acfApplicationRatingsVisible $ template >>= cfAllocation)
|
||||||
|
<*> apreq (natFieldI MsgCourseAllocationMinCapacityMustBeNonNegative) (fslI MsgCourseAllocationMinCapacity & setTooltip MsgCourseAllocationMinCapacityTip) (fmap acfMinCapacity $ template >>= cfAllocation)
|
||||||
|
|
||||||
optionalActionW allocationForm' (fslI MsgCourseAllocationParticipate & setTooltip MsgCourseAllocationParticipateTip) (is _Just . cfAllocation <$> template)
|
optionalActionW allocationForm' (fslI MsgCourseAllocationParticipate & setTooltip MsgCourseAllocationParticipateTip) (is _Just . cfAllocation <$> template)
|
||||||
|
|
||||||
@ -279,9 +279,24 @@ makeCourseForm miButtonAction template = identifyForm FIDcourse $ \html -> do
|
|||||||
|
|
||||||
validateCourse :: (MonadHandler m, HandlerSite m ~ UniWorX) => CourseForm -> m [Text]
|
validateCourse :: (MonadHandler m, HandlerSite m ~ UniWorX) => CourseForm -> m [Text]
|
||||||
validateCourse CourseForm{..} = do
|
validateCourse CourseForm{..} = do
|
||||||
|
now <- liftIO getCurrentTime
|
||||||
uid <- liftHandlerT requireAuthId
|
uid <- liftHandlerT requireAuthId
|
||||||
userAdmin <- liftHandlerT . runDB . getBy $ UniqueUserAdmin uid cfSchool -- FIXME: This /needs/ to be a call to `isAuthorized` on a route
|
userAdmin <- liftHandlerT . runDB . getBy $ UniqueUserAdmin uid cfSchool -- FIXME: This /needs/ to be a call to `isAuthorized` on a route
|
||||||
MsgRenderer mr <- getMsgRenderer
|
MsgRenderer mr <- getMsgRenderer
|
||||||
|
allocationTerm <- for (acfAllocation <$> cfAllocation) $ fmap allocationTerm . liftHandlerT . runDB . getJust
|
||||||
|
|
||||||
|
oldAllocatedCapacity <- fmap join . for cfCourseId $ \cid -> liftHandlerT . runDB $ do
|
||||||
|
prevAllocationCourse <- getBy $ UniqueAllocationCourse cid
|
||||||
|
prevAllocation <- fmap join . traverse get $ allocationCourseAllocation . entityVal <$> prevAllocationCourse
|
||||||
|
|
||||||
|
fmap join . for prevAllocation $ \Allocation{allocationStaffRegisterTo} -> if
|
||||||
|
| is _Just userAdmin
|
||||||
|
-> return Nothing
|
||||||
|
| NTop allocationStaffRegisterTo <= NTop (Just now)
|
||||||
|
-> Just . courseCapacity <$> getJust cid
|
||||||
|
| otherwise
|
||||||
|
-> return Nothing
|
||||||
|
|
||||||
|
|
||||||
return
|
return
|
||||||
[ mr msg | (False, msg) <-
|
[ mr msg | (False, msg) <-
|
||||||
@ -296,6 +311,15 @@ validateCourse CourseForm{..} = do
|
|||||||
, ( maybe (anyOf (traverse . _Right . _1) (== uid) cfLecturers) (\(Entity _ UserAdmin{}) -> True) userAdmin
|
, ( maybe (anyOf (traverse . _Right . _1) (== uid) cfLecturers) (\(Entity _ UserAdmin{}) -> True) userAdmin
|
||||||
, MsgCourseUserMustBeLecturer
|
, MsgCourseUserMustBeLecturer
|
||||||
)
|
)
|
||||||
|
, ( is _Nothing cfAllocation || is _Just cfCapacity
|
||||||
|
, MsgCourseAllocationRequiresCapacity
|
||||||
|
)
|
||||||
|
, ( maybe True (== cfTerm) allocationTerm
|
||||||
|
, MsgCourseAllocationTermMustMatch
|
||||||
|
)
|
||||||
|
, ( maybe True (== cfCapacity) oldAllocatedCapacity
|
||||||
|
, MsgCourseAllocationCapacityMayNotBeChanged
|
||||||
|
)
|
||||||
] ]
|
] ]
|
||||||
|
|
||||||
|
|
||||||
@ -481,49 +505,55 @@ courseEditHandler miButtonAction mbCourseForm = do
|
|||||||
upsertAllocationCourse :: (MonadHandler m, HandlerSite m ~ UniWorX) => CourseId -> Maybe AllocationCourseForm -> ReaderT SqlBackend m ()
|
upsertAllocationCourse :: (MonadHandler m, HandlerSite m ~ UniWorX) => CourseId -> Maybe AllocationCourseForm -> ReaderT SqlBackend m ()
|
||||||
upsertAllocationCourse cid cfAllocation = do
|
upsertAllocationCourse cid cfAllocation = do
|
||||||
now <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
|
uid <- liftHandlerT requireAuthId
|
||||||
|
Course{..} <- getJust cid
|
||||||
prevAllocationCourse <- getBy $ UniqueAllocationCourse cid
|
prevAllocationCourse <- getBy $ UniqueAllocationCourse cid
|
||||||
prevAllocation <- fmap join . traverse get $ allocationCourseAllocation . entityVal <$> prevAllocationCourse
|
prevAllocation <- fmap join . traverse get $ allocationCourseAllocation . entityVal <$> prevAllocationCourse
|
||||||
|
userAdmin <- liftHandlerT . runDB . getBy $ UniqueUserAdmin uid courseSchool -- FIXME: This /needs/ to be a call to `isAuthorized` on a route
|
||||||
|
|
||||||
if -- TODO: loophole for admins
|
doEdit <- if
|
||||||
|
| is _Just userAdmin
|
||||||
|
-> return True
|
||||||
| Just Allocation{allocationStaffRegisterTo} <- prevAllocation
|
| Just Allocation{allocationStaffRegisterTo} <- prevAllocation
|
||||||
, NTop allocationStaffRegisterTo <= NTop (Just now)
|
, NTop allocationStaffRegisterTo <= NTop (Just now)
|
||||||
-> permissionDeniedI MsgAllocationStaffRegisterToExpired
|
-> False <$ addMessageI Error MsgAllocationStaffRegisterToExpired
|
||||||
| otherwise
|
| otherwise
|
||||||
-> return ()
|
-> return True
|
||||||
|
|
||||||
case cfAllocation of
|
|
||||||
Just AllocationCourseForm{..} -> do
|
|
||||||
Entity acId _ <- upsert AllocationCourse
|
|
||||||
{ allocationCourseAllocation = acfAllocation
|
|
||||||
, allocationCourseCourse = cid
|
|
||||||
, allocationCourseMinCapacity = acfMinCapacity
|
|
||||||
, allocationCourseInstructions = acfInstructions
|
|
||||||
, allocationCourseApplicationText = acfApplicationText
|
|
||||||
, allocationCourseApplicationFiles = acfApplicationFiles
|
|
||||||
, allocationCourseRatingsVisible = acfApplicationRatingsVisible
|
|
||||||
}
|
|
||||||
[ AllocationCourseAllocation =. acfAllocation
|
|
||||||
, AllocationCourseCourse =. cid
|
|
||||||
, AllocationCourseMinCapacity =. acfMinCapacity
|
|
||||||
, AllocationCourseInstructions =. acfInstructions
|
|
||||||
, AllocationCourseApplicationText =. acfApplicationText
|
|
||||||
, AllocationCourseApplicationFiles =. acfApplicationFiles
|
|
||||||
, AllocationCourseRatingsVisible =. acfApplicationRatingsVisible
|
|
||||||
]
|
|
||||||
|
|
||||||
let
|
when doEdit $
|
||||||
finsert val = do
|
case cfAllocation of
|
||||||
fId <- lift $ either return insert val
|
Just AllocationCourseForm{..} -> do
|
||||||
tell $ Set.singleton fId
|
Entity acId _ <- upsert AllocationCourse
|
||||||
lift $
|
{ allocationCourseAllocation = acfAllocation
|
||||||
void . insertUnique $ AllocationCourseFile acId fId
|
, allocationCourseCourse = cid
|
||||||
keep <- execWriterT . runConduit $ transPipe liftHandlerT (traverse_ id acfFiles) .| C.mapM_ finsert
|
, allocationCourseMinCapacity = acfMinCapacity
|
||||||
acfs <- selectList [ AllocationCourseFileAllocationCourse ==. acId, AllocationCourseFileFile /<-. Set.toList keep ] []
|
, allocationCourseInstructions = acfInstructions
|
||||||
mapM_ deleteCascade $ map (allocationCourseFileFile . entityVal) acfs
|
, allocationCourseApplicationText = acfApplicationText
|
||||||
Nothing
|
, allocationCourseApplicationFiles = acfApplicationFiles
|
||||||
| Just (Entity prevId _) <- prevAllocationCourse
|
, allocationCourseRatingsVisible = acfApplicationRatingsVisible
|
||||||
-> do
|
}
|
||||||
acfs <- selectList [ AllocationCourseFileAllocationCourse ==. prevId ] []
|
[ AllocationCourseAllocation =. acfAllocation
|
||||||
mapM_ deleteCascade $ map (allocationCourseFileFile . entityVal) acfs
|
, AllocationCourseCourse =. cid
|
||||||
delete prevId
|
, AllocationCourseMinCapacity =. acfMinCapacity
|
||||||
_other -> return ()
|
, AllocationCourseInstructions =. acfInstructions
|
||||||
|
, AllocationCourseApplicationText =. acfApplicationText
|
||||||
|
, AllocationCourseApplicationFiles =. acfApplicationFiles
|
||||||
|
, AllocationCourseRatingsVisible =. acfApplicationRatingsVisible
|
||||||
|
]
|
||||||
|
|
||||||
|
let
|
||||||
|
finsert val = do
|
||||||
|
fId <- lift $ either return insert val
|
||||||
|
tell $ Set.singleton fId
|
||||||
|
lift $
|
||||||
|
void . insertUnique $ AllocationCourseFile acId fId
|
||||||
|
keep <- execWriterT . runConduit $ transPipe liftHandlerT (traverse_ id acfFiles) .| C.mapM_ finsert
|
||||||
|
acfs <- selectList [ AllocationCourseFileAllocationCourse ==. acId, AllocationCourseFileFile /<-. Set.toList keep ] []
|
||||||
|
mapM_ deleteCascade $ map (allocationCourseFileFile . entityVal) acfs
|
||||||
|
Nothing
|
||||||
|
| Just (Entity prevId _) <- prevAllocationCourse
|
||||||
|
-> do
|
||||||
|
acfs <- selectList [ AllocationCourseFileAllocationCourse ==. prevId ] []
|
||||||
|
mapM_ deleteCascade $ map (allocationCourseFileFile . entityVal) acfs
|
||||||
|
delete prevId
|
||||||
|
_other -> return ()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user