packages feed

happstack-authenticate 2.3.4.16 → 2.6.1

raw patch · 12 files changed

Files

Happstack/Authenticate/Core.hs view
@@ -37,6 +37,11 @@     , isAuthAdmin     , usernameAcceptable     , requireEmail+    , systemFromAddress+    , systemReplyToAddress+    , systemSendmailPath+    , postLoginRedirect+    , createUserCallback     , HappstackAuthenticateI18N(..)     , UserId(..)     , unUserId@@ -60,6 +65,7 @@     , IxUser     , SharedSecret(..)     , unSharedSecret+    , SimpleAddress(..)     , genSharedSecret     , genSharedSecretDevURandom     , genSharedSecretSysRandom@@ -87,6 +93,8 @@     , GetUserByUsername(..)     , GetUserByUserId(..)     , GetUserByEmail(..)+    , GetUsers(..)+    , GetUsersByEmail(..)     , GetAuthenticateState(..)     , getOrGenSharedSecret     , Token(..)@@ -110,6 +118,9 @@     , AuthenticateURL(..)     , rAuthenticationMethods     , rControllers+    , systemFromAddress+    , systemReplyToAddress+    , systemSendmailPath     , authenticateURL     , nestAuthenticationMethod     ) where@@ -136,7 +147,7 @@ import qualified Data.Map              as Map import Data.Maybe                      (fromMaybe, maybeToList) import Data.Monoid                     ((<>), mconcat, mempty)-import Data.SafeCopy                   (SafeCopy, base, deriveSafeCopy)+import Data.SafeCopy                   (SafeCopy, Migrate(..), base, deriveSafeCopy, extension) import Data.IxSet.Typed import qualified Data.IxSet.Typed      as IxSet import           Data.Set              (Set)@@ -157,6 +168,7 @@ import Text.Boomerang.TH               (makeBoomerangs) import Text.Shakespeare.I18N           (RenderMessage(renderMessage), mkMessageFor) import Web.JWT                         (Algorithm(HS256), JWT, VerifiedJWT, JWTClaimsSet(..), encodeSigned, claims, decode, decodeAndVerifySignature, secondsSinceEpoch, intDate, verify)+import qualified Web.JWT               as JWT #if MIN_VERSION_jwt(0,8,0) import Web.JWT                         (ClaimsMap(..), hmacSecret) #else@@ -335,14 +347,31 @@              (ixFun $ maybeToList . view email)  ------------------------------------------------------------------------------+-- SimpleAddress+------------------------------------------------------------------------------++data SimpleAddress = SimpleAddress+ { _saName :: Maybe Text+ , _saEmail :: Email+ }+ deriving (Eq, Ord, Read, Show, Data, Typeable, Generic)+deriveSafeCopy 0 'base ''SimpleAddress+makeLenses ''SimpleAddress++------------------------------------------------------------------------------ -- AuthenticateConfig ------------------------------------------------------------------------------  -- | Various configuration options that apply to all authentication methods data AuthenticateConfig = AuthenticateConfig-    { _isAuthAdmin        :: UserId -> IO Bool           -- ^ can user administrate the authentication system?-    , _usernameAcceptable :: Username -> Maybe CoreError -- ^ enforce username policies, valid email, etc. 'Nothing' == ok, 'Just Text' == error message-    , _requireEmail       :: Bool+    { _isAuthAdmin          :: UserId -> IO Bool           -- ^ can user administrate the authentication system?+    , _usernameAcceptable   :: Username -> Maybe CoreError -- ^ enforce username policies, valid email, etc. 'Nothing' == ok, 'Just Text' == error message+    , _requireEmail         :: Bool                        -- ^ require use to supply an email address when creating an account+    , _systemFromAddress    :: Maybe SimpleAddress         -- ^ From: line for emails sent by the server+    , _systemReplyToAddress :: Maybe SimpleAddress         -- ^ Reply-To: line for emails sent by the server+    , _systemSendmailPath   :: Maybe FilePath              -- ^ path to sendmail if it is not \/usr\/sbin\/sendmail+    , _postLoginRedirect    :: Maybe Text                  -- ^ path to redirect to after a successful login+    , _createUserCallback   :: Maybe (User -> IO ())       -- ^ a function to call when a new user is created. Useful for adding them to mailing lists or other stuff     }     deriving (Typeable, Generic) makeLenses ''AuthenticateConfig@@ -556,6 +585,13 @@     do us <- view users        return $ getOne $ us @= userId +-- | find all 'Users'+--+getUsers :: Query AuthenticateState (Set User)+getUsers =+    do us <- view users+       return $ toSet $ us+ -- | look up a 'User' by their 'Email' -- -- NOTE: if the email is associated with more than one account this will return 'Nothing'@@ -565,6 +601,14 @@     do us <- view users        return $ getOne $ us @= email +-- | find all 'Users' which match 'Email'+--+getUsersByEmail :: Email+               -> Query AuthenticateState (Set User)+getUsersByEmail email =+    do us <- view users+       return $ toSet $ us @= email+ -- | get the entire AuthenticateState value getAuthenticateState :: Query AuthenticateState AuthenticateState getAuthenticateState = ask@@ -582,7 +626,9 @@     , 'deleteUser     , 'getUserByUsername     , 'getUserByUserId+    , 'getUsers     , 'getUserByEmail+    , 'getUsersByEmail     , 'getAuthenticateState     ] @@ -689,7 +735,9 @@                       Nothing -> return Nothing                       (Just ssecret) ->                         -- finally we can verify all the claims-#if MIN_VERSION_jwt(0,8,0)+#if MIN_VERSION_jwt(0,11,0)+                        case verify (JWT.toVerify $ hmacSecret (_unSharedSecret ssecret)) unverified of+#elif MIN_VERSION_jwt(0,8,0)                         case verify (hmacSecret (_unSharedSecret ssecret)) unverified of #else                         case verify (secret (_unSharedSecret ssecret)) unverified of
Happstack/Authenticate/OpenId/Controllers.hs view
@@ -11,7 +11,7 @@ import Happstack.Authenticate.Core        (AuthenticateState, AuthenticateURL, getToken, tokenIsAuthAdmin) import Happstack.Authenticate.OpenId.Core (GetOpenIdRealm(..), OpenIdState) import Happstack.Authenticate.OpenId.URL  (OpenIdURL(BeginDance, Partial, ReturnTo), nestOpenIdURL)-import Happstack.Authenticate.OpenId.PartialsURL (PartialURL(UsingGoogle, UsingYahoo, RealmForm))+import Happstack.Authenticate.OpenId.PartialsURL (PartialURL(UsingYahoo, RealmForm)) import Happstack.Server                   (Happstack) import Language.Javascript.JMacro import Web.Routes@@ -70,14 +70,6 @@            error(callback);        };      }]);--   openId.directive('openidGoogle', ['$rootScope', function ($rootScope) {-     return {-       restrict: 'E',-       replace: true,-       templateUrl: `(showURL (Partial UsingGoogle) [])`-     };-    }]);     openId.directive('openidYahoo', ['$rootScope', function ($rootScope) {      return {
Happstack/Authenticate/OpenId/Core.hs view
@@ -82,12 +82,14 @@     , _openIdRealm :: Maybe Text     }     deriving (Eq, Ord, Read, Show, Data, Typeable, Generic)-deriveSafeCopy 2 'extension ''OpenIdState-makeLenses ''OpenIdState  instance Migrate OpenIdState where   type MigrateFrom OpenIdState = OpenIdState_1   migrate (OpenIdState_1 ids) = OpenIdState ids Nothing++deriveSafeCopy 2 'extension ''OpenIdState+makeLenses ''OpenIdState+  initialOpenIdState :: OpenIdState initialOpenIdState = OpenIdState
Happstack/Authenticate/OpenId/Partials.hs view
@@ -26,7 +26,7 @@ import HSP.JMacro                           () import Prelude                              hiding ((.), id) import Text.Shakespeare.I18N                (Lang, mkMessageFor, renderMessage)-import Web.Authenticate.OpenId.Providers    (google, yahoo)+import Web.Authenticate.OpenId.Providers    (yahoo) import Web.Routes import Web.Routes.XMLGenT                   () import Web.Routes.TH                        (derivePathInfo)@@ -35,8 +35,7 @@ type Partial  m = XMLGenT (RouteT AuthenticateURL (ReaderT [Lang] m))  data PartialMsgs-  = UsingGoogleMsg-  | UsingYahooMsg+  = UsingYahooMsg   | SetRealmMsg   | OpenIdRealmMsg @@ -60,24 +59,15 @@   -> Partial m XML routePartial authenticateState openIdState url =   case url of-    UsingGoogle    -> usingGoogle     UsingYahoo     -> usingYahoo     RealmForm      -> realmForm openIdState -usingGoogle :: (Functor m, Monad m) =>-                      Partial m XML-usingGoogle =-  do danceURL <- lift $ nestOpenIdURL  $ showURL (BeginDance (Text.pack google))-     [hsx|-       <a ng-click=("openIdWindow('" <> danceURL <> "')")><img src="https://raw.githubusercontent.com/intridea/authbuttons/master/png/google_32.png" alt=UsingGoogleMsg /></a>-     |]- usingYahoo :: (Functor m, Monad m) =>               Partial m XML usingYahoo =   do danceURL <- lift $ nestOpenIdURL  $ showURL (BeginDance (Text.pack yahoo))      [hsx|-       <a ng-click=("openIdWindow('" <> danceURL <> "')")><img src="https://raw.githubusercontent.com/intridea/authbuttons/master/png/yahoo_32.png" alt=UsingYahooMsg /></a>+       <a ng-click=("openIdWindow('" <> danceURL <> "')")><img src="https://raw.githubusercontent.com/Happstack/authbuttons/master/png/yahoo_32.png" alt=UsingYahooMsg /></a>      |]  realmForm
Happstack/Authenticate/OpenId/PartialsURL.hs view
@@ -10,8 +10,7 @@ import Web.Routes.Boomerang                 (Router, (:-), (<>), boomerangFromPathSegments, boomerangToPathSegments)  data PartialURL-  = UsingGoogle-  | UsingYahoo+  = UsingYahoo   | RealmForm   deriving (Eq, Ord, Data, Typeable, Generic, Read, Show) @@ -19,8 +18,7 @@  partialURL :: Router () (PartialURL :- ()) partialURL =-  (  "using-google"         . rUsingGoogle-  <> "using-yahoo"          . rUsingYahoo+  (  "using-yahoo"          . rUsingYahoo   <> "realm"                . rRealmForm   ) 
Happstack/Authenticate/OpenId/Route.hs view
@@ -2,6 +2,8 @@ module Happstack.Authenticate.OpenId.Route where  import Control.Applicative   ((<$>))+import Control.Concurrent.STM (atomically)+import Control.Concurrent.STM.TVar (TVar, readTVar) import Control.Monad.Reader  (ReaderT, runReaderT) import Control.Monad.Trans   (liftIO) import Data.Acid             (AcidState, closeAcidState, makeAcidic)@@ -31,26 +33,28 @@  routeOpenId :: (Happstack m) =>                AcidState AuthenticateState-            -> AuthenticateConfig+            -> TVar AuthenticateConfig             -> AcidState OpenIdState             -> [Text]             -> RouteT AuthenticateURL (ReaderT [Lang] m) Response-routeOpenId authenticateState authenticateConfig openIdState pathSegments =+routeOpenId authenticateState authenticateConfigTV openIdState pathSegments =   case parseSegments fromPathSegments pathSegments of     (Left _) -> notFound $ toJSONError URLDecodeFailed     (Right url) ->-      case url of-        (Partial u) ->-           do xml <- unXMLGenT (routePartial authenticateState openIdState u)-              ok $ toResponse (html4StrictFrag, xml)-        (BeginDance providerURL) ->-          do returnURL <- nestOpenIdURL $ showURL ReturnTo-             realm <- query' openIdState GetOpenIdRealm-             forwardURL <- liftIO $ do manager <- newManager tlsManagerSettings-                                       getForwardUrl providerURL returnURL realm [] manager -- [("Email", "http://schema.openid.net/contact/email")]-             seeOther forwardURL (toResponse ())-        ReturnTo -> token authenticateState authenticateConfig openIdState-        Realm    -> realm authenticateState openIdState+      do case url of+           (Partial u) ->+             do xml <- unXMLGenT (routePartial authenticateState openIdState u)+                ok $ toResponse (html4StrictFrag, xml)+           (BeginDance providerURL) ->+             do returnURL <- nestOpenIdURL $ showURL ReturnTo+                realm <- query' openIdState GetOpenIdRealm+                forwardURL <- liftIO $ do manager <- newManager tlsManagerSettings+                                          getForwardUrl providerURL returnURL realm [] manager -- [("Email", "http://schema.openid.net/contact/email")]+                seeOther forwardURL (toResponse ())+           ReturnTo ->+             do authenticateConfig <- liftIO $ atomically $ readTVar authenticateConfigTV+                token authenticateState authenticateConfig openIdState+           Realm    -> realm authenticateState openIdState  ------------------------------------------------------------------------------ -- initOpenId@@ -58,9 +62,9 @@  initOpenId :: FilePath            -> AcidState AuthenticateState-           -> AuthenticateConfig+           -> TVar AuthenticateConfig            -> IO (Bool -> IO (), (AuthenticationMethod, AuthenticationHandler), RouteT AuthenticateURL (ServerPartT IO) JStat)-initOpenId basePath authenticateState authenticateConfig =+initOpenId basePath authenticateState authenticateConfigTV =   do openIdState <- openLocalStateFrom (combine basePath "openId") initialOpenIdState      let shutdown = \normal ->            if normal@@ -70,6 +74,6 @@            do langsOveride <- queryString $ lookTexts' "_LANG"               langs        <- bestLanguage <$> acceptLanguage               mapRouteT (flip runReaderT (langsOveride ++ langs)) $-               routeOpenId authenticateState authenticateConfig openIdState pathSegments+               routeOpenId authenticateState authenticateConfigTV openIdState pathSegments      return (shutdown, (openIdAuthenticationMethod, authenticationHandler), openIdCtrl authenticateState openIdState) 
Happstack/Authenticate/Password/Controllers.hs view
@@ -1,22 +1,27 @@ {-# LANGUAGE QuasiQuotes #-} module Happstack.Authenticate.Password.Controllers where +import Control.Concurrent.STM               (atomically)+import Control.Concurrent.STM.TVar          (TVar, readTVar)+import Control.Monad.Trans                  (MonadIO(liftIO))+import Data.Maybe                           (isJust, fromJust) import Data.Text                            (Text) import qualified Data.Text                  as T-import Happstack.Authenticate.Core          (AuthenticateURL)+import Happstack.Authenticate.Core          (AuthenticateConfig(_postLoginRedirect), AuthenticateURL) import Happstack.Authenticate.Password.URL (PasswordURL(Account, Token, Partial, PasswordReset, PasswordRequestReset), nestPasswordURL) import Happstack.Authenticate.Password.PartialsURL (PartialURL(ChangePassword, Logout, Login, LoginInline, SignupPassword, ResetPasswordForm, RequestResetPasswordForm)) import Language.Javascript.JMacro import Web.Routes -usernamePasswordCtrl :: (Monad m) => RouteT AuthenticateURL m JStat-usernamePasswordCtrl =+usernamePasswordCtrl :: (MonadIO m) => TVar AuthenticateConfig -> RouteT AuthenticateURL m JStat+usernamePasswordCtrl authenticateConfigTV =   nestPasswordURL $     do fn <- askRouteFn-       return $ usernamePasswordCtrlJs fn+       plr <- liftIO (_postLoginRedirect <$> (atomically $ readTVar authenticateConfigTV))+       return $ usernamePasswordCtrlJs plr fn -usernamePasswordCtrlJs :: (PasswordURL -> [(Text, Maybe Text)] -> Text) -> JStat-usernamePasswordCtrlJs showURL = [jmacro|+usernamePasswordCtrlJs :: Maybe Text -> (PasswordURL -> [(Text, Maybe Text)] -> Text) -> JStat+usernamePasswordCtrlJs postLoginRedirect showURL = [jmacro|   {     var usernamePassword = angular.module('usernamePassword', ['happstackAuthentication']); @@ -38,6 +43,7 @@             if (datum.jrStatus == "Ok") {               $scope.username_password_error = '';               userService.updateFromToken(datum.jrData.token);+              `loginRedirect postLoginRedirect`             } else {               userService.clearUser();               $scope.username_password_error = datum.jrData;@@ -299,3 +305,12 @@    }   |]+  where+    loginRedirect (Just plr) =+      [jmacro|+             // console.log(`"loginRedirect - " ++ show plr`);+             window.location.href = `plr`;+             |]+    loginRedirect Nothing =+      BlockStat []+
Happstack/Authenticate/Password/Core.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP, DataKinds, DeriveDataTypeable, DeriveGeneric, FlexibleInstances, MultiParamTypeClasses, RecordWildCards, TemplateHaskell, TypeFamilies, TypeSynonymInstances, OverloadedStrings #-}+{-# LANGUAGE CPP, DataKinds, DeriveDataTypeable, DeriveGeneric, FlexibleInstances, MultiParamTypeClasses, RecordWildCards, TemplateHaskell, TypeFamilies, TypeSynonymInstances, OverloadedStrings, StandaloneDeriving #-} module Happstack.Authenticate.Password.Core where  import Control.Applicative ((<$>), optional)@@ -13,6 +13,9 @@ import qualified Data.Aeson as Aeson import Data.Aeson         (Value(..), Object(..), Result(..), decode, encode, fromJSON) import Data.Aeson.Types   (ToJSON(..), FromJSON(..), Options(fieldLabelModifier), defaultOptions, genericToJSON, genericParseJSON)+#if MIN_VERSION_aeson(2,0,0)+import qualified Data.Aeson.KeyMap as KM+#endif import Data.ByteString (ByteString) import qualified Data.ByteString.Lazy as B import Data.Data (Data, Typeable)@@ -21,7 +24,7 @@ import qualified Data.Map as Map import Data.Maybe         (fromMaybe, fromJust) import Data.Monoid        ((<>), mempty)-import Data.SafeCopy (SafeCopy, base, deriveSafeCopy)+import Data.SafeCopy (SafeCopy, Migrate(..), base, extension, deriveSafeCopy) import           Data.Text (Text) import qualified Data.Text as Text import qualified Data.Text.IO as Text@@ -30,13 +33,13 @@ import Data.Time.Clock.POSIX          (getPOSIXTime) import Data.UserId (UserId) import GHC.Generics (Generic)-import Happstack.Authenticate.Core (AuthenticationHandler, AuthenticationMethod(..), AuthenticateState(..), AuthenticateConfig, usernameAcceptable, requireEmail, AuthenticateURL, CoreError(..), CreateUser(..), Email(..), unEmail, GetUserByUsername(..), HappstackAuthenticateI18N(..), SharedSecret(..), User(..), Username(..), GetSharedSecret(..), addTokenCookie, email, getToken, getOrGenSharedSecret, jsonOptions, userId, username, toJSONSuccess, toJSONResponse, toJSONError, tokenUser)+import Happstack.Authenticate.Core (AuthenticationHandler, AuthenticationMethod(..), AuthenticateState(..), AuthenticateConfig, usernameAcceptable, requireEmail, AuthenticateURL, CoreError(..), CreateUser(..), Email(..), unEmail, GetUserByUserId(..), GetUserByUsername(..), HappstackAuthenticateI18N(..), SharedSecret(..), SimpleAddress(..), User(..), Username(..), GetSharedSecret(..), addTokenCookie, createUserCallback, email, getToken, getOrGenSharedSecret, jsonOptions, userId, username, systemFromAddress, systemReplyToAddress, systemSendmailPath, toJSONSuccess, toJSONResponse, toJSONError, tokenUser) import Happstack.Authenticate.Password.URL (AccountURL(..)) import Happstack.Server import HSP.JMacro import Language.Javascript.JMacro import Network.HTTP.Types              (toQuery, renderQuery)-import Network.Mail.Mime               (Address(..), Mail, simpleMail', renderMail', renderSendMail, sendmail)+import Network.Mail.Mime               (Address(..), Mail(..), simpleMail', renderMail', renderSendMail, renderSendMailCustom, sendmail) import System.FilePath                 (combine) import qualified Text.Email.Validate   as Email import Text.Shakespeare.I18N           (RenderMessage(..), Lang, mkMessageFor)@@ -127,7 +130,7 @@  initialPasswordState :: PasswordState initialPasswordState = PasswordState-    { _passwords = Map.empty+    { _passwords      = Map.empty     }  ------------------------------------------------------------------------------@@ -220,7 +223,11 @@                    if not valid                      then unauthorized $ toJSONError InvalidUsernamePassword                      else do token <- addTokenCookie authenticateState authenticateConfig u+#if MIN_VERSION_aeson(2,0,0)+                             resp 201 $ toJSONSuccess (Object $ KM.fromList      [("token", toJSON token)]) -- toResponseBS "application/json" $ encode $ Object $ HashMap.fromList [("token", toJSON token)]+#else                              resp 201 $ toJSONSuccess (Object $ HashMap.fromList [("token", toJSON token)]) -- toResponseBS "application/json" $ encode $ Object $ HashMap.fromList [("token", toJSON token)]+#endif  ------------------------------------------------------------------------------ -- account@@ -281,6 +288,9 @@                                     (Right user) -> do                                        hashed <- mkHashedPass (_naPassword newAccount)                                        update' passwordState (SetPassword (user ^. userId) hashed)+                                       case (authenticateConfig ^. createUserCallback) of+                                         Nothing -> pure ()+                                         (Just callback) -> liftIO $ callback user                                        ok $ (Right (user ^. userId))     where       validEmail :: Bool -> Maybe Email -> Maybe PasswordError@@ -338,11 +348,12 @@  -- | request reset password passwordRequestReset :: (Happstack m) =>-                        PasswordConfig+                        AuthenticateConfig+                     -> PasswordConfig                      -> AcidState AuthenticateState                      -> AcidState PasswordState                      -> m (Either PasswordError Text)-passwordRequestReset passwordConfig authenticateState passwordState =+passwordRequestReset authenticateConfig passwordConfig authenticateState passwordState =   do method POST      ~(Just (Body body)) <- takeRequestBody =<< askRq      case Aeson.decode body of@@ -355,28 +366,39 @@                 case user ^. email of                   Nothing -> return $ Left NoEmailAddress                   (Just toEm) ->-                    do eResetToken <- issueResetToken authenticateState user-                       case eResetToken of-                         (Left err) -> return (Left err)-                         (Right resetToken) ->-                           do let resetLink' = (passwordConfig ^. resetLink) <> (Text.decodeUtf8 $ renderQuery True $ toQuery [("reset_token"::Text, resetToken)])-                              liftIO $ Text.putStrLn resetLink' -- FIXME: don't print to stdout-                              sendResetEmail toEm (Email ("no-reply@" <> (passwordConfig ^. domain))) resetLink'-                              return (Right "password reset request email sent.") -- FIXME: I18N+                    do resetToken <- issueResetToken authenticateState user+                       let resetLink' = resetTokenLink (passwordConfig ^. resetLink) resetToken+                       -- liftIO $ Text.putStrLn resetLink' -- FIXME: don't print to stdout+                       let from = fromMaybe (SimpleAddress Nothing (Email ("no-reply@" <> (passwordConfig ^. domain)))) (authenticateConfig ^. systemFromAddress)+                       sendResetEmail (authenticateConfig ^. systemSendmailPath) toEm from (authenticateConfig ^. systemReplyToAddress) resetLink'+                       return (Right "password reset request email sent.") -- FIXME: I18N +-- | generate a reset token for a UserId+resetTokenForUserId :: Text -> AcidState AuthenticateState -> AcidState PasswordState -> UserId -> IO (Either PasswordError Text)+resetTokenForUserId resetLink authenticateState passwordState userId =+  do mUser <- query' authenticateState (GetUserByUserId userId)+     case mUser of+       Nothing     -> pure $ Left (CoreError InvalidUserId)+       (Just user) ->+         do resetToken <- issueResetToken authenticateState user+            pure $ Right $ resetTokenLink resetLink resetToken++-- | create a link for a reset token+resetTokenLink :: Text -- ^ base URI+               -> Text -- ^ reset token+               -> Text+resetTokenLink baseURI resetToken = baseURI <> (Text.decodeUtf8 $ renderQuery True $ toQuery [("reset_token"::Text, resetToken)])+ -- | issueResetToken issueResetToken :: (MonadIO m) =>                    AcidState AuthenticateState                 -> User-                -> m (Either PasswordError Text)+                -> m Text issueResetToken authenticateState user =-  case user ^. email of-    Nothing     -> return (Left NoEmailAddress)-    (Just addr) ->-      do ssecret <- getOrGenSharedSecret authenticateState (user ^. userId)-         -- FIXME: add expiration time-         now <- liftIO getPOSIXTime-         let claims = JWT.JWTClaimsSet+  do ssecret <- getOrGenSharedSecret authenticateState (user ^. userId)+     -- FIXME: add expiration time+     now <- liftIO getPOSIXTime+     let claims = JWT.JWTClaimsSet                         { JWT.iss = Nothing                         , JWT.sub = Nothing                         , JWT.aud = Nothing@@ -391,24 +413,32 @@                                Map.singleton "reset-password" (toJSON user)                         } #if MIN_VERSION_jwt(0,10,0)-         return $ Right $ encodeSigned (hmacSecret $ _unSharedSecret ssecret) mempty claims+     return $ encodeSigned (hmacSecret $ _unSharedSecret ssecret) mempty claims #elif MIN_VERSION_jwt(0,9,0)-         return $ Right $ encodeSigned (hmacSecret $ _unSharedSecret ssecret) claims+     return $ encodeSigned (hmacSecret $ _unSharedSecret ssecret) claims #else-         return $ Right $ encodeSigned HS256 (secret $ _unSharedSecret ssecret) claims+     return $ encodeSigned HS256 (secret $ _unSharedSecret ssecret) claims #endif  -- FIXME: I18N -- FIXME: call renderSendMail sendResetEmail :: (MonadIO m) =>-                  Email+                  Maybe FilePath                -> Email+               -> SimpleAddress+               -> Maybe SimpleAddress                -> Text                -> m ()-sendResetEmail (Email toEm) (Email fromEm) resetLink = liftIO $-  do mailBS <- renderMail' $ simpleMail' (Address Nothing toEm)  (Address (Just "no-reply") fromEm) "Reset Password Request" (LT.fromStrict resetLink)-     -- B.putStr mailBS-     sendmail mailBS+sendResetEmail mSendmailPath (Email toEm) (SimpleAddress fromNm (Email fromEm)) mReplyTo resetLink = liftIO $+  do let mail = addReplyTo mReplyTo $ simpleMail' (Address Nothing toEm)  (Address fromNm fromEm) "Reset Password Request" (LT.fromStrict resetLink)+     case mSendmailPath of+       Nothing -> renderSendMail mail+       (Just sendmailPath) -> renderSendMailCustom sendmailPath ["-t"] mail+  where+    addReplyTo :: Maybe SimpleAddress -> Mail -> Mail+    addReplyTo Nothing m = m+    addReplyTo (Just (SimpleAddress rplyToNm rplyToEm)) m =+      let m' = m { mailHeaders = (mailHeaders m) } in m'  -- | JSON record for new account data data ResetPasswordData = ResetPasswordData@@ -487,7 +517,9 @@                     case mssecret of                       Nothing -> return Nothing                       (Just ssecret) ->-#if MIN_VERSION_jwt(0,8,0)+#if MIN_VERSION_jwt(0,11,0)+                        case verify (JWT.toVerify $ hmacSecret (_unSharedSecret ssecret)) unverified of+#elif MIN_VERSION_jwt(0,8,0)                         case verify (hmacSecret (_unSharedSecret ssecret)) unverified of #else                         case verify (secret (_unSharedSecret ssecret)) unverified of
Happstack/Authenticate/Password/Route.hs view
@@ -2,6 +2,9 @@  import Control.Applicative   ((<$>)) import Control.Monad.Reader  (ReaderT, runReaderT)+import Control.Monad.Trans   (MonadIO(liftIO))+import Control.Concurrent.STM      (atomically)+import Control.Concurrent.STM.TVar (TVar, newTVar, readTVar) import Data.Acid             (AcidState, closeAcidState, makeAcidic) import Data.Acid.Local       (createCheckpointAndClose, openLocalStateFrom) import Data.Text             (Text)@@ -25,24 +28,26 @@ ------------------------------------------------------------------------------  routePassword :: (Happstack m) =>-                 PasswordConfig+                 TVar PasswordConfig               -> AcidState AuthenticateState-              -> AuthenticateConfig+              -> TVar AuthenticateConfig               -> AcidState PasswordState               -> [Text]               -> RouteT AuthenticateURL (ReaderT [Lang] m) Response-routePassword passwordConfig authenticateState authenticateConfig passwordState pathSegments =+routePassword passwordConfigTV authenticateState authenticateConfigTV passwordState pathSegments =   case parseSegments fromPathSegments pathSegments of     (Left _) -> notFound $ toJSONError URLDecodeFailed     (Right url) ->-      case url of-        Token        -> token authenticateState authenticateConfig passwordState-        Account mUrl -> toJSONResponse <$> account authenticateState passwordState authenticateConfig passwordConfig mUrl-        (Partial u)  -> do xml <- unXMLGenT (routePartial authenticateState u)-                           return $ toResponse (html4StrictFrag, xml)-        PasswordRequestReset -> toJSONResponse <$> passwordRequestReset passwordConfig authenticateState passwordState-        PasswordReset        -> toJSONResponse <$> passwordReset authenticateState passwordState passwordConfig-        UsernamePasswordCtrl -> toResponse <$> usernamePasswordCtrl+      do authenticateConfig <- liftIO $ atomically $ readTVar authenticateConfigTV+         passwordConfig     <- liftIO $ atomically $ readTVar passwordConfigTV+         case url of+           Token        -> token authenticateState authenticateConfig passwordState+           Account mUrl -> toJSONResponse <$> account authenticateState passwordState authenticateConfig passwordConfig mUrl+           (Partial u)  -> do xml <- unXMLGenT (routePartial authenticateState u)+                              return $ toResponse (html4StrictFrag, xml)+           PasswordRequestReset -> toJSONResponse <$> passwordRequestReset authenticateConfig passwordConfig authenticateState passwordState+           PasswordReset        -> toJSONResponse <$> passwordReset authenticateState passwordState passwordConfig+           UsernamePasswordCtrl -> toResponse <$> usernamePasswordCtrl authenticateConfigTV  ------------------------------------------------------------------------------ -- initPassword@@ -51,17 +56,27 @@ initPassword :: PasswordConfig              -> FilePath              -> AcidState AuthenticateState-             -> AuthenticateConfig+             -> TVar AuthenticateConfig              -> IO (Bool -> IO (), (AuthenticationMethod, AuthenticationHandler), RouteT AuthenticateURL (ServerPartT IO) JStat)-initPassword passwordConfig basePath authenticateState authenticateConfig =+initPassword passwordConfig basePath authenticateState authenticateConfigTV =   do passwordState <- openLocalStateFrom (combine basePath "password") initialPasswordState-     let shutdown = \normal ->-           if normal-           then createCheckpointAndClose passwordState-           else closeAcidState passwordState-         authenticationHandler pathSegments =-           do langsOveride <- queryString $ lookTexts' "_LANG"-              langs        <- bestLanguage <$> acceptLanguage-              mapRouteT (flip runReaderT (langsOveride ++ langs)) $-               routePassword passwordConfig authenticateState authenticateConfig passwordState pathSegments-     return (shutdown, (passwordAuthenticationMethod, authenticationHandler), usernamePasswordCtrl)+     passwordConfigTV <- atomically $ newTVar passwordConfig+     initPassword' passwordConfigTV passwordState basePath authenticateState authenticateConfigTV++initPassword' :: TVar PasswordConfig+              -> AcidState PasswordState+              -> FilePath+              -> AcidState AuthenticateState+              -> TVar AuthenticateConfig+              -> IO (Bool -> IO (), (AuthenticationMethod, AuthenticationHandler), RouteT AuthenticateURL (ServerPartT IO) JStat)+initPassword' passwordConfigTV passwordState basePath authenticateState authenticateConfigTV =+     do let shutdown = \normal ->+              if normal+              then createCheckpointAndClose passwordState+              else closeAcidState passwordState+            authenticationHandler pathSegments =+              do langsOveride <- queryString $ lookTexts' "_LANG"+                 langs        <- bestLanguage <$> acceptLanguage+                 mapRouteT (flip runReaderT (langsOveride ++ langs)) $+                   routePassword passwordConfigTV authenticateState authenticateConfigTV passwordState pathSegments+        pure (shutdown, (passwordAuthenticationMethod, authenticationHandler), usernamePasswordCtrl authenticateConfigTV)
Happstack/Authenticate/Route.hs view
@@ -2,6 +2,8 @@ module Happstack.Authenticate.Route where  import Control.Applicative ((<$>))+import Control.Concurrent.STM (atomically)+import Control.Concurrent.STM.TVar (TVar, newTVar) import Control.Monad.Trans (MonadIO(liftIO)) import Data.Acid (AcidState) import Data.Acid.Local (openLocalStateFrom, createCheckpointAndClose)@@ -47,16 +49,17 @@ initAuthentication   :: Maybe FilePath   -> AuthenticateConfig-  -> [FilePath -> AcidState AuthenticateState -> AuthenticateConfig -> IO (Bool -> IO (), (AuthenticationMethod, AuthenticationHandler), RouteT AuthenticateURL (ServerPartT IO) JStat)]-  -> IO (IO (), AuthenticateURL -> RouteT AuthenticateURL (ServerPartT IO) Response, AcidState AuthenticateState)+  -> [FilePath -> AcidState AuthenticateState -> TVar AuthenticateConfig -> IO (Bool -> IO (), (AuthenticationMethod, AuthenticationHandler), RouteT AuthenticateURL (ServerPartT IO) JStat)]+  -> IO (IO (), AuthenticateURL -> RouteT AuthenticateURL (ServerPartT IO) Response, AcidState AuthenticateState, TVar AuthenticateConfig) initAuthentication mBasePath authenticateConfig initMethods =   do let authenticatePath = combine (fromMaybe "state" mBasePath) "authenticate"      authenticateState <- openLocalStateFrom (combine authenticatePath "core") initialAuthenticateState+     authenticateConfigTV <- atomically $ newTVar authenticateConfig      -- FIXME: need to deal with one of the initMethods throwing an exception-     (cleanupPartial, handlers, javascript) <- unzip3 <$> mapM (\initMethod -> initMethod authenticatePath authenticateState authenticateConfig) initMethods+     (cleanupPartial, handlers, javascript) <- unzip3 <$> mapM (\initMethod -> initMethod authenticatePath authenticateState authenticateConfigTV) initMethods      let cleanup = sequence_ $ createCheckpointAndClose authenticateState : (map (\c -> c True) cleanupPartial)          h       = route javascript (Map.fromList handlers)-     return (cleanup, h, authenticateState)+     return (cleanup, h, authenticateState, authenticateConfigTV)  instance (Functor m, MonadIO m) => IntegerSupply (RouteT AuthenticateURL m) where  nextInteger =
happstack-authenticate.cabal view
@@ -1,5 +1,5 @@ Name:                happstack-authenticate-Version:             2.3.4.16+Version:             2.6.1 Synopsis:            Happstack Authentication Library Description:         A themeable authentication library with support for username+password and OpenId. Homepage:            http://www.happstack.com/@@ -10,8 +10,8 @@ Copyright:           2011-2015 SeeReason Partners, LLC Category:            Web Build-type:          Simple-Cabal-version:       >=1.8-tested-with:         GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.1, GHC == 8.2.2, GHC == 8.4.1, GHC == 8.6.3+Cabal-version:       >=1.10+tested-with:         GHC==8.0.2, GHC==8.2.2, GHC==8.4.4, GHC==8.6.5, GHC==8.8.4, GHC==8.10.7, GHC==9.0.2, GHC==9.2.2 data-files:   messages/core/en.msg   messages/openid/error/en.msg@@ -24,6 +24,7 @@     location: https://github.com/Happstack/happstack-authenticate.git  Library+  Default-language:    Haskell2010   Exposed-modules:     Happstack.Authenticate.Core                        Happstack.Authenticate.Controller                        Happstack.Authenticate.Route@@ -42,36 +43,37 @@     Build-depends:       base                         > 4     && < 5,-                       acid-state                   >= 0.6  && < 0.16,-                       aeson                        (>= 0.4  && < 0.10) || (>= 0.11 && < 1.5),+                       acid-state                   >= 0.6  && < 0.17,+                       aeson                        (>= 0.4  && < 0.10) || (>= 0.11 && < 1.6) || (>= 2.0 && < 2.1),                        authenticate                 == 1.3.*,-                       base64-bytestring            >= 1.0  && < 1.1,+                       base64-bytestring            >= 1.0  && < 1.3,                        boomerang                    >= 1.4  && < 1.5,-                       bytestring                   >= 0.9  && < 0.11,+                       bytestring                   >= 0.9  && < 0.12,                        containers                   >= 0.4  && < 0.7,                        data-default                 >= 0.5  && < 0.8,                        email-validate               >= 2.1  && < 2.4,                        filepath                     >= 1.3  && < 1.5,                        hsx2hs                       >= 0.13 && < 0.15,                        jmacro                       >= 0.6.11  && < 0.7,-                       jwt                          >= 0.3  && < 0.11,-                       ixset-typed                  >= 0.3  && < 0.5,+                       jwt                          >= 0.3  && < 0.12,+                       ixset-typed                  >= 0.3  && < 0.6,                        happstack-jmacro             >= 7.0  && < 7.1,-                       happstack-server             >= 6.0  && < 7.6,+                       happstack-server             >= 6.0  && < 7.8,                        happstack-hsp                >= 7.3  && < 7.4,                        http-conduit                 >= 2.1.0 && < 2.4,                        http-types                   >= 0.6  && < 0.13,                        hsp                          >= 0.10 && < 0.11,                        hsx-jmacro                   >= 7.3  && < 7.4,-                       safecopy                     >= 0.8  && < 0.10,+                       safecopy                     >= 0.8  && < 0.11,                        mime-mail                    >= 0.4  && < 0.6,                        mtl                          >= 2.0  && < 2.3,-                       lens                         >= 4.2  && < 4.18,+                       lens                         >= 4.2  && < 5.2,                        pwstore-purehaskell          == 2.1.*,-                       text                         >= 0.11 && < 1.3,-                       time                         >= 1.2  && < 1.10,+                       stm                          >= 2.4  && < 2.6,+                       text                         >= 0.11 && < 2.1,+                       time                         >= 1.2  && < 1.14,                        userid                       >= 0.1  && < 0.2,-                       random                       >= 1.0  && < 1.2,+                       random                       >= 1.0  && < 1.3,                        shakespeare                  >= 2.0  && < 2.1,                        unordered-containers         == 0.2.*,                        web-routes                   >= 0.26 && < 0.28,
messages/openid/partials/en.msg view
@@ -1,4 +1,3 @@-UsingGoogleMsg: Google OpenId UsingYahooMsg: Yahoo OpenId SetRealmMsg: Update OpenId Realm OpenIdRealmMsg: OpenId Realm