on master help sign in
Resolved #10.

Resolved #4.

commited66c4386eb48ddfd2147fd7374289fc18f1e0e2 authoriko <ilyakooo0@gmail.com> dateFri 8 Mar 2019, 00:01 (8 years ago) parenta91dcd7
6 files changed+58−26
denis/docs/Docs.hs+9−3 denis/src/Data/Connection.hs+9−1 denis/src/Server/API.hs+8−5 denis/src/Server/API/Posts.hs−1 denis/src/Server/Auth.hs+31−15 denis/src/Server/Error.hs+1−1
denis/docs/Docs.hs+9−3
@@ -6,8 +6,6 @@
66
77import Server.API
88import Servant.Docs
9−import Servant.API
10−import Data.Post
119import qualified Data.Text as T
1210import Servant.Server
1311import Servant
@@ -20,13 +18,16 @@ import Data.Int (Int64)
2018import Data.Word (Word64)
2119import Server.API.Posts
2220import Server.Logger
21+import Server.Auth
2322
2423docsWriter :: HasDocs api => String -> Proxy api -> IO ()
25−docsWriter file = writeFile ("docs/" ++ file ++ ".md") . markdown . docs
24+docsWriter file = writeFile ("docs/" ++ file ++ ".md") . normalizer . markdown . docs
2625
2726main = do
2827 docsWriter "posts" (Proxy :: Proxy ("posts" :> PostApi))
2928 docsWriter "all" serverProxy
29+ docsWriter "authentication" (Proxy :: Proxy ("authentication" :> AuthenticationHandler :<|>
30+ Authentication :> "authentication" :> "me" :> Post '[JSON] UserId ))
3031
3132instance ToSample Char where
3233 toSamples _ = singleSample 'c'
@@ -39,3 +40,8 @@ instance ToSample Word64 where
3940
4041instance ToSample Log where
4142 toSamples _ = noSamples
43+
44+normalizer :: String -> String
45+normalizer (' ':' ':' ':' ':'`':'`':'`':xs) = "```" ++ normalizer xs
46+normalizer (x:xs) = x : normalizer xs
47+normalizer "" = ""
denis/src/Data/Connection.hs+9−1
@@ -13,7 +13,8 @@ module Data.Connection (
1313 runQ',
1414 runQnotFound,
1515 runQerror,
16− maybeNotFound
16+ maybeNotFound,
17+ maybeInvalidToken
1718) where
1819
1920import Squeal.PostgreSQL
@@ -23,6 +24,7 @@ import Server.App
2324import Control.Monad.Reader
2425import Control.Monad.Except
2526import Servant.Server
27+import Server.Error
2628
2729type StaticPQ = PoolPQ Schema Handler
2830
@@ -43,3 +45,9 @@ maybeNotFound :: App (Maybe a) -> App a
4345maybeNotFound = (>>= (\t -> case t of
4446 Just y -> return y
4547 Nothing -> throwError err404))
48+
49+maybeInvalidToken :: App (Maybe a) -> App a
50+maybeInvalidToken = (>>= (\t -> case t of
51+ Just y -> return y
52+ Nothing -> throwError invalidToken))
53+
denis/src/Server/API.hs+8−5
@@ -9,7 +9,8 @@
99module Server.API (
1010 API,
1111 serverProxy,
12− mkServerAPI
12+ mkServerAPI,
13+ Authentication
1314) where
1415
1516import Data.Proxy
@@ -30,7 +31,7 @@ import Control.Lens
3031
3132instance HasDocs api => HasDocs (Authentication :> api) where
3233 docsFor Proxy (endpoint, action) =
33− docsFor (Proxy :: Proxy api) (endpoint, action & notes <>~ [DocNote "Authentication" ["This method requires cookies set in the `POST /authenticate` method.\n\nReturns error `401 Unauthorized` if the cookies are invalid or don't premit access to the requested data."]])
34+ docsFor (Proxy :: Proxy api) (endpoint, action & notes <>~ [DocNote "Authentication" ["This method requires cookies set in the `POST /authenticate` method.\n\nReturns error `498 Invalid Token` if the token is invalid or the token header is missing. Returns `401 Unathorized` if the token doesn't premit access to the requested data."]])
3435
3536
3637-- MARK: Implementation
@@ -39,8 +40,9 @@ type Authentication = AuthProtect "basicAuth"
3940
4041type API =
4142 LoggerAPI :<|>
42− "authenticate" :> AuthenticationHandler :<|>
43− Authentication :> (
43+ "authentication" :> AuthenticationHandler :<|>
44+ Authentication :> (
45+ "authentication" :> "me" :> Post '[JSON] UserId :<|>
4446 "users" :> ReqBody '[JSON] [UserId] :> Post '[JSON] [User] :<|>
4547 "posts" :> PostApi
4648 )
@@ -51,7 +53,8 @@ serverProxy = Proxy
5153mkServerAPI :: Logger -> ServerT API App
5254mkServerAPI l =
5355 l :<|>
54− authenticate :<|> (\uId ->
56+ authenticationAPI :<|> (\uId ->
57+ return uId :<|>
5558 maybeNotFound . runQnotFound . getUsers :<|>
5659 postApi uId
5760 )
denis/src/Server/API/Posts.hs−1
@@ -20,7 +20,6 @@ import Servant.Server
2020import Data.Query
2121import Data.User
2222import Data.Connection
23−import Servant
2423import Data.Word (Word64)
2524import Servant.Docs (ToSample, toSamples, samples)
2625
denis/src/Server/Auth.hs+31−15
@@ -14,9 +14,10 @@ module Server.Auth (
1414 cookieTokenKey,
1515 genAuthServerContext,
1616 AuthenticationHandler,
17− authenticate,
17+ logIn,
1818 contextProxy,
19− AuthenticationCredits(..)
19+ AuthenticationCredits(..),
20+ authenticationAPI
2021) where
2122
2223import Server.App
@@ -35,6 +36,7 @@ import Network.Wai (Request, requestHeaders)
3536import Servant.Server
3637import Web.Cookie
3738import Data.Time.Clock
39+import Data.Time.Calendar
3840import Data.Proxy
3941import qualified GHC.Generics as GHC
4042import Data.Aeson
@@ -48,27 +50,41 @@ instance ToSample AuthenticationCredits where
4850
4951-- MARK: Implementation
5052
51−type AuthenticationHandler = ReqBody '[JSON] AuthenticationCredits :> PostNoContent '[JSON, PlainText, FormUrlEncoded] (Headers '[Header "Set-Cookie" String] NoContent)
53+type AuthenticationHandler =
54+ "login" :>
55+ ReqBody '[JSON] AuthenticationCredits :>
56+ Post '[JSON] (Headers '[Header "Set-Cookie" String] UserId) :<|>
57+ "logout" :>
58+ PostNoContent '[JSON, PlainText, FormUrlEncoded] (Headers '[Header "Set-Cookie" String] NoContent)
5259
53−data AuthenticationCredits = AuthenticationCredits {authenticationId :: UserId}
60+newtype AuthenticationCredits = AuthenticationCredits {authenticationId :: UserId}
5461 deriving GHC.Generic
5562
5663instance ToJSON AuthenticationCredits
5764instance FromJSON AuthenticationCredits
5865
59−authenticate :: ServerT AuthenticationHandler App
60−authenticate (AuthenticationCredits uId) = do
66+authenticationAPI :: ServerT AuthenticationHandler App
67+authenticationAPI = logIn :<|> logOut
68+
69+logIn :: AuthenticationCredits -> App (Headers '[Header "Set-Cookie" String] UserId)
70+logIn (AuthenticationCredits uId) = do
6171 expire <- liftIO $ fmap (addUTCTime (1 * 24 * 60 * 60)) getCurrentTime -- one days
62− tId <- fmap userId . runQ invalidToken . getUser $ uId
72+ tId <- fmap userId . runQ err404 . getUser $ uId
6373 let cookie = defaultSetCookie {
6474 setCookieName = cookieTokenKey,
6575 setCookieValue = C.pack $ show tId,
6676 setCookieExpires = Just expire,
67− -- setCookieDomain = Just "127.0.0.1",
6877 setCookiePath = Just "/" }
69− -- let token = cookieTokenKeyString <> "=" <> show tId <> "; Expires=" <> show expire <> "; Domain=127.0.0.1:2000; Path=/" -- ; Secure" --; HttpOnly"
70− return $ addHeader (L.unpack . toLazyByteString . renderSetCookie $ cookie) NoContent
78+ return $ addHeader (L.unpack . toLazyByteString . renderSetCookie $ cookie) tId
7179
80+logOut :: App (Headers '[Header "Set-Cookie" String] NoContent)
81+logOut =
82+ let cookie = defaultSetCookie {
83+ setCookieName = cookieTokenKey,
84+ setCookieValue = "invalid",
85+ setCookieExpires = Just (UTCTime (ModifiedJulianDay 0) 0),
86+ setCookiePath = Just "/" }
87+ in return $ addHeader (L.unpack . toLazyByteString . renderSetCookie $ cookie) NoContent
7288
7389checkToken :: DBConnection -> BS.ByteString -> Handler UserId
7490checkToken conn token = do
@@ -80,11 +96,11 @@ checkToken conn token = do
8096authHandler :: DBConnection -> AuthHandler Request UserId
8197authHandler conn = mkAuthHandler handler
8298 where
83− maybeToEither e = maybe (Left e) Right
84− throw401 msg = throwError $ err401 { errBody = msg }
85− handler req = either throw401 (checkToken conn) $ do
86− cookie <- maybeToEither "Missing cookie header" $ lookup "cookie" $ requestHeaders req
87− maybeToEither "Missing token in cookie" $ lookup cookieTokenKey $ parseCookies cookie
99+ maybeToEither = maybe (Left ()) Right
100+ throw = const $ throwError invalidToken
101+ handler req = either throw (checkToken conn) $ do
102+ cookie <- maybeToEither $ lookup "cookie" $ requestHeaders req
103+ maybeToEither $ lookup cookieTokenKey $ parseCookies cookie
88104
89105cookieTokenKey :: BS.ByteString
90106cookieTokenKey = "servant-auth-cookie"
denis/src/Server/Error.hs+1−1
@@ -8,7 +8,7 @@ import Servant.Server
88
99invalidToken :: ServantErr
1010invalidToken = ServantErr {
11− errHTTPCode = 401,
11+ errHTTPCode = 498,
1212 errReasonPhrase = "Invalid Token",
1313 errBody = "The supplied token is invalid",
1414 errHeaders = []