password-instances-0.2.0.0: src/Data/Password/Instances.hs
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-|
Module : Data.Password.Instances
Copyright : (c) Dennis Gosnell, 2019
License : BSD-style (see LICENSE file)
Maintainer : cdep.illabout@gmail.com
Stability : experimental
Portability : POSIX
This module provides the same interface as "Data.Password", but it also
provides additional typeclass instances for 'Pass' and 'PassHash'.
See the "Data.Password" module for more information.
-}
module Data.Password.Instances
(
-- * Plaintext Password
Pass(..)
-- * Hashed Password
, PassHash(..)
, Salt(..)
-- * Functions for Hashing Plaintext Passwords
, hashPass
, hashPassWithSalt
, newSalt
-- * Functions for Checking Plaintext Passwords Against Hashed Passwords
, checkPass
, PassCheck(..)
-- * Unsafe Debugging Functions for Showing a Password
, unsafeShowPassword
, unsafeShowPasswordText
, -- * Setup for doctests.
-- $setup
) where
import Data.Aeson (FromJSON)
import Data.Password
import Database.Persist.Class (PersistField)
import Web.HttpApiData (FromHttpApiData)
-- $setup
-- >>> :set -XOverloadedStrings
--
-- Import needed functions.
--
-- >>> import Data.Aeson (decode)
-- >>> import Data.Text (Text)
-- >>> import Database.Persist.Class (PersistField(toPersistValue))
-- >>> import Web.HttpApiData (parseUrlPiece)
-- | This instance allows a 'Pass' to be created from a JSON blob.
--
-- >>> let maybePass = decode "\"foobar\"" :: Maybe Pass
-- >>> fmap unsafeShowPassword maybePass
-- Just "foobar"
--
-- There is no instance for 'ToJSON' for 'Pass' because we don't want to
-- accidentally encode a plain-text 'Pass' to JSON and send it to the end-user.
--
-- Similarly, there is no 'ToJSON' and 'FromJSON' instance for 'PassHash'
-- because we don't want to accidentally send the password hash to the end
-- user.
deriving newtype instance FromJSON Pass
-- | This instance allows a 'Pass' to be created with functions like
-- 'Web.HttpApiData.parseUrlPiece' or 'Web.HttpApiData.parseQueryParam'.
--
-- >>> let eitherPass = parseUrlPiece "foobar" :: Either Text Pass
-- >>> fmap unsafeShowPassword eitherPass
-- Right "foobar"
deriving newtype instance FromHttpApiData Pass
-- | This instance allows a 'PassHash' to be stored as a field in a database using
-- "Database.Persist".
--
-- >>> let salt = Salt "abcdefghijklmnopqrstuvwxyz012345"
-- >>> let pass = Pass "foobar"
-- >>> let hashedPassword = hashPassWithSalt (Pass "foobar") salt
-- >>> toPersistValue hashedPassword
-- PersistText "14|8|1|YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXowMTIzNDU=|nENDaqWBmPKapAqQ3//H0iBImweGjoTqn5SvBS8Mc9FPFbzq6w65maYPZaO+SPamVZRXQjARQ8Y+5rhuDhjIhw=="
--
-- 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 'Pass', because we don't
-- want to make it easy to store a plain-text password in the database.
deriving newtype instance PersistField PassHash