packages feed

yesod-auth-hashdb 1.4.3 → 1.5

raw patch · 5 files changed

+108/−201 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Yesod.Auth.HashDB: User :: !Text -> !Text -> !Text -> UserGeneric backend
- Yesod.Auth.HashDB: [userPassword] :: UserGeneric backend -> !Text
- Yesod.Auth.HashDB: [userSalt] :: UserGeneric backend -> !Text
- Yesod.Auth.HashDB: [userUsername] :: UserGeneric backend -> !Text
- Yesod.Auth.HashDB: data UserGeneric backend
- Yesod.Auth.HashDB: getAuthIdHashDB :: HashDBPersist site user => (AuthRoute -> Route site) -> (Text -> Maybe (Unique user)) -> Creds site -> HandlerT site IO (Maybe (AuthId site))
- Yesod.Auth.HashDB: instance Data.Aeson.Types.Class.FromJSON (Database.Persist.Class.PersistStore.BackendKey backend) => Data.Aeson.Types.Class.FromJSON (Database.Persist.Class.PersistEntity.Key (Yesod.Auth.HashDB.UserGeneric backend))
- Yesod.Auth.HashDB: instance Data.Aeson.Types.Class.ToJSON (Database.Persist.Class.PersistStore.BackendKey backend) => Data.Aeson.Types.Class.ToJSON (Database.Persist.Class.PersistEntity.Key (Yesod.Auth.HashDB.UserGeneric backend))
- Yesod.Auth.HashDB: instance Database.Persist.Class.PersistField.PersistField (Database.Persist.Class.PersistStore.BackendKey backend) => Database.Persist.Class.PersistField.PersistField (Database.Persist.Class.PersistEntity.Key (Yesod.Auth.HashDB.UserGeneric backend))
- Yesod.Auth.HashDB: instance Database.Persist.Class.PersistStore.PersistStore backend => Database.Persist.Class.PersistEntity.PersistEntity (Yesod.Auth.HashDB.UserGeneric backend)
- Yesod.Auth.HashDB: instance Database.Persist.Class.PersistStore.PersistStore backend => Database.Persist.Class.PersistField.PersistField (Yesod.Auth.HashDB.UserGeneric backend)
- Yesod.Auth.HashDB: instance Database.Persist.Class.PersistStore.PersistStore backend => Database.Persist.Class.PersistStore.ToBackendKey backend (Yesod.Auth.HashDB.UserGeneric backend)
- Yesod.Auth.HashDB: instance Database.Persist.Class.PersistStore.PersistStore backend => Database.Persist.Sql.Class.PersistFieldSql (Yesod.Auth.HashDB.UserGeneric backend)
- Yesod.Auth.HashDB: instance Database.Persist.Sql.Class.PersistFieldSql (Database.Persist.Class.PersistStore.BackendKey backend) => Database.Persist.Sql.Class.PersistFieldSql (Database.Persist.Class.PersistEntity.Key (Yesod.Auth.HashDB.UserGeneric backend))
- Yesod.Auth.HashDB: instance GHC.Classes.Eq (Database.Persist.Class.PersistStore.BackendKey backend) => GHC.Classes.Eq (Database.Persist.Class.PersistEntity.Key (Yesod.Auth.HashDB.UserGeneric backend))
- Yesod.Auth.HashDB: instance GHC.Classes.Ord (Database.Persist.Class.PersistStore.BackendKey backend) => GHC.Classes.Ord (Database.Persist.Class.PersistEntity.Key (Yesod.Auth.HashDB.UserGeneric backend))
- Yesod.Auth.HashDB: instance GHC.Read.Read (Database.Persist.Class.PersistStore.BackendKey backend) => GHC.Read.Read (Database.Persist.Class.PersistEntity.Key (Yesod.Auth.HashDB.UserGeneric backend))
- Yesod.Auth.HashDB: instance GHC.Show.Show (Database.Persist.Class.PersistStore.BackendKey backend) => GHC.Show.Show (Database.Persist.Class.PersistEntity.Key (Yesod.Auth.HashDB.UserGeneric backend))
- Yesod.Auth.HashDB: instance Web.HttpApiData.Internal.FromHttpApiData (Database.Persist.Class.PersistStore.BackendKey backend) => Web.HttpApiData.Internal.FromHttpApiData (Database.Persist.Class.PersistEntity.Key (Yesod.Auth.HashDB.UserGeneric backend))
- Yesod.Auth.HashDB: instance Web.HttpApiData.Internal.ToHttpApiData (Database.Persist.Class.PersistStore.BackendKey backend) => Web.HttpApiData.Internal.ToHttpApiData (Database.Persist.Class.PersistEntity.Key (Yesod.Auth.HashDB.UserGeneric backend))
- Yesod.Auth.HashDB: instance Web.PathPieces.PathPiece (Database.Persist.Class.PersistStore.BackendKey backend) => Web.PathPieces.PathPiece (Database.Persist.Class.PersistEntity.Key (Yesod.Auth.HashDB.UserGeneric backend))
- Yesod.Auth.HashDB: instance Yesod.Auth.HashDB.HashDBUser (Yesod.Auth.HashDB.UserGeneric backend)
- Yesod.Auth.HashDB: migrateUsers :: Migration
- Yesod.Auth.HashDB: setSaltAndPasswordHash :: HashDBUser user => Text -> Text -> user -> user
- Yesod.Auth.HashDB: setUserHashAndSalt :: HashDBUser user => Text -> Text -> user -> user
- Yesod.Auth.HashDB: type User = UserGeneric SqlBackend
- Yesod.Auth.HashDB: type UserId = Key User
- Yesod.Auth.HashDB: class HashDBUser user where userPasswordSalt _ = Just "" setPasswordHash = setSaltAndPasswordHash "" setUserHashAndSalt = error "Define setSaltAndPasswordHash to get old-database compatibility" setSaltAndPasswordHash = setUserHashAndSalt
+ Yesod.Auth.HashDB: class HashDBUser user where userPasswordSalt _ = Just ""

Files

ChangeLog.md view
@@ -1,3 +1,12 @@+## 1.5++This release can break both old code and old database entries.  For details+of upgrading, please see+[Upgrading.md](https://github.com/paul-rouse/yesod-auth-hashdb/blob/master/Upgrading.md).++* First phase of removing compatibility with old databases designed for versions before 1.3+* Remove deprecated utilities (`getAuthIdHashDB` and pre-defined `User` data type)+ ## 1.4.3  * Changes to work with persistent-2.5
Yesod/Auth/HashDB.hs view
@@ -1,60 +1,26 @@ {-# LANGUAGE CPP                        #-}-{-# LANGUAGE DeriveDataTypeable         #-} {-# LANGUAGE QuasiQuotes                #-} {-# LANGUAGE ConstraintKinds            #-} {-# LANGUAGE FlexibleContexts           #-}-{-# LANGUAGE FlexibleInstances          #-} {-# LANGUAGE TypeFamilies               #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MultiParamTypeClasses      #-}-{-# LANGUAGE TemplateHaskell            #-} {-# LANGUAGE OverloadedStrings          #-}-{-# LANGUAGE GADTs                      #-} ------------------------------------------------------------------------------- -- | -- Module      :  Yesod.Auth.HashDB--- Copyright   :  (c) Patrick Brisbin 2010, Paul Rouse 2014-2015+-- Copyright   :  (c) Patrick Brisbin 2010, Paul Rouse 2014-2016 -- License     :  MIT -- -- Maintainer  :  Paul Rouse <pyr@doynton.org> -- Stability   :  Stable -- Portability :  Portable ----- A yesod-auth AuthPlugin designed to look users up in a Persistent+-- A Yesod authentication plugin designed to look users up in a Persistent -- database where the hash of their password is stored. ----- This module was removed from @yesod-auth-1.3.0.0@ and is now--- maintained separately.--- Versions of this module prior to @yesod-auth-1.3@ used a relatively weak--- hashing algorithm (a single round of SHA1) which does not provide--- adequate protection against an attacker who discovers the hashed passwords.--- See: <https://github.com/yesodweb/yesod/issues/668>.------ It has now been rewritten to use "Crypto.PasswordStore", but this has been--- done in a way which preserves compatibility both with the API and--- with databases which have been set up using older versions of this module.--- There are two levels of database compatibility:------ * The verification code recognises both the old and new hash formats,---   so passwords can be verified against database entries which still---   contain old-style hashes.------ * The function 'upgradePasswordHash' can be used to migrate---   existing user records to use the new format hash.  Unlike---   freshly created password hashes, entries converted this way---   must still have the old salt field, since the old hash function---   remains part of the algorithm needed for verification.  (The---   new hash is layered on top of the old one.)------ On the other hand, new passwords set up by 'setPassword' or--- 'setPasswordStrength' no longer use a separate salt field, so new users--- of this module need only provide a single password field in the user data,--- and can ignore the salt.------ In a system which has been migrated from the old format, passwords--- which are reset will use the new format and will have an empty salt field.--- Once all the entries are of this form, it is safe to change the model--- to remove the salt, and change the 'HashDBUser' instance accordingly.+-- __Release 1.5 is the first of two which remove compatibility with old__+-- __(pre 1.3) databases.  Some other deprecated functionality is removed__+-- __too.  Please see__+-- __<https://github.com/paul-rouse/yesod-auth-hashdb/blob/master/Upgrading.md>__ -- -- To use this in a Yesod application, the foundation data type must be an -- instance of YesodPersist, and the username and hashed passwords should@@ -69,9 +35,7 @@ -- >     password Text Maybe   -- password hash for HashDB -- >     UniqueUser name ----- Create an instance of 'HashDBUser' for this data type.  For historical--- reasons "Yesod.Auth.HashDB" exports some names which are quite likely to--- clash with your own, so it is a good idea to import just the ones you need:+-- Create an instance of 'HashDBUser' for this data type: -- -- > import Yesod.Auth.HashDB (HashDBUser(..)) -- > ....@@ -140,7 +104,6 @@ ------------------------------------------------------------------------------- module Yesod.Auth.HashDB     ( HashDBUser(..)-    , Unique (..)     , defaultStrength     , setPasswordStrength     , setPassword@@ -150,32 +113,23 @@     , validateUser     , authHashDB     , authHashDBWithForm-    , getAuthIdHashDB-      -- * Predefined data type-    , User-    , UserGeneric (..)-    , UserId-    , EntityField (..)-    , migrateUsers     ) where -import Yesod.Persist-import Yesod.Form-import Yesod.Auth-import Yesod.Core-import qualified Yesod.Auth.Message as Msg  #if __GLASGOW_HASKELL__ < 710-import Control.Applicative         ((<$>), (<*>))+import           Control.Applicative         ((<$>), (<*>)) #endif-import Data.Typeable-+import qualified Crypto.Hash           as CH (SHA1, Digest, hash)+import           Crypto.PasswordStore        (makePassword, strengthenPassword,+                                              verifyPassword, passwordStrength) import qualified Data.ByteString.Char8 as BS (pack, unpack)-import qualified Crypto.Hash as CH (SHA1, Digest, hash)-import Data.Text                   (Text, pack, unpack, append)-import Data.Maybe                  (fromMaybe)-import Crypto.PasswordStore        (makePassword, verifyPassword,-                                    passwordStrength, strengthenPassword)+import           Data.Maybe                  (fromMaybe)+import           Data.Text                   (Text, pack, unpack, append)+import           Yesod.Auth+import qualified Yesod.Auth.Message    as Msg+import           Yesod.Core+import           Yesod.Form+import           Yesod.Persist  -- | Default strength used for passwords (see "Crypto.PasswordStore" for --   details).@@ -186,50 +140,23 @@ --   be an instance of this class.  It just provides the getters and setters --   used by the functions in this module. class HashDBUser user where-  -- | Retrieve password hash from user data-  userPasswordHash :: user -> Maybe Text-  -- | Retrieve salt for password from user data.  This is needed only for-  --   compatibility with old database entries, which contain the salt-  --   as a separate field.  New implementations do not require a separate-  --   salt field in the user data, and should leave this as the default.-  userPasswordSalt :: user -> Maybe Text-  userPasswordSalt _ = Just ""--  -- | Callback for 'setPassword' and 'upgradePasswordHash'.  Produces a-  --   version of the user data with the hash set to the new value.-  ---  --   This is the method which you should define for new applications, which-  --   do not require compatibility with databases containing hashes written-  --   by previous versions of this module.  If you do need compatibility,-  --   define 'setSaltAndPasswordHash' instead.-  setPasswordHash :: Text   -- ^ Password hash-                     -> user -> user-  setPasswordHash = setSaltAndPasswordHash ""--  setUserHashAndSalt :: Text    -- ^ Salt-                     -> Text    -- ^ Password hash-                     -> user -> user-  setUserHashAndSalt =-      error "Define setSaltAndPasswordHash to get old-database compatibility"+    -- | Retrieve password hash from user data+    userPasswordHash :: user -> Maybe Text+    -- | Retrieve salt for password from user data.  This is needed only for+    --   compatibility with old database entries, which contain the salt+    --   as a separate field.  New implementations do not require a separate+    --   salt field in the user data, and should leave this as the default.+    userPasswordSalt :: user -> Maybe Text+    userPasswordSalt _ = Just "" -  -- | Callback used in 'upgradePasswordHash' when compatibility is needed-  --   with old-style hashes (including ones already upgraded using-  --   'upgradePasswordHash').  This is not required for new applications,-  --   which do not have a separate salt field in user data: please define-  --   'setPasswordHash' instead.-  ---  --   The default implementation produces a runtime error, and will only be-  --   called if a non-empty salt value needs to be set for compatibility-  --   with an old database.-  setSaltAndPasswordHash :: Text    -- ^ Salt-                     -> Text    -- ^ Password hash-                     -> user -> user-  setSaltAndPasswordHash = setUserHashAndSalt+    -- | Callback for 'setPassword' and 'upgradePasswordHash'.  Produces a+    --   version of the user data with the hash set to the new value.+    --+    setPasswordHash :: Text   -- ^ Password hash+                       -> user -> user -  {-# MINIMAL userPasswordHash, (setPasswordHash | (userPasswordSalt, setSaltAndPasswordHash)) #-}-{-# DEPRECATED userPasswordSalt "Compatibility with old data containing a separate salt field will be removed eventually" #-}-{-# DEPRECATED setUserHashAndSalt "Please use setSaltAndPasswordHash instead" #-}-{-# DEPRECATED setSaltAndPasswordHash "Compatibility with old data containing a separate salt field will be removed eventually" #-}+    {-# MINIMAL userPasswordHash, setPasswordHash #-}+{-# DEPRECATED userPasswordSalt "Verification against old data containing a separate salt field will be removed in version 1.6" #-}   -- | Calculate salted hash using SHA1.  Retained for compatibility with@@ -238,7 +165,7 @@            -> Text              -- ^ Password            -> Text saltedHash salt pw =-  pack $ show (CH.hash $ BS.pack $ unpack $ append salt pw :: CH.Digest CH.SHA1)+    pack $ show (CH.hash $ BS.pack $ unpack $ append salt pw :: CH.Digest CH.SHA1)  -- | Calculate a new-style password hash using "Crypto.PasswordStore". passwordHash :: MonadIO m => Int -> Text -> m Text@@ -300,28 +227,25 @@         else return $ hash == saltedHash salt passwd  -- | Upgrade existing user credentials to a stronger hash.  The existing---   hash may have been produced either by previous versions of this module,---   which used a weak algorithm, or from a weaker setting in the current+--   hash will have been produced from a weaker setting in the current --   algorithm.  Use this function to produce an updated user record to --   store in the database. -----   To allow transitional use, starting from hashes produced by older---   versions of this module, and upgrading them to the new format,---   we have to use the hash alone, without knowledge of the user's---   plaintext password.  In this case, we apply the new algorithm to the---   old hash, resulting in both hash functions, old and new, being used---   one on top of the other; this situation is recognised by the hash---   having the new format while the separate salt field is non-empty.+--   As of version 1.5 this function cannot be used to upgrade a hash+--   which has a non-empty separate salt field.  Such entries would have+--   been produced originally by versions of this module prior to 1.3,+--   but may have been upgraded using earlier versions of this function. -- --   Returns Nothing if the user has no password (ie if 'userPasswordHash' u---   is 'Nothing' and/or 'userPasswordSalt' u is 'Nothing').+--   is 'Nothing'), if the password hash is not in the new format, or if the+--   user has a non-empty salt field resulting from the old-style hashing+--   algorithm. upgradePasswordHash :: (MonadIO m, HashDBUser user) => Int -> user -> m (Maybe user) upgradePasswordHash strength u = do-    let old = do h <- userPasswordHash u-                 s <- userPasswordSalt u-                 return (h, s)+    let old = userPasswordHash u+        oldSalt = fromMaybe "" $ userPasswordSalt u     case old of-        Just (oldHash, oldSalt) -> do+        Just oldHash -> do             let oldHash' = BS.pack $ unpack oldHash             if passwordStrength oldHash' > 0               then@@ -329,11 +253,10 @@                 let newHash = pack $ BS.unpack $ strengthenPassword oldHash' strength                 in if oldSalt == ""                    then return $ Just $ setPasswordHash newHash u-                   else return $ Just $ setSaltAndPasswordHash oldSalt newHash u+                   else return Nothing   -- Can't handle non-empty salt               else do-                -- Old-style hash: do extra layer of hash with the new algorithm-                newHash <- passwordHash strength oldHash-                return $ Just $ setSaltAndPasswordHash oldSalt newHash u+                -- Old-style hash: cannot upgrade+                return Nothing         Nothing -> return Nothing  @@ -347,7 +270,6 @@     ( YesodAuthPersist master     , PersistUnique (YesodPersistBackend master)     , AuthEntity master ~ user-    , AuthId master ~ Key user #if MIN_VERSION_persistent(2,5,0)     , PersistEntityBackend user ~ BaseBackend (YesodPersistBackend master) #else@@ -366,9 +288,9 @@              -> Text            -- ^ Password in plaintext              -> HandlerT site IO Bool validateUser userID passwd = do-  -- Get user data-  user <- runDB $ getBy userID-  return $ fromMaybe False $ flip validatePass passwd . entityVal =<< user+    -- Get user data+    user <- runDB $ getBy userID+    return $ fromMaybe False $ flip validatePass passwd . entityVal =<< user   login :: AuthRoute@@ -392,32 +314,6 @@         else loginErrorMessageI LoginR Msg.InvalidUsernamePass  --- | A drop in for the getAuthId method of your YesodAuth instance which---   can be used if authHashDB is the only plugin in use.-getAuthIdHashDB :: HashDBPersist site user =>-                   (AuthRoute -> Route site)     -- ^ your site's Auth Route-                -> (Text -> Maybe (Unique user)) -- ^ gets user ID-                -> Creds site                    -- ^ the creds argument-                -> HandlerT site IO (Maybe (AuthId site))-getAuthIdHashDB authR uniq creds = do-    muid <- maybeAuthId-    case muid of-        -- user already authenticated-        Just uid -> return $ Just uid-        Nothing       -> do-            x <- case uniq (credsIdent creds) of-                   Nothing -> return Nothing-                   Just u  -> runDB (getBy u)-            case x of-                -- user exists-                Just (Entity uid _) -> return $ Just uid-                Nothing       -> do-                    mr <- getMessageRender-                    _ <- loginErrorMessage (authR LoginR) (mr Msg.InvalidUsernamePass)-                    return Nothing--{-# DEPRECATED getAuthIdHashDB "If this is a problem, please discuss at <https://github.com/paul-rouse/yesod-auth-hashdb/issues/5>" #-}- -- | Prompt for username and password, validate that against a database --   which holds the username and a hash of the password authHashDB :: HashDBPersist site user =>@@ -476,27 +372,3 @@                 }  |]---------------------------------------------------------------------- Predefined datatype--------------------------------------------------------------------- | Generate data base instances for a valid user-share [mkPersist sqlSettings { mpsGeneric = True }, mkMigrate "migrateUsers"]-         [persistUpperCase|-User-    username Text Eq-    password Text-    salt     Text-    UniqueUser username-    deriving Typeable-|]-{-# DEPRECATED User, migrateUsers "The predefined User data type will be removed soon - please define your own database table and accompanying instance of HashDBUser" #-}--instance HashDBUser (UserGeneric backend) where-  userPasswordHash = Just . userPassword-  userPasswordSalt = Just . userSalt-  setSaltAndPasswordHash s h u = u { userSalt     = s-                               , userPassword = h-                               }
test/ExampleData.hs view
@@ -9,8 +9,10 @@     oldStyleValidUser,     oldStyleBadUser1,     oldStyleBadUser2,+    oldStyleUpgradedUser,     newStyleValidUser,     newStyleBadUser,+    newStyleInOldFormat,     stronger ) where @@ -26,9 +28,9 @@ instance HashDBUser OldStyleUser where     userPasswordHash = oldStylePass     userPasswordSalt = oldStyleSalt-    setSaltAndPasswordHash s h u = u { oldStyleSalt = Just s,-                                       oldStylePass = Just h-                                     }+    setPasswordHash h u = u { oldStyleSalt = Just "",+                              oldStylePass = Just h+                            }  data NewStyleUser = NewStyleUser {                         newStyleName :: Text,@@ -68,6 +70,12 @@                  (Just "8e3e33029e71b4e25ba95a00a88c4bfeb93d766a")                  Nothing +oldStyleUpgradedUser :: OldStyleUser+oldStyleUpgradedUser =+    OldStyleUser "foo"+                 (Just "sha256|17|GkImOI0oV9RyOE3oJpYKRg==|KPPYL9JaP6UQjwLVvRsK3Pw2tl1LWyjqlh11jjKRQVM=")+                 (Just "somesalt")+ newStyleValidUser :: NewStyleUser newStyleValidUser =     NewStyleUser "fox"@@ -77,6 +85,12 @@ newStyleBadUser =     NewStyleUser "bad"                  Nothing++newStyleInOldFormat :: OldStyleUser+newStyleInOldFormat =+    OldStyleUser "fox"+                 (Just "sha256|14|2hL7cNopkA/dGy/5CQTuSg==|CUTPW6ICMISSjohFep851f9PdqIn7Y4B75/I77BvEYM=")+                 (Just "")  stronger :: Int stronger = defaultStrength + 2
test/NonDBTests.hs view
@@ -31,6 +31,12 @@         it "fails validation giving Nothing" $             validatePass oldStyleBadUser2 mypassword `shouldBe` Nothing +      context "Previously upgraded old-style user" $ do+        it "verifies OK with correct password" $+            validatePass oldStyleUpgradedUser mypassword `shouldBe` Just True+        it "fails validation with a wrong password giving Just False" $+            validatePass oldStyleUpgradedUser changedpw `shouldBe` Just False+       context "New style valid user" $ do         it "verifies OK with correct password" $             validatePass newStyleValidUser mypassword `shouldBe` Just True@@ -41,31 +47,26 @@         it "fails validation giving Nothing" $             validatePass newStyleBadUser mypassword `shouldBe` Nothing +      context "New-style hash in old-style format with blank salt" $ do+        it "verifies OK with correct password" $+            validatePass newStyleInOldFormat mypassword `shouldBe` Just True+        it "fails validation with a wrong password giving Just False" $+            validatePass newStyleInOldFormat changedpw `shouldBe` Just False +     describe "upgradePasswordHash" $ do        context "Upgrade of old-style password hash" $ do-        it "has the same salt value" $ do-            newuser <- upgradePasswordHash defaultStrength oldStyleValidUser-            let newsalt = newuser >>= userPasswordSalt-            newsalt `shouldBe` Just "somesalt"-        it "still verifies with the same password" $ do-            newuser <- upgradePasswordHash defaultStrength oldStyleValidUser-            let valid = newuser >>= flip validatePass mypassword-            valid `shouldBe` Just True-        it "still works after a second upgrade to a stronger setting" $ do+        it "is Nothing if the user has a valid password hash" $ do             newuser <- upgradePasswordHash defaultStrength oldStyleValidUser-            case newuser of-                Just u -> do-                    neweruser <- upgradePasswordHash stronger u-                    let valid = neweruser >>= flip validatePass mypassword-                    valid `shouldBe` Just True-                Nothing -> expectationFailure "Failed to upgrade user!"-        it "is Nothing if there is no password hash" $ do+            newuser `shouldBe` Nothing+        it "is Nothing if there is no password hash (but salt non-empty)" $ do             newuser <- upgradePasswordHash defaultStrength oldStyleBadUser1             newuser `shouldBe` Nothing-        it "is Nothing if there is no salt" $ do-            newuser <- upgradePasswordHash defaultStrength oldStyleBadUser2++      context "Upgrade of previously upgraded old-style user" $ do+        it "is Nothing since salt is non-empty" $ do+            newuser <- upgradePasswordHash defaultStrength oldStyleUpgradedUser             newuser `shouldBe` Nothing        context "Upgrade of new-style password hash to stronger setting" $ do@@ -81,6 +82,17 @@         it "is Nothing if there is no password hash" $ do             newuser <- upgradePasswordHash stronger newStyleBadUser             newuser `shouldBe` Nothing++      context "Upgrade of new-style hash in old-style format with blank salt" $ do+        it "really does have a hash containing the new strength" $ do+            newuser <- upgradePasswordHash stronger newStyleInOldFormat+            let s = pack $ "|" ++ show stronger ++ "|"+                found = fmap (s `isInfixOf`) $ newuser >>= oldStylePass+            found `shouldBe` Just True+        it "still verifies with the same password" $ do+            newuser <- upgradePasswordHash stronger newStyleInOldFormat+            let valid = newuser >>= flip validatePass mypassword+            valid `shouldBe` Just True       describe "setPassword" $ do
yesod-auth-hashdb.cabal view
@@ -1,5 +1,5 @@ name:            yesod-auth-hashdb-version:         1.4.3+version:         1.5 license:         MIT license-file:    LICENSE author:          Patrick Brisbin, later changes Paul Rouse