@@ -133,34 +133,10 @@ import Network.HTTP.Conduit hiding
133133 (Request)
134134import qualified Network.HTTP.Conduit as Client
135135import Network.HTTP.Types
136−
137−-- | Attempt credentials discovery via the following steps:
138−--
139−-- * Read the default credentials from a file specified by
140−-- the environment variable @GOOGLE_APPLICATION_CREDENTIALS@ if it exists.
141−--
142−-- * Read the platform equivalent of @~\/.config\/gcloud\/application_default_credentials.json@ if it exists.
143−-- The @~/.config@ component of the path can be overriden by the environment
144−-- variable @CLOUDSDK_CONFIG@ if it exists.
145−--
146−-- * Retrieve the default service account application credentials if
147−-- running on GCE. The environment variable @NO_GCE_CHECK@ can be used to
148−-- skip this check if set to a truthy value such as @1@ or @true@.
149−--
150−-- The specified 'Scope's are used to authorize any @service_account@ that is
151−-- found with the appropriate scopes, otherwise they are not used. See the
152−-- top-level module of each individual @gogol-*@ library for a list of available
153−-- scopes, such as @Network.Google.Compute.computeScope@.
154−-- discover :: (MonadIO m, MonadCatch m)
155−-- => [OAuthScope]
156−-- -> Manager
157−-- -> m Credentials
158−-- discover ss m =
159−-- catching _MissingFileError (fromFile ss) $ \f -> do
160−-- p <- isGCE m
161−-- unless p $
162−-- throwingM _MissingFileError f
163−-- pure $! FromMetadata "default"
136+import System.Directory (doesFileExist, getHomeDirectory)
137+import System.Environment (lookupEnv)
138+import System.FilePath ((</>))
139+import System.Info (os)
164140
165141-- | The supported credential mechanisms.
166142data Credentials
@@ -189,6 +165,85 @@ data Credentials
189165 -- An 'AuthorizedUser' is typically created by the @gcloud init@ command
190166 -- of the Google CloudSDK Tools.
191167
168+-- | Attempt credentials discovery via the following steps:
169+--
170+-- * Read the default credentials from a file specified by
171+-- the environment variable @GOOGLE_APPLICATION_CREDENTIALS@ if it exists.
172+--
173+-- * Read the platform equivalent of @~\/.config\/gcloud\/application_default_credentials.json@ if it exists.
174+-- The @~/.config@ component of the path can be overriden by the environment
175+-- variable @CLOUDSDK_CONFIG@ if it exists.
176+--
177+-- * Retrieve the default service account application credentials if
178+-- running on GCE. The environment variable @NO_GCE_CHECK@ can be used to
179+-- skip this check if set to a truthy value such as @1@ or @true@.
180+--
181+-- The specified 'Scope's are used to authorize any @service_account@ that is
182+-- found with the appropriate scopes, otherwise they are not used. See the
183+-- top-level module of each individual @gogol-*@ library for a list of available
184+-- scopes, such as @Network.Google.Compute.computeScope@.
185+discover :: (MonadIO m, MonadCatch m)
186+ => [OAuthScope]
187+ -> Manager
188+ -> m Credentials
189+discover ss m =
190+ catching _MissingFileError (fromFile ss) $ \f -> do
191+ p <- isGCE m
192+ unless p $
193+ throwingM _MissingFileError f
194+ pure $! FromMetadata "default"
195+
196+-- | Attempt to load either a @service_account@ or @authorized_user@ formatted
197+-- file to obtain the credentials neccessary to perform a token refresh.
198+--
199+-- The specified 'Scope's are used to authorize any @service_account@ that is
200+-- found with the appropriate scopes, otherwise they are not used. See the
201+-- top-level module of each individual @gogol-*@ library for a list of available
202+-- scopes, such as @Network.Google.Compute.computeScope@.
203+--
204+-- /See:/ 'cloudSDKConfigPath', 'defaultCredentialsPath'.
205+fromFile :: (MonadIO m, MonadCatch m) => [OAuthScope] -> m Credentials
206+fromFile ss = do
207+ f <- defaultCredentialsPath
208+ case f of
209+ Just x -> fromFilePath ss x
210+ Nothing -> do
211+ x <- cloudSDKConfigPath
212+ fromFilePath ss x
213+
214+-- | Attempt to load either a @service_account@ or @authorized_user@ formatted
215+-- file to obtain the credentials neccessary to perform a token refresh from
216+-- the specified file.
217+--
218+-- The specified 'Scope's are used to authorize any @service_account@ that is
219+-- found with the appropriate scopes, otherwise they are not used. See the
220+-- top-level module of each individual @gogol-*@ library for a list of available
221+-- scopes, such as @Network.Google.Compute.computeScope@.
222+fromFilePath :: (MonadIO m, MonadCatch m)
223+ => [OAuthScope]
224+ -> FilePath
225+ -> m Credentials
226+fromFilePath ss f = do
227+ p <- liftIO (doesFileExist f)
228+ unless p $
229+ throwM (MissingFileError f)
230+ bs <- liftIO (LBS.readFile f)
231+ either (throwM . InvalidFileError f . Text.pack) pure
232+ (fromJSONCredentials ss bs)
233+
234+fromJSONCredentials :: [OAuthScope]
235+ -> LBS.ByteString
236+ -> Either String Credentials
237+fromJSONCredentials ss bs = do
238+ v <- eitherDecode' bs
239+ let x = (`FromAccount` ss) <$> parseEither parseJSON v
240+ y = FromUser <$> parseEither parseJSON v
241+ case (x, y) of
242+ (Left xe, Left ye) -> Left $
243+ "Failed parsing service_account: " ++ xe ++
244+ ", Failed parsing authorized_user: " ++ ye
245+ _ -> x <|> y
246+
192247exchange :: (MonadIO m, MonadCatch m)
193248 => Credentials
194249 -> Logger