|
|
|
|
@ -13,6 +13,7 @@
|
|
|
|
|
, AllowAmbiguousTypes
|
|
|
|
|
, LambdaCase
|
|
|
|
|
, FlexibleContexts
|
|
|
|
|
, KindSignatures
|
|
|
|
|
#-}
|
|
|
|
|
|
|
|
|
|
module Server
|
|
|
|
|
@ -35,19 +36,19 @@ import Control.Concurrent
|
|
|
|
|
import Control.Concurrent.STM (atomically)
|
|
|
|
|
import Control.Concurrent.STM.TVar (newTVarIO, readTVar, modifyTVar)
|
|
|
|
|
import Control.Exception (bracket)
|
|
|
|
|
import Control.Monad (unless, (>=>), foldM)
|
|
|
|
|
import Control.Monad (unless, (>=>), foldM, void)
|
|
|
|
|
import Control.Monad.IO.Class
|
|
|
|
|
import Control.Monad.Trans.Error (Error(..))
|
|
|
|
|
import Control.Monad.Trans.Reader
|
|
|
|
|
|
|
|
|
|
import Data.Aeson
|
|
|
|
|
import Data.ByteString (fromStrict, ByteString)
|
|
|
|
|
import Data.ByteString (fromStrict, toStrict, ByteString)
|
|
|
|
|
import Data.List (find, elemIndex)
|
|
|
|
|
import Data.Maybe (fromMaybe, fromJust, isJust, isNothing)
|
|
|
|
|
import Data.String (IsString (..))
|
|
|
|
|
import Data.Text hiding (elem, find, head, length, map, null, splitAt, tail, words)
|
|
|
|
|
import qualified Data.Text as T
|
|
|
|
|
import Data.Text.Encoding (encodeUtf8)
|
|
|
|
|
import Data.Text.Encoding (encodeUtf8, decodeUtf8)
|
|
|
|
|
import Data.Text.Encoding.Base64
|
|
|
|
|
import Data.Time.Clock (NominalDiffTime (..), nominalDay, UTCTime(..), getCurrentTime, addUTCTime)
|
|
|
|
|
|
|
|
|
|
@ -58,20 +59,22 @@ import GHC.Read (readPrec, lexP)
|
|
|
|
|
import Jose.Jwk (generateRsaKeyPair, KeyUse(..), KeyId)
|
|
|
|
|
import Jose.Jwt hiding (decode, encode)
|
|
|
|
|
|
|
|
|
|
import Network.HTTP.Client (newManager, defaultManagerSettings)
|
|
|
|
|
import Network.HTTP.Client (newManager, defaultManagerSettings, httpLbs, parseRequest)
|
|
|
|
|
import Network.Wai.Handler.Warp
|
|
|
|
|
|
|
|
|
|
import Servant
|
|
|
|
|
import Servant.Client hiding (client)
|
|
|
|
|
import Servant.API
|
|
|
|
|
|
|
|
|
|
import System.Environment (getEnv)
|
|
|
|
|
|
|
|
|
|
import Text.ParserCombinators.ReadPrec (look, pfail)
|
|
|
|
|
import Text.Read (readMaybe)
|
|
|
|
|
|
|
|
|
|
import qualified Text.Read.Lex as Lex
|
|
|
|
|
|
|
|
|
|
import Web.Cookie (parseCookiesText)
|
|
|
|
|
import Web.Internal.FormUrlEncoded (FromForm(..), parseUnique, parseMaybe, Form(..))
|
|
|
|
|
import Web.Internal.FormUrlEncoded (FromForm(..), parseUnique, parseMaybe, Form(..), urlEncodeParams)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -364,23 +367,66 @@ userListEndpoint = handleUserData
|
|
|
|
|
---- Logout ----
|
|
|
|
|
--------------
|
|
|
|
|
|
|
|
|
|
{-type Redirect (cs :: [*]) (hs :: [*]) a = Verb 'GET 303 cs (Headers (Header "Location" Text : hs) a)
|
|
|
|
|
|
|
|
|
|
type CookieLogout = "clogout"
|
|
|
|
|
:> QParam "redirect" QRedirect
|
|
|
|
|
:> Redirect '[HTML] '[] Text
|
|
|
|
|
clogoutEndpoint :: forall user userData . UserData user userData => AuthServer user CookieLogout
|
|
|
|
|
clogoutEndpoint uri = do
|
|
|
|
|
return $ addHeader uri ""-}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type Logout = "logout"
|
|
|
|
|
:> QueryParam "post_logout_redirect_uri" QRedirect
|
|
|
|
|
:> HeaderR "Cookie" QCookie
|
|
|
|
|
:> Get '[HTML] (Headers '[Header "Set-Cookie" Text] Html)
|
|
|
|
|
{- :> Redirect '[HTML] '[Header "Set-Cookie" Text] NoContent
|
|
|
|
|
:<|> "logout"
|
|
|
|
|
:> HeaderR "Cookie" QCookie
|
|
|
|
|
:> Get '[HTML] (Headers '[Header "Set-Cookie" Text] Html) -}
|
|
|
|
|
|
|
|
|
|
logoutEndpoint :: forall user userData . UserData user userData => AuthServer user Logout
|
|
|
|
|
logoutEndpoint = logout
|
|
|
|
|
logoutEndpoint = logout -- rLogout :<|> logout
|
|
|
|
|
where
|
|
|
|
|
logout :: Maybe QRedirect
|
|
|
|
|
-> QCookie
|
|
|
|
|
-> AuthHandler user (Headers '[Header "Set-Cookie" Text] Html)
|
|
|
|
|
logout mUri cookie = do
|
|
|
|
|
logout mRedir cookie = do
|
|
|
|
|
let mCreds = lookup authCookie . parseCookiesText $ encodeUtf8 cookie
|
|
|
|
|
unless (isJust mCreds) $ throwError err401 { errBody = "Missing auth cookie" }
|
|
|
|
|
addHeader (authCookie <> "=\"\"") <$> case mUri of
|
|
|
|
|
Just uri -> throwError err303 { errHeaders = [("Location", encodeUtf8 uri)]}
|
|
|
|
|
Nothing -> return logoutPage
|
|
|
|
|
liftIO . putStrLn $ "\nLOGOUT\n "
|
|
|
|
|
return . addHeader (authCookie <> "=\"\"") $ logoutPage mRedir
|
|
|
|
|
|
|
|
|
|
{- checkCookie :: QCookie -> AuthHandler user ()
|
|
|
|
|
checkCookie cookie = do
|
|
|
|
|
let mCreds = lookup authCookie . parseCookiesText $ encodeUtf8 cookie
|
|
|
|
|
unless (isJust mCreds) $ throwError err401 { errBody = "Missing auth cookie" }
|
|
|
|
|
return ()
|
|
|
|
|
|
|
|
|
|
rLogout :: QRedirect
|
|
|
|
|
-> QCookie
|
|
|
|
|
-> AuthHandler user (Headers '[ Header "Location" Text
|
|
|
|
|
, Header "Set-Cookie" Text
|
|
|
|
|
] NoContent)
|
|
|
|
|
rLogout uri cookie = do
|
|
|
|
|
liftIO . putStrLn $ "\nLOGOUT with uri " <> show uri <> "\n"
|
|
|
|
|
checkCookie cookie
|
|
|
|
|
let param = decodeUtf8 . toStrict $ urlEncodeParams [("redirect", uri)]
|
|
|
|
|
uri' = "../clogout?" <> param
|
|
|
|
|
addHeader uri' . addHeader (authCookie <> "=\"\"") <$> return NoContent
|
|
|
|
|
|
|
|
|
|
logout :: QCookie
|
|
|
|
|
-> AuthHandler user (Headers '[Header "Set-Cookie" Text] Html)
|
|
|
|
|
logout cookie = do
|
|
|
|
|
liftIO . putStrLn $ "\nLOGOUT\n "
|
|
|
|
|
checkCookie cookie
|
|
|
|
|
return . addHeader (authCookie <> "=\"\"")$ logoutPage Nothing
|
|
|
|
|
-- liftIO $ do
|
|
|
|
|
-- port <- getEnv "OAUTH2_SERVER_PORT"
|
|
|
|
|
-- manager <- newManager defaultManagerSettings
|
|
|
|
|
-- req <- parseRequest $ "GET http://localhost:" ++ port ++ "/clogout" -- TODO get root
|
|
|
|
|
-- void $ httpLbs req manager -}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-------------------
|
|
|
|
|
@ -392,6 +438,7 @@ type Routing user userData = Auth
|
|
|
|
|
:<|> Token
|
|
|
|
|
:<|> Me userData
|
|
|
|
|
:<|> UserList userData
|
|
|
|
|
-- :<|> CookieLogout
|
|
|
|
|
:<|> Logout
|
|
|
|
|
|
|
|
|
|
routing :: forall user userData . UserData user userData => AuthServer user (Routing user userData)
|
|
|
|
|
@ -400,6 +447,7 @@ routing = loginServer @user @userData
|
|
|
|
|
:<|> tokenEndpoint @user @userData
|
|
|
|
|
:<|> userEndpoint @user @userData
|
|
|
|
|
:<|> userListEndpoint @user @userData
|
|
|
|
|
-- :<|> clogoutEndpoint @user @userData
|
|
|
|
|
:<|> logoutEndpoint @user @userData
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|