Skip to content

Commit 3f1ee81

Browse files
committed
Use ADTs instead of Either String in OIDC Auth code
1 parent 5267ffe commit 3f1ee81

File tree

3 files changed

+56
-50
lines changed

3 files changed

+56
-50
lines changed

kubernetes-client/src/Kubernetes/Client/Auth/OIDC.hs

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ data OIDCGetTokenException = OIDCOAuthException (OAuth2Error OAuth2TokenRequest.
5959
deriving Show
6060
instance Exception OIDCGetTokenException
6161

62-
data OIDCAuthParsingException = OIDCAuthParsingException String
62+
data OIDCAuthParsingException = OIDCAuthCAParsingFailed ParseCertException
63+
| OIDCAuthMissingInformation String
6364
deriving Show
6465
instance Exception OIDCAuthParsingException
6566

@@ -119,7 +120,7 @@ oidcAuth :: DetectAuth
119120
oidcAuth AuthInfo{authProvider = Just(AuthProviderConfig "oidc" (Just cfg))} (tls, kubecfg)
120121
= Just
121122
$ parseOIDCAuthInfo cfg
122-
>>= either (throwM . OIDCAuthParsingException) (\oidc -> pure (tls, addAuthMethod kubecfg oidc))
123+
>>= either throwM (\oidc -> pure (tls, addAuthMethod kubecfg oidc))
123124
oidcAuth _ _ = Nothing
124125

125126
-- TODO: Consider doing this whole function atomically, as two threads may miss the cache simultaneously
@@ -129,54 +130,53 @@ oidcAuth _ _ = Nothing
129130
-}
130131
cachedOIDCAuth :: OIDCCache -> DetectAuth
131132
cachedOIDCAuth cache AuthInfo{authProvider = Just(AuthProviderConfig "oidc" (Just cfg))} (tls, kubecfg) = Just $ do
132-
m <- readTVarIO cache
133-
o <- case findInCache m cfg of
134-
Left e -> throwM $ OIDCAuthParsingException e
135-
Right (Just o) -> return o
136-
Right Nothing -> do
137-
o@(OIDCAuth{..}) <- parseOIDCAuthInfo cfg
138-
>>= either (throwM . OIDCAuthParsingException) pure
139-
let newCache = Map.insert (issuerURL, clientID) o m
133+
latestCache <- readTVarIO cache
134+
issuerURL <- lookupOrThrow "idp-issuer-url"
135+
clientID <- lookupOrThrow "client-id"
136+
case Map.lookup (issuerURL, clientID) latestCache of
137+
Just cacheHit -> return $ newTLSAndAuth cacheHit
138+
Nothing -> do
139+
parsedAuth <- parseOIDCAuthInfo cfg
140+
>>= either throwM pure
141+
let newCache = Map.insert (issuerURL, clientID) parsedAuth latestCache
140142
_ <- atomically $ swapTVar cache newCache
141-
return o
142-
pure (tls, addAuthMethod kubecfg o)
143+
return $ newTLSAndAuth parsedAuth
144+
where lookupOrThrow k = Map.lookup k cfg
145+
& maybe (throwM $ OIDCAuthMissingInformation $ Text.unpack k) pure
146+
newTLSAndAuth auth = (tls, addAuthMethod kubecfg auth)
143147
cachedOIDCAuth _ _ _ = Nothing
144148

145-
findInCache :: Map (Text, Text) a -> Map Text Text -> Either String (Maybe a)
146-
findInCache cache cfg = do
147-
issuerURL <- lookupEither cfg "idp-issuer-url"
148-
clientID <- lookupEither cfg "client-id"
149-
return $ Map.lookup (issuerURL, clientID) cache
150-
151-
parseOIDCAuthInfo :: Map Text Text -> IO (Either String OIDCAuth)
149+
parseOIDCAuthInfo :: Map Text Text -> IO (Either OIDCAuthParsingException OIDCAuth)
152150
parseOIDCAuthInfo m = do
153151
eitherTLSParams <- parseCA m
154152
idTokenTVar <- atomically $ newTVar $ Map.lookup "id-token" m
155153
refreshTokenTVar <- atomically $ newTVar $ Map.lookup "refresh-token" m
156154
return $ do
157-
tlsParams <- eitherTLSParams
158-
issuerURL <- lookupEither m "idp-issuer-url"
159-
clientID <- lookupEither m "client-id"
160-
clientSecret <- lookupEither m "client-secret"
155+
tlsParams <- mapLeft OIDCAuthCAParsingFailed eitherTLSParams
156+
issuerURL <- lookupEither "idp-issuer-url"
157+
clientID <- lookupEither "client-id"
158+
clientSecret <- lookupEither "client-secret"
161159
return OIDCAuth{..}
160+
where lookupEither k = Map.lookup k m
161+
& maybeToRight (OIDCAuthMissingInformation $ Text.unpack k)
162162

163-
parseCA :: Map Text Text -> IO (Either String TLS.ClientParams)
163+
parseCA :: Map Text Text -> IO (Either ParseCertException TLS.ClientParams)
164164
parseCA m = do
165165
t <- defaultTLSClientParams
166166
fromMaybe (pure $ pure t) (parseCAFile t m <|> parseCAData t m)
167167

168-
parseCAFile :: TLS.ClientParams -> Map Text Text -> Maybe (IO (Either String TLS.ClientParams))
168+
parseCAFile :: TLS.ClientParams -> Map Text Text -> Maybe (IO (Either ParseCertException TLS.ClientParams))
169169
parseCAFile t m = do
170170
caFile <- Text.unpack <$> Map.lookup "idp-certificate-authority" m
171-
return $ updateClientParams t <$> BS.readFile caFile
171+
Just $ do
172+
caText <- BS.readFile caFile
173+
return $ updateClientParams t caText
172174

173-
parseCAData :: TLS.ClientParams -> Map Text Text -> Maybe (IO (Either String TLS.ClientParams))
175+
parseCAData :: TLS.ClientParams -> Map Text Text -> Maybe (IO (Either ParseCertException TLS.ClientParams))
174176
parseCAData t m = do
175-
caText <- Map.lookup "idp-certificate-authority-data" m
176-
pure . pure
177-
$ (B64.decode $ Text.encodeUtf8 caText)
178-
>>= updateClientParams t
179-
180-
lookupEither :: (Show key, Ord key) => Map key val -> key -> Either String val
181-
lookupEither m k = maybeToRight e $ Map.lookup k m
182-
where e = "Couldn't find key: " <> show k <> " in OIDC auth info"
177+
caBase64 <- Map.lookup "idp-certificate-authority-data" m
178+
Just $ pure $ do
179+
caText <- Text.encodeUtf8 caBase64
180+
& B64.decode
181+
& mapLeft Base64ParsingFailed
182+
updateClientParams t caText

kubernetes-client/src/Kubernetes/Client/Config.hs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,11 @@ addCACertData cfg t =
125125
& (>>= (maybeToRight "cert data not provided" . certificateAuthorityData))
126126
in case eitherCertText of
127127
Left _ -> pure t
128-
Right certText ->
129-
(B64.decode $ T.encodeUtf8 certText)
130-
>>= updateClientParams t
131-
& either (throwM . ParsePEMCertsException) return
128+
Right certBase64 -> do
129+
certText <- B64.decode (T.encodeUtf8 certBase64)
130+
& either (throwM . Base64ParsingFailed) pure
131+
updateClientParams t certText
132+
& either throwM return
132133

133134
addCACertFile :: Config -> FilePath -> TLS.ClientParams -> IO TLS.ClientParams
134135
addCACertFile cfg dir t = do

kubernetes-client/src/Kubernetes/Client/Internal/TLSUtils.hs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import Control.Monad.IO.Class (MonadIO, liftIO)
66
import Data.ByteString (ByteString)
77
import Data.Default.Class (def)
88
import Data.Either (rights)
9+
import Data.Either.Combinators (mapLeft)
910
import Data.Function ((&))
1011
import Data.PEM (pemContent, pemParseBS)
11-
import Data.Typeable (Typeable)
1212
import Data.X509 (SignedCertificate, decodeSignedCertificate)
1313
import Data.X509.CertificateStore (CertificateStore, makeCertificateStore)
1414
import Lens.Micro
@@ -38,12 +38,14 @@ defaultTLSClientParams = do
3838
}
3939

4040
-- |Parses a PEM-encoded @ByteString@ into a list of certificates.
41-
parsePEMCerts :: B.ByteString -> Either String [SignedCertificate]
41+
parsePEMCerts :: B.ByteString -> Either ParseCertException [SignedCertificate]
4242
parsePEMCerts b = do
4343
pems <- pemParseBS b
44+
& mapLeft PEMParsingFailed
4445
return $ rights $ map (decodeSignedCertificate . pemContent) pems
4546

46-
updateClientParams :: TLS.ClientParams -> ByteString -> Either String TLS.ClientParams
47+
-- | Updates client params, sets CA store to passed bytestring of CA certificates
48+
updateClientParams :: TLS.ClientParams -> ByteString -> Either ParseCertException TLS.ClientParams
4749
updateClientParams cp certText = parsePEMCerts certText
4850
& (fmap (flip setCAStore cp))
4951

@@ -80,23 +82,26 @@ onCertificateRequestL :: Lens' TLS.ClientParams (([TLS.CertificateType], Maybe [
8082
onCertificateRequestL =
8183
clientHooksL . lens TLS.onCertificateRequest (\ch ocr -> ch { TLS.onCertificateRequest = ocr })
8284

83-
data ParsePEMCertsException = ParsePEMCertsException String deriving (Typeable, Show)
84-
85-
instance Exception ParsePEMCertsException
86-
8785
-- |Loads certificates from a PEM-encoded file.
8886
loadPEMCerts :: (MonadIO m, MonadThrow m) => FilePath -> m [SignedCertificate]
8987
loadPEMCerts p = do
9088
liftIO (B.readFile p)
91-
>>= throwLeft
89+
>>= (either throwM return)
9290
. parsePEMCerts
9391

9492
-- |Loads Base64 encoded certificate and private key
9593
loadB64EncodedCert :: (MonadThrow m) => B.ByteString -> B.ByteString -> m Credential
96-
loadB64EncodedCert certB64 keyB64 = throwLeft $ do
94+
loadB64EncodedCert certB64 keyB64 = either throwM pure $ do
9795
certText <- B64.decode certB64
96+
& mapLeft Base64ParsingFailed
9897
keyText <- B64.decode keyB64
98+
& mapLeft Base64ParsingFailed
9999
credentialLoadX509FromMemory certText keyText
100+
& mapLeft FailedToLoadCredential
101+
102+
data ParseCertException = PEMParsingFailed String
103+
| Base64ParsingFailed String
104+
| FailedToLoadCredential String
105+
deriving Show
100106

101-
throwLeft :: (MonadThrow m) => Either String a -> m a
102-
throwLeft = either (throwM . ParsePEMCertsException) return
107+
instance Exception ParseCertException

0 commit comments

Comments
 (0)