@@ -32,15 +32,9 @@ module Spar.API
32
32
, IdpDelete
33
33
) where
34
34
35
- import Bilge
36
35
import Brig.Types.User as Brig
37
- import Control.Exception
38
36
import Control.Monad.Except
39
- import Control.Monad.Reader
40
- import Data.Either
41
- import Data.EitherR (fmapL )
42
37
import Data.Id
43
- import Data.List.NonEmpty (NonEmpty ((:|) ))
44
38
import Data.Maybe (isJust , fromJust )
45
39
import Data.Proxy
46
40
import Data.String.Conversions
@@ -61,14 +55,9 @@ import Spar.Options
61
55
import Spar.Types
62
56
63
57
import qualified Data.ByteString as SBS
64
- import qualified Data.X509 as X509
65
- import qualified Network.HTTP.Client as Rq
66
58
import qualified SAML2.WebSSO as SAML
67
59
import qualified Spar.Data as Data
68
60
import qualified Spar.Intra.Brig as Intra
69
- import qualified Text.XML as XML
70
- import qualified Text.XML.DSig as SAML
71
- import qualified Text.XML.Util as SAML
72
61
import qualified URI.ByteString as URI
73
62
74
63
@@ -108,7 +97,7 @@ type APIAuthResp = "sso" :> "finalize-login" :> SAML.APIAuthResp
108
97
109
98
type IdpGet = Header " Z-User" UserId :> " identity-providers" :> Capture " id" SAML. IdPId :> Get '[JSON ] IdP
110
99
type IdpGetAll = Header " Z-User" UserId :> " identity-providers" :> Get '[JSON ] IdPList
111
- type IdpCreate = Header " Z-User" UserId :> " identity-providers" :> ReqBody '[JSON ] SAML. NewIdP :> PostCreated '[JSON ] IdP
100
+ type IdpCreate = Header " Z-User" UserId :> " identity-providers" :> ReqBody '[SAML. XML ] SAML. IdPMetadata :> PostCreated '[JSON ] IdP
112
101
type IdpDelete = Header " Z-User" UserId :> " identity-providers" :> Capture " id" SAML. IdPId :> DeleteNoContent '[JSON ] NoContent
113
102
114
103
-- FUTUREWORK (thanks jschaul): In a more recent version of servant, using Header '[Strict] becomes
@@ -184,10 +173,10 @@ idpDelete zusr idpid = withDebugLog "idpDelete" (const Nothing) $ do
184
173
return NoContent
185
174
186
175
-- | We generate a new UUID for each IdP used as IdPConfig's path, thereby ensuring uniqueness.
187
- idpCreate :: ZUsr -> SAML. NewIdP -> Spar IdP
188
- idpCreate zusr newIdP = withDebugLog " idpCreate" (Just . show . (^. SAML. idpId)) $ do
176
+ idpCreate :: ZUsr -> SAML. IdPMetadata -> Spar IdP
177
+ idpCreate zusr idpmeta = withDebugLog " idpCreate" (Just . show . (^. SAML. idpId)) $ do
189
178
teamid <- getZUsrOwnedTeam zusr
190
- idp <- validateNewIdP newIdP teamid
179
+ idp <- validateNewIdP idpmeta teamid
191
180
SAML. storeIdPConfig idp
192
181
pure idp
193
182
@@ -217,17 +206,15 @@ getZUsrOwnedTeam (Just uid) = do
217
206
Just teamid -> teamid <$ Intra. assertIsTeamOwner uid teamid
218
207
219
208
220
- -- | FUTUREWORK: much of this function could move to the saml2-web-sso package.
209
+ -- | FUTUREWORK: move this to the saml2-web-sso package. (same probably goes for get, create,
210
+ -- update, delete of idps.)
221
211
validateNewIdP :: forall m . (HasCallStack , m ~ Spar )
222
- => SAML. NewIdP -> TeamId -> m IdP
223
- validateNewIdP ( SAML. NewIdP _idpMetadataURI metadataPublicKey) _idpeTeam = do
212
+ => SAML. IdPMetadata -> TeamId -> m IdP
213
+ validateNewIdP _idpMetadata _idpeTeam = do
224
214
_idpId <- SAML. IdPId <$> SAML. createUUID
225
215
_idpeSPInfo <- wrapMonadClientWithEnv $ Data. getSPInfo _idpId
226
216
let _idpExtraInfo = IdPExtra { _idpeTeam, _idpeSPInfo }
227
217
228
- _idpMetadata :: SAML. IdPMetadata
229
- <- fetchMetadata _idpMetadataURI metadataPublicKey
230
-
231
218
wrapMonadClient (Data. getIdPIdByIssuer (_idpMetadata ^. SAML. edIssuer)) >>= \ case
232
219
Nothing -> pure ()
233
220
Just _ -> throwSpar SparNewIdPAlreadyInUse
@@ -244,40 +231,6 @@ validateNewIdP (SAML.NewIdP _idpMetadataURI metadataPublicKey) _idpeTeam = do
244
231
245
232
pure SAML. IdPConfig {.. }
246
233
247
- fetchMetadata :: forall m . (HasCallStack , m ~ Spar ) => URI. URI -> X509. SignedCertificate -> m SAML. IdPMetadata
248
- fetchMetadata metadataUrl pubkey = do
249
- let fetch :: URI. URI -> (Request -> Request ) -> m (Bilge. Response (Maybe LBS ))
250
- fetch uri modify = do
251
- req <- uri2req uri
252
- ntm (httpLbs req modify)
253
-
254
- uri2req :: URI. URI -> m Request
255
- uri2req = either (throwSpar . SparNewIdPBadMetaUrl . cs . show ) pure
256
- . Rq. parseRequest . cs . SAML. renderURI
257
-
258
- -- natural transformation into 'm'. needed for the http client that fetches the metadata url.
259
- -- if 'IO' throws an exception, we capture it with 'try' and re-throw it inside 'm', which
260
- -- yields much nicer client errors and logs.
261
- ntm :: forall a . Http a -> m a
262
- ntm (HttpT action) = do
263
- mgr :: Manager <- getManager
264
- result :: Either SomeException a <- liftIO . try $ runReaderT action mgr
265
- either (throwSpar . SparNewIdPBadMetaUrl . cs . show @ SomeException ) pure result
266
-
267
- metaResp :: Bilge. Response (Maybe LBS )
268
- <- fetch metadataUrl (method GET . expect2xx)
269
- metaBody :: LBS
270
- <- maybe (throwSpar $ SparNewIdPBadMetaUrl " No body in response." ) pure $ responseBody metaResp
271
- when (isLeft $ do
272
- creds <- SAML. certToCreds pubkey
273
- SAML. verifyRoot (creds :| [] ) metaBody) $ do
274
- throwSpar SparNewIdPBadMetaSig
275
- meta :: SAML. IdPMetadata
276
- <- either (throwSpar . SparNewIdPBadMetaUrl . cs) pure $ do
277
- XML. Document _ el _ <- fmapL show $ XML. parseLBS XML. def metaBody
278
- SAML. parseIdPMetadata el
279
- pure meta
280
-
281
234
282
235
-- | Type families to convert spar's 'API' type into an "outside-world-view" API type
283
236
-- to expose as swagger docs intended to be used by client developers.
0 commit comments