packages feed

gamgee 1.1.0 → 1.2.0

raw patch · 12 files changed

+238/−176 lines, 12 filesdep ~Hclipdep ~polysemyPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: Hclip, polysemy

API changes (from Hackage documentation)

+ Gamgee.Effects.TOTP: [GetSecret] :: TokenSpec -> TOTP m Text
+ Gamgee.Effects.TOTP: getSecret :: forall r_awmN. MemberWithError TOTP r_awmN => TokenSpec -> Sem r_awmN Text
+ Gamgee.Operation: changePassword :: Members [State Tokens, SecretInput Text, Crypto, TOTP, Error EffError] r => TokenIdentifier -> Sem r ()
- Gamgee.Effects.ByteStore: readByteStore :: forall r_abT2. MemberWithError ByteStore r_abT2 => Sem r_abT2 (Maybe ByteString)
+ Gamgee.Effects.ByteStore: readByteStore :: forall r_abQZ. MemberWithError ByteStore r_abQZ => Sem r_abQZ (Maybe LByteString)
- Gamgee.Effects.ByteStore: writeByteStore :: forall r_abT3. MemberWithError ByteStore r_abT3 => LByteString -> Sem r_abT3 ()
+ Gamgee.Effects.ByteStore: writeByteStore :: forall r_abR0. MemberWithError ByteStore r_abR0 => LByteString -> Sem r_abR0 ()
- Gamgee.Effects.CryptoRandom: randomBytes :: forall r_agni a_agl6. (MemberWithError CryptoRandom r_agni, ByteArray a_agl6) => Int -> Sem r_agni a_agl6
+ Gamgee.Effects.CryptoRandom: randomBytes :: forall r_aglU b_agjJ. (MemberWithError CryptoRandom r_aglU, ByteArray b_agjJ) => Int -> Sem r_aglU b_agjJ
- Gamgee.Effects.CryptoRandom: runCryptoRandomIO :: Member (Lift IO) r => Sem (CryptoRandom : r) a -> Sem r a
+ Gamgee.Effects.CryptoRandom: runCryptoRandomIO :: Member (Embed IO) r => Sem (CryptoRandom : r) a -> Sem r a
- Gamgee.Effects.JSONStore: jsonDecode :: forall a_at4A r_at66. MemberWithError (JSONStore a_at4A) r_at66 => Sem r_at66 a_at4A
+ Gamgee.Effects.JSONStore: jsonDecode :: forall o_at3k r_at4N. MemberWithError (JSONStore o_at3k) r_at4N => Sem r_at4N o_at3k
- Gamgee.Effects.JSONStore: jsonEncode :: forall o_at4y r_at64. MemberWithError (JSONStore o_at4y) r_at64 => o_at4y -> Sem r_at64 ()
+ Gamgee.Effects.JSONStore: jsonEncode :: forall o_at3i r_at4L. MemberWithError (JSONStore o_at3i) r_at4L => o_at3i -> Sem r_at4L ()
- Gamgee.Effects.SecretInput: runSecretInputIO :: Member (Lift IO) r => Sem (SecretInput Text : r) a -> Sem r a
+ Gamgee.Effects.SecretInput: runSecretInputIO :: Member (Embed IO) r => Sem (SecretInput Text : r) a -> Sem r a
- Gamgee.Effects.SecretInput: secretInput :: forall a_agDE r_agG5. MemberWithError (SecretInput a_agDE) r_agG5 => Text -> Sem r_agG5 a_agDE
+ Gamgee.Effects.SecretInput: secretInput :: forall i_agCh r_agEH. MemberWithError (SecretInput i_agCh) r_agEH => Text -> Sem r_agEH i_agCh
- Gamgee.Effects.TOTP: getTOTP :: forall r_awnz. MemberWithError TOTP r_awnz => TokenSpec -> POSIXTime -> Sem r_awnz Text
+ Gamgee.Effects.TOTP: getTOTP :: forall r_awmP. MemberWithError TOTP r_awmP => TokenSpec -> POSIXTime -> Sem r_awmP Text

Files

ChangeLog.md view
@@ -5,6 +5,9 @@  ## [Unreleased] +## [1.2.0] - 2019-09-11+- Added `change-password` command+ ## [1.1.0] - 2019-07-25 - Added an info command to get information about Gamgee installation (https://github.com/rkaippully/gamgee/issues/5) - Generate build/configuration files via dhall
app/Gamgee/Program/CommandLine.hs view
@@ -21,6 +21,7 @@              | DeleteToken Token.TokenIdentifier              | ListTokens              | GetOTP Token.TokenIdentifier OutputMode+             | ChangePassword Token.TokenIdentifier              | GetInfo  getCommand :: Parser Command@@ -28,6 +29,7 @@     command "list" (info listTokens $ progDesc "List the names of all known tokens")     <> command "add" (info addToken $ progDesc "Add a new token")     <> command "delete" (info deleteToken $ progDesc "Delete a token")+    <> command "change-password" (info changePassword $ progDesc "Change password of a token")     <> command "info" (info getInfo $ progDesc "Print information about this Gamgee installation")   )   <|> getOTPOperation@@ -76,6 +78,10 @@                   <$> strArgument (help "Get a one-time password" <> metavar "TOKEN-LABEL")                   <*> flag OutputClipboard OutputStdOut                       (long "stdout" <> help "Send the OTP to stdout instead of clipboard")++changePassword :: Parser Command+changePassword = ChangePassword+                 <$> strOption (long "label" <> short 'l' <> help "Label of the token")  getInfo :: Parser Command getInfo = pure GetInfo
app/Gamgee/Program/Effects.hs view
@@ -14,7 +14,7 @@ import qualified Data.Text.IO           as TIO import qualified Gamgee.Effects         as Eff import qualified Gamgee.Token           as Token-import           Polysemy               (Lift, Member, Members, Sem)+import           Polysemy               (Embed, Member, Members, Sem) import qualified Polysemy               as P import qualified Polysemy.Error         as P import qualified Polysemy.Output        as P@@ -27,24 +27,21 @@   -- | A version of runM that ignores its result-runM_ :: Monad m => Sem '[Lift m] a -> m ()+runM_ :: Monad m => Sem '[Embed m] a -> m () runM_ = void . P.runM -sendIO :: Member (Lift IO) r => IO a -> Sem r a-sendIO = P.sendM - ---------------------------------------------------------------------------------------------------- -- Interpret Output by writing it to stdout or clipboard ---------------------------------------------------------------------------------------------------- -runOutputStdOut :: Member (Lift IO) r => Sem (P.Output Text : r) a -> Sem r a+runOutputStdOut :: Member (Embed IO) r => Sem (P.Output Text : r) a -> Sem r a runOutputStdOut = P.interpret $ \case-  P.Output s -> sendIO $ putTextLn s+  P.Output s -> P.embed @IO $ putTextLn s -runOutputClipboard :: Member (Lift IO) r => Sem (P.Output Text : r) a -> Sem r a+runOutputClipboard :: Member (Embed IO) r => Sem (P.Output Text : r) a -> Sem r a runOutputClipboard = P.interpret $ \case-  P.Output s -> P.sendM $ Clip.setClipboard $ toString s+  P.Output s -> P.embed $ Clip.setClipboard $ toString s   ----------------------------------------------------------------------------------------------------@@ -69,21 +66,21 @@   toText (ReadError e)  = "Internal Error: Error reading configuration file: " <> show e   toText (WriteError e) = "Internal Error: Error saving configuration file: " <> show e -runErrorStdErr :: Member (Lift IO) r => Sem (P.Error Eff.EffError : P.Error ByteStoreError : r) a -> Sem r (Maybe a)+runErrorStdErr :: Member (Embed IO) r => Sem (P.Error Eff.EffError : P.Error ByteStoreError : r) a -> Sem r (Maybe a) runErrorStdErr = fmap join . runToTextError . runToTextError -runToTextError :: (Member (Lift IO) r, ToText e) => Sem (P.Error e : r) a -> Sem r (Maybe a)+runToTextError :: (Member (Embed IO) r, ToText e) => Sem (P.Error e : r) a -> Sem r (Maybe a) runToTextError a = P.runError a >>= either (printError . toText) (return . Just)   where-    printError :: Member (Lift IO) r => Text -> Sem r (Maybe a)-    printError msg = P.sendM (TIO.hPutStrLn stderr msg) $> Nothing+    printError :: Member (Embed IO) r => Text -> Sem r (Maybe a)+    printError msg = P.embed (TIO.hPutStrLn stderr msg) $> Nothing   ---------------------------------------------------------------------------------------------------- -- Interpret ByteStore using a file ---------------------------------------------------------------------------------------------------- -runByteStoreFile :: ( Members [Lift IO, P.Error e] r+runByteStoreFile :: ( Members [Embed IO, P.Error e] r                     , Exception e1                     , Exception e2)                  => FilePath@@ -93,18 +90,18 @@                  -> Sem r a runByteStoreFile file handleReadError handleWriteError = P.interpret $ \case   Eff.ReadByteStore        -> do-    res <- sendIO $ (Right . Just <$> readFileLBS file) `catch` (return . handleReadError)+    res <- P.embed @IO $ (Right . Just <$> readFileLBS file) `catch` (return . handleReadError)     either P.throw return res   Eff.WriteByteStore bytes -> do-    res <- sendIO $ (writeFileLBS file bytes $> Nothing) `catch` (return . handleWriteError)+    res <- P.embed @IO $ (writeFileLBS file bytes $> Nothing) `catch` (return . handleWriteError)     whenJust res P.throw-    P.sendM $ Files.setFileMode file $ Files.ownerReadMode `Files.unionFileModes` Files.ownerWriteMode+    P.embed $ Files.setFileMode file $ Files.ownerReadMode `Files.unionFileModes` Files.ownerWriteMode -runByteStoreIO :: Members [Lift IO, P.Error ByteStoreError] r+runByteStoreIO :: Members [Embed IO, P.Error ByteStoreError] r                => Sem (Eff.ByteStore : r) a                -> Sem r a runByteStoreIO prog = do-  file <- P.sendM configFilePath+  file <- P.embed configFilePath   runByteStoreFile file handleReadError handleWriteError prog    where
app/Main.hs view
@@ -1,4 +1,4 @@-module Main where+module Main (main) where  import qualified Data.Aeson                 as Aeson import qualified Data.Time.Clock.POSIX      as Clock@@ -8,22 +8,24 @@ import qualified Gamgee.Program.Effects     as Eff import qualified Gamgee.Token               as Token import qualified Options.Applicative        as Options-import           Polysemy                   (Lift, Member, Sem)+import           Polysemy                   (Embed, Member, Sem) import qualified Polysemy                   as P import qualified Polysemy.Error             as P import qualified Polysemy.Input             as P import qualified Polysemy.Output            as P import           Relude + main :: IO () main = do   command <- Options.execParser parserInfo   case command of-    Cmd.AddToken spec -> runAddToken spec-    Cmd.DeleteToken t -> runDeleteToken t-    Cmd.ListTokens    -> runListTokens-    Cmd.GetOTP t mode -> runGetOTP t mode-    Cmd.GetInfo       -> runGetInfo+    Cmd.AddToken spec    -> runAddToken spec+    Cmd.DeleteToken t    -> runDeleteToken t+    Cmd.ListTokens       -> runListTokens+    Cmd.GetOTP t mode    -> runGetOTP t mode+    Cmd.ChangePassword t -> runChangePassword t+    Cmd.GetInfo          -> runGetInfo  parserInfo :: Options.ParserInfo Cmd.Command parserInfo = Options.info (Options.helper@@ -72,7 +74,19 @@     $ Eff.runTOTP     $ Operation.getOTP t now -getConfig :: Member (Lift IO) r => Sem r (Maybe Token.Config)+runChangePassword :: Token.TokenIdentifier -> IO ()+runChangePassword t = Eff.runM_+                      $ Eff.runErrorStdErr+                      $ Eff.runCryptoRandomIO+                      $ Eff.runCrypto+                      $ Eff.runSecretInputIO+                      $ Eff.runByteStoreIO+                      $ Eff.runJSONStore+                      $ Eff.runStateJSON+                      $ Eff.runTOTP+                      $ Operation.changePassword t++getConfig :: Member (Embed IO) r => Sem r (Maybe Token.Config) getConfig = do   res <- fmap (rightToMaybe @Eff.EffError)          $ P.runError@@ -86,7 +100,7 @@ runGetInfo = do   path <- Eff.configFilePath   res <- P.runM-         $ P.runFoldMapOutput (decodeUtf8 . Aeson.encode @Aeson.Value)-         $ P.runConstInput path+         $ P.runOutputMonoid (decodeUtf8 . Aeson.encode @Aeson.Value)+         $ P.runInputConst path          $ Operation.getInfo getConfig   putTextLn $ fst res
gamgee.cabal view
@@ -1,119 +1,145 @@ cabal-version: 1.12---- This file has been generated from package.yaml by hpack version 0.31.2.------ see: https://github.com/sol/hpack------ hash: 6ef7fdb4196c49f7636ed723af430ecf585bbd1efa8278400c6d716d7fcc5a7b--name:           gamgee-version:        1.1.0-synopsis:       Tool for generating TOTP MFA tokens.-description:    Tool for generating TOTP MFA tokens. Please see the README on GitHub at <https://github.com/rkaippully/gamgee#readme>-category:       Authentication, Command Line-homepage:       https://github.com/rkaippully/gamgee#readme-bug-reports:    https://github.com/rkaippully/gamgee/issues-author:         Raghu Kaippully-maintainer:     rkaippully@gmail.com-copyright:      2018 Raghu Kaippully-license:        MPL-2.0-license-file:   LICENSE-build-type:     Simple+name: gamgee+version: 1.2.0+license: MPL-2.0+license-file: LICENSE+copyright: 2018 Raghu Kaippully+maintainer: rkaippully@gmail.com+author: Raghu Kaippully+homepage: https://github.com/rkaippully/gamgee#readme+bug-reports: https://github.com/rkaippully/gamgee/issues+synopsis: Tool for generating TOTP MFA tokens.+description:+    Tool for generating TOTP MFA tokens. Please see the README on GitHub at <https://github.com/rkaippully/gamgee#readme>+category: Authentication, Command Line+build-type: Simple extra-source-files:     ChangeLog.md     README.md     test/data/golden/getOTPTest.txt  source-repository head-  type: git-  location: https://github.com/rkaippully/gamgee+    type: git+    location: https://github.com/rkaippully/gamgee  library-  exposed-modules:-      Gamgee.Operation-      Gamgee.Token-      Gamgee.Effects-      Gamgee.Effects.Error-      Gamgee.Effects.Crypto-      Gamgee.Effects.CryptoRandom-      Gamgee.Effects.SecretInput-      Gamgee.Effects.TOTP-      Gamgee.Effects.JSONStore-      Gamgee.Effects.ByteStore-  other-modules:-      Paths_gamgee-  hs-source-dirs:-      src-  default-extensions: ApplicativeDo BangPatterns ConstraintKinds DataKinds DefaultSignatures DeriveAnyClass DeriveFoldable DeriveFunctor DeriveGeneric DeriveLift DeriveTraversable DerivingStrategies EmptyCase ExistentialQuantification FlexibleContexts FlexibleInstances FunctionalDependencies GADTs GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase MultiParamTypeClasses MultiWayIf NamedFieldPuns NoImplicitPrelude OverloadedStrings PatternSynonyms PolyKinds RankNTypes ScopedTypeVariables StandaloneDeriving TemplateHaskell TupleSections TypeApplications TypeFamilies TypeFamilyDependencies TypeOperators-  ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns-  build-depends:-      aeson-    , base >=4.12.0.0 && <4.13-    , base64-bytestring-    , bytestring-    , cryptonite-    , memory-    , polysemy-    , relude-    , safe-exceptions-    , text-    , time-  default-language: Haskell2010+    exposed-modules:+        Gamgee.Operation+        Gamgee.Token+        Gamgee.Effects+        Gamgee.Effects.Error+        Gamgee.Effects.Crypto+        Gamgee.Effects.CryptoRandom+        Gamgee.Effects.SecretInput+        Gamgee.Effects.TOTP+        Gamgee.Effects.JSONStore+        Gamgee.Effects.ByteStore+    hs-source-dirs: src+    other-modules:+        Paths_gamgee+    default-language: Haskell2010+    default-extensions: ApplicativeDo BangPatterns ConstraintKinds+                        DataKinds DefaultSignatures DeriveAnyClass DeriveFoldable+                        DeriveFunctor DeriveGeneric DeriveLift DeriveTraversable+                        DerivingStrategies EmptyCase ExistentialQuantification+                        FlexibleContexts FlexibleInstances FunctionalDependencies GADTs+                        GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase+                        MultiParamTypeClasses MultiWayIf NamedFieldPuns NoImplicitPrelude+                        OverloadedStrings PatternSynonyms PolyKinds RankNTypes+                        ScopedTypeVariables StandaloneDeriving TemplateHaskell+                        TupleSections TypeApplications TypeFamilies TypeFamilyDependencies+                        TypeOperators+    ghc-options: -Wall -Wcompat -Wredundant-constraints+                 -Wincomplete-record-updates -Wincomplete-uni-patterns+    build-depends:+        aeson >=1.4.4.0 && <1.5,+        base >=4.12.0.0 && <4.13,+        base64-bytestring >=1.0.0.2 && <1.1,+        bytestring >=0.10.8.2 && <0.11,+        cryptonite ==0.25.*,+        memory >=0.14.18 && <0.15,+        polysemy >=1.0.0.0 && <1.1,+        relude >=0.5.0 && <0.6,+        safe-exceptions >=0.1.7.0 && <0.2,+        text >=1.2.3.1 && <1.3,+        time >=1.8.0.2 && <1.9  executable gamgee-  main-is: Main.hs-  other-modules:-      Gamgee.Program.CommandLine-      Gamgee.Program.Effects-      Paths_gamgee-  hs-source-dirs:-      app-  default-extensions: ApplicativeDo BangPatterns ConstraintKinds DataKinds DefaultSignatures DeriveAnyClass DeriveFoldable DeriveFunctor DeriveGeneric DeriveLift DeriveTraversable DerivingStrategies EmptyCase ExistentialQuantification FlexibleContexts FlexibleInstances FunctionalDependencies GADTs GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase MultiParamTypeClasses MultiWayIf NamedFieldPuns NoImplicitPrelude OverloadedStrings PatternSynonyms PolyKinds RankNTypes ScopedTypeVariables StandaloneDeriving TemplateHaskell TupleSections TypeApplications TypeFamilies TypeFamilyDependencies TypeOperators-  ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns -threaded -rtsopts -with-rtsopts=-N-  build-depends:-      Hclip-    , aeson-    , base >=4.12.0.0 && <4.13-    , directory-    , filepath-    , gamgee-    , optparse-applicative-    , polysemy-    , relude-    , safe-exceptions-    , text-    , time-    , unix-  default-language: Haskell2010+    main-is: Main.hs+    hs-source-dirs: app+    other-modules:+        Gamgee.Program.CommandLine+        Gamgee.Program.Effects+        Paths_gamgee+    default-language: Haskell2010+    default-extensions: ApplicativeDo BangPatterns ConstraintKinds+                        DataKinds DefaultSignatures DeriveAnyClass DeriveFoldable+                        DeriveFunctor DeriveGeneric DeriveLift DeriveTraversable+                        DerivingStrategies EmptyCase ExistentialQuantification+                        FlexibleContexts FlexibleInstances FunctionalDependencies GADTs+                        GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase+                        MultiParamTypeClasses MultiWayIf NamedFieldPuns NoImplicitPrelude+                        OverloadedStrings PatternSynonyms PolyKinds RankNTypes+                        ScopedTypeVariables StandaloneDeriving TemplateHaskell+                        TupleSections TypeApplications TypeFamilies TypeFamilyDependencies+                        TypeOperators+    ghc-options: -Wall -Wcompat -Wredundant-constraints+                 -Wincomplete-record-updates -Wincomplete-uni-patterns -threaded+                 -rtsopts -with-rtsopts=-N+    build-depends:+        Hclip ==3.0.0.4,+        aeson >=1.4.4.0 && <1.5,+        base >=4.12.0.0 && <4.13,+        directory >=1.3.3.0 && <1.4,+        filepath >=1.4.2.1 && <1.5,+        gamgee -any,+        optparse-applicative >=0.14.3.0 && <0.15,+        polysemy >=1.0.0.0 && <1.1,+        relude >=0.5.0 && <0.6,+        safe-exceptions >=0.1.7.0 && <0.2,+        text >=1.2.3.1 && <1.3,+        time >=1.8.0.2 && <1.9,+        unix >=2.7.2.2 && <2.8  test-suite gamgee-test-  type: exitcode-stdio-1.0-  main-is: Main.hs-  other-modules:-      Gamgee.Test.Effects-      Gamgee.Test.Golden-      Gamgee.Test.Operation-      Gamgee.Test.Property-      Paths_gamgee-  hs-source-dirs:-      test-  default-extensions: ApplicativeDo BangPatterns ConstraintKinds DataKinds DefaultSignatures DeriveAnyClass DeriveFoldable DeriveFunctor DeriveGeneric DeriveLift DeriveTraversable DerivingStrategies EmptyCase ExistentialQuantification FlexibleContexts FlexibleInstances FunctionalDependencies GADTs GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase MultiParamTypeClasses MultiWayIf NamedFieldPuns NoImplicitPrelude OverloadedStrings PatternSynonyms PolyKinds RankNTypes ScopedTypeVariables StandaloneDeriving TemplateHaskell TupleSections TypeApplications TypeFamilies TypeFamilyDependencies TypeOperators-  ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns -threaded -rtsopts -with-rtsopts=-N-  build-depends:-      QuickCheck-    , aeson-    , base >=4.12.0.0 && <4.13-    , bytestring-    , cryptonite-    , filepath-    , gamgee-    , memory-    , polysemy-    , quickcheck-instances-    , relude-    , tasty-    , tasty-golden-    , tasty-quickcheck-    , text-    , time-  default-language: Haskell2010+    type: exitcode-stdio-1.0+    main-is: Main.hs+    hs-source-dirs: test+    other-modules:+        Gamgee.Test.Effects+        Gamgee.Test.Golden+        Gamgee.Test.Operation+        Gamgee.Test.Property+        Paths_gamgee+    default-language: Haskell2010+    default-extensions: ApplicativeDo BangPatterns ConstraintKinds+                        DataKinds DefaultSignatures DeriveAnyClass DeriveFoldable+                        DeriveFunctor DeriveGeneric DeriveLift DeriveTraversable+                        DerivingStrategies EmptyCase ExistentialQuantification+                        FlexibleContexts FlexibleInstances FunctionalDependencies GADTs+                        GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase+                        MultiParamTypeClasses MultiWayIf NamedFieldPuns NoImplicitPrelude+                        OverloadedStrings PatternSynonyms PolyKinds RankNTypes+                        ScopedTypeVariables StandaloneDeriving TemplateHaskell+                        TupleSections TypeApplications TypeFamilies TypeFamilyDependencies+                        TypeOperators+    ghc-options: -Wall -Wcompat -Wredundant-constraints+                 -Wincomplete-record-updates -Wincomplete-uni-patterns -threaded+                 -rtsopts -with-rtsopts=-N+    build-depends:+        QuickCheck >=2.13.2 && <2.14,+        aeson >=1.4.4.0 && <1.5,+        base >=4.12.0.0 && <4.13,+        bytestring >=0.10.8.2 && <0.11,+        cryptonite ==0.25.*,+        filepath >=1.4.2.1 && <1.5,+        gamgee -any,+        memory >=0.14.18 && <0.15,+        polysemy >=1.0.0.0 && <1.1,+        quickcheck-instances >=0.3.22 && <0.4,+        relude >=0.5.0 && <0.6,+        tasty >=1.2.3 && <1.3,+        tasty-golden >=2.3.2 && <2.4,+        tasty-quickcheck >=0.10.1 && <0.11,+        text >=1.2.3.1 && <1.3,+        time >=1.8.0.2 && <1.9
src/Gamgee/Effects/Crypto.hs view
@@ -51,7 +51,9 @@ -- Programs ---------------------------------------------------------------------------------------------------- -encryptSecret :: Members [SI.SecretInput Text, Crypto] r => Token.TokenSpec -> Sem r Token.TokenSpec+encryptSecret :: Members [SI.SecretInput Text, Crypto] r+              => Token.TokenSpec+              -> Sem r Token.TokenSpec encryptSecret spec =   case Token.tokenSecret spec of     -- Secret is already encrypted@@ -69,7 +71,9 @@         secret' <- encrypt secret password         return spec { Token.tokenSecret = secret' } -decryptSecret :: Members [SI.SecretInput Text, Crypto] r => Token.TokenSpec -> Sem r Text+decryptSecret :: Members [SI.SecretInput Text, Crypto] r+              => Token.TokenSpec+              -> Sem r Text decryptSecret spec =   case Token.tokenSecret spec of     Token.TokenSecretPlainText plainSecret  -> return plainSecret
src/Gamgee/Effects/CryptoRandom.hs view
@@ -11,7 +11,7 @@  import qualified Crypto.Random.Types as CRT import qualified Data.ByteArray      as BA-import           Polysemy            (Lift, Member, Sem)+import           Polysemy            (Embed, Member, Sem) import qualified Polysemy            as P import           Relude @@ -32,6 +32,6 @@ -- Interpretations ---------------------------------------------------------------------------------------------------- -runCryptoRandomIO :: Member (Lift IO) r => Sem (CryptoRandom : r) a -> Sem r a+runCryptoRandomIO :: Member (Embed IO) r => Sem (CryptoRandom : r) a -> Sem r a runCryptoRandomIO = P.interpret $ \case-  RandomBytes count -> P.sendM @IO $ CRT.getRandomBytes count+  RandomBytes count -> P.embed @IO $ CRT.getRandomBytes count
src/Gamgee/Effects/SecretInput.hs view
@@ -10,7 +10,7 @@     ) where  import           Control.Exception.Safe (bracket_)-import           Polysemy               (Lift, Member, Sem)+import           Polysemy               (Embed, Member, Sem) import qualified Polysemy               as P import           Relude import qualified System.IO              as IO@@ -37,9 +37,9 @@ -- Interpretations ---------------------------------------------------------------------------------------------------- -runSecretInputIO :: (Member (Lift IO) r) => Sem (SecretInput Text : r) a -> Sem r a+runSecretInputIO :: (Member (Embed IO) r) => Sem (SecretInput Text : r) a -> Sem r a runSecretInputIO = P.interpret $ \case-  SecretInput prompt -> P.sendM $ do+  SecretInput prompt -> P.embed $ do     putText prompt     IO.hFlush stdout     i <- withoutEcho getLine
src/Gamgee/Effects/TOTP.hs view
@@ -3,6 +3,7 @@       TOTP (..)        -- * Actions+    , getSecret     , getTOTP        -- * Interpretations@@ -30,7 +31,8 @@ ----------------------------------------------------------------------------------------------------  data TOTP m a where-  GetTOTP :: Token.TokenSpec -> Clock.POSIXTime -> TOTP m Text+  GetSecret :: Token.TokenSpec -> TOTP m Text+  GetTOTP   :: Token.TokenSpec -> Clock.POSIXTime -> TOTP m Text  P.makeSem ''TOTP @@ -41,14 +43,20 @@  runTOTP :: Members [SecretInput Text, Crypto, P.Error Err.EffError] r => Sem (TOTP : r) a -> Sem r a runTOTP = P.interpret $ \case-  GetTOTP spec time -> do-    secret <- Crypto.decryptSecret spec-    case Encoding.convertFromBase Encoding.Base32 (encodeUtf8 secret :: ByteString) of-      Left msg  -> P.throw $ Err.SecretDecryptError $ toText msg-      Right key -> computeTOTP spec key time+  GetSecret spec    -> snd <$> retrieveKeyAndSecret spec+  GetTOTP spec time -> fst <$> retrieveKeyAndSecret spec >>= computeTOTP spec time -computeTOTP :: Member (P.Error Err.EffError) r => Token.TokenSpec -> ByteString -> Clock.POSIXTime -> Sem r Text-computeTOTP spec key time =+retrieveKeyAndSecret :: Members [SecretInput Text, Crypto, P.Error Err.EffError] r+                     => Token.TokenSpec+                     -> Sem r (ByteString, Text)+retrieveKeyAndSecret spec = do+  secret <- Crypto.decryptSecret spec+  case Encoding.convertFromBase Encoding.Base32 (encodeUtf8 secret :: ByteString) of+    Left msg  -> P.throw $ Err.SecretDecryptError $ toText msg+    Right key -> return (key, secret)++computeTOTP :: Member (P.Error Err.EffError) r => Token.TokenSpec -> Clock.POSIXTime -> ByteString -> Sem r Text+computeTOTP spec time key =   case Token.tokenAlgorithm spec of     Token.AlgorithmSHA1   -> makeOTP <$> makeParams HashAlgos.SHA1     Token.AlgorithmSHA256 -> makeOTP <$> makeParams HashAlgos.SHA256
src/Gamgee/Operation.hs view
@@ -4,6 +4,7 @@   , listTokens   , getOTP   , getInfo+  , changePassword   ) where  @@ -14,7 +15,7 @@ import qualified Gamgee.Effects        as Eff import qualified Gamgee.Token          as Token import           Paths_gamgee          (version)-import           Polysemy              (Member, Members, Sem)+import           Polysemy              (Members, Sem) import qualified Polysemy.Error        as P import qualified Polysemy.Input        as P import qualified Polysemy.Output       as P@@ -23,9 +24,6 @@ import qualified Relude.Extra.Map      as Map  -getTokens :: Member (P.State Token.Tokens) r => Sem r Token.Tokens-getTokens = P.get- addToken :: Members [ P.State Token.Tokens                     , Eff.Crypto                     , Eff.SecretInput Text@@ -34,7 +32,7 @@          -> Sem r () addToken spec = do   let ident = Token.getIdentifier spec-  tokens <- getTokens+  tokens <- P.get @Token.Tokens   if ident `Map.member` tokens   then P.throw $ Eff.AlreadyExists ident   else do@@ -46,7 +44,7 @@             => Token.TokenIdentifier             -> Sem r () deleteToken ident = do-  tokens <- getTokens+  tokens <- P.get @Token.Tokens   case Map.lookup ident tokens of     Nothing -> P.throw $ Eff.NoSuchToken ident     Just _  -> P.put $ Map.delete ident tokens@@ -55,7 +53,7 @@                       , P.Output Text ] r            => Sem r () listTokens = do-  tokens <- getTokens+  tokens <- P.get @Token.Tokens   mapM_ (P.output . Token.unTokenIdentifier . Token.getIdentifier) tokens  getOTP :: Members [ P.State Token.Tokens@@ -66,7 +64,7 @@        -> Clock.POSIXTime        -> Sem r () getOTP ident time = do-  tokens <- getTokens+  tokens <- P.get @Token.Tokens   case Map.lookup ident tokens of     Nothing   -> P.throw $ Eff.NoSuchToken ident     Just spec -> Eff.getTOTP spec time >>= P.output@@ -88,3 +86,18 @@                           ]   P.output info +changePassword :: Members [ P.State Token.Tokens+                          , Eff.SecretInput Text+                          , Eff.Crypto+                          , Eff.TOTP+                          , P.Error Eff.EffError ] r+               => Token.TokenIdentifier+               -> Sem r ()+changePassword ident = do+  tokens <- P.get @Token.Tokens+  case Map.lookup ident tokens of+    Nothing   -> P.throw $ Eff.NoSuchToken ident+    Just spec -> do+      secret <- Eff.getSecret spec+      spec' <- Eff.encryptSecret spec{ Token.tokenSecret = Token.TokenSecretPlainText secret }+      P.put $ Map.insert ident spec' tokens
test/Gamgee/Test/Effects.hs view
@@ -1,6 +1,5 @@ module Gamgee.Test.Effects-  ( runOutputPure-  , runListSecretInput+  ( runListSecretInput   , runCryptoRandom   , runByteStoreST   ) where@@ -11,22 +10,13 @@ import           Data.STRef          (STRef) import qualified Data.STRef          as STRef import qualified Gamgee.Effects      as Eff-import           Polysemy            (Lift, Member, Sem)+import           Polysemy            (Embed, Member, Sem) import qualified Polysemy            as P-import qualified Polysemy.Output     as P import qualified Polysemy.State      as P import           Relude   ------------------------------------------------------------------------------------------------------- Interpret Output by accumulating it in a list-------------------------------------------------------------------------------------------------------runOutputPure :: Sem (P.Output o : r) a -> Sem r ([o], a)-runOutputPure = P.runFoldMapOutput one------------------------------------------------------------------------------------------------------- -- Interpret SecretInput by reading from a list ---------------------------------------------------------------------------------------------------- @@ -56,7 +46,7 @@ -- Interpret ByteStore using the ST monad ---------------------------------------------------------------------------------------------------- -runByteStoreST :: Member (Lift (ST s)) r => STRef s (Maybe LByteString) -> Sem (Eff.ByteStore : r) a -> Sem r a+runByteStoreST :: Member (Embed (ST s)) r => STRef s (Maybe LByteString) -> Sem (Eff.ByteStore : r) a -> Sem r a runByteStoreST ref = P.interpret $ \case-  Eff.ReadByteStore        -> P.sendM $ STRef.readSTRef ref-  Eff.WriteByteStore bytes -> P.sendM $ STRef.writeSTRef ref (Just bytes)+  Eff.ReadByteStore        -> P.embed $ STRef.readSTRef ref+  Eff.WriteByteStore bytes -> P.embed $ STRef.writeSTRef ref (Just bytes)
test/Gamgee/Test/Operation.hs view
@@ -18,6 +18,7 @@ import qualified Gamgee.Token          as Token import qualified Polysemy              as P import qualified Polysemy.Error        as P+import qualified Polysemy.Output       as P import           Relude  @@ -59,7 +60,7 @@ listTokens store = fmap fst                    <$> P.runM                        (P.runError-                        $ Eff.runOutputPure+                        $ P.runOutputList                         $ Eff.runByteStoreST store                         $ Eff.runJSONStore                         $ Eff.runStateJSON Operation.listTokens)@@ -75,7 +76,7 @@   fmap fst   <$> P.runM       (P.runError-       $ Eff.runOutputPure+       $ P.runOutputList        $ Eff.runCryptoRandom drg        $ Eff.runCrypto        $ Eff.runListSecretInput input