packages feed

password-instances 3.0.0.0 → 3.1.0.0

raw patch · 6 files changed

+58/−256 lines, 6 filesdep +password-aesondep +password-http-api-datadep +password-persistentdep −QuickCheckdep −aesondep −base-compatsetup-changedPVP ok

version bump matches the API change (PVP)

Dependencies added: password-aeson, password-http-api-data, password-persistent

Dependencies removed: QuickCheck, aeson, base-compat, doctest, http-api-data, password, password-instances, password-types, persistent, quickcheck-instances, tasty, tasty-hunit, tasty-quickcheck, template-haskell, text

API changes (from Hackage documentation)

- Data.Password.Instances: instance (TypeError ...) => Data.Aeson.Types.ToJSON.ToJSON Data.Password.Types.Password
- Data.Password.Instances: instance (TypeError ...) => Database.Persist.Class.PersistField.PersistField Data.Password.Types.Password
- Data.Password.Instances: instance (TypeError ...) => Web.Internal.HttpApiData.ToHttpApiData Data.Password.Types.Password
- Data.Password.Instances: instance Data.Aeson.Types.FromJSON.FromJSON Data.Password.Types.Password
- Data.Password.Instances: instance Database.Persist.Class.PersistField.PersistField (Data.Password.Types.PasswordHash a)
- Data.Password.Instances: instance Database.Persist.Sql.Class.PersistFieldSql (Data.Password.Types.PasswordHash a)
- Data.Password.Instances: instance Web.Internal.HttpApiData.FromHttpApiData Data.Password.Types.Password

Files

ChangeLog.md view
@@ -1,12 +1,23 @@ # Changelog for `password-instances` +## 3.1.0.0++-   Added cabal flags (`aeson`, `http-api-data`, `persistent`) to make+    dependencies optional. All flags default to `True`.+    [#89](https://github.com/cdepillabout/password/pull/89)+-   Added compile-time error if all flags are disabled.+-   Split into dedicated packages.+    [#87](https://github.com/cdepillabout/password/pull/87)++Thanks to [@blackheaven](https://github.com/blackheaven)+ ## 3.0.0.0  -   The `password-instances` library now depends on the new     `password-types` package, instead of on the `password` package.     [#40](https://github.com/cdepillabout/password/pull/40)     Thanks to [@Vlix](https://github.com/Vlix)--   Small update to the README+-   Small update to the README. -   Changed `PersistField` instance of `PasswordHash` to use     `decodeUtf8'` for better handling of UTF8 decoding exceptions.     [#43](https://github.com/cdepillabout/password/pull/43)
Setup.hs view
@@ -1,33 +1,4 @@-{-# LANGUAGE CPP #-}-{-# OPTIONS_GHC -Wall #-}-module Main (main) where--#ifndef MIN_VERSION_cabal_doctest-#define MIN_VERSION_cabal_doctest(x,y,z) 0-#endif--#if MIN_VERSION_cabal_doctest(1,0,0)--import Distribution.Extra.Doctest ( defaultMainWithDoctests )-main :: IO ()-main = defaultMainWithDoctests "doctests"--#else--#ifdef MIN_VERSION_Cabal--- If the macro is defined, we have new cabal-install,--- but for some reason we don't have cabal-doctest in package-db------ Probably we are running cabal sdist, when otherwise using new-build--- workflow-#warning You are configuring this package without cabal-doctest installed. \-         The doctests test-suite will not work as a result. \-         To fix this, install cabal-doctest before configuring.-#endif- import Distribution.Simple  main :: IO () main = defaultMain--#endif
password-instances.cabal view
@@ -1,8 +1,8 @@ cabal-version: 1.12  name:           password-instances-version:        3.0.0.0-category:       Data+version:        3.1.0.0+category:       Security synopsis:       typeclass instances for password package description:    A library providing typeclass instances for common libraries for the types from the password package. homepage:       https://github.com/cdepillabout/password/tree/master/password-instances#readme@@ -21,11 +21,25 @@   type: git   location: https://github.com/cdepillabout/password +flag aeson+  description: Enable instances for Aeson+  default: True+  manual: True++flag http-api-data+  description: Enable instances for HTTP API Data+  default: True+  manual: True++flag persistent+  description: Enable instances for Persistent+  default: True+  manual: True+ custom-setup   setup-depends:-      base-    , Cabal-    , cabal-doctest  >=1.0.6 && <1.1+      base          >= 4.9 && < 5+    , Cabal                   < 4  library   hs-source-dirs:@@ -36,57 +50,21 @@       Paths_password_instances   build-depends:       base          >= 4.9 && < 5-    , aeson         >= 0.2-    , http-api-data-    , password-types          < 2-    , persistent    >= 1.2-    , text   ghc-options:       -Wall   default-language:       Haskell2010--test-suite doctests-  type:-      exitcode-stdio-1.0-  hs-source-dirs:-      test/doctest-  main-is:-      doctest.hs-  ghc-options:-      -threaded -rtsopts -with-rtsopts=-N-  build-depends:-      base >=4.9 && <5-    , base-compat-    , doctest-    , password-    , password-instances-    , QuickCheck-    , quickcheck-instances-    , template-haskell-  default-language:-      Haskell2010--test-suite password-instances-tasty-  type:-      exitcode-stdio-1.0-  hs-source-dirs:-      test/tasty-  main-is:-      Spec.hs-  ghc-options:-      -threaded -rtsopts -with-rtsopts=-N-  build-depends:-      base >=4.9 && <5-    , password-instances-    , password-types-    , aeson-    , http-api-data-    , persistent-    , quickcheck-instances-    , tasty-    , tasty-hunit-    , tasty-quickcheck-    , text-  default-language:-      Haskell2010+  default-extensions:+      CPP+  if flag(aeson)+    build-depends:+        password-aeson+    cpp-options: -DFLAG_AESON+  if flag(http-api-data)+    build-depends:+        password-http-api-data+    cpp-options: -DFLAG_HTTP_API_DATA+  if flag(persistent)+    build-depends:+        password-persistent+    cpp-options: -DFLAG_PERSISTENT
src/Data/Password/Instances.hs view
@@ -1,14 +1,4 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DerivingStrategies #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE StandaloneDeriving #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE UndecidableInstances #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# OPTIONS_GHC -Wno-dodgy-exports -Wno-unused-imports #-}  {-| Module      : Data.Password.Instances@@ -24,99 +14,20 @@ See the "Data.Password.Types" module for more information. -} -module Data.Password.Instances () where+module Data.Password.Instances (module E) where -import Data.Aeson (FromJSON(..), ToJSON(..))-import Data.Password.Types (Password, PasswordHash(..), mkPassword)-#if !MIN_VERSION_base(4,13,0)-import Data.Semigroup ((<>))+#if !defined(FLAG_AESON) && !defined(FLAG_HTTP_API_DATA) && !defined(FLAG_PERSISTENT)+#error "At least one of the flags (aeson, http-api-data, persistent) must be enabled" #endif-import Data.Text (pack)-import Data.Text.Encoding as TE (decodeUtf8')-import Database.Persist (PersistValue(..))-import Database.Persist.Class (PersistField(..))-import Database.Persist.Sql (PersistFieldSql(..))-import GHC.TypeLits (TypeError, ErrorMessage(..))-import Web.HttpApiData (FromHttpApiData(..), ToHttpApiData(..)) ---- $setup--- >>> :set -XOverloadedStrings--- >>> :set -XDataKinds------ Import needed functions.------ >>> import Data.Aeson (decode)--- >>> import Data.Password.Bcrypt (Salt(..), hashPasswordWithSalt, unsafeShowPassword)--- >>> import Database.Persist.Class (PersistField(toPersistValue))--- >>> import Web.HttpApiData (parseUrlPiece)---- | This instance allows a 'Password' to be created from a JSON blob.------ >>> let maybePassword = decode "\"foobar\"" :: Maybe Password--- >>> fmap unsafeShowPassword maybePassword--- Just "foobar"------ There is no instance for 'ToJSON' for 'Password' because we don't want to--- accidentally encode a plain-text 'Password' to JSON and send it to the end-user.------ Similarly, there is no 'ToJSON' and 'FromJSON' instance for 'PasswordHash'--- because we don't want to accidentally send the password hash to the end--- user.-instance FromJSON Password where-  parseJSON = fmap mkPassword . parseJSON--type ErrMsg e = 'Text "Warning! Tried to convert plain-text Password to " ':<>: 'Text e ':<>: 'Text "!"-          ':$$: 'Text "  This is likely a security leak. Please make sure whether this was intended."-          ':$$: 'Text "  If this is intended, please use 'unsafeShowPassword' before converting to " ':<>: 'Text e-          ':$$: 'Text ""---- | Type error! Do not use 'toJSON' on a 'Password'!-instance TypeError (ErrMsg "JSON") => ToJSON Password where-  toJSON = error "unreachable"---- | This instance allows a 'Password' to be created with functions like--- 'Web.HttpApiData.parseUrlPiece' or 'Web.HttpApiData.parseQueryParam'.------ >>> let eitherPassword = parseUrlPiece "foobar"--- >>> fmap unsafeShowPassword eitherPassword--- Right "foobar"-instance FromHttpApiData Password where-  parseUrlPiece = fmap mkPassword . parseUrlPiece---- | Type error! Do not transmit plain-text 'Password's over HTTP!-instance TypeError (ErrMsg "HttpApiData") => ToHttpApiData Password where-  toUrlPiece = error "unreachable"---- | This instance allows a 'PasswordHash' to be stored as a field in a database using--- "Database.Persist".------ >>> let salt = Salt "abcdefghijklmnop"--- >>> let pass = mkPassword "foobar"--- >>> let hashedPassword = hashPasswordWithSalt 10 salt pass--- >>> toPersistValue hashedPassword--- PersistText "$2b$10$WUHhXETkX0fnYkrqZU3ta.N8Utt4U77kW4RVbchzgvBvBBEEdCD/u"------ In the example above, the long 'PersistText' will be the value you store in--- the database.------ We don't provide an instance of 'PersistField' for 'Password', because we don't--- want to make it easy to store a plain-text password in the database.-instance PersistField (PasswordHash a) where-  toPersistValue (PasswordHash hpw) = PersistText hpw-  fromPersistValue = \case-      PersistText txt -> Right $ PasswordHash txt-      PersistByteString bs ->-        either failed (Right . PasswordHash) $ TE.decodeUtf8' bs-      _ -> Left "did not parse PasswordHash from PersistValue"-    where-      failed e = Left $ "Failed decoding PasswordHash to UTF8: " <> pack (show e)+#ifdef FLAG_AESON+import Data.Password.Aeson  as E+#endif --- | This instance allows a 'PasswordHash' to be stored as a field in an SQL--- database in "Database.Persist.Sql".-deriving newtype instance PersistFieldSql (PasswordHash a)+#ifdef FLAG_HTTP_API_DATA+import Data.Password.HttpApiData as E+#endif --- | Type error! Do not store plain-text 'Password's in your database!-instance TypeError (ErrMsg "PersistValue") => PersistField Password where-  toPersistValue = error "unreachable"-  fromPersistValue = error "unreachable"+#ifdef FLAG_PERSISTENT+import Data.Password.Persistent as E+#endif
− test/doctest/doctest.hs
@@ -1,14 +0,0 @@-module Main where--import Build_doctests (flags, pkgs, module_sources)--- import Data.Foldable (traverse_)-import System.Environment.Compat (unsetEnv)-import Test.DocTest (doctest)--main :: IO ()-main = do-  -- traverse_ putStrLn args-  unsetEnv "GHC_ENVIRONMENT"-  doctest args-  where-    args = flags ++ pkgs ++ module_sources
− test/tasty/Spec.hs
@@ -1,55 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--import Data.Aeson-import Data.Aeson.Types (parseMaybe)-import Data.Text (Text)-import Database.Persist.Class (PersistField(..))-import Test.Tasty-import Test.Tasty.HUnit-import Test.Tasty.QuickCheck-import Test.QuickCheck.Instances.Text ()-import Web.HttpApiData (FromHttpApiData(..))--import Data.Password.Types (Password, PasswordHash(..), unsafeShowPassword)-import Data.Password.Instances()---main :: IO ()-main = defaultMain $ testGroup "Password Instances"-  [ aesonTest-  , fromHttpApiDataTest-  , persistTest-  ]--data TestUser = TestUser {-  name :: Text,-  password :: Password-} deriving (Show)--instance FromJSON TestUser where-  parseJSON = withObject "TestUser" $ \o ->-    TestUser <$> o .: "name" <*> o .: "password"---aesonTest :: TestTree-aesonTest = testCase "Password (Aeson)" $-    assertEqual "password doesn't match" (Just testPassword) $-      unsafeShowPassword . password <$> parseMaybe parseJSON testUser-  where-    testPassword = "testpass"-    testUser = object-      [ "name" .= String "testname"-      , "password" .= String testPassword-      ]--fromHttpApiDataTest :: TestTree-fromHttpApiDataTest = testCase "Password (FromHttpApiData)" $-    assertEqual "password doesn't match" (Right testPassword) $-      unsafeShowPassword <$> parseUrlPiece testPassword-  where-    testPassword = "passtest"--persistTest :: TestTree-persistTest = testProperty "PasswordHash (PersistField)" $ \pass ->-    let pwd = PasswordHash pass-    in fromPersistValue (toPersistValue pwd) === Right pwd