Skip to content

Commit 2f0a2b4

Browse files
committed
Remove error from cert auth and add unit tests
1 parent ea041b3 commit 2f0a2b4

File tree

7 files changed

+167
-33
lines changed

7 files changed

+167
-33
lines changed

kubernetes-client/package.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,14 @@ tests:
2323
- kubernetes-client
2424
- hspec
2525
- yaml
26+
- file-embed
2627
example:
2728
main: App.hs
2829
source-dirs: example
2930
dependencies:
3031
- kubernetes-client
3132
extra-source-files:
32-
- test/testdata/*
33+
- test/testdata/**/*
3334
- README.md
3435
dependencies:
3536
- base >=4.7 && <5.0

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module Kubernetes.Client.Auth.ClientCert where
22

3+
import Control.Exception.Safe (Exception, throwM)
34
import Data.Text.Encoding
45
import Kubernetes.Client.Auth.Internal.Types
56
import Kubernetes.Client.Internal.TLSUtils
@@ -13,7 +14,8 @@ clientCertFileAuth auth (tlsParams, cfg) = do
1314
certFile <- clientCertificate auth
1415
keyFile <- clientKey auth
1516
return $ do
16-
cert <- credentialLoadX509 certFile keyFile >>= either error return
17+
cert <- credentialLoadX509 certFile keyFile
18+
>>= either (throwM . CredentialLoadException) return
1719
let newParams = (setClientCert cert tlsParams)
1820
newCfg = (disableValidateAuthMethods cfg)
1921
return (newParams, newCfg)
@@ -33,3 +35,7 @@ clientCertDataAuth auth (tlsParams, cfg) = do
3335
disableValidateAuthMethods :: KubernetesClientConfig -> KubernetesClientConfig
3436
disableValidateAuthMethods kcfg = kcfg { configValidateAuthMethods = False }
3537

38+
data CredentialLoadException = CredentialLoadException String
39+
deriving Show
40+
41+
instance Exception CredentialLoadException
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
{-# LANGUAGE TemplateHaskell #-}
3+
module Kubernetes.Client.Auth.ClientCertSpec where
4+
5+
import Test.Hspec
6+
7+
import Data.FileEmbed
8+
import Data.Maybe (isJust, isNothing)
9+
import Data.Text.Encoding (decodeUtf8)
10+
import Kubernetes.Client.Auth.ClientCert
11+
import Kubernetes.Client.KubeConfig
12+
import Kubernetes.OpenAPI
13+
import Network.TLS (defaultParamsClient)
14+
15+
import qualified Data.ByteString.Base64 as B64
16+
17+
emptyAuthInfo :: AuthInfo
18+
emptyAuthInfo = AuthInfo Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
19+
20+
spec :: Spec
21+
spec = do
22+
let inputTLSParams = defaultParamsClient "" ""
23+
certFilePath = "test/testdata/certs/certificate.pem"
24+
keyFilePath = "test/testdata/certs/private-key.pem"
25+
base64EncodedCert = decodeUtf8 $ B64.encode $(embedFile $ "test/testdata/certs/certificate.pem")
26+
base64EncodedKey = decodeUtf8 $ B64.encode $(embedFile $ "test/testdata/certs/private-key.pem")
27+
describe "ClientCert File Authentication" $ do
28+
context "when cert and key file are provided in AuthInfo" $ do
29+
let auth = emptyAuthInfo { clientCertificate = Just $ certFilePath
30+
, clientKey = Just $ keyFilePath }
31+
it "should detect client cert file auth" $ do
32+
inputConfig <- newConfig
33+
isJust (clientCertFileAuth auth (inputTLSParams, inputConfig)) `shouldBe` True
34+
35+
it "should disable validate auth method" $ do
36+
inputConfig <- newConfig
37+
case clientCertFileAuth auth (inputTLSParams, inputConfig) of
38+
Nothing -> expectationFailure "expected to detect client cert file auth"
39+
Just detectedAuth -> do
40+
(_, cfg) <- detectedAuth
41+
configValidateAuthMethods cfg `shouldBe` False
42+
43+
it "should return Nothing if the cert file is not provided" $ do
44+
let auth = emptyAuthInfo {clientKey = Just "/some/file"}
45+
inputConfig <- newConfig
46+
isNothing (clientCertFileAuth auth (inputTLSParams, inputConfig)) `shouldBe` True
47+
48+
it "should return Nothing if the key file is not provided" $ do
49+
let auth = emptyAuthInfo {clientCertificate = Just "/some/file"}
50+
inputConfig <- newConfig
51+
isNothing (clientCertFileAuth auth (undefined, inputConfig)) `shouldBe` True
52+
53+
describe "ClientCert Data Authentication" $ do
54+
context "when cert and key file are provided in AuthInfo" $ do
55+
let auth = emptyAuthInfo { clientCertificateData = Just base64EncodedCert
56+
, clientKeyData = Just base64EncodedKey}
57+
it "should detect client cert data auth" $ do
58+
inputConfig <- newConfig
59+
isJust (clientCertDataAuth auth (inputTLSParams, inputConfig)) `shouldBe` True
60+
61+
it "should disable validate auth method" $ do
62+
inputConfig <- newConfig
63+
case clientCertDataAuth auth (inputTLSParams, inputConfig) of
64+
Nothing -> expectationFailure "expected to detect client cert file auth"
65+
Just detectedAuth -> do
66+
(_, cfg) <- detectedAuth
67+
configValidateAuthMethods cfg `shouldBe` False
68+
69+
it "should return Nothing if the cert file is not provided" $ do
70+
let auth = emptyAuthInfo {clientKeyData = Just base64EncodedKey}
71+
inputConfig <- newConfig
72+
isNothing (clientCertDataAuth auth (inputTLSParams, inputConfig)) `shouldBe` True
73+
74+
it "should return Nothing if the key file is not provided" $ do
75+
let auth = emptyAuthInfo {clientCertificateData = Just base64EncodedCert}
76+
inputConfig <- newConfig
77+
isNothing (clientCertDataAuth auth (inputTLSParams, inputConfig)) `shouldBe` True
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
{-# LANGUAGE ScopedTypeVariables #-}
3+
module Kubernetes.Client.KubeConfigSpec where
4+
5+
import Data.Aeson (decode, encode, parseJSON,
6+
toJSON)
7+
import Data.Maybe (fromJust)
8+
import Data.Yaml (decodeFile)
9+
import Kubernetes.Client.KubeConfig (AuthInfo (..), Cluster (..),
10+
Config, Context (..),
11+
getAuthInfo, getCluster,
12+
getContext)
13+
import Test.Hspec
14+
15+
spec :: Spec
16+
spec = do
17+
let getConfig :: IO Config
18+
getConfig = fromJust <$> decodeFile "test/testdata/kubeconfig.yaml"
19+
describe "FromJSON and ToJSON instances" $ do
20+
it "roundtrips successfully" $ do
21+
config <- getConfig
22+
decode (encode (toJSON config)) `shouldBe` Just config
23+
describe "getContext" $ do
24+
it "returns the correct context" $ do
25+
config <- getConfig
26+
getContext config `shouldBe` (Right (Context "cluster-aaa" "user-aaa" Nothing))
27+
28+
describe "getCluster" $ do
29+
it "returns the correct cluster" $ do
30+
config <- getConfig
31+
server <$> getCluster config `shouldBe` (Right "https://aaa.example.com")
32+
33+
describe "getAuthInfo" $ do
34+
it "returns the correct authInfo" $ do
35+
config <- getConfig
36+
fst <$> getAuthInfo config `shouldBe` (Right "user-aaa")

kubernetes-client/test/Spec.hs

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1 @@
1-
{-# LANGUAGE OverloadedStrings #-}
2-
{-# LANGUAGE ScopedTypeVariables #-}
3-
4-
import Data.Aeson (decode, encode, parseJSON,
5-
toJSON)
6-
import Data.Maybe (fromJust)
7-
import Data.Yaml (decodeFile)
8-
import Kubernetes.Client.KubeConfig (AuthInfo (..), Cluster (..),
9-
Config, Context (..),
10-
getAuthInfo, getCluster,
11-
getContext)
12-
import Test.Hspec
13-
14-
main :: IO ()
15-
main = do
16-
config :: Config <- fromJust <$> decodeFile "test/testdata/kubeconfig.yaml"
17-
hspec $ do
18-
describe "FromJSON and ToJSON instances" $ do
19-
it "roundtrips successfully" $ do
20-
decode (encode (toJSON config)) `shouldBe` Just config
21-
describe "getContext" $ do
22-
it "returns the correct context" $ do
23-
getContext config `shouldBe` (Right (Context "cluster-aaa" "user-aaa" Nothing))
24-
25-
describe "getCluster" $ do
26-
it "returns the correct cluster" $ do
27-
server <$> getCluster config `shouldBe` (Right "https://aaa.example.com")
28-
29-
describe "getAuthInfo" $ do
30-
it "returns the correct authInfo" $ do
31-
fst <$> getAuthInfo config `shouldBe` (Right "user-aaa")
1+
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICsDCCAZgCCQCyRyEmfEJy9jANBgkqhkiG9w0BAQUFADAZMRcwFQYDVQQDDA5z
3+
ZWxmLXNpZ25lZC1jYTAgFw0xOTA3MjMyMTUwMThaGA8yMTI5MDEyNzIxNTAxOFow
4+
GTEXMBUGA1UEAwwOc2VsZi1zaWduZWQtY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
5+
DwAwggEKAoIBAQDbxmgIae5FUbLu76NmsUbbo7D0Bk6cUpII/lhREQptoL4YkOo3
6+
MnLVQ6bkTG1hdWkN2A+eDHDJklNR6zzrCAA395lKhCZWSxq9o3AFWFMI8TWSptxL
7+
X/fl/vDh1pH4u/nGjKC1fSf2F9nvxywIgd0PNttWG8bNMzl3gZULfg0TWKvSX6Ln
8+
9SLA9Whsr3WMQno46JiKauB7z1+9Qc+26uEOi6kpc2mYIDTCUO5+umUEsV+seQS+
9+
TUe9lMQCLuZVmnWoygbCx5+eXm//iYOBSz8Z5jpEOuA6mY9FJAUU3uoGR656NImZ
10+
IBjgVSZBIDleq2V/lmdbjHy8GpQTMtButMorAgMBAAEwDQYJKoZIhvcNAQEFBQAD
11+
ggEBALHSlOI+Ra6QWr5V3FQS2ypkddK19RZ3TxEvCt8Brt2uNBw6Oxw/qLq17xiB
12+
C7bJV1SA9MNqdv1grk1kVil5aUeAGLIQtVD3C1/qiCgfP+HN1Fb0QnMcxJwESRmc
13+
TDQoYEkZp/TqgggDbmJD6S91EpXs1QiAvNA5+9L8ikOrOJQeHkzFen9PXoSqkVTl
14+
XPwOsCIzcBnuq+agt3pkiFcHtm348y4Yjv3kOimAhzanR96DH2DtTBCmDE/cYW7j
15+
s5aOI/MKlrZxh2gFvgqBZMjVUv8bNuDAU+8prDH4YzsDFeGKD42lDvnL+XQ69xKE
16+
oLBoM+xTo5QwQp0ZWW3srw47BKo=
17+
-----END CERTIFICATE-----
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
MIIEowIBAAKCAQEA28ZoCGnuRVGy7u+jZrFG26Ow9AZOnFKSCP5YUREKbaC+GJDq
3+
NzJy1UOm5ExtYXVpDdgPngxwyZJTUes86wgAN/eZSoQmVksavaNwBVhTCPE1kqbc
4+
S1/35f7w4daR+Lv5xoygtX0n9hfZ78csCIHdDzbbVhvGzTM5d4GVC34NE1ir0l+i
5+
5/UiwPVobK91jEJ6OOiYimrge89fvUHPturhDoupKXNpmCA0wlDufrplBLFfrHkE
6+
vk1HvZTEAi7mVZp1qMoGwsefnl5v/4mDgUs/GeY6RDrgOpmPRSQFFN7qBkeuejSJ
7+
mSAY4FUmQSA5Xqtlf5ZnW4x8vBqUEzLQbrTKKwIDAQABAoIBACG0nRHlRSCmdf3F
8+
DNdcCtT2ltXl/bplw3XTpDHSnjnP9DeKShFrEEd616advgy7WABCiaqgl8+iPFsM
9+
68vT70ymEYFnIQYNAK3i2fRH5nwxmhjCtHhu4HMKlWDdaoeuNJFp0d/jsPRCFi96
10+
6Vrop8GElUDwg53G5GJaokQf8dtsbg05Qv0C+BLMjYoXt+OCSYn0Jnzc3YLyn6pf
11+
0Ubhb5X73+pSFcY0JindJzHo/c3k5255VOorimxSA9Kr0glYJ2MlOuWM7khZfWjJ
12+
yJPEfa0hM/mPkR2WZ3Rif1Hb01BcrpjsNmQKFDKG+gumxL0CYzUlxOrVI5RO5+yC
13+
wKw8FYkCgYEA/QSzpNxc+qtYj9lJL3p2sxN8cQNOhDOF3DdEZmwToPF2JQKqnDo0
14+
5LDugNgUuOilVFYGxmLNC9RMB+PgOT7kHgZY99cZFuUCww85j2NIbdoATXFZvUdD
15+
O2E9o4X4RQ4+WQTZZ01sfjxMa6Y+t5HC49wvwHwdY0SMA3w6jPkBer8CgYEA3l1q
16+
yJObFV6rorCoeJIpLPGs1lQpXb0qgENmE6tnwYQ56mIuP0mLiSvqkISWwQyEiPjZ
17+
g9O70adOLom/0l9ssIsE1bHk/k/391rzYsvXvHM/uuIFNd7yqYApBLXq3jiNZbfq
18+
CeoWkymez0VOWzgIQxh2UHZ+5+qRSMDgV6hq55UCgYBr00ofcs2pAcZvHyFCO4VE
19+
UYSRwOAAFNjx/ReIMny29M/te9JrW57Y6tHpVKyYFIUIiNTATLCnXuS75A/VNYkP
20+
hpL5o9AMYrInoGBeS+g88E96sViWAj2Tm6AiBODFxQkq9JcVn/ghX98NbT6DCnos
21+
ktRCymHXwQmOHq3xD9jijwKBgGX3KFQ5e0/dTY8Yuugu/bqiR8MwbJeTer2+Kjyy
22+
yK0wWO5lfxd+PgH0pWcHpal4d/3nPrb4jJOiyHMGr3NkVo7N8LWdEYicWvSOPDT9
23+
jDvaDUtBAWqmhVe8cRK76Ktl+1C9eRB6y0dIOo6JFVk25HL/8KEM9Tybj2txJm6L
24+
yBnRAoGBAN3K6EhZ6eO0BorVNi8/5KaE8D0f3ZFEgUBEpVwiJLmL1hXbsiv2JyjE
25+
+dR+reScpl8NODycnnFzBzlIijB52CZWo+cxyFSvZxz3RrJZ8XaM/fg5MgNmcwLQ
26+
zejmoH7LuYD4z/OaPzgupJZlRUZNADOqZ8alsxSIvOHkImUAT9Q2
27+
-----END RSA PRIVATE KEY-----

0 commit comments

Comments
 (0)