content: {-# LANGUAGE OverloadedStrings #-}
  module Icepeak.Server.JwtSpec (spec) where
  
  --import qualified Data.Aeson as Aeson
  import Data.Maybe (fromJust)
  import Data.Time.Clock (NominalDiffTime)
  import Test.Hspec -- (Spec, describe, it, shouldBe, expectationFailure)
  import Test.Hspec.QuickCheck (prop)
  import Test.QuickCheck.Instances ()
  import qualified Web.JWT as JWT
  import qualified Data.Map.Strict as Map
  import qualified Data.Text.Encoding as Text
  
  import Icepeak.Server.JwtAuth
  import Icepeak.Server.AccessControl
  
  import OrphanInstances ()
  
  spec :: Spec
  spec = do
    describe "JWT" $ do
  
      let testAccess = IcepeakClaim
            [ AuthPath ["foo"] [ModeRead]
            , AuthPath ["bar", "baz"] [ModeRead, ModeWrite]
            ]
  
      let emptyClaim = JWT.JWTClaimsSet
            { JWT.iss = Nothing
            , JWT.sub = Nothing
            , JWT.aud = Nothing
            , JWT.exp = Nothing
            , JWT.nbf = Nothing
            , JWT.iat = Nothing
            , JWT.jti = Nothing
            , JWT.unregisteredClaims = JWT.ClaimsMap Map.empty
            }
  
      let testClaims = addIcepeakClaim testAccess emptyClaim
  
      -- create an EncodeSigner for encoding secrets
      let testSecret = JWT.hmacSecret "2o8357cEuc2o835cmsoei"
          verifySigner = JWT.toVerify testSecret
  
      let now = 12512 :: NominalDiffTime
  
      let joseHeader = JWT.JOSEHeader
            { JWT.typ = Just "JWT"
            , JWT.cty = Nothing
            , JWT.alg = Just JWT.HS256
            , JWT.kid = Nothing
            }
  
      it "should accept a valid token" $ do
        let token = Text.encodeUtf8 $ JWT.encodeSigned testSecret joseHeader testClaims
        extractClaim now verifySigner token `shouldBe` Right testAccess
  
      it "should reject an expired token" $ do
        let expDate = fromJust $ JWT.numericDate $ now - 10
            claims = testClaims { JWT.exp = Just expDate }
            expiredToken = Text.encodeUtf8 $ JWT.encodeSigned testSecret joseHeader claims
        extractClaim now verifySigner expiredToken `shouldBe` Left (VerificationError TokenExpired)
  
      it "should reject a token before its 'not before' date" $ do
        let nbfDate = fromJust $ JWT.numericDate $ now + 10
            claims = testClaims { JWT.nbf = Just nbfDate }
            nbfToken = Text.encodeUtf8 $ JWT.encodeSigned testSecret joseHeader claims
        extractClaim now verifySigner nbfToken `shouldBe` Left (VerificationError TokenUsedTooEarly)
  
      it "should reject a token with wrong secret" $ do
        let claims = testClaims
            nbfToken = Text.encodeUtf8 $ JWT.encodeSigned testSecret joseHeader claims
            otherSecret = JWT.toVerify $ JWT.hmacSecret "dfhwcmo845cm8e5"
        extractClaim now otherSecret nbfToken `shouldBe` Left (VerificationError TokenSignatureInvalid)
  
      prop "should correctly encode and decode token" $ \icepeakClaim ->
        let claims = addIcepeakClaim icepeakClaim emptyClaim
            encoded = Text.encodeUtf8 $ JWT.encodeSigned testSecret joseHeader claims
            decoded = extractClaim now verifySigner encoded
        in decoded == Right icepeakClaim
  