feat(errors): redirect errors back to ApprootDefault
This commit is contained in:
parent
f7fdc5cf13
commit
fbf21d7313
@ -250,4 +250,4 @@ token-buckets:
|
|||||||
|
|
||||||
fallback-personalised-sheet-files-keys-expire: 2419200
|
fallback-personalised-sheet-files-keys-expire: 2419200
|
||||||
|
|
||||||
download-token-expire: 14400
|
download-token-expire: 604801
|
||||||
|
|||||||
@ -1538,6 +1538,7 @@ BreadcrumbWorkflowWorkflowDelete: Löschen
|
|||||||
BreadcrumbGlobalWorkflowInstanceList: Systemweite Workflows
|
BreadcrumbGlobalWorkflowInstanceList: Systemweite Workflows
|
||||||
BreadcrumbTopWorkflowInstanceList: Workflows
|
BreadcrumbTopWorkflowInstanceList: Workflows
|
||||||
BreadcrumbTopWorkflowWorkflowList: Laufende Workflows
|
BreadcrumbTopWorkflowWorkflowList: Laufende Workflows
|
||||||
|
BreadcrumbError: Fehler
|
||||||
|
|
||||||
ExternalExamEdit coursen@CourseName examn@ExamName: Bearbeiten: #{coursen}, #{examn}
|
ExternalExamEdit coursen@CourseName examn@ExamName: Bearbeiten: #{coursen}, #{examn}
|
||||||
ExternalExamGrades coursen@CourseName examn@ExamName: Prüfungsleistungen: #{coursen}, #{examn}
|
ExternalExamGrades coursen@CourseName examn@ExamName: Prüfungsleistungen: #{coursen}, #{examn}
|
||||||
|
|||||||
@ -1538,6 +1538,7 @@ BreadcrumbWorkflowWorkflowDelete: Delete
|
|||||||
BreadcrumbGlobalWorkflowInstanceList: System-wide workflows
|
BreadcrumbGlobalWorkflowInstanceList: System-wide workflows
|
||||||
BreadcrumbTopWorkflowInstanceList: Workflows
|
BreadcrumbTopWorkflowInstanceList: Workflows
|
||||||
BreadcrumbTopWorkflowWorkflowList: Running workflows
|
BreadcrumbTopWorkflowWorkflowList: Running workflows
|
||||||
|
BreadcrumbError: Error
|
||||||
|
|
||||||
ExternalExamEdit coursen examn: Edit: #{coursen}, #{examn}
|
ExternalExamEdit coursen examn: Edit: #{coursen}, #{examn}
|
||||||
ExternalExamGrades coursen examn: Exam achievements: #{coursen}, #{examn}
|
ExternalExamGrades coursen examn: Exam achievements: #{coursen}, #{examn}
|
||||||
|
|||||||
2
routes
2
routes
@ -41,6 +41,8 @@
|
|||||||
|
|
||||||
/metrics MetricsR GET
|
/metrics MetricsR GET
|
||||||
|
|
||||||
|
/err ErrorR GET !free
|
||||||
|
|
||||||
/ NewsR GET !free
|
/ NewsR GET !free
|
||||||
/users UsersR GET POST -- no tags, i.e. admins only
|
/users UsersR GET POST -- no tags, i.e. admins only
|
||||||
/users/#CryptoUUIDUser AdminUserR GET POST
|
/users/#CryptoUUIDUser AdminUserR GET POST
|
||||||
|
|||||||
@ -129,6 +129,7 @@ import Handler.ExternalExam
|
|||||||
import Handler.Participants
|
import Handler.Participants
|
||||||
import Handler.StorageKey
|
import Handler.StorageKey
|
||||||
import Handler.Workflow
|
import Handler.Workflow
|
||||||
|
import Handler.Error
|
||||||
|
|
||||||
|
|
||||||
-- This line actually creates our YesodDispatch instance. It is the second half
|
-- This line actually creates our YesodDispatch instance. It is the second half
|
||||||
@ -183,7 +184,7 @@ makeFoundation appSettings'@AppSettings{..} = do
|
|||||||
-- logging function. To get out of this loop, we initially create a
|
-- logging function. To get out of this loop, we initially create a
|
||||||
-- temporary foundation without a real connection pool, get a log function
|
-- temporary foundation without a real connection pool, get a log function
|
||||||
-- from there, and then create the real foundation.
|
-- from there, and then create the real foundation.
|
||||||
let mkFoundation appConnPool appSmtpPool appLdapPool appCryptoIDKey appSessionStore appSecretBoxKey appWidgetMemcached appJSONWebKeySet appClusterID appMemcached appUploadCache appVerpSecret = UniWorX {..}
|
let mkFoundation appConnPool appSmtpPool appLdapPool appCryptoIDKey appSessionStore appSecretBoxKey appWidgetMemcached appJSONWebKeySet appClusterID appMemcached appUploadCache appVerpSecret appAuthKey = UniWorX {..}
|
||||||
-- The UniWorX {..} syntax is an example of record wild cards. For more
|
-- The UniWorX {..} syntax is an example of record wild cards. For more
|
||||||
-- information, see:
|
-- information, see:
|
||||||
-- https://ocharles.org.uk/blog/posts/2014-12-04-record-wildcards.html
|
-- https://ocharles.org.uk/blog/posts/2014-12-04-record-wildcards.html
|
||||||
@ -200,6 +201,7 @@ makeFoundation appSettings'@AppSettings{..} = do
|
|||||||
(error "memcached forced in tempFoundation")
|
(error "memcached forced in tempFoundation")
|
||||||
(error "MinioConn forced in tempFoundation")
|
(error "MinioConn forced in tempFoundation")
|
||||||
(error "VerpSecret forced in tempFoundation")
|
(error "VerpSecret forced in tempFoundation")
|
||||||
|
(error "AuthKey forced in tempFoundation")
|
||||||
|
|
||||||
runAppLoggingT tempFoundation $ do
|
runAppLoggingT tempFoundation $ do
|
||||||
$logInfoS "InstanceID" $ UUID.toText appInstanceID
|
$logInfoS "InstanceID" $ UUID.toText appInstanceID
|
||||||
@ -244,6 +246,7 @@ makeFoundation appSettings'@AppSettings{..} = do
|
|||||||
appJSONWebKeySet <- clusterSetting (Proxy :: Proxy 'ClusterJSONWebKeySet) `runSqlPool` sqlPool
|
appJSONWebKeySet <- clusterSetting (Proxy :: Proxy 'ClusterJSONWebKeySet) `runSqlPool` sqlPool
|
||||||
appClusterID <- clusterSetting (Proxy :: Proxy 'ClusterId) `runSqlPool` sqlPool
|
appClusterID <- clusterSetting (Proxy :: Proxy 'ClusterId) `runSqlPool` sqlPool
|
||||||
appVerpSecret <- clusterSetting (Proxy :: Proxy 'ClusterVerpSecret) `runSqlPool` sqlPool
|
appVerpSecret <- clusterSetting (Proxy :: Proxy 'ClusterVerpSecret) `runSqlPool` sqlPool
|
||||||
|
appAuthKey <- clusterSetting (Proxy :: Proxy 'ClusterAuthKey) `runSqlPool` sqlPool
|
||||||
|
|
||||||
appMemcached <- for appMemcachedConf $ \memcachedConf -> do
|
appMemcached <- for appMemcachedConf $ \memcachedConf -> do
|
||||||
$logDebugS "setup" "Memcached"
|
$logDebugS "setup" "Memcached"
|
||||||
@ -261,7 +264,7 @@ makeFoundation appSettings'@AppSettings{..} = do
|
|||||||
handleIf isBucketExists (const $ return ()) $ Minio.makeBucket appUploadCacheBucket Nothing
|
handleIf isBucketExists (const $ return ()) $ Minio.makeBucket appUploadCacheBucket Nothing
|
||||||
return conn
|
return conn
|
||||||
|
|
||||||
let foundation = mkFoundation sqlPool smtpPool ldapPool appCryptoIDKey appSessionStore appSecretBoxKey appWidgetMemcached appJSONWebKeySet appClusterID appMemcached appUploadCache appVerpSecret
|
let foundation = mkFoundation sqlPool smtpPool ldapPool appCryptoIDKey appSessionStore appSecretBoxKey appWidgetMemcached appJSONWebKeySet appClusterID appMemcached appUploadCache appVerpSecret appAuthKey
|
||||||
|
|
||||||
-- Return the foundation
|
-- Return the foundation
|
||||||
$logDebugS "setup" "Done"
|
$logDebugS "setup" "Done"
|
||||||
|
|||||||
@ -304,3 +304,6 @@ instance (MonadThrow m, MonadHandler m, HandlerSite m ~ UniWorX) => MonadCrypto
|
|||||||
|
|
||||||
instance {-# OVERLAPPING #-} (Monad m, MonadHandler m, HandlerSite m ~ UniWorX) => MonadSecretBox m where
|
instance {-# OVERLAPPING #-} (Monad m, MonadHandler m, HandlerSite m ~ UniWorX) => MonadSecretBox m where
|
||||||
secretBoxKey = getsYesod appSecretBoxKey
|
secretBoxKey = getsYesod appSecretBoxKey
|
||||||
|
|
||||||
|
instance {-# OVERLAPPING #-} (Monad m, MonadHandler m, HandlerSite m ~ UniWorX) => MonadAuth m where
|
||||||
|
authKey = getsYesod appAuthKey
|
||||||
|
|||||||
@ -65,6 +65,7 @@ instance BearerAuthSite UniWorX => YesodBreadcrumbs UniWorX where
|
|||||||
breadcrumb (StaticR _) = i18nCrumb MsgBreadcrumbStatic Nothing
|
breadcrumb (StaticR _) = i18nCrumb MsgBreadcrumbStatic Nothing
|
||||||
breadcrumb (WellKnownR _) = i18nCrumb MsgBreadcrumbWellKnown Nothing
|
breadcrumb (WellKnownR _) = i18nCrumb MsgBreadcrumbWellKnown Nothing
|
||||||
breadcrumb MetricsR = i18nCrumb MsgBreadcrumbMetrics Nothing
|
breadcrumb MetricsR = i18nCrumb MsgBreadcrumbMetrics Nothing
|
||||||
|
breadcrumb ErrorR = i18nCrumb MsgBreadcrumbError Nothing
|
||||||
|
|
||||||
breadcrumb NewsR = i18nCrumb MsgMenuNews Nothing
|
breadcrumb NewsR = i18nCrumb MsgMenuNews Nothing
|
||||||
breadcrumb UsersR = i18nCrumb MsgMenuUsers $ Just AdminR
|
breadcrumb UsersR = i18nCrumb MsgMenuUsers $ Just AdminR
|
||||||
|
|||||||
@ -6,7 +6,7 @@ module Foundation.Type
|
|||||||
, SomeSessionStorage(..)
|
, SomeSessionStorage(..)
|
||||||
, _SessionStorageMemcachedSql, _SessionStorageAcid
|
, _SessionStorageMemcachedSql, _SessionStorageAcid
|
||||||
, SMTPPool
|
, SMTPPool
|
||||||
, _appSettings', _appStatic, _appConnPool, _appSmtpPool, _appLdapPool, _appWidgetMemcached, _appHttpManager, _appLogger, _appLogSettings, _appCryptoIDKey, _appClusterID, _appInstanceID, _appJobState, _appSessionStore, _appSecretBoxKey, _appJSONWebKeySet, _appHealthReport
|
, _appSettings', _appStatic, _appConnPool, _appSmtpPool, _appLdapPool, _appWidgetMemcached, _appHttpManager, _appLogger, _appLogSettings, _appCryptoIDKey, _appClusterID, _appInstanceID, _appJobState, _appSessionStore, _appSecretBoxKey, _appJSONWebKeySet, _appHealthReport, _appMemcached, _appUploadCache, _appVerpSecret, _appAuthKey
|
||||||
, DB, Form, MsgRenderer, MailM, DBFile
|
, DB, Form, MsgRenderer, MailM, DBFile
|
||||||
) where
|
) where
|
||||||
|
|
||||||
@ -19,6 +19,7 @@ import Yesod.Core.Types (Logger)
|
|||||||
|
|
||||||
import qualified Crypto.Saltine.Core.SecretBox as SecretBox
|
import qualified Crypto.Saltine.Core.SecretBox as SecretBox
|
||||||
import qualified Crypto.Saltine.Core.AEAD as AEAD
|
import qualified Crypto.Saltine.Core.AEAD as AEAD
|
||||||
|
import qualified Crypto.Saltine.Core.Auth as Auth
|
||||||
import qualified Jose.Jwk as Jose
|
import qualified Jose.Jwk as Jose
|
||||||
|
|
||||||
import qualified Database.Memcached.Binary.IO as Memcached
|
import qualified Database.Memcached.Binary.IO as Memcached
|
||||||
@ -58,6 +59,7 @@ data UniWorX = UniWorX
|
|||||||
, appMemcached :: Maybe (AEAD.Key, Memcached.Connection)
|
, appMemcached :: Maybe (AEAD.Key, Memcached.Connection)
|
||||||
, appUploadCache :: Maybe MinioConn
|
, appUploadCache :: Maybe MinioConn
|
||||||
, appVerpSecret :: VerpSecret
|
, appVerpSecret :: VerpSecret
|
||||||
|
, appAuthKey :: Auth.Key
|
||||||
}
|
}
|
||||||
|
|
||||||
makeLenses_ ''UniWorX
|
makeLenses_ ''UniWorX
|
||||||
|
|||||||
@ -14,10 +14,13 @@ import Foundation.Routes
|
|||||||
|
|
||||||
import qualified Data.Aeson as JSON
|
import qualified Data.Aeson as JSON
|
||||||
import qualified Data.Text as Text
|
import qualified Data.Text as Text
|
||||||
|
|
||||||
|
import qualified Network.Wai as W
|
||||||
|
|
||||||
|
|
||||||
errorHandler :: ( MonadSecretBox (HandlerFor UniWorX)
|
errorHandler :: ( MonadSecretBox (HandlerFor UniWorX)
|
||||||
, MonadSecretBox (WidgetFor UniWorX)
|
, MonadSecretBox (WidgetFor UniWorX)
|
||||||
|
, MonadAuth (HandlerFor UniWorX)
|
||||||
, BearerAuthSite UniWorX
|
, BearerAuthSite UniWorX
|
||||||
, Button UniWorX ButtonSubmit
|
, Button UniWorX ButtonSubmit
|
||||||
, BackendCompatible SqlBackend (YesodPersistBackend UniWorX)
|
, BackendCompatible SqlBackend (YesodPersistBackend UniWorX)
|
||||||
@ -25,12 +28,35 @@ errorHandler :: ( MonadSecretBox (HandlerFor UniWorX)
|
|||||||
)
|
)
|
||||||
=> ErrorResponse -> HandlerFor UniWorX TypedContent
|
=> ErrorResponse -> HandlerFor UniWorX TypedContent
|
||||||
errorHandler err = do
|
errorHandler err = do
|
||||||
|
-- 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
|
||||||
|
|
||||||
shouldEncrypt <- do
|
shouldEncrypt <- do
|
||||||
canDecrypt <- (== Authorized) <$> evalAccess AdminErrMsgR True
|
canDecrypt <- (== Authorized) <$> evalAccess AdminErrMsgR True
|
||||||
shouldEncrypt <- getsYesod $ view _appEncryptErrors
|
shouldEncrypt <- getsYesod $ view _appEncryptErrors
|
||||||
return $ shouldEncrypt && not canDecrypt
|
return $ shouldEncrypt && not canDecrypt
|
||||||
|
|
||||||
sessErr <- bool return (_InternalError $ encodedSecretBox SecretBoxShort) shouldEncrypt err
|
sessErr <- bool return (_InternalError $ encodedSecretBox SecretBoxShort) shouldEncrypt err
|
||||||
|
|
||||||
|
void . runMaybeT $ do
|
||||||
|
reqHost <- MaybeT $ W.requestHeaderHost <$> waiRequest
|
||||||
|
approotHost <- MaybeT . getsYesod $ approotScopeHost ApprootDefault
|
||||||
|
when (approotHost /= reqHost) $ do
|
||||||
|
authErr <- lift $ encodedAuth sessErr
|
||||||
|
redirect (ErrorR, [(toPathPiece GetError, authErr)])
|
||||||
|
|
||||||
setSessionJson SessionError sessErr
|
setSessionJson SessionError sessErr
|
||||||
|
|
||||||
selectRep $ do
|
selectRep $ do
|
||||||
|
|||||||
12
src/Handler/Error.hs
Normal file
12
src/Handler/Error.hs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
module Handler.Error
|
||||||
|
( getErrorR
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
import Yesod.Core.Types (HandlerContents(HCError))
|
||||||
|
|
||||||
|
|
||||||
|
getErrorR :: Handler Void
|
||||||
|
getErrorR = do
|
||||||
|
encodedErrResponse <- maybe (redirect NewsR) return =<< lookupGlobalGetParam GetError
|
||||||
|
throwM . HCError =<< throwExceptT (encodedAuthVerify encodedErrResponse)
|
||||||
@ -67,6 +67,10 @@ dispatchHealthCheckMatchingClusterConfig
|
|||||||
ourSetting <- getsYesod appVerpSecret
|
ourSetting <- getsYesod appVerpSecret
|
||||||
dbSetting <- clusterSetting @'ClusterVerpSecret
|
dbSetting <- clusterSetting @'ClusterVerpSecret
|
||||||
return $ Just ourSetting == dbSetting
|
return $ Just ourSetting == dbSetting
|
||||||
|
clusterSettingMatches ClusterAuthKey = do
|
||||||
|
ourSetting <- getsYesod appAuthKey
|
||||||
|
dbSetting <- clusterSetting @'ClusterAuthKey
|
||||||
|
return $ Just ourSetting == dbSetting
|
||||||
|
|
||||||
|
|
||||||
clusterSetting :: forall key.
|
clusterSetting :: forall key.
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import qualified Data.Aeson as Aeson
|
|||||||
|
|
||||||
import qualified Crypto.Saltine.Core.SecretBox as SecretBox
|
import qualified Crypto.Saltine.Core.SecretBox as SecretBox
|
||||||
import qualified Crypto.Saltine.Core.AEAD as AEAD
|
import qualified Crypto.Saltine.Core.AEAD as AEAD
|
||||||
|
import qualified Crypto.Saltine.Core.Auth as Auth
|
||||||
import qualified Crypto.Saltine.Class as Saltine
|
import qualified Crypto.Saltine.Class as Saltine
|
||||||
|
|
||||||
import Data.CryptoID.ByteString (CryptoIDKey)
|
import Data.CryptoID.ByteString (CryptoIDKey)
|
||||||
@ -49,6 +50,7 @@ data ClusterSettingsKey
|
|||||||
| ClusterId
|
| ClusterId
|
||||||
| ClusterMemcachedKey
|
| ClusterMemcachedKey
|
||||||
| ClusterVerpSecret
|
| ClusterVerpSecret
|
||||||
|
| ClusterAuthKey
|
||||||
deriving (Eq, Ord, Enum, Bounded, Show, Read)
|
deriving (Eq, Ord, Enum, Bounded, Show, Read)
|
||||||
|
|
||||||
instance Universe ClusterSettingsKey
|
instance Universe ClusterSettingsKey
|
||||||
@ -98,9 +100,9 @@ instance ToJSON AEAD.Key where
|
|||||||
toJSON = Aeson.String . decodeUtf8 . Base64.encode . Saltine.encode
|
toJSON = Aeson.String . decodeUtf8 . Base64.encode . Saltine.encode
|
||||||
|
|
||||||
instance FromJSON AEAD.Key where
|
instance FromJSON AEAD.Key where
|
||||||
parseJSON = Aeson.withText "Key" $ \t -> do
|
parseJSON = Aeson.withText "AEAD.Key" $ \t -> do
|
||||||
bytes <- either fail return . Base64.decode $ encodeUtf8 t
|
bytes <- either fail return . Base64.decode $ encodeUtf8 t
|
||||||
maybe (fail "Could not parse key") return $ Saltine.decode bytes
|
maybe (fail "Could not parse AEAD.Key") return $ Saltine.decode bytes
|
||||||
|
|
||||||
|
|
||||||
instance ClusterSetting 'ClusterSecretBoxKey where
|
instance ClusterSetting 'ClusterSecretBoxKey where
|
||||||
@ -112,9 +114,9 @@ instance ToJSON SecretBox.Key where
|
|||||||
toJSON = Aeson.String . decodeUtf8 . Base64.encode . Saltine.encode
|
toJSON = Aeson.String . decodeUtf8 . Base64.encode . Saltine.encode
|
||||||
|
|
||||||
instance FromJSON SecretBox.Key where
|
instance FromJSON SecretBox.Key where
|
||||||
parseJSON = Aeson.withText "Key" $ \t -> do
|
parseJSON = Aeson.withText "SecretBox.Key" $ \t -> do
|
||||||
bytes <- either fail return . Base64.decode $ encodeUtf8 t
|
bytes <- either fail return . Base64.decode $ encodeUtf8 t
|
||||||
maybe (fail "Could not parse key") return $ Saltine.decode bytes
|
maybe (fail "Could not parse SecretBox.Key") return $ Saltine.decode bytes
|
||||||
|
|
||||||
|
|
||||||
instance ClusterSetting 'ClusterJSONWebKeySet where
|
instance ClusterSetting 'ClusterJSONWebKeySet where
|
||||||
@ -152,3 +154,16 @@ instance ClusterSetting 'ClusterVerpSecret where
|
|||||||
type ClusterSettingValue 'ClusterVerpSecret = VerpSecret
|
type ClusterSettingValue 'ClusterVerpSecret = VerpSecret
|
||||||
initClusterSetting _ = liftIO $ Crypto.getRandomBytes 16
|
initClusterSetting _ = liftIO $ Crypto.getRandomBytes 16
|
||||||
knownClusterSetting _ = ClusterVerpSecret
|
knownClusterSetting _ = ClusterVerpSecret
|
||||||
|
|
||||||
|
instance ToJSON Auth.Key where
|
||||||
|
toJSON = Aeson.String . decodeUtf8 . Base64.encode . Saltine.encode
|
||||||
|
|
||||||
|
instance FromJSON Auth.Key where
|
||||||
|
parseJSON = Aeson.withText "Auth.Key" $ \t -> do
|
||||||
|
bytes <- either fail return . Base64.decode $ encodeUtf8 t
|
||||||
|
maybe (fail "Could not parse Auth.Key") return $ Saltine.decode bytes
|
||||||
|
|
||||||
|
instance ClusterSetting 'ClusterAuthKey where
|
||||||
|
type ClusterSettingValue 'ClusterAuthKey = Auth.Key
|
||||||
|
initClusterSetting _ = liftIO $ Auth.newKey
|
||||||
|
knownClusterSetting _ = ClusterAuthKey
|
||||||
|
|||||||
57
src/Utils.hs
57
src/Utils.hs
@ -83,6 +83,7 @@ import Data.Universe
|
|||||||
import qualified Crypto.Saltine.Internal.ByteSizes as Saltine
|
import qualified Crypto.Saltine.Internal.ByteSizes as Saltine
|
||||||
import qualified Data.ByteString.Base64.URL as Base64
|
import qualified Data.ByteString.Base64.URL as Base64
|
||||||
import qualified Crypto.Saltine.Core.SecretBox as SecretBox
|
import qualified Crypto.Saltine.Core.SecretBox as SecretBox
|
||||||
|
import qualified Crypto.Saltine.Core.Auth as Auth
|
||||||
import qualified Crypto.Saltine.Class as Saltine
|
import qualified Crypto.Saltine.Class as Saltine
|
||||||
import qualified Crypto.Data.PKCS7 as PKCS7
|
import qualified Crypto.Data.PKCS7 as PKCS7
|
||||||
import Crypto.MAC.KMAC (KMAC, HashSHAKE)
|
import Crypto.MAC.KMAC (KMAC, HashSHAKE)
|
||||||
@ -1141,6 +1142,62 @@ encodedSecretBoxOpen ciphertext = do
|
|||||||
sKey <- secretBoxKey
|
sKey <- secretBoxKey
|
||||||
encodedSecretBoxOpen' sKey ciphertext
|
encodedSecretBoxOpen' sKey ciphertext
|
||||||
|
|
||||||
|
encodedAuthSep :: Text
|
||||||
|
encodedAuthSep = "."
|
||||||
|
|
||||||
|
encodedAuth' :: ToJSON a
|
||||||
|
=> Auth.Key
|
||||||
|
-> a -> Text
|
||||||
|
encodedAuth' aKey val = base64 msg <> encodedAuthSep <> base64 (Saltine.encode auth)
|
||||||
|
where msg = toStrict $ Aeson.encode val
|
||||||
|
auth = Auth.auth aKey msg
|
||||||
|
base64 = decodeUtf8 . Base64.encodeUnpadded
|
||||||
|
|
||||||
|
data EncodedAuthException
|
||||||
|
= EncodedAuthInvalidSeparation
|
||||||
|
| EncodedAuthInvalidBase64 !String
|
||||||
|
| EncodedAuthCouldNotDecodeAuthenticator
|
||||||
|
| EncodedAuthInvalidAuthenticator
|
||||||
|
| EncodedAuthCouldNotDecodePlaintext !String
|
||||||
|
deriving (Eq, Ord, Read, Show, Generic, Typeable)
|
||||||
|
deriving anyclass (Exception)
|
||||||
|
|
||||||
|
encodedAuthVerify' :: (FromJSON a, MonadError EncodedAuthException m)
|
||||||
|
=> Auth.Key
|
||||||
|
-> Text -> m a
|
||||||
|
encodedAuthVerify' aKey bothEncoded = do
|
||||||
|
(msgEncoded, authEncoded) <- case Text.splitOn encodedAuthSep bothEncoded of
|
||||||
|
[msgEncoded, authEncoded] -> return (msgEncoded, authEncoded)
|
||||||
|
_other -> throwError EncodedAuthInvalidSeparation
|
||||||
|
authBS <- either (throwError . EncodedAuthInvalidBase64) return . Base64.decode $ encodeUtf8 authEncoded
|
||||||
|
auth <- maybe (throwError EncodedAuthCouldNotDecodeAuthenticator) return $ Saltine.decode authBS
|
||||||
|
msgDecoded <- either (throwError . EncodedAuthInvalidBase64) return . Base64.decode $ encodeUtf8 msgEncoded
|
||||||
|
unless (Auth.verify aKey auth msgDecoded) $
|
||||||
|
throwError EncodedAuthInvalidAuthenticator
|
||||||
|
either (throwError . EncodedAuthCouldNotDecodePlaintext) return $ Aeson.eitherDecodeStrict' msgDecoded
|
||||||
|
|
||||||
|
class Monad m => MonadAuth m where
|
||||||
|
authKey :: m Auth.Key
|
||||||
|
|
||||||
|
instance MonadAuth ((->) Auth.Key) where
|
||||||
|
authKey = id
|
||||||
|
|
||||||
|
instance Monad m => MonadAuth (ReaderT Auth.Key m) where
|
||||||
|
authKey = ask
|
||||||
|
|
||||||
|
encodedAuth :: ( ToJSON a, MonadAuth m )
|
||||||
|
=> a -> m Text
|
||||||
|
encodedAuth val = do
|
||||||
|
aKey <- authKey
|
||||||
|
return $ encodedAuth' aKey val
|
||||||
|
|
||||||
|
encodedAuthVerify :: ( FromJSON a, MonadError EncodedAuthException m, MonadAuth m )
|
||||||
|
=> Text -> m a
|
||||||
|
encodedAuthVerify bothEncoded = do
|
||||||
|
aKey <- authKey
|
||||||
|
encodedAuthVerify' aKey bothEncoded
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
kmaclazy :: forall a string key ba chunk.
|
kmaclazy :: forall a string key ba chunk.
|
||||||
( HashSHAKE a
|
( HashSHAKE a
|
||||||
|
|||||||
@ -21,7 +21,14 @@ import Data.Universe
|
|||||||
import Control.Monad.Trans.Maybe (MaybeT(..))
|
import Control.Monad.Trans.Maybe (MaybeT(..))
|
||||||
|
|
||||||
|
|
||||||
data GlobalGetParam = GetLang | GetReferer | GetBearer | GetRecipient | GetCsvExampleData | GetDryRun | GetDownload
|
data GlobalGetParam = GetLang
|
||||||
|
| GetReferer
|
||||||
|
| GetBearer
|
||||||
|
| GetRecipient
|
||||||
|
| GetCsvExampleData
|
||||||
|
| GetDryRun
|
||||||
|
| GetDownload
|
||||||
|
| GetError
|
||||||
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic)
|
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic)
|
||||||
deriving anyclass (Universe, Finite)
|
deriving anyclass (Universe, Finite)
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ module Web.ServerSession.Frontend.Yesod.Jwt
|
|||||||
-- serversession-frontend-yesod-1.0@sha256:8ddb112a1ef6ee863f5ea13978dd08e1c39444c1a252f775a780013430bcc884,1230
|
-- serversession-frontend-yesod-1.0@sha256:8ddb112a1ef6ee863f5ea13978dd08e1c39444c1a252f775a780013430bcc884,1230
|
||||||
|
|
||||||
|
|
||||||
import Import.NoModel hiding (State, state, Header, deleteCookie)
|
import Import.NoModel hiding (State, state, Header, deleteCookie, authKey)
|
||||||
import Yesod.Core.Types
|
import Yesod.Core.Types
|
||||||
import Model.Types.Common
|
import Model.Types.Common
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user