fix(errors): better handling of errors from separated approots
This commit is contained in:
parent
fbf21d7313
commit
833b674c31
@ -548,6 +548,8 @@ UnauthorizedWorkflowRead: Der Workflow enthält aktuell keine Zustände oder Dat
|
|||||||
UnauthorizedWorkflowInstancesNotEmpty: Es gibt Workflow Instanzen für die Sie einen neuen laufenden Workflow initiieren dürfen
|
UnauthorizedWorkflowInstancesNotEmpty: Es gibt Workflow Instanzen für die Sie einen neuen laufenden Workflow initiieren dürfen
|
||||||
UnauthorizedWorkflowWorkflowsNotEmpty: Es gibt laufende Workflows, die Sie einsehen dürfen
|
UnauthorizedWorkflowWorkflowsNotEmpty: Es gibt laufende Workflows, die Sie einsehen dürfen
|
||||||
UnauthorizedWorkflowFiles: Sie dürfen die angegebenen Workflow-Dateien nicht im angegebenen historischen Zustand herunterladen
|
UnauthorizedWorkflowFiles: Sie dürfen die angegebenen Workflow-Dateien nicht im angegebenen historischen Zustand herunterladen
|
||||||
|
UnauthorizedNotAuthenticatedInDifferentApproot: Sie konnten im Kontext einer separierten Domain (z.B. zum sicheren Download von Dateien) nicht authentifiziert werden. Vermutlich haben Sie kein oder ein abgelaufenes Token verwendet. Sie können versuchen auf die gewünschte Resource mit einem neu generierten Download-Link zuzugreifen.
|
||||||
|
UnauthorizedCsrfDisabled: Ihre Anfrage hätte wmgl. Änderungen am Server-Zustand ausgelöst. Da die sog. CSRF-Protection für Ihre Anfrage deaktiviert ist, musste sie daher abgelehnt werden.
|
||||||
|
|
||||||
WorkflowRoleUserMismatch: Sie sind nicht einer der vom Workflow geforderten Benutzer
|
WorkflowRoleUserMismatch: Sie sind nicht einer der vom Workflow geforderten Benutzer
|
||||||
WorkflowRoleAlreadyInitiated: Dieser Workflow wurde bereits initiiert
|
WorkflowRoleAlreadyInitiated: Dieser Workflow wurde bereits initiiert
|
||||||
|
|||||||
@ -545,6 +545,8 @@ UnauthorizedWorkflowRead: The workflow currently contains no states or data you
|
|||||||
UnauthorizedWorkflowInstancesNotEmpty: There are workflow instances for which you are allowed to initiate a new running workflow
|
UnauthorizedWorkflowInstancesNotEmpty: There are workflow instances for which you are allowed to initiate a new running workflow
|
||||||
UnauthorizedWorkflowWorkflowsNotEmpty: There are running workflows, which you may view
|
UnauthorizedWorkflowWorkflowsNotEmpty: There are running workflows, which you may view
|
||||||
UnauthorizedWorkflowFiles: You are not allowed to download the given workflow files in the given historical state
|
UnauthorizedWorkflowFiles: You are not allowed to download the given workflow files in the given historical state
|
||||||
|
UnauthorizedNotAuthenticatedInDifferentApproot: You could not be authenticated in the context of a separate domain (e.g. for secure downloading of files). You probably used no or an expired token. You can try to access the resource with a newly generated download link.
|
||||||
|
UnauthorizedCsrfDisabled: Your request might have triggered a state change on the server. Since CSRF-protection was disabled for your request, it had to be rejected.
|
||||||
|
|
||||||
WorkflowRoleUserMismatch: You aren't any of the users authorized by the workflow
|
WorkflowRoleUserMismatch: You aren't any of the users authorized by the workflow
|
||||||
WorkflowRoleAlreadyInitiated: This workflow was already initiated
|
WorkflowRoleAlreadyInitiated: This workflow was already initiated
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import qualified Network.Wai as W
|
|||||||
|
|
||||||
errorHandler :: ( MonadSecretBox (HandlerFor UniWorX)
|
errorHandler :: ( MonadSecretBox (HandlerFor UniWorX)
|
||||||
, MonadSecretBox (WidgetFor UniWorX)
|
, MonadSecretBox (WidgetFor UniWorX)
|
||||||
|
, MonadSecretBox (ExceptT EncodedSecretBoxException (HandlerFor UniWorX))
|
||||||
, MonadAuth (HandlerFor UniWorX)
|
, MonadAuth (HandlerFor UniWorX)
|
||||||
, BearerAuthSite UniWorX
|
, BearerAuthSite UniWorX
|
||||||
, Button UniWorX ButtonSubmit
|
, Button UniWorX ButtonSubmit
|
||||||
@ -28,27 +29,20 @@ errorHandler :: ( MonadSecretBox (HandlerFor UniWorX)
|
|||||||
)
|
)
|
||||||
=> ErrorResponse -> HandlerFor UniWorX TypedContent
|
=> ErrorResponse -> HandlerFor UniWorX TypedContent
|
||||||
errorHandler err = do
|
errorHandler err = do
|
||||||
-- when (is _NotAuthenticated err) $ do
|
let shouldEncrypt' = getsYesod $ view _appEncryptErrors
|
||||||
-- authed <- is _Just <$> maybeAuthId
|
canDecrypt' = is _Authorized <$> evalAccess AdminErrMsgR True
|
||||||
-- unless authed $ do
|
decrypted' <- runMaybeT $ do
|
||||||
-- mCurrent <- getCurrentRoute
|
internalErrorContent <- hoistMaybe $ err ^? _InternalError
|
||||||
-- gets' <- reqGetParams <$> getRequest
|
exceptTMaybe $ encodedSecretBoxOpen @Text internalErrorContent
|
||||||
-- wai <- waiRequest
|
let isEncrypted = is _Just decrypted'
|
||||||
-- maybe clearUltDest setUltDest $ do
|
shouldEncrypt <- andM
|
||||||
-- current <- mCurrent
|
[ shouldEncrypt'
|
||||||
-- case current of
|
, return $ not isEncrypted
|
||||||
-- _ | W.requestMethod wai `notElem` ["GET"] -> Nothing
|
, not <$> canDecrypt'
|
||||||
-- ErrorR -> Nothing
|
]
|
||||||
-- current' -> Just (current', gets')
|
let decrypted = guardOnM (not shouldEncrypt) decrypted'
|
||||||
-- $logInfoS "errorHandler" "Redirect to LoginR"
|
|
||||||
-- redirect $ AuthR LoginR
|
|
||||||
|
|
||||||
shouldEncrypt <- do
|
|
||||||
canDecrypt <- (== Authorized) <$> evalAccess AdminErrMsgR True
|
|
||||||
shouldEncrypt <- getsYesod $ view _appEncryptErrors
|
|
||||||
return $ shouldEncrypt && not canDecrypt
|
|
||||||
|
|
||||||
sessErr <- bool return (_InternalError $ encodedSecretBox SecretBoxShort) shouldEncrypt err
|
sessErr <- bool return (traverseOf _InternalError $ encodedSecretBox SecretBoxShort) shouldEncrypt err
|
||||||
|
|
||||||
void . runMaybeT $ do
|
void . runMaybeT $ do
|
||||||
reqHost <- MaybeT $ W.requestHeaderHost <$> waiRequest
|
reqHost <- MaybeT $ W.requestHeaderHost <$> waiRequest
|
||||||
@ -57,28 +51,44 @@ errorHandler err = do
|
|||||||
authErr <- lift $ encodedAuth sessErr
|
authErr <- lift $ encodedAuth sessErr
|
||||||
redirect (ErrorR, [(toPathPiece GetError, authErr)])
|
redirect (ErrorR, [(toPathPiece GetError, authErr)])
|
||||||
|
|
||||||
|
when (is _NotAuthenticated err) $ do
|
||||||
|
authed <- is _Just <$> maybeAuthId
|
||||||
|
unless authed $ do
|
||||||
|
mCurrent <- getCurrentRoute
|
||||||
|
gets' <- reqGetParams <$> getRequest
|
||||||
|
wai <- waiRequest
|
||||||
|
maybe clearUltDest setUltDest $ do
|
||||||
|
current <- mCurrent
|
||||||
|
case current of
|
||||||
|
_ | W.requestMethod wai `notElem` ["GET"] -> Nothing
|
||||||
|
ErrorR -> Nothing
|
||||||
|
current' -> Just (current', gets')
|
||||||
|
$logInfoS "errorHandler" "Redirect to LoginR"
|
||||||
|
redirect $ AuthR LoginR
|
||||||
|
|
||||||
setSessionJson SessionError sessErr
|
setSessionJson SessionError sessErr
|
||||||
|
|
||||||
selectRep $ do
|
selectRep $ do
|
||||||
provideRep $ do
|
provideRep $ do
|
||||||
mr <- getMessageRender
|
mr <- getMessageRender
|
||||||
let
|
let
|
||||||
encrypted :: ToJSON a => a -> WidgetFor UniWorX () -> WidgetFor UniWorX ()
|
encrypted :: Text -> WidgetFor UniWorX () -> WidgetFor UniWorX ()
|
||||||
encrypted plaintextJson plaintext = do
|
encrypted plaintextJson plaintext = do
|
||||||
if
|
let displayEncrypted ciphertext =
|
||||||
| shouldEncrypt -> do
|
|
||||||
ciphertext <- encodedSecretBox SecretBoxPretty plaintextJson
|
|
||||||
|
|
||||||
[whamlet|
|
[whamlet|
|
||||||
|
$newline never
|
||||||
<p>_{MsgErrorResponseEncrypted}
|
<p>_{MsgErrorResponseEncrypted}
|
||||||
<pre .literal-error>
|
<pre .literal-error>
|
||||||
#{ciphertext}
|
#{ciphertext}
|
||||||
|]
|
|]
|
||||||
|
if
|
||||||
|
| isEncrypted && shouldEncrypt -> displayEncrypted plaintextJson
|
||||||
|
| shouldEncrypt -> displayEncrypted =<< encodedSecretBox SecretBoxPretty plaintextJson
|
||||||
| otherwise -> plaintext
|
| otherwise -> plaintext
|
||||||
|
|
||||||
errPage = case err of
|
errPage = case err of
|
||||||
NotFound -> [whamlet|<p>_{MsgErrorResponseNotFound}|]
|
NotFound -> [whamlet|<p>_{MsgErrorResponseNotFound}|]
|
||||||
InternalError err' -> encrypted err' [whamlet|<p .literal-error>#{err'}|]
|
InternalError err' -> encrypted err' [whamlet|<p .literal-error>#{fromMaybe err' decrypted}|]
|
||||||
InvalidArgs errs -> [whamlet|
|
InvalidArgs errs -> [whamlet|
|
||||||
<ul>
|
<ul>
|
||||||
$forall err' <- errs
|
$forall err' <- errs
|
||||||
@ -93,20 +103,27 @@ errorHandler err = do
|
|||||||
provideRep . fmap PrettyValue $ case err of
|
provideRep . fmap PrettyValue $ case err of
|
||||||
PermissionDenied err' -> return $ object [ "message" JSON..= err' ]
|
PermissionDenied err' -> return $ object [ "message" JSON..= err' ]
|
||||||
InternalError err'
|
InternalError err'
|
||||||
|
| isEncrypted && shouldEncrypt
|
||||||
|
-> return $ object [ "message" JSON..= err'
|
||||||
|
, "encrypted" JSON..= True
|
||||||
|
]
|
||||||
| shouldEncrypt -> do
|
| shouldEncrypt -> do
|
||||||
ciphertext <- encodedSecretBox SecretBoxShort err'
|
ciphertext <- encodedSecretBox SecretBoxShort err'
|
||||||
return $ object [ "message" JSON..= ciphertext
|
return $ object [ "message" JSON..= ciphertext
|
||||||
, "encrypted" JSON..= True
|
, "encrypted" JSON..= True
|
||||||
]
|
]
|
||||||
| otherwise -> return $ object [ "message" JSON..= err' ]
|
| otherwise -> return $ object [ "message" JSON..= fromMaybe err' decrypted ]
|
||||||
InvalidArgs errs -> return $ object [ "messages" JSON..= errs ]
|
InvalidArgs errs -> return $ object [ "messages" JSON..= errs ]
|
||||||
_other -> return $ object []
|
_other -> return $ object []
|
||||||
provideRep $ case err of
|
provideRep $ case err of
|
||||||
PermissionDenied err' -> return err'
|
PermissionDenied err' -> return err'
|
||||||
InternalError err'
|
InternalError err'
|
||||||
|
| isEncrypted && shouldEncrypt -> do
|
||||||
|
addHeader "Encrypted-Error-Message" "True"
|
||||||
|
return err'
|
||||||
| shouldEncrypt -> do
|
| shouldEncrypt -> do
|
||||||
addHeader "Encrypted-Error-Message" "True"
|
addHeader "Encrypted-Error-Message" "True"
|
||||||
encodedSecretBox SecretBoxPretty err'
|
encodedSecretBox SecretBoxPretty err'
|
||||||
| otherwise -> return err'
|
| otherwise -> return $ fromMaybe err' decrypted
|
||||||
InvalidArgs errs -> return . Text.unlines . map (Text.replace "\n" "\n\t") $ errs
|
InvalidArgs errs -> return . Text.unlines . map (Text.replace "\n" "\n\t") $ errs
|
||||||
_other -> return Text.empty
|
_other -> return Text.empty
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import Import.NoFoundation hiding (yesodMiddleware)
|
|||||||
import Foundation.Type
|
import Foundation.Type
|
||||||
import Foundation.Routes
|
import Foundation.Routes
|
||||||
import Foundation.Authorization
|
import Foundation.Authorization
|
||||||
|
import Foundation.I18n
|
||||||
|
|
||||||
import Utils.Metrics
|
import Utils.Metrics
|
||||||
import Utils.Workflow
|
import Utils.Workflow
|
||||||
@ -30,7 +31,7 @@ yesodMiddleware :: ( BearerAuthSite UniWorX
|
|||||||
, BackendCompatible SqlBackend (YesodPersistBackend UniWorX)
|
, BackendCompatible SqlBackend (YesodPersistBackend UniWorX)
|
||||||
)
|
)
|
||||||
=> HandlerFor UniWorX res -> HandlerFor UniWorX res
|
=> HandlerFor UniWorX res -> HandlerFor UniWorX res
|
||||||
yesodMiddleware = storeBearerMiddleware . csrfMiddleware . dryRunMiddleware . observeYesodCacheSizeMiddleware . languagesMiddleware appLanguages . headerMessagesMiddleware . defaultYesodMiddleware . normalizeRouteMiddleware . updateFavouritesMiddleware . setActiveAuthTagsMiddleware
|
yesodMiddleware = cacheControlMiddleware . storeBearerMiddleware . csrfMiddleware . dryRunMiddleware . observeYesodCacheSizeMiddleware . languagesMiddleware appLanguages . headerMessagesMiddleware . securityMiddleware . normalizeRouteMiddleware . updateFavouritesMiddleware . setActiveAuthTagsMiddleware
|
||||||
where
|
where
|
||||||
dryRunMiddleware :: HandlerFor UniWorX a -> HandlerFor UniWorX a
|
dryRunMiddleware :: HandlerFor UniWorX a -> HandlerFor UniWorX a
|
||||||
dryRunMiddleware handler = do
|
dryRunMiddleware handler = do
|
||||||
@ -90,9 +91,19 @@ yesodMiddleware = storeBearerMiddleware . csrfMiddleware . dryRunMiddleware . ob
|
|||||||
reqHost <- W.requestHeaderHost <$> waiRequest
|
reqHost <- W.requestHeaderHost <$> waiRequest
|
||||||
userGeneratedHost <- getsYesod $ \app ->
|
userGeneratedHost <- getsYesod $ \app ->
|
||||||
guardOnM (views _appRoot ($ ApprootDefault) app /= views _appRoot ($ ApprootUserGenerated) app) $ approotScopeHost ApprootUserGenerated app
|
guardOnM (views _appRoot ($ ApprootDefault) app /= views _appRoot ($ ApprootUserGenerated) app) $ approotScopeHost ApprootUserGenerated app
|
||||||
|
mCurrentRoute <- getCurrentRoute
|
||||||
|
let isError = case mCurrentRoute of
|
||||||
|
Just ErrorR -> True
|
||||||
|
_other -> False
|
||||||
|
|
||||||
if | hasBearer || fromMaybe False ((==) <$> reqHost <*> userGeneratedHost)
|
if | hasBearer
|
||||||
-> local (\HandlerData{..} -> HandlerData{ handlerRequest = handlerRequest { reqToken = Nothing }, .. }) handler
|
-> local (\HandlerData{..} -> HandlerData{ handlerRequest = handlerRequest { reqToken = Nothing }, .. }) handler
|
||||||
|
| fromMaybe False ((==) <$> reqHost <*> userGeneratedHost) || isError -> do
|
||||||
|
whenIsJust mCurrentRoute $ \currentRoute -> do
|
||||||
|
isWrite <- isWriteRequest currentRoute
|
||||||
|
when isWrite $
|
||||||
|
permissionDeniedI MsgUnauthorizedCsrfDisabled
|
||||||
|
handler
|
||||||
| otherwise
|
| otherwise
|
||||||
-> csrfSetCookieMiddleware' . defaultCsrfCheckMiddleware $ handler
|
-> csrfSetCookieMiddleware' . defaultCsrfCheckMiddleware $ handler
|
||||||
where
|
where
|
||||||
@ -115,6 +126,15 @@ yesodMiddleware = storeBearerMiddleware . csrfMiddleware . dryRunMiddleware . ob
|
|||||||
for_ mAuthTagActive $ setSessionJson SessionActiveAuthTags . review _ReducedActiveAuthTags
|
for_ mAuthTagActive $ setSessionJson SessionActiveAuthTags . review _ReducedActiveAuthTags
|
||||||
|
|
||||||
handler
|
handler
|
||||||
|
securityMiddleware :: HandlerFor UniWorX a -> HandlerFor UniWorX a
|
||||||
|
securityMiddleware handler = do
|
||||||
|
addHeader "X-XSS-Protection" "1; mode=block"
|
||||||
|
addHeader "X-Frame-Options" "sameorigin"
|
||||||
|
addHeader "X-Content-Type-Options" "nosniff"
|
||||||
|
authorizationCheck
|
||||||
|
handler
|
||||||
|
cacheControlMiddleware :: HandlerFor UniWorX a -> HandlerFor UniWorX a
|
||||||
|
cacheControlMiddleware = (addHeader "Vary" "Accept, Accept-Language" *>)
|
||||||
|
|
||||||
updateFavourites :: forall m backend.
|
updateFavourites :: forall m backend.
|
||||||
( MonadHandler m, HandlerSite m ~ UniWorX
|
( MonadHandler m, HandlerSite m ~ UniWorX
|
||||||
|
|||||||
@ -4,6 +4,7 @@ module Foundation.Yesod.Session
|
|||||||
|
|
||||||
import Import.NoFoundation hiding (makeSessionBackend)
|
import Import.NoFoundation hiding (makeSessionBackend)
|
||||||
|
|
||||||
|
import Foundation.Routes
|
||||||
import Foundation.Type
|
import Foundation.Type
|
||||||
|
|
||||||
import qualified Web.ServerSession.Core as ServerSession
|
import qualified Web.ServerSession.Core as ServerSession
|
||||||
@ -12,12 +13,15 @@ import qualified Web.ServerSession.Frontend.Yesod.Jwt as JwtSession
|
|||||||
import qualified Network.Wai as W
|
import qualified Network.Wai as W
|
||||||
import qualified Network.HTTP.Types.Header as W
|
import qualified Network.HTTP.Types.Header as W
|
||||||
import qualified Network.Wai.Middleware.HttpAuth as W (extractBearerAuth)
|
import qualified Network.Wai.Middleware.HttpAuth as W (extractBearerAuth)
|
||||||
|
import qualified Network.HTTP.Types as HTTP
|
||||||
|
|
||||||
|
import qualified Data.Map as Map
|
||||||
|
|
||||||
import Web.Cookie
|
import Web.Cookie
|
||||||
|
|
||||||
|
|
||||||
makeSessionBackend :: Yesod UniWorX => UniWorX -> IO (Maybe SessionBackend)
|
makeSessionBackend :: Yesod UniWorX => UniWorX -> IO (Maybe SessionBackend)
|
||||||
makeSessionBackend app@UniWorX{ appSettings' = AppSettings{..}, ..} = notFor isUserGenerated . notFor isBearer . sameSite $ case appSessionStore of
|
makeSessionBackend app@UniWorX{ appSettings' = AppSettings{..}, ..} = noCreateFor (return . forRoute isError) . notFor isUserGenerated . notFor isBearer . sameSite $ case appSessionStore of
|
||||||
SessionStorageMemcachedSql sqlStore
|
SessionStorageMemcachedSql sqlStore
|
||||||
-> mkBackend . stateSettings =<< ServerSession.createState sqlStore
|
-> mkBackend . stateSettings =<< ServerSession.createState sqlStore
|
||||||
SessionStorageAcid acidStore
|
SessionStorageAcid acidStore
|
||||||
@ -58,6 +62,24 @@ makeSessionBackend app@UniWorX{ appSettings' = AppSettings{..}, ..} = notFor isU
|
|||||||
if | not pMatches -> load req
|
if | not pMatches -> load req
|
||||||
| otherwise -> return (mempty, const $ return [])
|
| otherwise -> return (mempty, const $ return [])
|
||||||
|
|
||||||
|
noCreateFor :: (W.Request -> IO Bool) -> IO (Maybe SessionBackend) -> IO (Maybe SessionBackend)
|
||||||
|
noCreateFor f = fmap $ fmap notFor'
|
||||||
|
where notFor' :: SessionBackend -> SessionBackend
|
||||||
|
notFor' (SessionBackend load) = SessionBackend $ \req -> do
|
||||||
|
pMatches <- f req
|
||||||
|
if | not pMatches -> load req
|
||||||
|
| otherwise -> noCreate <$> load req
|
||||||
|
noCreate resp@(session, _)
|
||||||
|
| Map.null session = (session, const $ return [])
|
||||||
|
| otherwise = resp
|
||||||
|
|
||||||
|
forRoute :: (Route UniWorX -> Bool) -> (W.Request -> Bool)
|
||||||
|
forRoute f req = maybe False f mRoute
|
||||||
|
where mRoute = parseRoute
|
||||||
|
( W.pathInfo req
|
||||||
|
, over (mapped . _2) (fromMaybe "") . HTTP.queryToQueryText $ W.queryString req
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
isBearer req = return $ if
|
isBearer req = return $ if
|
||||||
| aHdrs <- mapMaybe (\(h, v) -> v <$ guard (h == W.hAuthorization)) $ W.requestHeaders req
|
| aHdrs <- mapMaybe (\(h, v) -> v <$ guard (h == W.hAuthorization)) $ W.requestHeaders req
|
||||||
@ -74,3 +96,7 @@ makeSessionBackend app@UniWorX{ appSettings' = AppSettings{..}, ..} = notFor isU
|
|||||||
-> True
|
-> True
|
||||||
| otherwise
|
| otherwise
|
||||||
-> False
|
-> False
|
||||||
|
|
||||||
|
isError = \case
|
||||||
|
ErrorR -> True
|
||||||
|
_other -> False
|
||||||
|
|||||||
@ -9,4 +9,9 @@ import Yesod.Core.Types (HandlerContents(HCError))
|
|||||||
getErrorR :: Handler Void
|
getErrorR :: Handler Void
|
||||||
getErrorR = do
|
getErrorR = do
|
||||||
encodedErrResponse <- maybe (redirect NewsR) return =<< lookupGlobalGetParam GetError
|
encodedErrResponse <- maybe (redirect NewsR) return =<< lookupGlobalGetParam GetError
|
||||||
throwM . HCError =<< throwExceptT (encodedAuthVerify encodedErrResponse)
|
errResponse <- throwExceptT (encodedAuthVerify encodedErrResponse)
|
||||||
|
|
||||||
|
isAuthed <- is _Just <$> maybeAuthId
|
||||||
|
case errResponse of
|
||||||
|
NotAuthenticated | isAuthed -> permissionDeniedI MsgUnauthorizedNotAuthenticatedInDifferentApproot
|
||||||
|
_ -> throwM $ HCError errResponse
|
||||||
|
|||||||
@ -157,7 +157,7 @@ getMaterialListR tid ssh csh = do
|
|||||||
|
|
||||||
|
|
||||||
getMFileR :: TermId -> SchoolId -> CourseShorthand -> MaterialName -> FilePath -> Handler TypedContent
|
getMFileR :: TermId -> SchoolId -> CourseShorthand -> MaterialName -> FilePath -> Handler TypedContent
|
||||||
getMFileR tid ssh csh mnm title = serveOneFile $ fileQuery .| C.map entityVal
|
getMFileR tid ssh csh mnm title = error "MFileR" . serveOneFile $ fileQuery .| C.map entityVal
|
||||||
where
|
where
|
||||||
fileQuery = E.selectSource $ E.from $
|
fileQuery = E.selectSource $ E.from $
|
||||||
\(course `E.InnerJoin` material `E.InnerJoin` matFile) -> do
|
\(course `E.InnerJoin` material `E.InnerJoin` matFile) -> do
|
||||||
|
|||||||
@ -2,7 +2,6 @@ module Import.NoModel
|
|||||||
( module Import
|
( module Import
|
||||||
, MForm
|
, MForm
|
||||||
, WeekDay
|
, WeekDay
|
||||||
, requireAuthId
|
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import ClassyPrelude.Yesod as Import
|
import ClassyPrelude.Yesod as Import
|
||||||
@ -21,6 +20,8 @@ import ClassyPrelude.Yesod as Import
|
|||||||
, htmlField, fileField, urlField
|
, htmlField, fileField, urlField
|
||||||
, mreq, areq, wreq -- Use `mreqMsg`, `areqMsg`, `wreqMsg`
|
, mreq, areq, wreq -- Use `mreqMsg`, `areqMsg`, `wreqMsg`
|
||||||
, sinkFile, sourceFile
|
, sinkFile, sourceFile
|
||||||
|
, defaultYesodMiddleware
|
||||||
|
, authorizationCheck
|
||||||
)
|
)
|
||||||
|
|
||||||
import UnliftIO.Async.Utils as Import
|
import UnliftIO.Async.Utils as Import
|
||||||
@ -30,7 +31,7 @@ import Model.Types.TH.Wordlist as Import
|
|||||||
|
|
||||||
import Mail as Import
|
import Mail as Import
|
||||||
|
|
||||||
import Yesod.Auth as Import hiding (requireAuthId)
|
import Yesod.Auth as Import hiding (requireAuth, requireAuthId, requireAuthPair)
|
||||||
import Yesod.Core.Types as Import (loggerSet)
|
import Yesod.Core.Types as Import (loggerSet)
|
||||||
import Yesod.Default.Config2 as Import
|
import Yesod.Default.Config2 as Import
|
||||||
import Yesod.Core.Types.Instances as Import
|
import Yesod.Core.Types.Instances as Import
|
||||||
@ -40,6 +41,7 @@ import Utils.Frontend.I18n as Import
|
|||||||
import Utils.DB as Import
|
import Utils.DB as Import
|
||||||
import Utils.Sql as Import
|
import Utils.Sql as Import
|
||||||
import Utils.Widgets as Import
|
import Utils.Widgets as Import
|
||||||
|
import Utils.Auth as Import
|
||||||
|
|
||||||
import Data.Fixed as Import
|
import Data.Fixed as Import
|
||||||
|
|
||||||
@ -213,14 +215,7 @@ import Data.Scientific as Import (Scientific, formatScientific)
|
|||||||
|
|
||||||
import Control.Monad.Trans.RWS (RWST)
|
import Control.Monad.Trans.RWS (RWST)
|
||||||
|
|
||||||
import qualified Yesod.Auth as Yesod
|
|
||||||
import GHC.Stack
|
|
||||||
|
|
||||||
type MForm m = RWST (Maybe (Env, FileEnv), HandlerSite m, [Lang]) Enctype Ints m
|
type MForm m = RWST (Maybe (Env, FileEnv), HandlerSite m, [Lang]) Enctype Ints m
|
||||||
|
|
||||||
type WeekDay = DayOfWeek
|
type WeekDay = DayOfWeek
|
||||||
|
|
||||||
requireAuthId :: (MonadHandler m, YesodAuth (HandlerSite m), HasCallStack) => m (AuthId (HandlerSite m))
|
|
||||||
requireAuthId = do
|
|
||||||
$logDebugS "requireAuthId" . pack $ prettyCallStack callStack
|
|
||||||
Yesod.requireAuthId
|
|
||||||
|
|||||||
@ -1136,7 +1136,8 @@ encodedSecretBox pretty val = do
|
|||||||
sKey <- secretBoxKey
|
sKey <- secretBoxKey
|
||||||
encodedSecretBox' sKey pretty val
|
encodedSecretBox' sKey pretty val
|
||||||
|
|
||||||
encodedSecretBoxOpen :: ( FromJSON a, MonadError EncodedSecretBoxException m, MonadSecretBox m )
|
encodedSecretBoxOpen :: forall a m.
|
||||||
|
( FromJSON a, MonadError EncodedSecretBoxException m, MonadSecretBox m )
|
||||||
=> Text -> m a
|
=> Text -> m a
|
||||||
encodedSecretBoxOpen ciphertext = do
|
encodedSecretBoxOpen ciphertext = do
|
||||||
sKey <- secretBoxKey
|
sKey <- secretBoxKey
|
||||||
|
|||||||
50
src/Utils/Auth.hs
Normal file
50
src/Utils/Auth.hs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
module Utils.Auth
|
||||||
|
( requireAuth, requireAuthId, requireAuthPair, authorizationCheck
|
||||||
|
) where
|
||||||
|
|
||||||
|
import ClassyPrelude.Yesod hiding (authorizationCheck)
|
||||||
|
import Yesod.Auth hiding (requireAuth, requireAuthId, requireAuthPair)
|
||||||
|
import GHC.Stack
|
||||||
|
|
||||||
|
|
||||||
|
requireAuthId :: (MonadHandler m, YesodAuth (HandlerSite m), HasCallStack) => m (AuthId (HandlerSite m))
|
||||||
|
requireAuthId = do
|
||||||
|
$logDebugS "requireAuthId" . pack $ prettyCallStack callStack
|
||||||
|
maybeAuthId >>= maybe notAuthenticated return
|
||||||
|
|
||||||
|
requireAuth :: ( YesodAuthPersist master
|
||||||
|
, val ~ AuthEntity master
|
||||||
|
, Key val ~ AuthId master
|
||||||
|
, PersistEntity val
|
||||||
|
, Typeable val
|
||||||
|
, MonadHandler m
|
||||||
|
, HandlerSite m ~ master
|
||||||
|
, HasCallStack
|
||||||
|
) => m (Entity val)
|
||||||
|
requireAuth = do
|
||||||
|
$logDebugS "requireAuth" . pack $ prettyCallStack callStack
|
||||||
|
maybeAuth >>= maybe notAuthenticated return
|
||||||
|
|
||||||
|
requireAuthPair :: ( YesodAuthPersist master
|
||||||
|
, Typeable (AuthEntity master)
|
||||||
|
, MonadHandler m
|
||||||
|
, HandlerSite m ~ master
|
||||||
|
, HasCallStack
|
||||||
|
)
|
||||||
|
=> m (AuthId master, AuthEntity master)
|
||||||
|
requireAuthPair = do
|
||||||
|
$logDebugS "requireAuthPair" . pack $ prettyCallStack callStack
|
||||||
|
maybeAuthPair >>= maybe notAuthenticated return
|
||||||
|
|
||||||
|
authorizationCheck :: (Yesod site, HasCallStack) => HandlerFor site ()
|
||||||
|
authorizationCheck = do
|
||||||
|
$logDebugS "authorizationCheck" . pack $ prettyCallStack callStack
|
||||||
|
getCurrentRoute >>= maybe (return ()) checkUrl
|
||||||
|
where
|
||||||
|
checkUrl url = do
|
||||||
|
isWrite <- isWriteRequest url
|
||||||
|
ar <- isAuthorized url isWrite
|
||||||
|
case ar of
|
||||||
|
Authorized -> return ()
|
||||||
|
AuthenticationRequired -> notAuthenticated
|
||||||
|
Unauthorized s' -> permissionDenied s'
|
||||||
Reference in New Issue
Block a user