Spock-auth (empty) → 0.1.0.0
raw patch · 3 files changed
+172/−0 lines, 3 filesdep +Spockdep +basedep +http-typessetup-changed
Dependencies added: Spock, base, http-types, text, time
Files
- Setup.hs +2/−0
- Spock-auth.cabal +26/−0
- src/Web/Spock/Auth.hs +144/−0
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ Spock-auth.cabal view
@@ -0,0 +1,26 @@+name: Spock-auth+version: 0.1.0.0+synopsis: Provides authentification helpers for Spock+Homepage: https://github.com/agrafix/Spock-auth+Bug-reports: https://github.com/agrafix/Spock-auth/issues+license: BSD3+author: Alexander Thiemann <mail@agrafix.net>+maintainer: mail@agrafix.net+copyright: (c) 2014 Alexander Thiemann+category: Web+build-type: Simple+cabal-version: >=1.8++library+ hs-source-dirs: src+ exposed-modules: Web.Spock.Auth+ build-depends: base >= 4 && < 5,+ Spock >=0.4.3.0,+ text,+ http-types,+ time+ ghc-options: -Wall -fno-warn-orphans++source-repository head+ type: git+ location: git://github.com/agrafix/Spock-auth.git
+ src/Web/Spock/Auth.hs view
@@ -0,0 +1,144 @@+{-# LANGUAGE DoAndIfThenElse #-}+{-# LANGUAGE OverloadedStrings #-}+module Web.Spock.Auth+ ( -- * Initialisation helpers+ authSessCfg, AuthCfg (..)+ -- * Handeling custom session data+ , writeSessionData, readSessionData, modifySessionData+ -- * Access control+ , VisitorSession, NoAccessReason (..)+ , NoAccessHandler, LoadUserFun, CheckRightsFun, UserRights+ , markAsLoggedIn+ , markAsGuest+ , userRoute+ )+where++import Web.Spock++import Control.Applicative+import Data.Time.Clock+import qualified Network.HTTP.Types as Http+import qualified Data.Text as T++-- | Configuration+data AuthCfg sess+ = AuthCfg+ { ac_sessionTTL :: NominalDiffTime+ , ac_emptySession :: sess+ }++-- | Assign the current session roles/permission, eg. admin or user+type UserRights = T.Text++-- | Describes why access was denied to a user+data NoAccessReason+ = NotEnoughRights+ | NotLoggedIn+ | NotValidUser+ deriving (Show, Eq, Read, Enum)++-- | Define what happens to non-authorized requests+type NoAccessHandler conn sess userId st =+ NoAccessReason -> SpockAction conn (VisitorSession sess userId) st ()++-- | How should a session be transformed into a user? Can access the database using 'runQuery'+type LoadUserFun conn sess userId st user =+ userId -> SpockAction conn (VisitorSession sess userId) st (Maybe user)++-- | What rights does the current user have? Can access the database using 'runQuery'+type CheckRightsFun conn sess userId st user =+ user -> [UserRights] -> SpockAction conn (VisitorSession sess userId) st Bool++data SessionType userId+ = GuestSession+ | UserSession userId+ deriving (Show, Eq)++data VisitorSession sess userId+ = VisitorSession+ { vs_type :: SessionType userId+ , vs_data :: sess+ }+ deriving (Show, Eq)++-- | Plug this into the 'spock' function to create SessionCfg+authSessCfg :: AuthCfg sess -> SessionCfg (VisitorSession sess userId)+authSessCfg authCfg =+ SessionCfg+ { sc_cookieName = "spocksession"+ , sc_sessionTTL = ac_sessionTTL authCfg+ , sc_sessionIdEntropy = 42+ , sc_emptySession = VisitorSession GuestSession (ac_emptySession authCfg)+ }++-- | Mark current visitor as logged in+markAsLoggedIn :: userId -> SpockAction conn (VisitorSession sess userId) st ()+markAsLoggedIn userId =+ modifySession (\oldData -> oldData { vs_type = (UserSession userId) })++-- | Mark current visitor as guest+markAsGuest :: SpockAction conn (VisitorSession sess userId) st ()+markAsGuest =+ modifySession (\oldData -> oldData { vs_type = GuestSession })++-- | Replacement for 'readSession'+readSessionData :: SpockAction conn (VisitorSession sess userId) st sess+readSessionData =+ vs_data <$> readSession++-- | Replacement for 'modifySession'+modifySessionData :: (sess -> sess) -> SpockAction conn (VisitorSession sess userId) st ()+modifySessionData f =+ modifySession (\oldData -> oldData { vs_data = f (vs_data oldData) })++-- | Replacement for 'writeSession'+writeSessionData :: sess -> SpockAction conn (VisitorSession sess userId) st ()+writeSessionData v =+ modifySessionData (const v)++-- | Before the request is performed, you can check if the signed in user has permissions to+-- view the contents of the request. You may want to define a helper function that+-- proxies this function to not pass around 'NoAccessHandler', 'LoadUserFun' and 'CheckRightsFun'+-- all the time.+-- Example:+--+-- > type MyWebMonad a = SpockAction Connection (VisitorSession () UserId) () a+-- > newtype MyUser = MyUser { unMyUser :: T.Text }+-- >+-- > http403 msg =+-- > do status Http.status403+-- > text (show msg)+-- >+-- > login :: Http.StdMethod+-- > -> [UserRights]+-- > -> RoutePattern+-- > -> (MyUser -> MyWebMonad ())+-- > -> MyWebMonad ()+-- > login =+-- > userRoute http403 myLoadUser myCheckRights+--+userRoute :: NoAccessHandler conn sess userId st+ -> LoadUserFun conn sess userId st user+ -> CheckRightsFun conn sess userId st user+ -> Http.StdMethod+ -> [UserRights]+ -> RoutePattern+ -> (user -> SpockAction conn (VisitorSession sess userId) st ())+ -> SpockM conn (VisitorSession sess userId) st ()+userRoute noAccessHandler loadUser checkRights reqTy requiredRights route action =+ addroute reqTy route $+ do sessData <- readSession+ case vs_type sessData of+ GuestSession ->+ noAccessHandler NotLoggedIn+ UserSession userId ->+ do mUser <- loadUser userId+ case mUser of+ Nothing ->+ noAccessHandler NotValidUser+ Just user ->+ do isOk <- checkRights user requiredRights+ if isOk+ then action user+ else noAccessHandler NotEnoughRights