{-# OPTIONS_GHC -fno-warn-orphans #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} module Generators ( JwtString(..) , JwtText(..) , SomeAlgorithm(..) , genAlgorithm ) 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 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 data SomeAlgorithm = forall k . (Show k, SigningKey k) => SomeAlgorithm (Algorithm k) deriving stock instance Show SomeAlgorithm instance Arbitrary SomeAlgorithm where arbitrary = genAlgorithm genAlgorithm :: Gen SomeAlgorithm genAlgorithm = elements [ SomeAlgorithm hs256 , SomeAlgorithm hs512 , SomeAlgorithm rs256 , SomeAlgorithm rs384 , SomeAlgorithm rs512 , SomeAlgorithm es256 , SomeAlgorithm es384 , SomeAlgorithm es512 , SomeAlgorithm AlgNone ] where hs256 = HMAC256 "MWNmYzExODA5OWFjOGM3NDNmMmM5Zjg5ZDc0YTM3M2VhMGNkMzA2MDY3ZjFhZDk5N2I3OTc5YjdmNDg3NDBkMiAgLQo" hs512 = HMAC512 "MjZkMDY2OWFiZmRjYTk5YjczZWFiZjYzMmRjMzU5NDYyMjMxODBjMTg3ZmY5OTZjM2NhM2NhN2Mx\ \YzFiNDNlYjc4NTE1MjQxZGI0OWM1ZWI2ZDUyZmMzZDlhMmFiNjc5OWJlZTUxNjE2ZDRlYTNkYjU5\ \Y2IwMDZhYWY1MjY1OTQgIC0K" rs256 = RSA256 E.testRsa2048KeyPair rs384 = RSA384 E.testRsa2048KeyPair rs512 = RSA512 E.testRsa2048KeyPair es256 = ECDSA256 E.testEcP256KeyPair es384 = ECDSA384 E.testEcP384KeyPair es512 = ECDSA512 E.testEcP521KeyPair instance Arbitrary Typ where arbitrary = frequency [(16, pure JWT), (3, pure $ Typ Nothing), (1, pure $ Typ $ Just "some-typ")] 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