1 {-# LANGUAGE TemplateHaskell #-}
2 {-# OPTIONS_GHC -fno-warn-orphans #-}
4 {-| Unittests for ganeti-htools.
10 Copyright (C) 2009, 2010, 2011, 2012 Google Inc.
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful, but
18 WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
29 module Test.Ganeti.Confd.Utils (testConfdUtils) where
31 import Control.Applicative
32 import Test.QuickCheck
33 import qualified Text.JSON as J
35 import Test.Ganeti.TestHelper
36 import Test.Ganeti.TestCommon
38 import qualified Ganeti.BasicTypes as BasicTypes
39 import qualified Ganeti.Confd as Confd
40 import qualified Ganeti.Confd.Utils as Confd.Utils
41 import qualified Ganeti.Constants as C
42 import qualified Ganeti.Hash as Hash
44 instance Arbitrary Confd.ConfdRequestType where
45 arbitrary = elements [minBound..maxBound]
47 instance Arbitrary Confd.ConfdReqField where
48 arbitrary = elements [minBound..maxBound]
50 instance Arbitrary Confd.ConfdReqQ where
51 arbitrary = Confd.ConfdReqQ <$> arbitrary <*> arbitrary <*>
52 arbitrary <*> arbitrary
54 instance Arbitrary Confd.ConfdQuery where
55 arbitrary = oneof [ pure Confd.EmptyQuery
56 , Confd.PlainQuery <$> getName
57 , Confd.DictQuery <$> arbitrary
60 instance Arbitrary Confd.ConfdRequest where
61 arbitrary = Confd.ConfdRequest <$> arbitrary <*> arbitrary <*> arbitrary
64 -- | Test that signing messages and checking signatures is correct. It
65 -- also tests, indirectly the serialisation of messages so we don't
66 -- need a separate test for that.
67 prop_ConfdUtils_req_sign :: Hash.HashKey -- ^ The hash key
68 -> NonNegative Integer -- ^ The base timestamp
69 -> Positive Integer -- ^ Delta for out of window
70 -> Bool -- ^ Whether delta should be + or -
73 prop_ConfdUtils_req_sign key (NonNegative timestamp) (Positive bad_delta)
75 forAll (choose (0, fromIntegral C.confdMaxClockSkew)) $ \ good_delta ->
76 let encoded = J.encode crq
78 signed = J.encode $ Confd.Utils.signMessage key salt encoded
79 good_timestamp = timestamp + if pm then good_delta else (-good_delta)
80 bad_delta' = fromIntegral C.confdMaxClockSkew + bad_delta
81 bad_timestamp = timestamp + if pm then bad_delta' else (-bad_delta')
82 ts_ok = Confd.Utils.parseMessage key signed good_timestamp
83 ts_bad = Confd.Utils.parseMessage key signed bad_timestamp
84 in printTestCase "Failed to parse good message"
85 (ts_ok ==? BasicTypes.Ok (encoded, crq)) .&&.
86 printTestCase ("Managed to deserialise message with bad\
87 \ timestamp, got " ++ show ts_bad)
88 (ts_bad ==? BasicTypes.Bad "Too old/too new timestamp or clock skew")
90 -- | Tests that signing with a different key fails detects failure
92 prop_ConfdUtils_bad_key :: String -- ^ Salt
93 -> Confd.ConfdRequest -- ^ Request
95 prop_ConfdUtils_bad_key salt crq =
96 -- fixme: we hardcode here the expected length of a sha1 key, as
97 -- otherwise we could have two short keys that differ only in the
98 -- final zero elements count, and those will be expanded to be the
100 forAll (vector 20) $ \key_sign ->
101 forAll (vector 20 `suchThat` (/= key_sign)) $ \key_verify ->
102 let signed = Confd.Utils.signMessage key_sign salt (J.encode crq)
103 encoded = J.encode signed
104 in printTestCase ("Accepted message signed with different key" ++ encoded) $
105 BasicTypes.Bad "HMAC verification failed" ==?
106 Confd.Utils.parseRequest key_verify encoded
108 testSuite "ConfdUtils"
109 [ 'prop_ConfdUtils_req_sign
110 , 'prop_ConfdUtils_bad_key