@@ -25,8 +25,6 @@ module Spar.App
25
25
( Spar (.. ),
26
26
Env (.. ),
27
27
toLevel ,
28
- wrapMonadClientWithEnv ,
29
- wrapMonadClient ,
30
28
wrapMonadClientSem ,
31
29
verdictHandler ,
32
30
GetUserResult (.. ),
@@ -41,14 +39,12 @@ module Spar.App
41
39
deleteTeam ,
42
40
wrapSpar ,
43
41
liftSem ,
44
- liftMonadClient ,
45
42
)
46
43
where
47
44
48
45
import Bilge
49
46
import Brig.Types (ManagedBy (.. ), User , userId , userTeam )
50
47
import Brig.Types.Intra (AccountStatus (.. ), accountStatus , accountUser )
51
- import Cassandra
52
48
import qualified Cassandra as Cas
53
49
import Control.Exception (assert )
54
50
import Control.Lens hiding ((.=) )
@@ -68,7 +64,9 @@ import Imports hiding (log)
68
64
import qualified Network.HTTP.Types.Status as Http
69
65
import qualified Network.Wai.Utilities.Error as Wai
70
66
import Polysemy
67
+ import Polysemy.Error
71
68
import Polysemy.Final
69
+ import qualified Polysemy.Reader as ReaderEff
72
70
import SAML2.Util (renderURI )
73
71
import SAML2.WebSSO
74
72
( Assertion (.. ),
@@ -84,7 +82,6 @@ import SAML2.WebSSO
84
82
SPStoreIdP (getIdPConfigByIssuerOptionalSPId ),
85
83
UnqualifiedNameID (.. ),
86
84
explainDeniedReason ,
87
- fromTime ,
88
85
idpExtraInfo ,
89
86
idpId ,
90
87
uidTenant ,
@@ -93,11 +90,20 @@ import qualified SAML2.WebSSO as SAML
93
90
import qualified SAML2.WebSSO.Types.Email as SAMLEmail
94
91
import Servant
95
92
import qualified Servant.Multipart as Multipart
96
- import qualified Spar.Data as Data hiding ( deleteSAMLUser , deleteSAMLUsersByIssuer , getIdPConfig , getSAMLAnyUserByIssuer , getSAMLSomeUsersByIssuer , getSAMLUser , insertSAMLUser , storeIdPConfig )
93
+ import qualified Spar.Data as Data ( GetIdPResult ( .. ) )
97
94
import Spar.Error
98
95
import qualified Spar.Intra.Brig as Intra
99
96
import qualified Spar.Intra.Galley as Intra
100
97
import Spar.Orphans ()
98
+ import Spar.Sem.AReqIDStore (AReqIDStore )
99
+ import qualified Spar.Sem.AReqIDStore as AReqIDStore
100
+ import Spar.Sem.AReqIDStore.Cassandra (aReqIDStoreToCassandra , ttlErrorToSparError )
101
+ import Spar.Sem.AssIDStore (AssIDStore )
102
+ import qualified Spar.Sem.AssIDStore as AssIDStore
103
+ import Spar.Sem.AssIDStore.Cassandra (assIDStoreToCassandra )
104
+ import Spar.Sem.BindCookieStore (BindCookieStore )
105
+ import qualified Spar.Sem.BindCookieStore as BindCookieStore
106
+ import Spar.Sem.BindCookieStore.Cassandra (bindCookieStoreToCassandra )
101
107
import Spar.Sem.DefaultSsoCode (DefaultSsoCode )
102
108
import Spar.Sem.DefaultSsoCode.Cassandra (defaultSsoCodeToCassandra )
103
109
import Spar.Sem.IdP (GetIdPResult (.. ))
@@ -186,15 +192,15 @@ toLevel = \case
186
192
SAML. Debug -> Log. Debug
187
193
SAML. Trace -> Log. Trace
188
194
189
- instance SPStoreID AuthnRequest (Spar r ) where
190
- storeID i r = wrapMonadClientWithEnv $ Data. storeAReqID i r
191
- unStoreID r = wrapMonadClient $ Data. unStoreAReqID r
192
- isAliveID r = wrapMonadClient $ Data. isAliveAReqID r
195
+ instance Member AReqIDStore r => SPStoreID AuthnRequest (Spar r ) where
196
+ storeID i r = wrapMonadClientSem $ AReqIDStore. store i r
197
+ unStoreID r = wrapMonadClientSem $ AReqIDStore. unStore r
198
+ isAliveID r = wrapMonadClientSem $ AReqIDStore. isAlive r
193
199
194
- instance SPStoreID Assertion (Spar r ) where
195
- storeID i r = wrapMonadClientWithEnv $ Data. storeAssID i r
196
- unStoreID r = wrapMonadClient $ Data. unStoreAssID r
197
- isAliveID r = wrapMonadClient $ Data. isAliveAssID r
200
+ instance Member AssIDStore r => SPStoreID Assertion (Spar r ) where
201
+ storeID i r = wrapMonadClientSem $ AssIDStore. store i r
202
+ unStoreID r = wrapMonadClientSem $ AssIDStore. unStore r
203
+ isAliveID r = wrapMonadClientSem $ AssIDStore. isAlive r
198
204
199
205
instance Member IdPEffect. IdP r => SPStoreIdP SparError (Spar r ) where
200
206
type IdPConfigExtra (Spar r ) = WireIdP
@@ -215,13 +221,6 @@ instance Member IdPEffect.IdP r => SPStoreIdP SparError (Spar r) where
215
221
res@ (Data. GetIdPNonUnique _) -> throwSpar $ SparIdPNotFound (cs $ show res)
216
222
res@ (Data. GetIdPWrongTeam _) -> throwSpar $ SparIdPNotFound (cs $ show res)
217
223
218
- -- | 'wrapMonadClient' with an 'Env' in a 'ReaderT', and exceptions. If you
219
- -- don't need either of those, 'wrapMonadClient' will suffice.
220
- wrapMonadClientWithEnv :: forall r a . ReaderT Data. Env (ExceptT TTLError Cas. Client ) a -> Spar r a
221
- wrapMonadClientWithEnv action = do
222
- denv <- Data. mkEnv <$> (sparCtxOpts <$> ask) <*> (fromTime <$> getNow)
223
- either (throwSpar . SparCassandraTTLError ) pure =<< wrapMonadClient (runExceptT $ action `runReaderT` denv)
224
-
225
224
instance Member (Final IO ) r => Catch. MonadThrow (Sem r ) where
226
225
throwM = embedFinal . Catch. throwM @ IO
227
226
@@ -232,22 +231,6 @@ instance Member (Final IO) r => Catch.MonadCatch (Sem r) where
232
231
handler' <- bindS handler
233
232
pure $ m' `Catch.catch` \ e -> handler' $ e <$ st
234
233
235
- -- | Call a cassandra command in the 'Spar' monad. Catch all exceptions and re-throw them as 500 in
236
- -- Handler.
237
- wrapMonadClient :: Cas. Client a -> Spar r a
238
- wrapMonadClient action =
239
- Spar $ do
240
- ctx <- asks sparCtxCas
241
- fromSpar $ wrapMonadClientSem $ embedFinal @ IO $ runClient ctx action
242
-
243
- -- | Lift a cassandra command into the 'Spar' monad. Like 'wrapMonadClient',
244
- -- but doesn't catch any exceptions.
245
- liftMonadClient :: Cas. Client a -> Spar r a
246
- liftMonadClient action =
247
- Spar $ do
248
- ctx <- asks sparCtxCas
249
- lift $ lift $ embedFinal @ IO $ runClient ctx action
250
-
251
234
-- | Call a 'Sem' command in the 'Spar' monad. Catch all (IO) exceptions and
252
235
-- re-throw them as 500 in Handler.
253
236
wrapMonadClientSem :: Sem r a -> Spar r a
@@ -425,7 +408,27 @@ bindUser buid userref = do
425
408
Ephemeral -> err oldStatus
426
409
PendingInvitation -> Intra. setStatus buid Active
427
410
428
- instance (r ~ '[ScimExternalIdStore , ScimUserTimesStore , ScimTokenStore , DefaultSsoCode , IdPEffect. IdP , SAMLUserStore , Embed (Cas. Client ), Embed IO , Final IO ]) => SPHandler SparError (Spar r ) where
411
+ instance
412
+ ( r
413
+ ~ '[ BindCookieStore ,
414
+ AssIDStore ,
415
+ AReqIDStore ,
416
+ ScimExternalIdStore ,
417
+ ScimUserTimesStore ,
418
+ ScimTokenStore ,
419
+ DefaultSsoCode ,
420
+ IdPEffect. IdP ,
421
+ SAMLUserStore ,
422
+ Embed (Cas. Client ),
423
+ ReaderEff. Reader Opts ,
424
+ Error TTLError ,
425
+ Error SparError ,
426
+ Embed IO ,
427
+ Final IO
428
+ ]
429
+ ) =>
430
+ SPHandler SparError (Spar r )
431
+ where
429
432
type NTCTX (Spar r ) = Env
430
433
nt :: forall a . Env -> Spar r a -> Handler a
431
434
nt ctx (Spar action) = do
@@ -434,18 +437,25 @@ instance (r ~ '[ScimExternalIdStore, ScimUserTimesStore, ScimTokenStore, Default
434
437
where
435
438
actionHandler :: Handler (Either SparError a )
436
439
actionHandler =
437
- liftIO $
438
- runFinal $
439
- embedToFinal @ IO $
440
- interpretClientToIO (sparCtxCas ctx) $
441
- samlUserStoreToCassandra @ Cas. Client $
442
- idPToCassandra @ Cas. Client $
443
- defaultSsoCodeToCassandra $
444
- scimTokenStoreToCassandra $
445
- scimUserTimesStoreToCassandra $
446
- scimExternalIdStoreToCassandra $
447
- runExceptT $
448
- runReaderT action ctx
440
+ fmap join $
441
+ liftIO $
442
+ runFinal $
443
+ embedToFinal @ IO $
444
+ runError @ SparError $
445
+ ttlErrorToSparError $
446
+ ReaderEff. runReader (sparCtxOpts ctx) $
447
+ interpretClientToIO (sparCtxCas ctx) $
448
+ samlUserStoreToCassandra @ Cas. Client $
449
+ idPToCassandra @ Cas. Client $
450
+ defaultSsoCodeToCassandra $
451
+ scimTokenStoreToCassandra $
452
+ scimUserTimesStoreToCassandra $
453
+ scimExternalIdStoreToCassandra $
454
+ aReqIDStoreToCassandra $
455
+ assIDStoreToCassandra $
456
+ bindCookieStoreToCassandra $
457
+ runExceptT $
458
+ runReaderT action ctx
449
459
throwErrorAsHandlerException :: Either SparError a -> Handler a
450
460
throwErrorAsHandlerException (Left err) =
451
461
sparToServerErrorWithLogging (sparCtxLogger ctx) err >>= throwError
@@ -475,14 +485,21 @@ instance Intra.MonadSparToGalley (Spar r) where
475
485
-- signed in-response-to info in the assertions matches the unsigned in-response-to field in the
476
486
-- 'SAML.Response', and fills in the response id in the header if missing, we can just go for the
477
487
-- latter.
478
- verdictHandler :: HasCallStack => Members '[ScimTokenStore , IdPEffect. IdP , SAMLUserStore ] r => Maybe BindCookie -> Maybe TeamId -> SAML. AuthnResponse -> SAML. AccessVerdict -> Spar r SAML. ResponseVerdict
488
+ verdictHandler ::
489
+ HasCallStack =>
490
+ Members '[BindCookieStore , AReqIDStore , ScimTokenStore , IdPEffect. IdP , SAMLUserStore ] r =>
491
+ Maybe BindCookie ->
492
+ Maybe TeamId ->
493
+ SAML. AuthnResponse ->
494
+ SAML. AccessVerdict ->
495
+ Spar r SAML. ResponseVerdict
479
496
verdictHandler cky mbteam aresp verdict = do
480
497
-- [3/4.1.4.2]
481
498
-- <SubjectConfirmation> [...] If the containing message is in response to an <AuthnRequest>, then
482
499
-- the InResponseTo attribute MUST match the request's ID.
483
500
SAML. logger SAML. Debug $ " entering verdictHandler: " <> show (fromBindCookie <$> cky, aresp, verdict)
484
501
reqid <- either (throwSpar . SparNoRequestRefInResponse . cs) pure $ SAML. rspInResponseTo aresp
485
- format :: Maybe VerdictFormat <- wrapMonadClient $ Data . getVerdictFormat reqid
502
+ format :: Maybe VerdictFormat <- wrapMonadClientSem $ AReqIDStore . getVerdictFormat reqid
486
503
resp <- case format of
487
504
Just (VerdictFormatWeb ) ->
488
505
verdictHandlerResult cky mbteam verdict >>= verdictHandlerWeb
@@ -500,7 +517,13 @@ data VerdictHandlerResult
500
517
| VerifyHandlerError { _vhrLabel :: ST , _vhrMessage :: ST }
501
518
deriving (Eq , Show )
502
519
503
- verdictHandlerResult :: HasCallStack => Members '[ScimTokenStore , IdPEffect. IdP , SAMLUserStore ] r => Maybe BindCookie -> Maybe TeamId -> SAML. AccessVerdict -> Spar r VerdictHandlerResult
520
+ verdictHandlerResult ::
521
+ HasCallStack =>
522
+ Members '[BindCookieStore , ScimTokenStore , IdPEffect. IdP , SAMLUserStore ] r =>
523
+ Maybe BindCookie ->
524
+ Maybe TeamId ->
525
+ SAML. AccessVerdict ->
526
+ Spar r VerdictHandlerResult
504
527
verdictHandlerResult bindCky mbteam verdict = do
505
528
SAML. logger SAML. Debug $ " entering verdictHandlerResult: " <> show (fromBindCookie <$> bindCky)
506
529
result <- catchVerdictErrors $ verdictHandlerResultCore bindCky mbteam verdict
@@ -539,13 +562,19 @@ moveUserToNewIssuer oldUserRef newUserRef uid = do
539
562
Intra. setBrigUserVeid uid (UrefOnly newUserRef)
540
563
wrapMonadClientSem $ SAMLUserStore. delete uid oldUserRef
541
564
542
- verdictHandlerResultCore :: HasCallStack => Members '[ScimTokenStore , IdPEffect. IdP , SAMLUserStore ] r => Maybe BindCookie -> Maybe TeamId -> SAML. AccessVerdict -> Spar r VerdictHandlerResult
565
+ verdictHandlerResultCore ::
566
+ HasCallStack =>
567
+ Members '[BindCookieStore , ScimTokenStore , IdPEffect. IdP , SAMLUserStore ] r =>
568
+ Maybe BindCookie ->
569
+ Maybe TeamId ->
570
+ SAML. AccessVerdict ->
571
+ Spar r VerdictHandlerResult
543
572
verdictHandlerResultCore bindCky mbteam = \ case
544
573
SAML. AccessDenied reasons -> do
545
574
pure $ VerifyHandlerDenied reasons
546
575
SAML. AccessGranted userref -> do
547
576
uid :: UserId <- do
548
- viaBindCookie <- maybe (pure Nothing ) (wrapMonadClient . Data. lookupBindCookie ) bindCky
577
+ viaBindCookie <- maybe (pure Nothing ) (wrapMonadClientSem . BindCookieStore. lookup ) bindCky
549
578
viaSparCassandra <- getUserIdByUref mbteam userref
550
579
-- race conditions: if the user has been created on spar, but not on brig, 'getUser'
551
580
-- returns 'Nothing'. this is ok assuming 'createUser', 'bindUser' (called below) are
0 commit comments