fix(personalised-sheet-files): more thorough check wrt sub-warnings

This commit is contained in:
Gregor Kleen 2020-08-10 10:35:09 +02:00
parent c9b86d8770
commit 0b0eaff20d
4 changed files with 66 additions and 12 deletions

View File

@ -1572,13 +1572,23 @@ evalAccessFor mAuthId route isWrite = do
evalAccessForDB :: (MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX, BackendCompatible SqlBackend backend) => Maybe (AuthId UniWorX) -> Route UniWorX -> Bool -> ReaderT backend m AuthResult evalAccessForDB :: (MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX, BackendCompatible SqlBackend backend) => Maybe (AuthId UniWorX) -> Route UniWorX -> Bool -> ReaderT backend m AuthResult
evalAccessForDB = evalAccessFor evalAccessForDB = evalAccessFor
evalAccess :: (MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX) => Route UniWorX -> Bool -> m AuthResult evalAccessWith :: (MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX) => [(AuthTag, Bool)] -> Route UniWorX -> Bool -> m AuthResult
evalAccess route isWrite = do evalAccessWith assumptions route isWrite = do
mAuthId <- liftHandler maybeAuthId mAuthId <- liftHandler maybeAuthId
tagActive <- fromMaybe def <$> lookupSessionJson SessionActiveAuthTags tagActive <- fromMaybe def <$> lookupSessionJson SessionActiveAuthTags
dnf <- either throwM return $ routeAuthTags route dnf <- either throwM return $ routeAuthTags route
(result, deactivated) <- runWriterT $ evalAuthTags tagActive dnf mAuthId route isWrite let dnf' = ala Endo foldMap (map ((=<<) . uncurry dnfAssumeValue) assumptions) $ Just dnf
result <$ tellSessionJson SessionInactiveAuthTags deactivated case dnf' of
Nothing -> return Authorized
Just dnf'' -> do
(result, deactivated) <- runWriterT $ evalAuthTags tagActive dnf'' mAuthId route isWrite
result <$ tellSessionJson SessionInactiveAuthTags deactivated
evalAccessWithDB :: (MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX, BackendCompatible SqlBackend backend) => [(AuthTag, Bool)] -> Route UniWorX -> Bool -> ReaderT backend m AuthResult
evalAccessWithDB = evalAccessWith
evalAccess :: (MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX) => Route UniWorX -> Bool -> m AuthResult
evalAccess = evalAccessWith []
evalAccessDB :: (MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX, BackendCompatible SqlBackend backend) => Route UniWorX -> Bool -> ReaderT backend m AuthResult evalAccessDB :: (MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX, BackendCompatible SqlBackend backend) => Route UniWorX -> Bool -> ReaderT backend m AuthResult
evalAccessDB = evalAccess evalAccessDB = evalAccess
@ -1598,6 +1608,29 @@ hasReadAccessTo = flip hasAccessTo False
hasWriteAccessTo :: (MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX) => Route UniWorX -> m Bool hasWriteAccessTo :: (MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX) => Route UniWorX -> m Bool
hasWriteAccessTo = flip hasAccessTo True hasWriteAccessTo = flip hasAccessTo True
wouldHaveAccessTo :: ( MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX )
=> [(AuthTag, Bool)] -- ^ Assumptions
-> Route UniWorX
-> Bool
-> m Bool
wouldHaveAccessTo assumptions route isWrite = (== Authorized) <$> evalAccessWith assumptions route isWrite
wouldHaveReadAccessTo, wouldHaveWriteAccessTo
:: ( MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX )
=> [(AuthTag, Bool)] -- ^ Assumptions
-> Route UniWorX
-> m Bool
wouldHaveReadAccessTo assumptions route = wouldHaveAccessTo assumptions route False
wouldHaveWriteAccessTo assumptions route = wouldHaveAccessTo assumptions route True
wouldHaveReadAccessToIff, wouldHaveWriteAccessToIff
:: ( MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX )
=> [(AuthTag, Bool)] -- ^ Assumptions
-> Route UniWorX
-> m Bool
wouldHaveReadAccessToIff assumptions route = and2M (fmap not $ hasReadAccessTo route) $ wouldHaveReadAccessTo assumptions route
wouldHaveWriteAccessToIff assumptions route = and2M (fmap not $ hasWriteAccessTo route) $ wouldHaveWriteAccessTo assumptions route
-- | Conditional redirect that hides the URL if the user is not authorized for the route -- | Conditional redirect that hides the URL if the user is not authorized for the route
redirectAccess :: (MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX) => Route UniWorX -> m a redirectAccess :: (MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX) => Route UniWorX -> m a
redirectAccess url = do redirectAccess url = do

View File

@ -116,7 +116,11 @@ getSShowR tid ssh csh shn = do
, formEncoding = generateEnctype , formEncoding = generateEnctype
, formSubmit = FormNoSubmit , formSubmit = FormNoSubmit
} }
mRequiredExam <- fmap join . for (sheetRequireExamRegistration sheet) $ \eId -> fmap (fmap $(E.unValueN 4)) . runDB . E.selectMaybe . E.from $ \(exam `E.InnerJoin` course) -> do checkExamRegistration <- orM
[ wouldHaveWriteAccessToIff [(AuthExamRegistered, True)] $ CSheetR tid ssh csh shn SubmissionNewR
, wouldHaveReadAccessToIff [(AuthExamRegistered, True)] $ CSheetR tid ssh csh shn SArchiveR
]
mRequiredExam <- fmap join . for (guardOnM checkExamRegistration $ sheetRequireExamRegistration sheet) $ \eId -> fmap (fmap $(E.unValueN 4)) . runDB . E.selectMaybe . E.from $ \(exam `E.InnerJoin` course) -> do
E.on $ exam E.^. ExamCourse E.==. course E.^. CourseId E.on $ exam E.^. ExamCourse E.==. course E.^. CourseId
E.where_ $ exam E.^. ExamId E.==. E.val eId E.where_ $ exam E.^. ExamId E.==. E.val eId
return (course E.^. CourseTerm, course E.^. CourseSchool, course E.^. CourseShorthand, exam E.^. ExamName) return (course E.^. CourseTerm, course E.^. CourseSchool, course E.^. CourseShorthand, exam E.^. ExamName)
@ -125,14 +129,15 @@ getSShowR tid ssh csh shn = do
let eUrl = CExamR etid essh ecsh examn EShowR let eUrl = CExamR etid essh ecsh examn EShowR
guardM $ hasReadAccessTo eUrl guardM $ hasReadAccessTo eUrl
return eUrl return eUrl
mMissingExamRegistration <- for (sheetRequireExamRegistration sheet) $ \eId -> maybeT (return True) $ do mMissingExamRegistration <- for (guardOnM checkExamRegistration $ sheetRequireExamRegistration sheet) $ \eId -> maybeT (return True) $ do
uid <- MaybeT maybeAuthId uid <- MaybeT maybeAuthId
lift . fmap not . runDB $ exists [ ExamRegistrationExam ==. eId, ExamRegistrationUser ==. uid ] lift . fmap not . runDB $ exists [ ExamRegistrationExam ==. eId, ExamRegistrationUser ==. uid ]
let checkPersonalisedFiles checkPersonalisedFiles <- andM
= not (sheetAllowNonPersonalisedSubmission sheet) [ return . not $ sheetAllowNonPersonalisedSubmission sheet
&& NTop (sheetActiveFrom sheet) <= NTop (Just now) , return $ NTop (sheetActiveFrom sheet) <= NTop (Just now), return $ NTop (Just now) <= NTop (sheetActiveTo sheet)
&& NTop (sheetActiveTo sheet) >= NTop (Just now) , wouldHaveWriteAccessToIff [(AuthPersonalisedSheetFiles, True)] $ CSheetR tid ssh csh shn SubmissionNewR
]
mMissingPersonalisedFiles <- for (guardOnM checkPersonalisedFiles muid) $ \uid -> runDB $ mMissingPersonalisedFiles <- for (guardOnM checkPersonalisedFiles muid) $ \uid -> runDB $
fmap not . E.selectExists . E.from $ \psFile -> fmap not . E.selectExists . E.from $ \psFile ->
E.where_ $ psFile E.^. PersonalisedSheetFileUser E.==. E.val uid E.where_ $ psFile E.^. PersonalisedSheetFileUser E.==. E.val uid

View File

@ -127,6 +127,8 @@ data PredLiteral a = PLVariable { plVar :: a } | PLNegated { plVar :: a }
deriving (Eq, Ord, Read, Show, Generic, Typeable) deriving (Eq, Ord, Read, Show, Generic, Typeable)
deriving anyclass (Hashable, Binary) deriving anyclass (Hashable, Binary)
makeLenses_ ''PredLiteral
deriveJSON defaultOptions deriveJSON defaultOptions
{ constructorTagModifier = camelToPathPiece' 1 { constructorTagModifier = camelToPathPiece' 1
, sumEncoding = TaggedObject "val" "var" , sumEncoding = TaggedObject "val" "var"
@ -145,6 +147,8 @@ newtype PredDNF a = PredDNF { dnfTerms :: Set (NonNull (Set (PredLiteral a))) }
deriving newtype (Semigroup, Monoid) deriving newtype (Semigroup, Monoid)
deriving anyclass (Binary, Hashable) deriving anyclass (Binary, Hashable)
makeLenses_ ''PredDNF
$(return []) $(return [])
instance ToJSON a => ToJSON (PredDNF a) where instance ToJSON a => ToJSON (PredDNF a) where
@ -161,6 +165,20 @@ type AuthLiteral = PredLiteral AuthTag
type AuthDNF = PredDNF AuthTag type AuthDNF = PredDNF AuthTag
dnfAssumeValue :: forall a. Ord a => a -> Bool -> PredDNF a -> Maybe (PredDNF a)
-- ^ `Nothing` corresponds to @@
dnfAssumeValue var val
= fmap (PredDNF . Set.fromList) . sequence
. foldMapOf (_dnfTerms . folded) (pure @[] . fromNullable . Set.filter (not . agrees) . toNullable)
. over _dnfTerms (Set.filter $ none disagrees . toNullable)
where
agrees, disagrees :: PredLiteral a -> Bool
agrees PLVariable{..} = plVar == var && val
agrees PLNegated{..} = plVar == var && not val
disagrees PLNegated{..} = plVar == var && val
disagrees PLVariable{..} = plVar == var && not val
data UserGroupName data UserGroupName
= UserGroupMetrics = UserGroupMetrics
| UserGroupCustom { userGroupCustomName :: CI Text } | UserGroupCustom { userGroupCustomName :: CI Text }

View File

@ -180,8 +180,6 @@ makePrisms ''OccurrenceException
makeLenses_ ''Occurrences makeLenses_ ''Occurrences
makeLenses_ ''PredDNF
makeLenses_ ''Invitation makeLenses_ ''Invitation
makeLenses_ ''ExamBonusRule makeLenses_ ''ExamBonusRule