{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Generators
( JwtString(..)
, JwtText(..)
, genHeader
, genJwt
, shrinkJwt
)
where
import Web.Libjwt
import qualified Env as E
import Libjwt.Classes
import Libjwt.NumericDate ( toPOSIX )
import Control.Applicative ( liftA2 )
import Data.Aeson ( FromJSON
, ToJSON
)
import Data.ByteString ( ByteString )
import Data.Coerce
import Data.Text ( Text )
import qualified Data.Text as T
import Data.UUID ( UUID )
import Test.QuickCheck
import Test.QuickCheck.Instances ( )
import Prelude hiding ( exp )
instance Arbitrary Iss where
arbitrary = genIss
shrink = shrinkIss
genIss :: Gen Iss
genIss = coerce genMaybeShortPrintable
shrinkIss :: Iss -> [Iss]
shrinkIss (Iss x) = coerce $ shrink x
instance Arbitrary Sub where
arbitrary = genSub
shrink = shrinkSub
genSub :: Gen Sub
genSub = coerce genMaybeShortPrintable
shrinkSub :: Sub -> [Sub]
shrinkSub (Sub x) = coerce $ shrink x
instance Arbitrary Jti where
arbitrary = genJti
genJti :: Gen Jti
genJti = coerce $ arbitrary @(Maybe UUID)
instance Arbitrary NumericDate where
arbitrary = genNumericDate
shrink = shrinkNumericDate
genNumericDate :: Gen NumericDate
genNumericDate = fromPOSIX <$> arbitrary `suchThat` (>= 0)
shrinkNumericDate :: NumericDate -> [NumericDate]
shrinkNumericDate = shrinkMap fromPOSIX toPOSIX
instance Arbitrary Exp where
arbitrary = genExp
shrink = shrinkExp
genExp :: Gen Exp
genExp = coerce $ arbitrary @(Maybe NumericDate)
shrinkExp :: Exp -> [Exp]
shrinkExp (Exp x) = coerce $ shrink x
instance Arbitrary Nbf where
arbitrary = genNbf
shrink = shrinkNbf
genNbf :: Gen Nbf
genNbf = coerce $ arbitrary @(Maybe NumericDate)
shrinkNbf :: Nbf -> [Nbf]
shrinkNbf (Nbf x) = coerce $ shrink x
instance Arbitrary Iat where
arbitrary = genIat
shrink = shrinkIat
genIat :: Gen Iat
genIat = coerce $ arbitrary @(Maybe NumericDate)
shrinkIat :: Iat -> [Iat]
shrinkIat (Iat x) = coerce $ shrink x
instance Arbitrary Aud where
arbitrary = genAud
shrink = shrinkAud
genAud :: Gen Aud
genAud =
coerce $ frequency [(8, pure []), (2, resize 5 $ listOf genShortPrintable)]
shrinkAud :: Aud -> [Aud]
shrinkAud (Aud aud) = coerce $ shrink aud
instance Arbitrary ASCII where
arbitrary = genASCII
shrink = shrinkASCII
genASCII :: Gen ASCII
genASCII = coerce $ listOf $ arbitraryASCIIChar `suchThat` (/= '\NUL')
shrinkASCII :: ASCII -> [ASCII]
shrinkASCII = coerce . filter (notElem '\NUL') . shrink . getASCII
instance Arbitrary a => Arbitrary (Flag a) where
arbitrary = coerce $ arbitrary @a
instance Arbitrary Alg where
arbitrary = genAlg
genAlg :: Gen Alg
genAlg = elements [hs256, hs512, rs256, rs512, es256, es384, es512, None]
where
hs256 =
HS256
"MWNmYzExODA5OWFjOGM3NDNmMmM5Zjg5ZDc0YTM3M2VhMGNkMzA2MDY3ZjFhZDk5N2I3OTc5Yjdm\
\NDg3NDBkMiAgLQo"
hs512 =
HS512
"MjZkMDY2OWFiZmRjYTk5YjczZWFiZjYzMmRjMzU5NDYyMjMxODBjMTg3ZmY5OTZjM2NhM2NhN2Mx\
\YzFiNDNlYjc4NTE1MjQxZGI0OWM1ZWI2ZDUyZmMzZDlhMmFiNjc5OWJlZTUxNjE2ZDRlYTNkYjU5\
\Y2IwMDZhYWY1MjY1OTQgIC0K"
rs256 = RS256 E.testRsa2048KeyPair
rs512 = RS512 E.testRsa2048KeyPair
es256 = ES256 E.testEcP256KeyPair
es384 = ES384 E.testEcP384KeyPair
es512 = ES512 E.testEcP521KeyPair
instance Arbitrary ValidationSettings where
arbitrary = Settings <$> genLeeway <*> arbitrary
where genLeeway = arbitrary `suchThat` (>= 0)
shrink Settings { leeway, appName } =
Settings <$> shrinkLeeway leeway <*> shrink appName
where shrinkLeeway = filter (>= 0) . shrink
newtype JwtString = S { correctJwtString :: String }
deriving newtype (JwtRep ByteString, Show, Eq)
correctJwtChar :: Char -> Bool
correctJwtChar '\NUL' = False
correctJwtChar '\65534' = False
correctJwtChar '\65535' = False
correctJwtChar _ = True
instance Arbitrary JwtString where
arbitrary = coerce $ listOf $ arbitrary `suchThat` correctJwtChar
shrink = coerce . filter (all correctJwtChar) . shrink . correctJwtString
newtype JwtText = T { correctJwtText :: Text }
deriving newtype (JwtRep ByteString, Show, Eq, JsonBuilder, JsonParser, FromJSON, ToJSON)
instance Arbitrary JwtText where
arbitrary = coerce $ T.pack . correctJwtString <$> arbitrary
shrink =
shrinkMap (T . T.pack . correctJwtString) $ S . T.unpack . correctJwtText
instance Arbitrary Header where
arbitrary = genHeader
genHeader :: Gen Header
genHeader = Header <$> genAlg <*> pure JWT
instance Arbitrary (PrivateClaims ts ns) => Arbitrary (Jwt ts ns) where
arbitrary = genJwt
shrink = shrinkJwt
genJwt :: Arbitrary (PrivateClaims ts ns) => Gen (Jwt ts ns)
genJwt = Jwt <$> genHeader <*> arbitrary
shrinkJwt :: Arbitrary (PrivateClaims pc ns) => Jwt pc ns -> [Jwt pc ns]
shrinkJwt jwt = shrinkMap (\payload' -> jwt { payload = payload' }) payload jwt
instance Arbitrary (PrivateClaims ts ns) => Arbitrary (Payload ts ns) where
arbitrary =
ClaimsSet
<$> genIss
<*> genSub
<*> genAud
<*> genExp
<*> genNbf
<*> genIat
<*> genJti
<*> arbitrary
shrink ClaimsSet { iss, sub, aud, exp, nbf, iat, jti, privateClaims } =
tail
$ ClaimsSet
<$> shrink' iss
<*> shrink' sub
<*> shrink' aud
<*> shrink' exp
<*> shrink' nbf
<*> shrink' iat
<*> shrink' jti
<*> shrink' privateClaims
where shrink' x = x : shrink x
instance Arbitrary (PrivateClaims Empty 'NoNs) where
arbitrary = pure nullClaims
instance
( Arbitrary a
, CanAdd n tl
, Arbitrary (PrivateClaims tl 'NoNs)
)
=> Arbitrary (PrivateClaims (n ->> a ': tl) 'NoNs) where
arbitrary = liftA2 (.:) genWitness arbitrary
shrink (a :< tl) = tail
$ liftA2 (.:) (map (ClaimName ->>) $ shrink' a) (shrink' tl)
where shrink' x = x : shrink x
instance Arbitrary (PrivateClaims ts 'NoNs) => Arbitrary (PrivateClaims ts ('SomeNs ns)) where
arbitrary = someNs Ns <$> arbitrary
shrink = shrinkMap (someNs Ns) noNs
genWitness :: Arbitrary a => Gen (ClaimWitness name a)
genWitness = (ClaimName ->>) <$> arbitrary
genShortPrintable :: Gen PrintableString
genShortPrintable = resize 32 arbitrary
genMaybeShortPrintable :: Gen (Maybe PrintableString)
genMaybeShortPrintable = liftArbitrary genShortPrintable