@@ -52,6 +52,8 @@ import Data.Maybe
52import Server.Auth.Token
52import Server.Auth.Token
53import Server.Auth.Mail
53import Server.Auth.Mail
54import Data.Int
54import Data.Int
56+import qualified Data.Text.Lazy as TL
56-- MARK: Documentation
58-- MARK: Documentation
@@ -80,6 +82,7 @@ type RegisterDescription = Description "Register the user for a new account.\n\n
80type AuthenticationHandler =
82type AuthenticationHandler =
81 "login" :> LoginDescription :>
83 "login" :> LoginDescription :>
82 ReqBody '[JSON] AuthenticationCredits :>
84 ReqBody '[JSON] AuthenticationCredits :>
85+ Header "User-Agent" String :>
83 Post '[JSON] (Headers '[Header "Set-Cookie" String] UserLoginResponse) :<|>
86 Post '[JSON] (Headers '[Header "Set-Cookie" String] UserLoginResponse) :<|>
84 "logout" :> LogoutDescription
87 "logout" :> LogoutDescription
85 :> Header "cookie" String
88 :> Header "cookie" String
@@ -87,7 +90,9 @@ type AuthenticationHandler =
87 "verify" :> VerifyDescription :>
90 "verify" :> VerifyDescription :>
88 Header "cookie" String :> ReqBody '[JSON] Int32 :> PostNoContent '[JSON, PlainText, FormUrlEncoded] NoContent :<|>
91 Header "cookie" String :> ReqBody '[JSON] Int32 :> PostNoContent '[JSON, PlainText, FormUrlEncoded] NoContent :<|>
89 "register" :> RegisterDescription :>
92 "register" :> RegisterDescription :>
93+ ReqBody '[JSON] UserCreation :>
94+ Header "User-Agent" String :>
90− ReqBody '[JSON] UserCreation :> PostNoContent '[JSON, PlainText, FormUrlEncoded] (Headers '[Header "Set-Cookie" String] NoContent)
95+ PostNoContent '[JSON, PlainText, FormUrlEncoded] (Headers '[Header "Set-Cookie" String] NoContent)
92data UserResponseStatus = UserIsRegistered | UserCanRegister | InvalidUser
97data UserResponseStatus = UserIsRegistered | UserCanRegister | InvalidUser
93 deriving (Show)
98 deriving (Show)
@@ -121,8 +126,8 @@ newtype AuthenticationCredits = AuthenticationCredits {authenticationEmail :: Us
121authenticationAPI :: ServerT AuthenticationHandler App
126authenticationAPI :: ServerT AuthenticationHandler App
122authenticationAPI = logIn :<|> logOut :<|> verifyToken :<|> register
127authenticationAPI = logIn :<|> logOut :<|> verifyToken :<|> register
124−logIn :: AuthenticationCredits -> App (Headers '[Header "Set-Cookie" String] UserLoginResponse)
129+logIn :: AuthenticationCredits -> Maybe String -> App (Headers '[Header "Set-Cookie" String] UserLoginResponse)
125−logIn (AuthenticationCredits email') = do
130+logIn (AuthenticationCredits email') ua' = do
126 let email = T.toLower . T.strip $ email'
131 let email = T.toLower . T.strip $ email'
127 if (not . validateEmail) email
132 if (not . validateEmail) email
128 then return . noHeader $ UserLoginResponse InvalidUser
133 then return . noHeader $ UserLoginResponse InvalidUser
@@ -131,12 +136,13 @@ logIn (AuthenticationCredits email') = do
131 case user of
136 case user of
132 Nothing -> return . noHeader $ UserLoginResponse UserCanRegister
137 Nothing -> return . noHeader $ UserLoginResponse UserCanRegister
133 Just User{..} -> do
138 Just User{..} -> do
139+ let ua = getUserAgent ua'
134− token <- generateToken userId
140+ token <- generateToken userId ua
135 runQerror . createToken $ token
141 runQerror . createToken $ token
136 let cookie = generateCookie token
142 let cookie = generateCookie token
137 case tokenVerificationCode token of
143 case tokenVerificationCode token of
138 Just code -> do
144 Just code -> do
139− sendTokenVerificationEmail code email
145+ sendTokenVerificationEmail code (TL.fromStrict ua) email
140 return . addHeader cookie $ UserLoginResponse UserIsRegistered
146 return . addHeader cookie $ UserLoginResponse UserIsRegistered
141 Nothing -> throwError err500
147 Nothing -> throwError err500
@@ -182,25 +188,29 @@ checkToken conn (AuthenticationCookieData uId tokenData) = do
182 token <- runQ' conn err500 $ getVerifiedToken uId tokenData
188 token <- runQ' conn err500 $ getVerifiedToken uId tokenData
183 case token of
189 case token of
184 Nothing -> throwError invalidToken
190 Nothing -> throwError invalidToken
185− Just (Token newUId _ _ _ _) -> return newUId
191+ Just (Token newUId _ _ _ _ _ _ _) -> return newUId
187−register :: UserCreation -> App (Headers '[Header "Set-Cookie" String] NoContent)
193+register :: UserCreation -> Maybe String -> App (Headers '[Header "Set-Cookie" String] NoContent)
188−register creation' = do
194+register creation' ua' = do
189 let creation = normalizaUser creation'
195 let creation = normalizaUser creation'
190 unless (validateUser creation) $ throwError err400
196 unless (validateUser creation) $ throwError err400
191 genToken <- generateTokenM
197 genToken <- generateTokenM
198+ let ua = getUserAgent ua'
192 token <- runQ err409 . commitedTransactionallyUpdate $ do
199 token <- runQ err409 . commitedTransactionallyUpdate $ do
193 uId <- createUser creation
200 uId <- createUser creation
194− let token = genToken uId
201+ let token = genToken uId ua
195 createToken token
202 createToken token
196 return token
203 return token
197 case tokenVerificationCode token of
204 case tokenVerificationCode token of
198 Just code -> do
205 Just code -> do
199− sendTokenVerificationEmail code (userCreationUserEmail creation)
206+ sendTokenVerificationEmail code (TL.fromStrict ua) (userCreationUserEmail creation)
200 let cookie = generateCookie token
207 let cookie = generateCookie token
201 return $ addHeader cookie NoContent
208 return $ addHeader cookie NoContent
202 Nothing -> throwError err500
209 Nothing -> throwError err500
211+getUserAgent :: Maybe String -> T.Text
212+getUserAgent = fromMaybe "Unknown" . fmap osrFamily . join . fmap parseOS . fmap C.pack
204normalizaUser :: UserCreation -> UserCreation
214normalizaUser :: UserCreation -> UserCreation
205normalizaUser (UserCreation fName mName lName faculty email) =
215normalizaUser (UserCreation fName mName lName faculty email) =
206 UserCreation
216 UserCreation