packages feed

libjwt-typed-0.2: bench/Benchmarks/Libjwt.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}

module Benchmarks.Libjwt
  ( signing, decoding
  )
where

import           Web.Libjwt
import           Benchmarks.Data
import           Env

import           Criterion                      ( Benchmark
                                                , bench
                                                , env
                                                , nf
                                                , whnfAppIO
                                                )

import           Data.ByteString                ( ByteString )

import           Data.Default

import           Data.List.NonEmpty             ( NonEmpty )

import           Data.Text                      ( Text )

import           Data.Time.Clock                ( UTCTime )

import           Data.UUID                      ( UUID )

import           Prelude                 hiding ( exp )

signing :: SigningKey k => [Algorithm k -> Benchmark]
signing =
  [ signSimple
  , signCustomClaims
  , signWithNs
  , signComplexCustomClaims
  ]

decoding :: SigningKey k => [Algorithm k -> Benchmark]
decoding =
  [ decodeSimple
  , decodeCustomClaims
  , decodeWithNs
  , decodeComplexCustomClaims
  ]

basePayload :: BenchEnv -> Payload Empty 'NoNs
basePayload LocalEnv {..} = def { iss = Iss (Just "benchmarks")
                                , aud = Aud ["https://example.com"]
                                , sub = Sub (Just subject)
                                , iat = Iat (Just $ fromPOSIX currentTime)
                                , exp = Exp (Just $ fromPOSIX someFutureTime)
                                , nbf = Nbf (Just $ fromPOSIX currentTime)
                                }

prepareToken
  :: (SigningKey k, Encode (PrivateClaims pc ns))
  => Algorithm k
  -> (BenchEnv -> Payload pc ns)
  -> IO ByteString
prepareToken a mkPayload = getToken . sign a . mkPayload <$> localEnv



type SimpleJwt = Jwt Empty 'NoNs

signSimple :: SigningKey k => Algorithm k -> Benchmark
signSimple a = env localEnv $ bench "simple" . nf benchmark
  where benchmark = getToken . sign a . basePayload

decodeSimple :: SigningKey k => Algorithm k -> Benchmark
decodeSimple a =
  env (prepareToken a basePayload) $ bench "simple" . whnfAppIO benchmark
 where
  benchmark
    :: ByteString -> IO (ValidationNEL ValidationFailure (Validated SimpleJwt))
  benchmark = jwtFromByteString validationSettings (checkIssuer "benchmarks") a



type CustomJwt
  = Jwt
      '["userName" ->> Text, "isRoot" ->> Bool, "clientId" ->> UUID, "created" ->> UTCTime, "scope" ->> Flag Scope]
      'NoNs

customPayload :: BenchEnv
                   -> Payload
                        '["userName" ->> Text, "isRoot" ->> Bool, "clientId" ->> UUID,
                          "created" ->> UTCTime, "scope" ->> Flag Scope]
                        'NoNs
customPayload e@LocalEnv {..} = (basePayload e)
  { privateClaims = toPrivateClaims
                      ( #userName ->> shortPrintableText
                      , #isRoot ->> flipBit
                      , #clientId ->> uuid
                      , #created ->> currentTimeUtc
                      , #scope ->> Flag Login
                      )
  }

signCustomClaims :: SigningKey k => Algorithm k -> Benchmark
signCustomClaims a = env localEnv $ bench "custom-claims" . nf benchmark
  where benchmark = getToken . sign a . customPayload

decodeCustomClaims :: SigningKey k => Algorithm k -> Benchmark
decodeCustomClaims a =
  env (prepareToken a customPayload)
    $ bench "custom-claims"
    . whnfAppIO benchmark
 where
  benchmark
    :: ByteString -> IO (ValidationNEL ValidationFailure (Validated CustomJwt))
  benchmark = jwtFromByteString validationSettings (checkIssuer "benchmarks") a



type CustomJwtWithNs
  = Jwt
      '["userName" ->> Text, "isRoot" ->> Bool, "clientId" ->> UUID, "created" ->> UTCTime, "scope" ->> Flag Scope]
      ( 'SomeNs "https://www.example.com/test")

customPayloadWithNs :: BenchEnv
                         -> Payload
                              '["userName" ->> Text, "isRoot" ->> Bool, "clientId" ->> UUID,
                                "created" ->> UTCTime, "scope" ->> Flag Scope]
                              ('SomeNs "https://www.example.com/test")
customPayloadWithNs e@LocalEnv {..} = (basePayload e)
  { privateClaims = toPrivateClaims $ withNs
                      (Ns @"https://www.example.com/test")
                      ( #userName ->> shortPrintableText
                      , #isRoot ->> flipBit
                      , #clientId ->> uuid
                      , #created ->> currentTimeUtc
                      , #scope ->> Flag Login
                      )
  }

signWithNs :: SigningKey k => Algorithm k -> Benchmark
signWithNs a = env localEnv $ bench "custom-claims-with-ns" . nf benchmark
  where benchmark = getToken . sign a . customPayloadWithNs

decodeWithNs :: SigningKey k => Algorithm k -> Benchmark
decodeWithNs a =
  env (prepareToken a customPayloadWithNs)
    $ bench "custom-claims-with-ns"
    . whnfAppIO benchmark
 where
  benchmark
    :: ByteString
    -> IO (ValidationNEL ValidationFailure (Validated CustomJwtWithNs))
  benchmark = jwtFromByteString validationSettings (checkIssuer "benchmarks") a



type ComplexJwt
  = Jwt
      '["user_name" ->> Text, "is_root" ->> Bool, "client_id" ->> UUID, "created" ->> UTCTime, "accounts" ->> NonEmpty (UUID, Text), "scopes" ->> [Flag Scope], "emails" ->> [String]]
      'NoNs

complexPayload :: BenchEnv
                    -> Payload
                         '["user_name" ->> Text, "is_root" ->> Bool, "client_id" ->> UUID,
                           "created" ->> UTCTime, "accounts" ->> NonEmpty (UUID, Text),
                           "scopes" ->> [Flag Scope], "emails" ->> [String]]
                         'NoNs
complexPayload e@LocalEnv {..} = (basePayload e)
  { privateClaims = toPrivateClaims
                      ( #user_name ->> shortPrintableText
                      , #is_root ->> flipBit
                      , #client_id ->> uuid
                      , #created ->> currentTimeUtc
                      , #accounts ->> accountList
                      , #scopes
                        ->> [ Flag Login
                            , Flag Extended
                            , Flag UserRead
                            , Flag UserWrite
                            , Flag AccountRead
                            , Flag AccountWrite
                            ]
                      , #emails ->> emailsList
                      )
  }

signComplexCustomClaims :: SigningKey k => Algorithm k -> Benchmark
signComplexCustomClaims a =
  env localEnv $ bench "complex-claims" . nf benchmark
  where benchmark = getToken . sign a . complexPayload

decodeComplexCustomClaims :: SigningKey k => Algorithm k -> Benchmark
decodeComplexCustomClaims a =
  env (prepareToken a complexPayload)
    $ bench "complex-claims"
    . whnfAppIO benchmark
 where
  benchmark
    :: ByteString -> IO (ValidationNEL ValidationFailure (Validated ComplexJwt))
  benchmark = jwtFromByteString validationSettings (checkIssuer "benchmarks") a



validationSettings :: ValidationSettings
validationSettings =
  defaultValidationSettings { appName = Just "https://example.com", leeway = 5 }