on master help sign in
Restructure auth types
af636e7Kirill Elagin
package.yaml+2−1
@@ -15,6 +15,7 @@ extra-source-files:
15
15
16default-extensions:
16default-extensions:
17 - ApplicativeDo
17 - ApplicativeDo
18+ - DeriveDataTypeable
18 - DeriveGeneric
19 - DeriveGeneric
19 - FlexibleContexts
20 - FlexibleContexts
20 - FlexibleInstances
21 - FlexibleInstances
@@ -44,7 +45,7 @@ library:
44 - http-client
45 - http-client
45 - http-client-tls
46 - http-client-tls
46 - http-types
47 - http-types
47− - jwt
48+ - jwt >= 0.8.0
48 - mtl
49 - mtl
49 - safe-exceptions
50 - safe-exceptions
50 - text
51 - text
src/GitHub/App/Auth.hs+26−23
@@ -7,23 +7,24 @@
7{-# LANGUAGE DataKinds #-}
7{-# LANGUAGE DataKinds #-}
8
8
9module GitHub.App.Auth
9module GitHub.App.Auth
10+ ( AppAuth
11+
10− ( InstallationAuth
12+ , InstallationAuth
11 , mkInstallationAuth
13 , mkInstallationAuth
12
14
13 , obtainAccessToken
15 , obtainAccessToken
14−
15− , AppId
16− , InstallationId
17 ) where
16 ) where
18
17
19import Prelude hiding (exp)
18import Prelude hiding (exp)
20
19
21import Control.Concurrent (MVar, newMVar, putMVar, readMVar, takeMVar)
20import Control.Concurrent (MVar, newMVar, putMVar, readMVar, takeMVar)
22import Control.Exception.Safe (bracketOnError, catch)
21import Control.Exception.Safe (bracketOnError, catch)
23−import Control.Monad.Except (MonadError (throwError), ExceptT, runExceptT)
22+import Control.Monad.Except (ExceptT, MonadError (throwError), runExceptT)
24−import Data.Functor (($>))
23+import Control.Monad.Trans (lift)
25import Crypto.Types.PubKey.RSA (PrivateKey)
24import Crypto.Types.PubKey.RSA (PrivateKey)
26import Data.Aeson (FromJSON (..), withObject, (.:))
25import Data.Aeson (FromJSON (..), withObject, (.:))
26+import qualified Data.ByteString.Lazy as LBS
27+import Data.Functor (($>))
27import Data.Maybe (fromMaybe)
28import Data.Maybe (fromMaybe)
28import Data.Semigroup ((<>))
29import Data.Semigroup ((<>))
29import Data.Text (Text)
30import Data.Text (Text)
@@ -31,18 +32,18 @@ import qualified Data.Text as T
31import Data.Text.Encoding (encodeUtf8)
32import Data.Text.Encoding (encodeUtf8)
32import Data.Time (NominalDiffTime, UTCTime, diffUTCTime, getCurrentTime)
33import Data.Time (NominalDiffTime, UTCTime, diffUTCTime, getCurrentTime)
33import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds)
34import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds)
34−import Control.Monad.Trans (lift)
35−import Data.Word (Word64)
36import GitHub.Auth (Auth (OAuth))
35import GitHub.Auth (Auth (OAuth))
36+import GitHub.Data.Apps (App)
37import GitHub.Data.Definitions (Error (HTTPError))
37import GitHub.Data.Definitions (Error (HTTPError))
38+import GitHub.Data.Id (Id, untagId)
39+import GitHub.Data.Installations (Installation)
38import GitHub.Data.Request (StatusMap)
40import GitHub.Data.Request (StatusMap)
39import GitHub.Request (parseResponse)
41import GitHub.Request (parseResponse)
40−import Web.JWT (JWTClaimsSet (exp, iat, iss), Signer (RSAPrivateKey), encodeSigned,
41− numericDate, stringOrURI)
42import Network.HTTP.Client as HTTP
42import Network.HTTP.Client as HTTP
43import qualified Network.HTTP.Client.Internal as HTTP
43import qualified Network.HTTP.Client.Internal as HTTP
44import qualified Network.HTTP.Types as HTTP
44import qualified Network.HTTP.Types as HTTP
45−import qualified Data.ByteString.Lazy as LBS
45+import Web.JWT (JWTClaimsSet (exp, iat, iss), Signer (RSAPrivateKey), encodeSigned, numericDate,
46+ stringOrURI)
46
47
47
48
48-- | JWT expiration time. Maximum accepted by GitHub is 10 minutes
49-- | JWT expiration time. Maximum accepted by GitHub is 10 minutes
@@ -64,10 +65,16 @@ baseUrl :: Text
64baseUrl = "https://api.github.com"
65baseUrl = "https://api.github.com"
65
66
66
67
68+-- | Credentials of a GitHub App
69+data AppAuth = AppAuth
70+ { aaAppId :: !(Id App)
71+ , aaPrivateKey :: !PrivateKey
72+ }
73+
67-- | Cached GitHub installation access token
74-- | Cached GitHub installation access token
68data InstallationToken = InstallationToken
75data InstallationToken = InstallationToken
69− { itToken :: Auth
76+ { itToken :: !Auth
70− , itExpirationTime :: UTCTime
77+ , itExpirationTime :: !UTCTime
71 } deriving (Show)
78 } deriving (Show)
72
79
73instance FromJSON InstallationToken where
80instance FromJSON InstallationToken where
@@ -77,7 +84,7 @@ instance FromJSON InstallationToken where
77 <*> o .: "expires_at"
84 <*> o .: "expires_at"
78
85
79
86
80−-- | Credentials required for an app to authenticate as an installation
87+-- | Credentials required for an App to authenticate as an installation
81data InstallationAuth = InstallationAuth
88data InstallationAuth = InstallationAuth
82 { iaClaimsSet :: !JWTClaimsSet -- ^ Prefilled claims set
89 { iaClaimsSet :: !JWTClaimsSet -- ^ Prefilled claims set
83 , iaAppPrivateKey :: !PrivateKey -- ^ Private key to sign token requests
90 , iaAppPrivateKey :: !PrivateKey -- ^ Private key to sign token requests
@@ -86,20 +93,16 @@ data InstallationAuth = InstallationAuth
86 }
93 }
87
94
88
95
89−type AppId = Word64
90−type InstallationId = Word64
91−
92-- | Smart constructor for 'InstallationAuth'
96-- | Smart constructor for 'InstallationAuth'
93mkInstallationAuth
97mkInstallationAuth
94− :: AppId -- ^ GitHub App ID
98+ :: AppAuth
95− -> PrivateKey -- ^ GitHub App Private Key
96− -> InstallationId -- ^ Installation ID
99+ -> Id Installation
97 -> IO InstallationAuth
100 -> IO InstallationAuth
98−mkInstallationAuth appId pk instId = do
101+mkInstallationAuth AppAuth{aaAppId, aaPrivateKey} instId = do
99 varToken <- newMVar Nothing
102 varToken <- newMVar Nothing
100− let issuer = fromMaybe (error "impossible") . stringOrURI . T.pack . show $ appId
103+ let issuer = fromMaybe (error "impossible") . stringOrURI . T.pack . show . untagId $ aaAppId
101 claimsSet = mempty { iss = Just issuer }
104 claimsSet = mempty { iss = Just issuer }
102− pure $ InstallationAuth claimsSet pk (T.pack . show $ instId) varToken
105+ pure $ InstallationAuth claimsSet aaPrivateKey (T.pack . show . untagId $ instId) varToken
103
106
104-- | Create a request which, when executed, will obtain a new access token
107-- | Create a request which, when executed, will obtain a new access token
105createAccessTokenR :: InstallationAuth -> IO HTTP.Request
108createAccessTokenR :: InstallationAuth -> IO HTTP.Request
src/GitHub/Data/Apps.hsnew file+31
@@ -0,0 +1,31 @@
1+{- This Source Code Form is subject to the terms of the Mozilla Public
2+ - License, v. 2.0. If a copy of the MPL was not distributed with this
3+ - file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+ -}
5+
6+{-# LANGUAGE CPP #-}
7+
8+module GitHub.Data.Apps where
9+
10+import GitHub.Data.Definitions (SimpleOwner)
11+import GitHub.Data.Id (Id)
12+import GitHub.Data.Name (Name)
13+import GitHub.Data.URL (URL)
14+import GitHub.Internal.Prelude
15+import Prelude ()
16+
17+
18+data App = App
19+ { appId :: !(Id App)
20+ , appOwner :: !SimpleOwner
21+ , appName :: !(Name App)
22+ , appDescription :: !(Maybe Text)
23+ , appExternalUrl :: !URL
24+ , appHtmlUrl :: !URL
25+ , appCreatedAt :: !(Maybe UTCTime)
26+ , appUpdatedAt :: !(Maybe UTCTime)
27+ }
28+ deriving (Show, Data, Typeable, Eq, Ord, Generic)
29+
30+instance NFData App where rnf = genericRnf
31+instance Binary App
src/GitHub/Data/Installations.hsnew file+34
@@ -0,0 +1,34 @@
1+{- This Source Code Form is subject to the terms of the Mozilla Public
2+ - License, v. 2.0. If a copy of the MPL was not distributed with this
3+ - file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+ -}
5+
6+{-# LANGUAGE CPP #-}
7+
8+module GitHub.Data.Installations where
9+
10+import GitHub.Data.Apps (App)
11+import GitHub.Data.Id (Id)
12+import GitHub.Data.URL (URL)
13+import GitHub.Internal.Prelude
14+import Prelude ()
15+
16+
17+data Installation = Installation
18+ { installationId :: !(Id Installation)
19+-- , installationAccount
20+ , installationAccesssTokensUrl :: !URL
21+ , installationRepositoriesUrl :: !URL
22+ , installationHtmlUrl :: !URL
23+ , installationAppId :: (Id App)
24+-- , installationTargetId
25+-- , installationTargetType
26+-- , installationPermissions
27+-- , installationEvents
28+-- , installationSingleFileName
29+-- , installationRepositorySelection
30+ }
31+ deriving (Show, Data, Typeable, Eq, Ord, Generic)
32+
33+instance NFData Installation where rnf = genericRnf
34+instance Binary Installation