password-aeson (empty) → 0.1.0.0
raw patch · 8 files changed
+297/−0 lines, 8 filesdep +aesondep +basedep +base-compatbuild-type:Customsetup-changed
Dependencies added: aeson, base, base-compat, doctest, password, password-aeson, password-types, quickcheck-instances, tasty, tasty-quickcheck, text
Files
- ChangeLog.md +5/−0
- LICENSE +30/−0
- README.md +11/−0
- Setup.hs +33/−0
- password-aeson.cabal +82/−0
- src/Data/Password/Aeson.hs +84/−0
- test/doctest/doctest.hs +14/−0
- test/tasty/Spec.hs +38/−0
+ ChangeLog.md view
@@ -0,0 +1,5 @@+# Changelog for `password-aeson`++## 0.1.0.0++- Split from `password-instances`.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) Dennis Gosnell, 2019; Felix Paulusma, 2020++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Dennis Gosnell, Felix Paulusma nor the names+ of other contributors may be used to endorse or promote products+ derived from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,11 @@+# password-aeson++[](http://github.com/cdepillabout/password)+[](https://hackage.haskell.org/package/password-aeson)+[](http://stackage.org/lts/package/password-aeson)+[](http://stackage.org/nightly/package/password-aeson)+[](./LICENSE)++This package provides `aeson` typeclass instances for the plain-text password+and hashed password datatypes from the+[password](https://hackage.haskell.org/package/password) package.
+ Setup.hs view
@@ -0,0 +1,33 @@+{-# 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-aeson.cabal view
@@ -0,0 +1,82 @@+cabal-version: 1.12++name: password-aeson+version: 0.1.0.0+category: Security+synopsis: aeson typeclass instances for password package+description: A library providing typeclass instances for aeson for the types from the password package.+homepage: https://github.com/cdepillabout/password/tree/master/password-aeson#readme+bug-reports: https://github.com/cdepillabout/password/issues+author: Dennis Gosnell, Felix Paulusma+maintainer: cdep.illabout@gmail.com, felix.paulusma@gmail.com+copyright: Copyright (c) Dennis Gosnell, 2019; Felix Paulusma, 2020+license: BSD3+license-file: LICENSE+build-type: Custom+extra-source-files:+ README.md+ ChangeLog.md++source-repository head+ type: git+ location: https://github.com/cdepillabout/password++custom-setup+ setup-depends:+ base >= 4.9 && < 5+ , Cabal < 4+ , cabal-doctest >=1.0.6 && <1.1++library+ hs-source-dirs:+ src+ exposed-modules:+ Data.Password.Aeson+-- other-modules:+-- Paths_password_aeson+ build-depends:+ base >= 4.9 && < 5+ , aeson >= 0.2 && < 3+ , password-types < 2+ 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+ default-language:+ Haskell2010++test-suite password-aeson-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-aeson+ , password-types+ , aeson+ , quickcheck-instances+ , tasty+ , tasty-quickcheck+ , text+ default-language:+ Haskell2010
+ src/Data/Password/Aeson.hs view
@@ -0,0 +1,84 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++{-|+Module : Data.Password.Aeson+Copyright : (c) Dennis Gosnell, 2019; Felix Paulusma, 2020+License : BSD-style (see LICENSE file)+Maintainer : cdep.illabout@gmail.com+Stability : experimental+Portability : POSIX++This module provides additional typeclass instances+for 'Password' and 'PasswordHash', along with the+'ExposedPassword' newtype if you /absolutely have to/+convert a plain text password into JSON.++See the "Data.Password.Types" module for more information.+-}++module Data.Password.Aeson (ExposedPassword (..)) where++import Data.Aeson (FromJSON(..), ToJSON(..))+import Data.Password.Types (+ mkPassword,+ unsafeShowPassword,+ Password,+ PasswordHash (PasswordHash),+ )+import GHC.TypeLits (TypeError, ErrorMessage(..))++-- $setup+-- >>> :set -XOverloadedStrings+-- >>> :set -XDataKinds+--+-- Import needed functions.+--+-- >>> import Data.Aeson (decode)+-- >>> import Data.Password.Bcrypt (Salt(..), hashPasswordWithSalt, unsafeShowPassword)++-- | 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 = 'Text "Warning! Tried to convert plain-text Password to JSON!"+ :$$: '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 JSON"+ :$$: 'Text ""++-- | Type error! Do not use 'toJSON' on a 'Password'!+instance TypeError ErrMsg => ToJSON Password where+ toJSON = error "unreachable"++-- | WARNING: DO NOT USE UNLESS ABSOLUTELY NECESSARY!+--+-- Using this newtype will allow your plain text password to be turned into+-- JSON. Keep this type tightly bound to only the section where you want to+-- expose the `Password`, since it's easy for a bigger type that contains+-- this `ExposedPassword` to be logged or printed as JSON, and now you've+-- accidentally leaked passwords in your logs or database.+newtype ExposedPassword = ExposedPassword Password+ deriving newtype (FromJSON)++instance ToJSON ExposedPassword where+ toJSON (ExposedPassword p) = toJSON $ unsafeShowPassword p++deriving newtype instance FromJSON (PasswordHash a)++deriving newtype instance ToJSON (PasswordHash a)
+ test/doctest/doctest.hs view
@@ -0,0 +1,14 @@+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 view
@@ -0,0 +1,38 @@+{-# LANGUAGE OverloadedStrings #-}++import Data.Aeson+import Data.Aeson.Types (parseMaybe)+import Data.Text (Text)+import Test.Tasty (TestTree, defaultMain, testGroup)+import Test.Tasty.QuickCheck (testProperty, (===))+import Test.QuickCheck.Instances.Text ()++import Data.Password.Types (Password, unsafeShowPassword)+import Data.Password.Aeson ()+++main :: IO ()+main = defaultMain $ testGroup "Password Instances"+ [ aesonTest+ ]++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 =+ testProperty "Password (Aeson)" $ \pwd ->+ Just pwd === (unsafeShowPassword . password <$> parseIt pwd)+ where+ parseIt pwd =+ parseMaybe parseJSON $+ object+ [ "name" .= String "testname"+ , "password" .= String pwd+ ]