diff --git a/package.yaml b/package.yaml index 9330f17..883da20 100644 --- a/package.yaml +++ b/package.yaml @@ -64,6 +64,8 @@ dependencies: - warp-tls - yaml - yesod + - yesod-auth + - yesod-auth-basic - yesod-core - yesod-persistent diff --git a/src/Foundation.hs b/src/Foundation.hs index 7d994b1..6c09f30 100644 --- a/src/Foundation.hs +++ b/src/Foundation.hs @@ -33,18 +33,43 @@ import qualified Yesod.Core.Unsafe as Unsafe import Control.Monad.Logger.Extras ( wrapSGRCode ) import Control.Monad.Reader.Has ( Has(extract, update) ) +import Crypto.Hash ( SHA256(SHA256) + , hashWith + ) import Data.String.Interpolate.IsString ( i ) import qualified Data.Text as T import Language.Haskell.TH ( Loc(..) ) import Lib.PkgRepository import Lib.Types.AppIndex +import Model ( Admin + , EntityField(AdminName, AdminPassHash) + , Unique(UniqueAdmin) + ) import Settings import System.Console.ANSI.Codes ( Color(..) , ColorIntensity(..) , ConsoleLayer(Foreground) , SGR(SetColor) ) +import Yesod ( FormMessage + , defaultFormMessage + ) +import Yesod.Auth ( AuthEntity + , Creds(credsIdent) + , YesodAuth + ( AuthId + , authPlugins + , getAuthId + , loginDest + , logoutDest + , maybeAuthId + ) + , YesodAuthPersist(getAuthEntity) + ) +import Yesod.Auth.Http.Basic ( defaultAuthSettings + , defaultMaybeBasicAuthId + ) import Yesod.Persist.Core -- | The foundation datatype for your application. This can be a good place to @@ -175,6 +200,27 @@ instance YesodPersistRunner RegistryCtx where getDBRunner :: Handler (DBRunner RegistryCtx, Handler ()) getDBRunner = defaultGetDBRunner appConnPool +instance RenderMessage RegistryCtx FormMessage where + renderMessage _ _ = defaultFormMessage +instance YesodAuth RegistryCtx where + type AuthId RegistryCtx = Text + getAuthId = pure . Just . credsIdent + maybeAuthId = do + pool <- getsYesod appConnPool + let checkCreds k s = flip runSqlPool pool $ do + let passHash = hashWith SHA256 . encodeUtf8 . ("start9_admin:" <>) $ decodeUtf8 s + ls <- selectList [AdminName ==. decodeUtf8 k, AdminPassHash ==. passHash] [] + pure . not . null $ ls + defaultMaybeBasicAuthId checkCreds defaultAuthSettings + loginDest _ = PackageListR + logoutDest _ = PackageListR + authPlugins _ = [] + +instance YesodAuthPersist RegistryCtx where + type AuthEntity RegistryCtx = Admin + getAuthEntity = (entityVal <<$>>) . liftHandler . runDB . getBy . UniqueAdmin + + unsafeHandler :: RegistryCtx -> Handler a -> IO a unsafeHandler = Unsafe.fakeHandlerGetLogger appLogger diff --git a/src/Model.hs b/src/Model.hs index 78338ca..fa1bcc2 100644 --- a/src/Model.hs +++ b/src/Model.hs @@ -11,9 +11,11 @@ module Model where +import Crypto.Hash import Database.Persist.TH import Lib.Types.AppIndex import Lib.Types.Emver +import Orphans.Cryptonite ( ) import Orphans.Emver ( ) import Startlude @@ -100,4 +102,16 @@ PkgDependency UniquePkgDepVersion pkgId pkgVersion depId deriving Eq deriving Show + +Admin + createdAt UTCTime + name Text + passHash (Digest SHA256) + UniqueAdmin name + +Upload + uploader AdminId + pkgId PkgRecordId + pkgVersion Version + createdAt UTCTime |] diff --git a/src/Orphans/Cryptonite.hs b/src/Orphans/Cryptonite.hs new file mode 100644 index 0000000..66e57ab --- /dev/null +++ b/src/Orphans/Cryptonite.hs @@ -0,0 +1,42 @@ +{-# OPTIONS_GHC -fno-warn-orphans #-} +{-# LANGUAGE TypeApplications #-} +module Orphans.Cryptonite where +import Crypto.Hash ( Digest + , digestFromByteString + ) +import Crypto.Hash.Algorithms ( SHA256 ) +import Data.ByteArray.Encoding ( Base(Base16) + , convertFromBase + , convertToBase + ) +import Data.Text ( pack ) +import Database.Persist ( PersistField(..) + , PersistValue(PersistText) + , SqlType(SqlString) + ) +import Database.Persist.Sql ( PersistFieldSql(..) ) +import Startlude ( ($) + , (.) + , Bifunctor(bimap, first) + , ByteString + , Either(Left) + , Semigroup((<>)) + , decodeUtf8 + , encodeUtf8 + , join + , note + , show + ) + +instance PersistField (Digest SHA256) where + toPersistValue = PersistText . decodeUtf8 . convertToBase Base16 + fromPersistValue (PersistText t) = + join + . bimap pack (note "Invalid SHA256 Digest" . digestFromByteString) + . convertFromBase @_ @ByteString Base16 + . encodeUtf8 + $ t + fromPersistValue v = Left $ "Invalid PersistValue type: " <> show v + +instance PersistFieldSql (Digest SHA256) where + sqlType _ = SqlString diff --git a/stack.yaml b/stack.yaml index 1ba6103..e70c35a 100644 --- a/stack.yaml +++ b/stack.yaml @@ -45,6 +45,7 @@ extra-deps: - monad-logger-extras-0.1.1.1 - wai-request-spec-0.10.2.4 - warp-3.3.19 + - yesod-auth-basic-0.1.0.3 # Override default flag values for local packages and extra-deps # flags: {}