packages feed

elocrypt 0.4.1 → 0.6.0

raw patch · 6 files changed

+212/−181 lines, 6 filesdep ~basePVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base

API changes (from Hackage documentation)

+ Data.Elocrypt: mkPassphrase :: Int -> IO [String]
- Data.Elocrypt: genPassword :: RandomGen g => Int -> g -> (String, g)
+ Data.Elocrypt: genPassword :: RandomGen g => Int -> Bool -> g -> (String, g)
- Data.Elocrypt: genPasswords :: RandomGen g => Int -> g -> ([String], g)
+ Data.Elocrypt: genPasswords :: RandomGen g => Int -> Bool -> g -> ([String], g)
- Data.Elocrypt: mkPassword :: MonadRandom m => Int -> m String
+ Data.Elocrypt: mkPassword :: MonadRandom m => Int -> Bool -> m String
- Data.Elocrypt: mkPasswords :: MonadRandom m => Int -> m [String]
+ Data.Elocrypt: mkPasswords :: MonadRandom m => Int -> Bool -> m [String]
- Data.Elocrypt: newPassword :: RandomGen g => Int -> g -> String
+ Data.Elocrypt: newPassword :: RandomGen g => Int -> Bool -> g -> String
- Data.Elocrypt: newPasswords :: RandomGen g => Int -> g -> [String]
+ Data.Elocrypt: newPasswords :: RandomGen g => Int -> Bool -> g -> [String]

Files

+ cli-test/CliTests.hs view
@@ -0,0 +1,130 @@+{-# LANGUAGE TemplateHaskell #-}+module Main where++import Data.Functor+import Data.List+import Data.Maybe+import Control.Monad+import Text.Printf++import Test.Proctest+import Test.Proctest.Assertions+import Test.QuickCheck+import Test.QuickCheck.Property+import Test.Tasty hiding (Timeout)+import Test.Tasty.QuickCheck (QuickCheckTests(..), testProperty)+import Test.Tasty.TH++import Test.Elocrypt.Instances++main :: IO ()+main = defaultMain (options tests)++options :: TestTree -> TestTree+options = localOption (QuickCheckTests 10)++tests :: TestTree+tests = testGroup "CLI Tests" [testGroup']++testGroup' = $(testGroupGenerator)++newtype CliArgs = CliArgs  { getArgs :: [String] }+               deriving Eq++instance Show CliArgs where+  show = concat . intersperse " " . getArgs++instance Arbitrary CliArgs where+  arbitrary = do+    len  <- arbitrary `suchThat` (>0) `suchThat` (<=40) :: Gen Int+    num  <- arbitrary `suchThat` (>2) `suchThat` (<=20) :: Gen Int+    args <- sublistOf ["-n %d" `printf` num,+                       show len]+    return (CliArgs args)+              +prop_shouldPrintPasswordsWithLength :: CliArgs -> Property+prop_shouldPrintPasswordsWithLength (CliArgs args)+  = isJust (getPosParam args) ==>+    ioProperty $ do+      (in', out', err', p) <- run' "dist/build/elocrypt/elocrypt" args+      response <- readHandle out'++      let len    = read . fromJust . getPosParam $ args+          words' = words response++      return (all ((==) len . length) words')++prop_shouldPrintNumberPasswords :: CliArgs -> Property+prop_shouldPrintNumberPasswords (CliArgs args)+  = isJust (getArg "-n" args) ==>+    ioProperty $ do+      (in', out', err', p) <- run' "dist/build/elocrypt/elocrypt" args+      response <- readHandle out'++      let number = read . fromJust . getArg "-n" $ args+          words' = words response+          +      return (number == length words')++prop_shouldPrintMultPasswordsPerLine :: CliArgs -> Property+prop_shouldPrintMultPasswordsPerLine (CliArgs args)+  = (read . fromMaybe "8" . getPosParam $ args) <= 38 ==>+    ioProperty $ do+      (in', out', err', p) <- run' "dist/build/elocrypt/elocrypt" args+      response <- readHandle out'+      return . all (>1) . tail . reverse . map length . map words . lines $ response++prop_shouldPrintDefaultMultPasswordsPerLine :: CliArgs -> Property+prop_shouldPrintDefaultMultPasswordsPerLine (CliArgs args)+  = isNothing (getArg "-n" args) ==>+    (read . fromMaybe "8" . getPosParam $ args) <= 38 ==>+    ioProperty $ do+      (in', out', err', p) <- run' "dist/build/elocrypt/elocrypt" args+      response <- readHandle out'+      return . all (>1) . tail . reverse . map length . map words . lines $ response++prop_shouldNotPrintZeroPasswords :: CliArgs -> Property+prop_shouldNotPrintZeroPasswords (CliArgs args)+  = isNothing (getPosParam args) ==>+    ioProperty $ do+      let args' = args ++ ["0"]+      +      (in', out', err', p) <- run' "dist/build/elocrypt/elocrypt" args'+      response <- readHandle out'+      return (response == "")++prop_shouldPrintLongPasswords :: GreaterThan79 Int -> Property+prop_shouldPrintLongPasswords (GT79 a)+  = ioProperty $ do+      (in', out', err', p) <- run' "dist/build/elocrypt/elocrypt" [show a]+      response <- readHandle out'+      return . all (==1) . map length . map words . lines $ response+++-- Utility functions+run' :: FilePath -> [String] -> IO (Handle, Handle, Handle, ProcessHandle)+run' exe args = do+  res@(in', out', err', p) <- run exe args+  sleep'+  assertExitedSuccess (seconds 2) p+  return res++readHandle :: Handle -> IO String+readHandle = (<$>) asUtf8Str . waitOutput (seconds 2) 5000++assertExitedSuccess :: Timeout -> ProcessHandle -> IO Bool+assertExitedSuccess t = liftM (== ExitSuccess) . assertExitedTimeout t++assertExitedFailure :: Timeout -> ProcessHandle -> IO Bool+assertExitedFailure t = liftM not . assertExitedSuccess t++sleep' :: IO ()+sleep' = sleep (seconds 0.0001)++getArg :: String -> [String] -> Maybe String+getArg prefix args = (tail . dropWhile (not . elem')) `liftM` arg+  where arg = find (isPrefixOf prefix) args+        elem' = (flip elem) [' ', '=']++getPosParam :: [String] -> Maybe String+getPosParam = find $ (/= '-') . head
− cli-test/Tests.hs
@@ -1,130 +0,0 @@-{-# LANGUAGE TemplateHaskell #-}-module Main where--import Data.Functor-import Data.List-import Data.Maybe-import Control.Monad-import Text.Printf--import Test.Proctest-import Test.Proctest.Assertions-import Test.QuickCheck-import Test.QuickCheck.Property-import Test.Tasty hiding (Timeout)-import Test.Tasty.QuickCheck (QuickCheckTests(..), testProperty)-import Test.Tasty.TH--import Test.Elocrypt.Instances--main :: IO ()-main = defaultMain (options tests)--options :: TestTree -> TestTree-options = localOption (QuickCheckTests 10)--tests :: TestTree-tests = testGroup "CLI Tests" [testGroup']--testGroup' = $(testGroupGenerator)--newtype CliArgs = CliArgs  { getArgs :: [String] }-               deriving Eq--instance Show CliArgs where-  show = concat . intersperse " " . getArgs--instance Arbitrary CliArgs where-  arbitrary = do-    len  <- arbitrary `suchThat` (>0) `suchThat` (<=40) :: Gen Int-    num  <- arbitrary `suchThat` (>2) `suchThat` (<=20) :: Gen Int-    args <- sublistOf ["-n %d" `printf` num,-                       show len]-    return (CliArgs args)-              -prop_shouldPrintPasswordsWithLength :: CliArgs -> Property-prop_shouldPrintPasswordsWithLength (CliArgs args)-  = isJust (getPosParam args) ==>-    ioProperty $ do-      (in', out', err', p) <- run' "dist/build/elocrypt/elocrypt" args-      response <- readHandle out'--      let len    = read . fromJust . getPosParam $ args-          words' = words response--      return (all ((==) len . length) words')--prop_shouldPrintNumberPasswords :: CliArgs -> Property-prop_shouldPrintNumberPasswords (CliArgs args)-  = isJust (getArg "-n" args) ==>-    ioProperty $ do-      (in', out', err', p) <- run' "dist/build/elocrypt/elocrypt" args-      response <- readHandle out'--      let number = read . fromJust . getArg "-n" $ args-          words' = words response-          -      return (number == length words')--prop_shouldPrintMultPasswordsPerLine :: CliArgs -> Property-prop_shouldPrintMultPasswordsPerLine (CliArgs args)-  = (read . fromMaybe "8" . getPosParam $ args) <= 38 ==>-    ioProperty $ do-      (in', out', err', p) <- run' "dist/build/elocrypt/elocrypt" args-      response <- readHandle out'-      return . all (>1) . tail . reverse . map length . map words . lines $ response--prop_shouldPrintDefaultMultPasswordsPerLine :: CliArgs -> Property-prop_shouldPrintDefaultMultPasswordsPerLine (CliArgs args)-  = isNothing (getArg "-n" args) ==>-    (read . fromMaybe "8" . getPosParam $ args) <= 38 ==>-    ioProperty $ do-      (in', out', err', p) <- run' "dist/build/elocrypt/elocrypt" args-      response <- readHandle out'-      return . all (>1) . tail . reverse . map length . map words . lines $ response--prop_shouldNotPrintZeroPasswords :: CliArgs -> Property-prop_shouldNotPrintZeroPasswords (CliArgs args)-  = isNothing (getPosParam args) ==>-    ioProperty $ do-      let args' = args ++ ["0"]-      -      (in', out', err', p) <- run' "dist/build/elocrypt/elocrypt" args'-      response <- readHandle out'-      return (response == "")--prop_shouldPrintLongPasswords :: GreaterThan79 Int -> Property-prop_shouldPrintLongPasswords (GT79 a)-  = ioProperty $ do-      (in', out', err', p) <- run' "dist/build/elocrypt/elocrypt" [show a]-      response <- readHandle out'-      return . all (==1) . map length . map words . lines $ response----- Utility functions-run' :: FilePath -> [String] -> IO (Handle, Handle, Handle, ProcessHandle)-run' exe args = do-  res@(in', out', err', p) <- run exe args-  sleep'-  assertExitedSuccess (seconds 2) p-  return res--readHandle :: Handle -> IO String-readHandle = (<$>) asUtf8Str . waitOutput (seconds 2) 5000--assertExitedSuccess :: Timeout -> ProcessHandle -> IO Bool-assertExitedSuccess t = liftM (== ExitSuccess) . assertExitedTimeout t--assertExitedFailure :: Timeout -> ProcessHandle -> IO Bool-assertExitedFailure t = liftM not . assertExitedSuccess t--sleep' :: IO ()-sleep' = sleep (seconds 0.0001)--getArg :: String -> [String] -> Maybe String-getArg prefix args = (tail . dropWhile (not . elem')) `liftM` arg-  where arg = find (isPrefixOf prefix) args-        elem' = (flip elem) [' ', '=']--getPosParam :: [String] -> Maybe String-getPosParam = find $ (/= '-') . head
elocrypt.cabal view
@@ -1,5 +1,5 @@ name:                elocrypt-version:             0.4.1+version:             0.6.0 synopsis:            Generate easy-to-remember, hard-to-guess passwords homepage:            https://www.github.com/sgillespie/elocrypt license:             OtherLicense@@ -26,21 +26,21 @@ source-repository this   type: git   location: https://github.com/sgillespie/elocrypt.git-  tag: v0.4.1+  tag: v0.6.0                    library   exposed-modules:     Data.Elocrypt,                        Data.Elocrypt.Trigraph   -- other-modules:          -- other-extensions:    -  build-depends:       base >=4.7 && <4.9,+  build-depends:       base >=4.7 && <5,                        MonadRandom,                        random   hs-source-dirs:      src/lib   default-language:    Haskell2010  executable elocrypt-  build-depends:       base >= 4.7 && <4.9,+  build-depends:       base >= 4.7 && <5,                        elocrypt,                        random   default-language:    Haskell2010@@ -48,7 +48,7 @@   main-is:             Main.hs  test-suite test-  build-depends:       base >= 4.7 && <4.9,+  build-depends:       base >= 4.7 && <5,                        elocrypt,                        MonadRandom,                        QuickCheck,@@ -59,10 +59,13 @@   default-language:    Haskell2010   hs-source-dirs:      test   main-is:             Tests.hs+  other-modules:       Test.Elocrypt.Instances,+                       Test.Elocrypt.TrigraphTest,+                       Test.ElocryptTest   type:                exitcode-stdio-1.0  test-suite ui-test-  build-depends:       base >= 4.7 && <4.9,+  build-depends:       base >= 4.7 && <5,                        elocrypt,                        proctest,                        MonadRandom,@@ -74,5 +77,6 @@   default-language:    Haskell2010   hs-source-dirs:      cli-test,                        test-  main-is:             Tests.hs+  main-is:             CliTests.hs+  other-modules:       Test.Elocrypt.Instances   type:                exitcode-stdio-1.0
src/cli/Main.hs view
@@ -11,7 +11,7 @@  import Data.Elocrypt -version = "elocrypt 0.4.1"+version = "elocrypt 0.6.0" termLen = 80 termHeight = 10 @@ -35,7 +35,7 @@  passwords :: RandomGen g => Options -> g -> String passwords opts gen = format . group' cols $ ps-  where ps = take num . newPasswords (optLength opts) $ gen+  where ps = take num . newPasswords (optLength opts) False $ gen -- TODO[sgillespie]: Add caps         format = concat . intersperse "\n" . map (concat . intersperse "  ")         cols = if optLength opts <= termLen - 2                   then termLen `div` (optLength opts + 2)
src/lib/Data/Elocrypt.hs view
@@ -14,9 +14,14 @@  import Control.Monad import Control.Monad.Random hiding (next)+import Data.Bool import Data.Maybe import System.Random hiding (next) +-- |Generate a finite number of words of random length (between 3 and 9 chars).+mkPassphrase :: Int -> IO [String]+mkPassphrase n = sequence $ replicate n $ randomRIO (3, 9) >>= flip mkPassword False+ -- * Random password generators  -- |Generate a password using the generator g, returning the result and the@@ -25,13 +30,14 @@ --  @ --  -- Generate a password of length 10 using the system generator --  myGenPassword :: IO (String, StdGen)---  myGenPassword = genPassword 10 \`liftM\` getStdGen+--  myGenPassword = genPassword 10 True \`liftM\` getStdGen --  @ genPassword :: RandomGen g-               => Int -- ^ password length-               -> g   -- ^ random generator+               => Int  -- ^ password length+               -> Bool -- ^ include capitals?+               -> g    -- ^ random generator                -> (String, g)-genPassword = runRand . mkPassword+genPassword len = runRand . mkPassword len  -- |Plural version of genPassword.  Generates an infinite list of passwords --  using the generator g, returning the result and the updated generator.@@ -39,26 +45,28 @@ -- @ -- -- Generate 10 passwords of length 10 using the system generator -- myGenPasswords :: IO ([String], StdGen)--- myGenPasswords = (\(ls, g) -> (take 10 ls, g) `liftM` genPasswords 10 `liftM` getStdGen+-- myGenPasswords = (\(ls, g) -> (take 10 ls, g) `liftM` genPasswords 10 True `liftM` getStdGen -- @ genPasswords :: RandomGen g-                => Int -- ^ password length-                -> g   -- ^ random generator+                => Int  -- ^ password length+                -> Bool -- ^ include capitals?+                -> g    -- ^ random generator                 -> ([String], g)-genPasswords = runRand . mkPasswords+genPasswords len = runRand . mkPasswords len  -- |Generate a password using the generator g, returning the result. -- --  @ --  -- Generate a password of length 10 using the system generator --  myNewPassword :: IO String---  myNewPassword = newPassword 10 \`liftM\` getStdGen+--  myNewPassword = newPassword 10 True \`liftM\` getStdGen --  @ newPassword :: RandomGen g-               => Int -- ^ password length-               -> g   -- ^ random generator+               => Int  -- ^ password length+               -> Bool -- ^ include capitals?+               -> g    -- ^ random generator                -> String-newPassword = evalRand . mkPassword+newPassword len = evalRand . mkPassword len  -- |Plural version of newPassword.  Generates an infinite list of passwords --  using the generator g, returning the result@@ -66,13 +74,14 @@ -- @ -- -- Generate 10 passwords of length 10 using the system generator -- myNewPasswords :: IO [String]--- myNewPasswords = (take 10 . genPasswords 10) `liftM` getStdGen+-- myNewPasswords = (take 10 . genPasswords 10 True) `liftM` getStdGen -- @ newPasswords :: RandomGen g-                => Int -- ^ password length-                -> g   -- ^ random generator+                => Int  -- ^ password length+                -> Bool -- ^ include capitals?+                -> g    -- ^ random generator                 -> [String]-newPasswords = evalRand . mkPasswords+newPasswords len = evalRand . mkPasswords len  -- |Generate a password using the MonadRandom m. MonadRandom is exposed here --  for extra control.@@ -80,12 +89,13 @@ --  @ --  -- Generate a password of length 10 using the system generator --  myPasswords :: IO String---  myPasswords = evalRand (mkPassword 10) \`liftM\` getStdGen+--  myPasswords = evalRand (mkPassword 10 True) \`liftM\` getStdGen --  @ mkPassword :: MonadRandom m-              => Int -- ^ password length+              => Int  -- ^ password length+              -> Bool -- ^ include capitals?               -> m String-mkPassword len = do+mkPassword len caps = do   f2 <- reverse `liftM` first2   if len > 2     then reverse `liftM` lastN (len - 2) f2@@ -97,13 +107,14 @@ -- @ -- -- Generate an infinite list of passwords of length 10 using the system generator -- myMkPasswords :: IO [String]--- myMkPasswords = evalRand (mkPasswords 10) \`liftM\` getStdGen+-- myMkPasswords = evalRand (mkPasswords 10 True) \`liftM\` getStdGen -- @ mkPasswords :: MonadRandom m-               => Int -- ^ password length+               => Int  -- ^ password length+               -> Bool -- ^ include capitals?                -> m [String]-mkPasswords = sequence . repeat . mkPassword-               +mkPasswords len = sequence . repeat . mkPassword len+ -- |The alphabet we sample random values from alphabet :: [Char] alphabet = ['a'..'z']@@ -119,10 +130,23 @@ -- |Generate a random character based on the previous two characters and --  their 'Elocrypt.Trigraph.trigraph' next :: MonadRandom m => String -> m Char-next (x:xs:_) = fromList . zip ['a'..'z'] . fromJust . findFrequency $ [xs,x]+next (x:xs:_) = fromList+                . zip ['a'..'z']+                . defaultFreqs+                . fromJust+                . findFrequency $ [xs, x] +  -- Fix frequencies if they are all 0, since MonadRandom prohibits this.+  -- Use all 1s in this case to give every item an equal weight.+  where defaultFreqs :: [Rational] -> [Rational]+        defaultFreqs f = bool (replicate 26 1) f (any (>0) f)++-- This shouldn't ever happen+next _ = undefined+ -- |Generate the last n characters using previous two characters --  and their 'Elocrypt.Trigraph.trigraph' lastN :: MonadRandom m => Int -> String -> m String lastN 0 ls   = return ls lastN len ls = next ls >>= lastN (len - 1) . (:ls)+
test/Test/ElocryptTest.hs view
@@ -6,6 +6,7 @@ import Data.Maybe import System.Random hiding (next) import Test.QuickCheck+import Test.Tasty import Test.Tasty.QuickCheck (testProperty) import Test.Tasty.TH @@ -13,33 +14,35 @@ import Data.Elocrypt.Trigraph import Test.Elocrypt.Instances +tests :: TestTree tests = $(testGroupGenerator) -prop_genPasswordShouldBeUnique :: GreaterThan2 Int -> StdGen -> Bool-prop_genPasswordShouldBeUnique (GT2 len) gen-  = p /= fst (genPassword len gen')-  where (p, gen') = genPassword len gen+prop_genPasswordShouldBeUnique :: GreaterThan2 Int -> Bool -> StdGen -> Bool+prop_genPasswordShouldBeUnique (GT2 len) caps gen+  = p /= fst (genPassword len caps gen')+  where (p, gen') = genPassword len caps gen -prop_genPasswordsShouldBeUnique :: GreaterThan2 Int -> StdGen -> Bool-prop_genPasswordsShouldBeUnique (GT2 len) gen+prop_genPasswordsShouldBeUnique :: GreaterThan2 Int -> Bool -> StdGen -> Bool+prop_genPasswordsShouldBeUnique (GT2 len) caps gen   = p /= ps-  where (p:ps:_) = fst (genPasswords len gen)+  where (p:ps:_) = fst (genPasswords len caps gen) -prop_newPasswordShouldBeLength :: Int -> StdGen -> Property-prop_newPasswordShouldBeLength len gen = len > 0 ==> length (newPassword len gen) == len+prop_newPasswordShouldBeLength :: Int -> Bool -> StdGen -> Property+prop_newPasswordShouldBeLength len caps gen = len > 0 ==>+                                              length (newPassword len caps gen) == len -prop_newPasswordShouldConsistOfAlphabet :: Int -> StdGen -> Bool-prop_newPasswordShouldConsistOfAlphabet len gen-  = all ((flip elem) alphabet) (newPassword len gen)+prop_newPasswordShouldConsistOfAlphabet :: Int -> Bool -> StdGen -> Bool+prop_newPasswordShouldConsistOfAlphabet len caps gen+  = all ((flip elem) alphabet) (newPassword len caps gen) -prop_newPasswordsShouldBeUnique :: GreaterThan2 Int -> StdGen -> Bool-prop_newPasswordsShouldBeUnique (GT2 len) gen+prop_newPasswordsShouldBeUnique :: GreaterThan2 Int -> Bool -> StdGen -> Bool+prop_newPasswordsShouldBeUnique (GT2 len) caps gen   = p /= ps-  where (p:ps:_) = newPasswords len gen+  where (p:ps:_) = newPasswords len caps gen -prop_newPasswordShouldHaveLen :: Int -> StdGen -> Property-prop_newPasswordShouldHaveLen len gen-  = len >= 0 ==> length (newPassword len gen) == len+prop_newPasswordShouldHaveLen :: Int -> Bool -> StdGen -> Property+prop_newPasswordShouldHaveLen len caps gen+  = len >= 0 ==> length (newPassword len caps gen) == len  prop_first2ShouldHaveLength2 :: StdGen -> Bool prop_first2ShouldHaveLength2 g = length (evalRand first2 g) == 2