Skip to content

Commit 34e693b

Browse files
authored
Polysemy: Separate more Cassandra effects from Spar (#1792)
* Make sure to actually wrap the action in 'wrapMonadClientSem' * Implement wrapMonadClient in terms of wrapMonadClientSem * Pull out IdP effect * Push Member IdP constraints throughout * Pull application logic out of Data and into App * Use application-level functions instead * Remove deleteTeam from Data too * Get rid of wrapMonadClientWithEnvSem * Implement wrapSpar * Undo accidental formatting * Update cabal * make format * Update changelog * Get rid of the untouchable variable in liftSem * Be very careful about wrapping in the same places * Resort exports * Changelog * DefaultSsoCode effect * ScimTokenStore effect * wip BindCookie effect * Forgot some callsites * Get tests compiling again * Get everything compiling * remove runSparCassSem * Change the tests to use IdP * Finish all SAMLUser and IdP effects refs in tests * Excise all references to IdP and SAMLUser effects * make format * make format * Remove all references to new effects * make format * Add ScimUserTimesStore effect * ScimExternalIdStore effect * make format * Implement scimExternalIdStoreToCassandra * Use Members when appropriate * make format * Fixes. * Remove unwritten BindCookie effect modules * SAMLUser -> SAMLUserStore * Don't do extraneous lifting * Changelog.d
1 parent c4a6c29 commit 34e693b

File tree

23 files changed

+383
-173
lines changed

23 files changed

+383
-173
lines changed

changelog.d/5-internal/misc-effects

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Pull more polysemy effects out of Spar.

services/spar/spar.cabal

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ cabal-version: 1.12
44
--
55
-- see: https://github.com/sol/hpack
66
--
7-
-- hash: 920daea26e271c6f0d5688476b71beb67c388d49d00e6f57723aaf825eb3df0d
7+
-- hash: f135fc7f6d1e85694286faaeb615c1808dedee43fd207e59f50864481c2a9cca
88

99
name: spar
1010
version: 0.1
@@ -34,11 +34,19 @@ library
3434
Spar.Scim.Auth
3535
Spar.Scim.Types
3636
Spar.Scim.User
37+
Spar.Sem.DefaultSsoCode
38+
Spar.Sem.DefaultSsoCode.Cassandra
3739
Spar.Sem.IdP
3840
Spar.Sem.IdP.Cassandra
3941
Spar.Sem.IdP.Mem
40-
Spar.Sem.SAMLUser
41-
Spar.Sem.SAMLUser.Cassandra
42+
Spar.Sem.SAMLUserStore
43+
Spar.Sem.SAMLUserStore.Cassandra
44+
Spar.Sem.ScimExternalIdStore
45+
Spar.Sem.ScimExternalIdStore.Cassandra
46+
Spar.Sem.ScimTokenStore
47+
Spar.Sem.ScimTokenStore.Cassandra
48+
Spar.Sem.ScimUserTimesStore
49+
Spar.Sem.ScimUserTimesStore.Cassandra
4250
other-modules:
4351
Paths_spar
4452
hs-source-dirs:

services/spar/src/Spar/API.hs

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,15 @@ import qualified Spar.Intra.Brig as Brig
6464
import qualified Spar.Intra.Galley as Galley
6565
import Spar.Orphans ()
6666
import Spar.Scim
67+
import Spar.Sem.DefaultSsoCode (DefaultSsoCode)
68+
import qualified Spar.Sem.DefaultSsoCode as DefaultSsoCode
6769
import qualified Spar.Sem.IdP as IdPEffect
68-
import Spar.Sem.SAMLUser (SAMLUser)
69-
import qualified Spar.Sem.SAMLUser as SAMLUser
70+
import Spar.Sem.SAMLUserStore (SAMLUserStore)
71+
import qualified Spar.Sem.SAMLUserStore as SAMLUserStore
72+
import Spar.Sem.ScimExternalIdStore (ScimExternalIdStore)
73+
import Spar.Sem.ScimTokenStore (ScimTokenStore)
74+
import qualified Spar.Sem.ScimTokenStore as ScimTokenStore
75+
import Spar.Sem.ScimUserTimesStore (ScimUserTimesStore)
7076
import qualified URI.ByteString as URI
7177
import Wire.API.Cookie
7278
import Wire.API.Routes.Public.Spar
@@ -78,7 +84,7 @@ app ctx =
7884
SAML.setHttpCachePolicy $
7985
serve (Proxy @API) (hoistServer (Proxy @API) (SAML.nt @SparError @(Spar _) ctx) (api $ sparCtxOpts ctx) :: Server API)
8086

81-
api :: Members [IdPEffect.IdP, SAMLUser] r => Opts -> ServerT API (Spar r)
87+
api :: Members '[ScimExternalIdStore, ScimUserTimesStore, ScimTokenStore, DefaultSsoCode, IdPEffect.IdP, SAMLUserStore] r => Opts -> ServerT API (Spar r)
8288
api opts =
8389
apiSSO opts
8490
:<|> authreqPrecheck
@@ -87,7 +93,7 @@ api opts =
8793
:<|> apiScim
8894
:<|> apiINTERNAL
8995

90-
apiSSO :: Members [IdPEffect.IdP, SAMLUser] r => Opts -> ServerT APISSO (Spar r)
96+
apiSSO :: Members '[ScimTokenStore, DefaultSsoCode, IdPEffect.IdP, SAMLUserStore] r => Opts -> ServerT APISSO (Spar r)
9197
apiSSO opts =
9298
SAML.meta appName (sparSPIssuer Nothing) (sparResponseURI Nothing)
9399
:<|> (\tid -> SAML.meta appName (sparSPIssuer (Just tid)) (sparResponseURI (Just tid)))
@@ -97,7 +103,7 @@ apiSSO opts =
97103
:<|> authresp . Just
98104
:<|> ssoSettings
99105

100-
apiIDP :: Members [IdPEffect.IdP, SAMLUser] r => ServerT APIIDP (Spar r)
106+
apiIDP :: Members '[ScimTokenStore, IdPEffect.IdP, SAMLUserStore] r => ServerT APIIDP (Spar r)
101107
apiIDP =
102108
idpGet
103109
:<|> idpGetRaw
@@ -106,7 +112,7 @@ apiIDP =
106112
:<|> idpUpdate
107113
:<|> idpDelete
108114

109-
apiINTERNAL :: Members [IdPEffect.IdP, SAMLUser] r => ServerT APIINTERNAL (Spar r)
115+
apiINTERNAL :: Members '[ScimTokenStore, DefaultSsoCode, IdPEffect.IdP, SAMLUserStore] r => ServerT APIINTERNAL (Spar r)
110116
apiINTERNAL =
111117
internalStatus
112118
:<|> internalDeleteTeam
@@ -181,7 +187,7 @@ validateRedirectURL uri = do
181187
unless ((SBS.length $ URI.serializeURIRef' uri) <= redirectURLMaxLength) $ do
182188
throwSpar $ SparBadInitiateLoginQueryParams "url-too-long"
183189

184-
authresp :: forall r. Members [IdPEffect.IdP, SAMLUser] r => Maybe TeamId -> Maybe ST -> SAML.AuthnResponseBody -> Spar r Void
190+
authresp :: forall r. Members '[ScimTokenStore, IdPEffect.IdP, SAMLUserStore] r => Maybe TeamId -> Maybe ST -> SAML.AuthnResponseBody -> Spar r Void
185191
authresp mbtid ckyraw arbody = logErrors $ SAML.authresp mbtid (sparSPIssuer mbtid) (sparResponseURI mbtid) go arbody
186192
where
187193
cky :: Maybe BindCookie
@@ -202,9 +208,9 @@ authresp mbtid ckyraw arbody = logErrors $ SAML.authresp mbtid (sparSPIssuer mbt
202208
(Multipart.inputs (SAML.authnResponseBodyRaw arbody))
203209
ckyraw
204210

205-
ssoSettings :: Spar r SsoSettings
211+
ssoSettings :: Member DefaultSsoCode r => Spar r SsoSettings
206212
ssoSettings = do
207-
SsoSettings <$> wrapMonadClient Data.getDefaultSsoCode
213+
SsoSettings <$> wrapMonadClientSem DefaultSsoCode.get
208214

209215
----------------------------------------------------------------------------
210216
-- IdP API
@@ -237,20 +243,20 @@ idpGetAll zusr = withDebugLog "idpGetAll" (const Nothing) $ do
237243
-- matter what the team size, it shouldn't choke any servers, just the client (which is
238244
-- probably curl running locally on one of the spar instances).
239245
-- https://github.com/zinfra/backend-issues/issues/1314
240-
idpDelete :: forall r. Members [SAMLUser, IdPEffect.IdP] r => Maybe UserId -> SAML.IdPId -> Maybe Bool -> Spar r NoContent
246+
idpDelete :: forall r. Members '[ScimTokenStore, SAMLUserStore, IdPEffect.IdP] r => Maybe UserId -> SAML.IdPId -> Maybe Bool -> Spar r NoContent
241247
idpDelete zusr idpid (fromMaybe False -> purge) = withDebugLog "idpDelete" (const Nothing) $ do
242248
idp <- SAML.getIdPConfig idpid
243249
_ <- authorizeIdP zusr idp
244250
let issuer = idp ^. SAML.idpMetadata . SAML.edIssuer
245251
team = idp ^. SAML.idpExtraInfo . wiTeam
246252
-- if idp is not empty: fail or purge
247-
idpIsEmpty <- wrapMonadClientSem $ isNothing <$> SAMLUser.getAnyByIssuer issuer
253+
idpIsEmpty <- wrapMonadClientSem $ isNothing <$> SAMLUserStore.getAnyByIssuer issuer
248254
let doPurge :: Spar r ()
249255
doPurge = do
250-
some <- wrapMonadClientSem (SAMLUser.getSomeByIssuer issuer)
256+
some <- wrapMonadClientSem (SAMLUserStore.getSomeByIssuer issuer)
251257
forM_ some $ \(uref, uid) -> do
252258
Brig.deleteBrigUser uid
253-
wrapMonadClientSem (SAMLUser.delete uid uref)
259+
wrapMonadClientSem (SAMLUserStore.delete uid uref)
254260
unless (null some) doPurge
255261
when (not idpIsEmpty) $ do
256262
if purge
@@ -262,9 +268,9 @@ idpDelete zusr idpid (fromMaybe False -> purge) = withDebugLog "idpDelete" (cons
262268
-- Delete tokens associated with given IdP (we rely on the fact that
263269
-- each IdP has exactly one team so we can look up all tokens
264270
-- associated with the team and then filter them)
265-
tokens <- liftMonadClient $ Data.getScimTokens team
271+
tokens <- liftSem $ ScimTokenStore.getByTeam team
266272
for_ tokens $ \ScimTokenInfo {..} ->
267-
when (stiIdP == Just idpid) $ liftMonadClient $ Data.deleteScimToken team stiId
273+
when (stiIdP == Just idpid) $ liftSem $ ScimTokenStore.delete team stiId
268274
-- Delete IdP config
269275
liftSem $ do
270276
IdPEffect.deleteConfig idpid issuer team
@@ -292,11 +298,11 @@ idpDelete zusr idpid (fromMaybe False -> purge) = withDebugLog "idpDelete" (cons
292298

293299
-- | This handler only does the json parsing, and leaves all authorization checks and
294300
-- application logic to 'idpCreateXML'.
295-
idpCreate :: Member IdPEffect.IdP r => Maybe UserId -> IdPMetadataInfo -> Maybe SAML.IdPId -> Maybe WireIdPAPIVersion -> Spar r IdP
301+
idpCreate :: Members '[ScimTokenStore, IdPEffect.IdP] r => Maybe UserId -> IdPMetadataInfo -> Maybe SAML.IdPId -> Maybe WireIdPAPIVersion -> Spar r IdP
296302
idpCreate zusr (IdPMetadataValue raw xml) midpid apiversion = idpCreateXML zusr raw xml midpid apiversion
297303

298304
-- | We generate a new UUID for each IdP used as IdPConfig's path, thereby ensuring uniqueness.
299-
idpCreateXML :: Member IdPEffect.IdP r => Maybe UserId -> Text -> SAML.IdPMetadata -> Maybe SAML.IdPId -> Maybe WireIdPAPIVersion -> Spar r IdP
305+
idpCreateXML :: Members '[ScimTokenStore, IdPEffect.IdP] r => Maybe UserId -> Text -> SAML.IdPMetadata -> Maybe SAML.IdPId -> Maybe WireIdPAPIVersion -> Spar r IdP
300306
idpCreateXML zusr raw idpmeta mReplaces (fromMaybe defWireIdPAPIVersion -> apiversion) = withDebugLog "idpCreate" (Just . show . (^. SAML.idpId)) $ do
301307
teamid <- Brig.getZUsrCheckPerm zusr CreateUpdateDeleteIdp
302308
Galley.assertSSOEnabled teamid
@@ -312,9 +318,9 @@ idpCreateXML zusr raw idpmeta mReplaces (fromMaybe defWireIdPAPIVersion -> apive
312318
-- data contains no information about the idp issuer, only the user name, so no valid saml
313319
-- credentials can be created. To fix this, we need to implement a way to associate scim
314320
-- tokens with IdPs. https://wearezeta.atlassian.net/browse/SQSERVICES-165
315-
assertNoScimOrNoIdP :: Member IdPEffect.IdP r => TeamId -> Spar r ()
321+
assertNoScimOrNoIdP :: Members '[ScimTokenStore, IdPEffect.IdP] r => TeamId -> Spar r ()
316322
assertNoScimOrNoIdP teamid = do
317-
numTokens <- length <$> wrapMonadClient (Data.getScimTokens teamid)
323+
numTokens <- length <$> wrapMonadClientSem (ScimTokenStore.getByTeam teamid)
318324
numIdps <- length <$> wrapMonadClientSem (IdPEffect.getConfigsByTeam teamid)
319325
when (numTokens > 0 && numIdps > 0) $ do
320326
throwSpar $
@@ -482,14 +488,14 @@ internalStatus = pure NoContent
482488

483489
-- | Cleanup handler that is called by Galley whenever a team is about to
484490
-- get deleted.
485-
internalDeleteTeam :: Members [IdPEffect.IdP, SAMLUser] r => TeamId -> Spar r NoContent
491+
internalDeleteTeam :: Members '[ScimTokenStore, IdPEffect.IdP, SAMLUserStore] r => TeamId -> Spar r NoContent
486492
internalDeleteTeam team = do
487493
wrapSpar $ deleteTeam team
488494
pure NoContent
489495

490-
internalPutSsoSettings :: Member IdPEffect.IdP r => SsoSettings -> Spar r NoContent
496+
internalPutSsoSettings :: Members '[DefaultSsoCode, IdPEffect.IdP] r => SsoSettings -> Spar r NoContent
491497
internalPutSsoSettings SsoSettings {defaultSsoCode = Nothing} = do
492-
wrapMonadClient $ Data.deleteDefaultSsoCode
498+
wrapMonadClientSem $ DefaultSsoCode.delete
493499
pure NoContent
494500
internalPutSsoSettings SsoSettings {defaultSsoCode = Just code} = do
495501
wrapMonadClientSem (IdPEffect.getConfig code) >>= \case
@@ -499,5 +505,5 @@ internalPutSsoSettings SsoSettings {defaultSsoCode = Just code} = do
499505
-- "Could not find IdP".
500506
throwSpar $ SparIdPNotFound mempty
501507
Just _ -> do
502-
wrapMonadClient $ Data.storeDefaultSsoCode code
508+
wrapMonadClientSem $ DefaultSsoCode.store code
503509
pure NoContent

0 commit comments

Comments
 (0)