diff options
Diffstat (limited to 'overlays/spm/server/Spm/Server.hs')
-rw-r--r-- | overlays/spm/server/Spm/Server.hs | 24 |
1 files changed, 13 insertions, 11 deletions
diff --git a/overlays/spm/server/Spm/Server.hs b/overlays/spm/server/Spm/Server.hs index df12e3d8..8e7f8786 100644 --- a/overlays/spm/server/Spm/Server.hs +++ b/overlays/spm/server/Spm/Server.hs | |||
@@ -44,7 +44,7 @@ import Database.Persist | |||
44 | import Database.Persist.Postgresql | 44 | import Database.Persist.Postgresql |
45 | import UnliftIO.Pool | 45 | import UnliftIO.Pool |
46 | 46 | ||
47 | import Control.Monad.Trans.Reader (ReaderT, runReaderT) | 47 | import Control.Monad.Trans.Reader (ReaderT, runReaderT, mapReaderT) |
48 | 48 | ||
49 | import Control.Monad.Logger | 49 | import Control.Monad.Logger |
50 | 50 | ||
@@ -58,12 +58,12 @@ import qualified Data.UUID as UUID | |||
58 | import qualified Data.UUID.V4 as UUID | 58 | import qualified Data.UUID.V4 as UUID |
59 | 59 | ||
60 | import qualified Data.Aeson as JSON | 60 | import qualified Data.Aeson as JSON |
61 | import Data.Aeson.Lens (_JSON) | ||
62 | 61 | ||
63 | import System.FilePath ((</>), isRelative) | 62 | import System.FilePath ((</>), isRelative) |
64 | 63 | ||
65 | import Crypto.JOSE.JWK hiding (Context) | 64 | import Crypto.JOSE.JWK hiding (Context) |
66 | import Crypto.JOSE.JWK.Instances () | 65 | import Crypto.JOSE.JWK.Instances () |
66 | import Crypto.JOSE.Error.Instances () | ||
67 | 67 | ||
68 | import Crypto.Random.Instances () | 68 | import Crypto.Random.Instances () |
69 | import qualified Crypto.Random as Crypto | 69 | import qualified Crypto.Random as Crypto |
@@ -197,10 +197,13 @@ mkSpmApp = do | |||
197 | spmSql :: ReaderT SqlBackend Handler' a -> Handler' a | 197 | spmSql :: ReaderT SqlBackend Handler' a -> Handler' a |
198 | spmSql act = do | 198 | spmSql act = do |
199 | sqlPool <- view sctxSqlPool | 199 | sqlPool <- view sctxSqlPool |
200 | withResource sqlPool $ runReaderT act | 200 | mapReaderT (mapLoggingT $ either throwError pure <=< liftIO) . withResource sqlPool $ mapReaderT (mapLoggingT runHandler) . runReaderT act |
201 | 201 | ||
202 | spmJWT :: forall error a. Show error => ServerError -> ExceptT error IO a -> Handler' a | 202 | spmJWT :: forall error a. Show error => ServerError -> JOSE error IO a -> Handler' a |
203 | spmJWT errTemplate = either (\err -> throwError errTemplate{ errBody = LBS.fromStrict . Text.encodeUtf8 . Text.pack $ show err }) return <=< liftIO . runExceptT | 203 | spmJWT errTemplate = either (\err -> throwError errTemplate{ errBody = LBS.fromStrict . Text.encodeUtf8 . Text.pack $ show err }) return <=< liftIO . runJOSE |
204 | |||
205 | withJOSE :: forall m e e' a. Functor m => (e -> e') -> JOSE e m a -> JOSE e' m a | ||
206 | withJOSE f = JOSE . withExceptT f . unwrapJOSE | ||
204 | 207 | ||
205 | generateLocal :: MonadIO m => SpmStyle -> m MailLocal | 208 | generateLocal :: MonadIO m => SpmStyle -> m MailLocal |
206 | generateLocal SpmWords = fmap (review _Wrapped . CI.mk) . liftIO $ do | 209 | generateLocal SpmWords = fmap (review _Wrapped . CI.mk) . liftIO $ do |
@@ -254,24 +257,23 @@ spmServer dom mbox = whoami | |||
254 | instanceId' <- view sctxInstanceId | 257 | instanceId' <- view sctxInstanceId |
255 | jwks <- view $ sctxJwkSet . _Wrapped | 258 | jwks <- view $ sctxJwkSet . _Wrapped |
256 | tokenId <- liftIO UUID.nextRandom | 259 | tokenId <- liftIO UUID.nextRandom |
257 | let claimsSet = emptyClaimsSet | 260 | let jwtClaims = (_SpmJWTLocal # local) |
258 | & claimIss ?~ (JWT.string # UUID.toText instanceId') | 261 | & claimIss ?~ (JWT.string # UUID.toText instanceId') |
259 | & claimAud ?~ Audience (pure $ dom ^. _Wrapped . to CI.original . re JWT.string) | 262 | & claimAud ?~ Audience (pure $ dom ^. _Wrapped . to CI.original . re JWT.string) |
260 | & claimNbf ?~ NumericDate t | 263 | & claimNbf ?~ NumericDate t |
261 | & claimIat ?~ NumericDate t | 264 | & claimIat ?~ NumericDate t |
262 | & claimExp ?~ NumericDate (600 `addUTCTime` t) | 265 | & claimExp ?~ NumericDate (600 `addUTCTime` t) |
263 | & claimJti ?~ UUID.toText tokenId | 266 | & claimJti ?~ UUID.toText tokenId |
264 | & unregisteredClaims . at "local" ?~ view (_Wrapped . re _JSON) local | ||
265 | spmJWT @JWT.Error err500 $ do | 267 | spmJWT @JWT.Error err500 $ do |
266 | (jwsAlg, selectedJwk) <- withExceptT (fromMaybe JWT.NoUsableKeys . getFirst) . asum $ map (\jwk' -> (, jwk') <$> withExceptT (First . Just) (bestJWSAlg jwk')) jwks | 268 | (jwsAlg, selectedJwk) <- withJOSE (fromMaybe JWT.NoUsableKeys . getFirst) . asum $ map (\jwk' -> (, jwk') <$> withJOSE (First . Just) (bestJWSAlg jwk')) jwks |
267 | signClaims selectedJwk (newJWSHeader ((), jwsAlg)) claimsSet | 269 | signJWT selectedJwk (newJWSHeader ((), jwsAlg)) jwtClaims |
268 | 270 | ||
269 | claim jwt = do | 271 | claim jwt = do |
270 | jwks <- view sctxJwkSet | 272 | jwks <- view sctxJwkSet |
271 | let validationSettings' = defaultJWTValidationSettings ((== Just dom) . fmap (review _Wrapped . CI.mk) . preview JWT.string) | 273 | let validationSettings' = defaultJWTValidationSettings ((== Just dom) . fmap (review _Wrapped . CI.mk) . preview JWT.string) |
272 | & jwtValidationSettingsAllowedSkew .~ 5 | 274 | & jwtValidationSettingsAllowedSkew .~ 5 |
273 | claims <- spmJWT @JWT.JWTError err403 $ verifyClaims validationSettings' jwks jwt | 275 | jwtClaims <- spmJWT @JWT.JWTError err403 $ verifyJWT validationSettings' jwks jwt |
274 | mailboxMappingLocal <- maybe (throwError err400{ errBody = "Claim ‘local’ missing" }) (return . Just) $ claims ^? unregisteredClaims . ix "local" . _JSON . to CI.mk . re _Wrapped | 276 | let mailboxMappingLocal = Just $ jwtClaims ^. _spmjwtLocal . _Wrapped . _Unwrapped |
275 | 277 | ||
276 | spmSql $ do | 278 | spmSql $ do |
277 | Entity mailboxMappingMailbox _ <- maybe (throwError err404) return <=< getBy $ UniqueMailbox mbox | 279 | Entity mailboxMappingMailbox _ <- maybe (throwError err404) return <=< getBy $ UniqueMailbox mbox |