packages feed

github-rest 1.1.4 → 1.2.0

raw patch · 5 files changed

+66/−56 lines, 5 filesdep +cryptondep +jose-jwtdep −jwtdep ~aeson-qqdep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: crypton, jose-jwt

Dependencies removed: jwt

Dependency ranges changed: aeson-qq, base

API changes (from Hackage documentation)

- GitHub.REST.Auth: loadSigner :: FilePath -> IO EncodeSigner
+ GitHub.REST.Auth: instance GHC.Exception.Type.Exception GitHub.REST.Auth.JwtError
+ GitHub.REST.Auth: instance GHC.Show.Show GitHub.REST.Auth.JwtError
- GitHub.REST: data StdMethod
+ GitHub.REST: data () => StdMethod
- GitHub.REST.Auth: getJWTToken :: EncodeSigner -> AppId -> IO Token
+ GitHub.REST.Auth: getJWTToken :: PrivateKey -> AppId -> IO Token

Files

CHANGELOG.md view
@@ -1,3 +1,10 @@+# v1.2.0++* Switch from `jwt` to `jose-jwt` + `crypton`+    * Removes the `loadSigner` helper, use normal `crypton`/`crypton-x509`/`crypton-x509-store` API+* Add support for GHC 9.8 + 9.10+* Drop support for GHC < 9.6+ # v1.1.4  * Fix a test failure due to GitHub changing URLs
github-rest.cabal view
@@ -1,11 +1,11 @@ cabal-version: >= 1.10 --- This file has been generated from package.yaml by hpack version 0.35.2.+-- This file has been generated from package.yaml by hpack version 0.36.0. -- -- see: https://github.com/sol/hpack  name:           github-rest-version:        1.1.4+version:        1.2.0 synopsis:       Query the GitHub REST API programmatically description:    Query the GitHub REST API programmatically, which can provide a more                 flexible and clear interface than if all of the endpoints and their types@@ -45,16 +45,17 @@   ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wnoncanonical-monad-instances -Wunused-packages   build-depends:       aeson <3-    , base >=4.15 && <5-    , bytestring <0.12+    , base <5+    , bytestring <0.13+    , crypton <1.1     , http-client >=0.5.13 && <0.8     , http-client-tls <0.4     , http-types >=0.11 && <0.13-    , jwt >=0.9 && <0.12+    , jose-jwt >=0.10.0 && <0.11     , mtl <2.4     , scientific <0.4-    , text <2.1-    , time <1.13+    , text <2.2+    , time <1.15     , transformers <0.7     , unliftio <0.3     , unliftio-core <0.3@@ -64,6 +65,7 @@   type: exitcode-stdio-1.0   main-is: Main.hs   other-modules:+      Auth       Endpoint       Helpers       MockQuery@@ -75,9 +77,10 @@   ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wnoncanonical-monad-instances -Wunused-packages   build-depends:       aeson >=1.5.6.0-    , aeson-qq+    , aeson-qq >=0.8.4     , base     , bytestring+    , crypton     , github-rest     , http-types     , mtl
src/GitHub/REST/Auth.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} @@ -16,27 +16,21 @@    -- * Helpers for using JWT tokens with the GitHub API   getJWTToken,-  loadSigner, ) where +import qualified Crypto.PubKey.RSA as Crypto+import Data.Aeson ((.=))+import qualified Data.Aeson as Aeson+import qualified Data.Aeson.Text as Aeson import Data.ByteString (ByteString)-import qualified Data.ByteString as ByteString--#if !MIN_VERSION_base(4,11,0)-import Data.Monoid ((<>))-#endif-import Data.Text (Text)-import qualified Data.Text as Text import qualified Data.Text.Encoding as Text-import Data.Time (addUTCTime, getCurrentTime)+import qualified Data.Text.Lazy as TextL+import Data.Time (getCurrentTime) import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds)-import qualified Web.JWT as JWT--#if MIN_VERSION_jwt(0,11,0)-type EncodeSigner = JWT.EncodeSigner-#else-type EncodeSigner = JWT.Signer-#endif+import qualified Jose.Jwa as Jose+import qualified Jose.Jws as Jose+import qualified Jose.Jwt as Jose+import UnliftIO.Exception (Exception, throwIO)  -- | The token to use to authenticate with GitHub. data Token@@ -55,37 +49,26 @@ type AppId = Int  -- | Create a JWT token that expires in 10 minutes.-getJWTToken :: EncodeSigner -> AppId -> IO Token-getJWTToken signer appId = mkToken <$> getNow-  where-    mkToken now =-      let claims =-            mempty-              { JWT.iat = JWT.numericDate $ utcTimeToPOSIXSeconds now-              , JWT.exp = JWT.numericDate $ utcTimeToPOSIXSeconds now + (10 * 60)-              , JWT.iss = JWT.stringOrURI $ Text.pack $ show appId-              }-       in BearerToken . Text.encodeUtf8 $ signToken signer claims-    -- lose a second in the case of rounding-    -- https://github.community/t5/GitHub-API-Development-and/quot-Expiration-time-claim-exp-is-too-far-in-the-future-quot/m-p/20457/highlight/true#M1127-    getNow = addUTCTime (-1) <$> getCurrentTime--signToken :: EncodeSigner -> JWT.JWTClaimsSet -> Text-#if MIN_VERSION_jwt(0,10,0)-signToken = flip JWT.encodeSigned mempty-#else-signToken = JWT.encodeSigned-#endif+getJWTToken :: Crypto.PrivateKey -> AppId -> IO Token+getJWTToken privKey appId = do+  -- use floor to ensure expiration doesn't go past 10 minutes+  -- https://github.com/orgs/community/discussions/24635#discussioncomment-3244803+  now <- floor . utcTimeToPOSIXSeconds <$> getCurrentTime --- | Load a RSA private key as a Signer from the given file path.-loadSigner :: FilePath -> IO EncodeSigner-loadSigner file = maybe badSigner return . readSigner =<< ByteString.readFile file+  BearerToken . Jose.unJwt <$> signToken (mkClaims now)   where-    badSigner = fail $ "Not a valid RSA private key file: " ++ file-    readSigner = fmap toEncodeRSAPrivateKey . JWT.readRsaSecret+    mkClaims now =+      Text.encodeUtf8 . TextL.toStrict . Aeson.encodeToLazyText $+        Aeson.object+          [ "iat" .= (now :: Integer)+          , "exp" .= (now + 10 * 60)+          , "iss" .= show appId+          ]+    signToken claims =+      Jose.rsaEncode Jose.RS256 privKey claims >>= \case+        Right jwt -> pure jwt+        Left e -> throwIO $ JwtError e -#if MIN_VERSION_jwt(0,11,0)-    toEncodeRSAPrivateKey = JWT.EncodeRSAPrivateKey-#else-    toEncodeRSAPrivateKey = JWT.RSAPrivateKey-#endif+-- https://github.com/tekul/jose-jwt/issues/30+data JwtError = JwtError Jose.JwtError+  deriving (Show, Exception)
+ test/Auth.hs view
@@ -0,0 +1,15 @@+module Auth (tests) where++import qualified Crypto.PubKey.RSA as Crypto+import Test.Tasty (TestTree)+import Test.Tasty.HUnit++import GitHub.REST.Auth++tests :: [TestTree]+tests =+  [ testCase "getJWTToken works" $ do+      (_, privKey) <- Crypto.generate 256 3+      _ <- getJWTToken privKey 123+      pure ()+  ]
test/Main.hs view
@@ -1,5 +1,6 @@ import Test.Tasty (defaultMain, testGroup) +import qualified Auth import qualified Endpoint import qualified Helpers import qualified MockQuery@@ -15,5 +16,6 @@       , testGroup "GitHub.REST.Endpoint" Endpoint.tests       , testGroup "GitHub.REST.Monad.Class" MockQuery.tests       , testGroup "GitHub.REST.PageLinks" PageLinks.tests+      , testGroup "GitHub.REST.Auth" Auth.tests       , testGroup "GitHub.REST (End-to-End)" Query.tests       ]