Use ADTs instead of Either String in GCP Auth code

This commit is contained in:
Akshay Mankar
2019-08-27 21:35:23 +01:00
parent 3f1ee81c27
commit 10ea92625b

View File

@@ -39,7 +39,7 @@ data GCPAuth = GCPAuth { gcpAccessToken :: TVar(Maybe Text)
instance AuthMethod GCPAuth where instance AuthMethod GCPAuth where
applyAuthMethod _ gcp req = do applyAuthMethod _ gcp req = do
token <- getToken gcp token <- getToken gcp
>>= either (throwM . GCPGetTokenException) pure >>= either throwM pure
pure pure
$ setHeader req [("Authorization", "Bearer " <> (Text.encodeUtf8 token))] $ setHeader req [("Authorization", "Bearer " <> (Text.encodeUtf8 token))]
& L.set rAuthTypesL [] & L.set rAuthTypesL []
@@ -50,19 +50,25 @@ gcpAuth AuthInfo{authProvider = Just(AuthProviderConfig "gcp" (Just cfg))} (tls,
= Just $ do = Just $ do
configOrErr <- parseGCPAuthInfo cfg configOrErr <- parseGCPAuthInfo cfg
case configOrErr of case configOrErr of
Left e -> throwM $ GCPAuthParsingException e Left e -> throwM e
Right gcp -> pure (tls, addAuthMethod kubecfg gcp) Right gcp -> pure (tls, addAuthMethod kubecfg gcp)
gcpAuth _ _ = Nothing gcpAuth _ _ = Nothing
data GCPAuthParsingException = GCPAuthParsingException String data GCPAuthParsingException = GCPAuthMissingInformation String
| GCPAuthInvalidExpiry String
| GCPAuthInvalidTokenJSONPath String
| GCPAuthInvalidExpiryJSONPath String
deriving Show deriving Show
instance Exception GCPAuthParsingException instance Exception GCPAuthParsingException
data GCPGetTokenException = GCPGetTokenException String data GCPGetTokenException = GCPCmdProducedInvalidJSON String
| GCPTokenNotFound String
| GCPTokenExpiryNotFound String
| GCPTokenExpiryInvalid String
deriving Show deriving Show
instance Exception GCPGetTokenException instance Exception GCPGetTokenException
getToken :: GCPAuth -> IO (Either String Text) getToken :: GCPAuth -> IO (Either GCPGetTokenException Text)
getToken g@(GCPAuth{..}) = getCurrentToken g getToken g@(GCPAuth{..}) = getCurrentToken g
>>= maybe (fetchToken g) (return . Right) >>= maybe (fetchToken g) (return . Right)
@@ -77,38 +83,49 @@ getCurrentToken (GCPAuth{..}) = do
then maybeToken then maybeToken
else Nothing else Nothing
-- TODO: log if parsed expiry is invalid fetchToken :: GCPAuth -> IO (Either GCPGetTokenException Text)
fetchToken :: GCPAuth -> IO (Either String Text)
fetchToken GCPAuth{..} = do fetchToken GCPAuth{..} = do
(stdOut, _) <- readProcess_ gcpCmd (stdOut, _) <- readProcess_ gcpCmd
let credsJSON = Aeson.eitherDecode stdOut case parseTokenAndExpiry stdOut of
token = runJSONPath gcpTokenKey =<< credsJSON Right (token, expiry) -> do
expText = runJSONPath gcpExpiryKey =<< credsJSON atomically $ do
expiry :: Either String (Maybe UTCTime) writeTVar gcpAccessToken (Just token)
expiry = Just <$> (parseExpiryTime =<< expText) writeTVar gcpTokenExpiry (Just expiry)
atomically $ do return $ Right token
writeTVar gcpAccessToken (rightToMaybe token) Left x -> return $ Left x
writeTVar gcpTokenExpiry (either (const Nothing) id expiry) where
return token parseTokenAndExpiry credsStr = do
credsJSON <- Aeson.eitherDecode credsStr
& mapLeft GCPCmdProducedInvalidJSON
token <- runJSONPath gcpTokenKey credsJSON
& mapLeft GCPTokenNotFound
expText <- runJSONPath gcpExpiryKey credsJSON
& mapLeft GCPTokenExpiryNotFound
expiry <- parseExpiryTime expText
& mapLeft GCPTokenExpiryInvalid
return (token, expiry)
parseGCPAuthInfo :: Map Text Text -> IO (Either String GCPAuth) parseGCPAuthInfo :: Map Text Text -> IO (Either GCPAuthParsingException GCPAuth)
parseGCPAuthInfo m = do parseGCPAuthInfo m = do
gcpAccessToken <- atomically $ newTVar $ Map.lookup "access-token" m gcpAccessToken <- atomically $ newTVar $ Map.lookup "access-token" m
case maybe (pure Nothing) ((Just <$>) . parseExpiryTime) $ Map.lookup "expiry" m of eitherGCPExpiryToken <- sequence $ fmap (atomically . newTVar) lookupAndParseExpiry
(Left e) -> return $ Left e return $ do
Right t -> do gcpTokenExpiry <- mapLeft GCPAuthInvalidExpiry eitherGCPExpiryToken
gcpTokenExpiry <- atomically $ newTVar t cmdPath <- Text.unpack <$> lookupEither "cmd-path"
return $ do cmdArgs <- Text.splitOn " " <$> lookupEither "cmd-args"
cmdPath <- Text.unpack <$> lookupEither m "cmd-path" gcpTokenKey <- readJSONPath m "token-key" [JSONPath [KeyChild "token_expiry"]]
cmdArgs <- Text.splitOn " " <$> lookupEither m "cmd-args" & mapLeft GCPAuthInvalidTokenJSONPath
gcpTokenKey <- readJSONPath m "token-key" [JSONPath [KeyChild "token_expiry"]] gcpExpiryKey <- readJSONPath m "expiry-key" [JSONPath [KeyChild "access_token"]]
gcpExpiryKey <- readJSONPath m "expiry-key" [JSONPath [KeyChild "access_token"]] & mapLeft GCPAuthInvalidExpiryJSONPath
let gcpCmd = proc cmdPath (map Text.unpack cmdArgs) let gcpCmd = proc cmdPath (map Text.unpack cmdArgs)
pure $ GCPAuth{..} pure $ GCPAuth{..}
where
lookupEither :: (Show key, Ord key) => Map key val -> key -> Either String val lookupAndParseExpiry =
lookupEither m k = maybeToRight e $ Map.lookup k m case Map.lookup "expiry" m of
where e = "Couldn't find key: " <> show k <> " in GCP auth info" Nothing -> Right Nothing
Just expiryText -> Just <$> parseExpiryTime expiryText
lookupEither key = Map.lookup key m
& maybeToRight (GCPAuthMissingInformation $ Text.unpack key)
parseExpiryTime :: Text -> Either String UTCTime parseExpiryTime :: Text -> Either String UTCTime
parseExpiryTime s = zonedTimeToUTC <$> parseTimeRFC3339 s parseExpiryTime s = zonedTimeToUTC <$> parseTimeRFC3339 s