Commit 25d87353 authored by Przemyslaw Kaminski's avatar Przemyslaw Kaminski

[forgot password] more forgot password work

NOTE: This doesn't compile yet because of HTML types.
parent fc2afd68
Pipeline #2830 failed with stage
in 12 minutes and 45 seconds
...@@ -145,6 +145,7 @@ library ...@@ -145,6 +145,7 @@ library
Gargantext.API.Swagger Gargantext.API.Swagger
Gargantext.API.Table Gargantext.API.Table
Gargantext.API.ThrowAll Gargantext.API.ThrowAll
Gargantext.API.Types
Gargantext.Core.Ext.IMT Gargantext.Core.Ext.IMT
Gargantext.Core.Ext.IMTUser Gargantext.Core.Ext.IMTUser
Gargantext.Core.Flow.Ngrams Gargantext.Core.Flow.Ngrams
......
...@@ -22,18 +22,21 @@ TODO-ACCESS Critical ...@@ -22,18 +22,21 @@ TODO-ACCESS Critical
{-# LANGUAGE MonoLocalBinds #-} {-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
module Gargantext.API.Admin.Auth module Gargantext.API.Admin.Auth
( auth ( auth
, forgotPassword , forgotPassword
, withAccess , withAccess
, ForgotPasswordAPI
) )
where where
import Control.Lens (view) import Control.Lens (view)
import Data.Text (Text)
import Data.Text.Lazy (toStrict) import Data.Text.Lazy (toStrict)
import Data.Text.Lazy.Encoding (decodeUtf8) import qualified Data.Text.Lazy.Encoding as LE
import Data.UUID (UUID) import Data.UUID (UUID, toText)
import Data.UUID.V4 (nextRandom) import Data.UUID.V4 (nextRandom)
import Servant import Servant
import Servant.Auth.Server import Servant.Auth.Server
...@@ -41,8 +44,10 @@ import qualified Gargantext.Prelude.Crypto.Auth as Auth ...@@ -41,8 +44,10 @@ import qualified Gargantext.Prelude.Crypto.Auth as Auth
import Gargantext.API.Admin.Auth.Types import Gargantext.API.Admin.Auth.Types
import Gargantext.API.Admin.Types import Gargantext.API.Admin.Types
import Gargantext.API.Prelude (HasJoseError(..), joseError, HasServerError, GargServerC) import Gargantext.API.Prelude (HasJoseError(..), joseError, HasServerError, GargServerC, GargServer)
import Gargantext.Core.Mail.Types (HasMail) import Gargantext.API.Types
import Gargantext.Core.Mail (MailModel(..), mail)
import Gargantext.Core.Mail.Types (HasMail, mailSettings)
import Gargantext.Core.Types.Individu (User(..), Username, GargPassword(..)) import Gargantext.Core.Types.Individu (User(..), Username, GargPassword(..))
import Gargantext.Database.Admin.Types.Node (NodeId(..), UserId) import Gargantext.Database.Admin.Types.Node (NodeId(..), UserId)
import Gargantext.Database.Prelude (Cmd', CmdM, HasConnectionPool, HasConfig) import Gargantext.Database.Prelude (Cmd', CmdM, HasConnectionPool, HasConfig)
...@@ -62,7 +67,7 @@ makeTokenForUser uid = do ...@@ -62,7 +67,7 @@ makeTokenForUser uid = do
jwtS <- view $ settings . jwtSettings jwtS <- view $ settings . jwtSettings
e <- liftBase $ makeJWT (AuthenticatedUser uid) jwtS Nothing e <- liftBase $ makeJWT (AuthenticatedUser uid) jwtS Nothing
-- TODO-SECURITY here we can implement token expiration ^^. -- TODO-SECURITY here we can implement token expiration ^^.
either joseError (pure . toStrict . decodeUtf8) e either joseError (pure . toStrict . LE.decodeUtf8) e
-- TODO not sure about the encoding... -- TODO not sure about the encoding...
checkAuthRequest :: (HasSettings env, HasConnectionPool env, HasJoseError err, HasConfig env, HasMail env) checkAuthRequest :: (HasSettings env, HasConnectionPool env, HasJoseError err, HasConfig env, HasMail env)
...@@ -138,9 +143,21 @@ User can invite User in Team as NodeNode only if Team in his parents. ...@@ -138,9 +143,21 @@ User can invite User in Team as NodeNode only if Team in his parents.
All users can access to the Team folder as if they were owner. All users can access to the Team folder as if they were owner.
-} -}
forgotPassword :: (HasSettings env, HasConnectionPool env, HasJoseError err, HasConfig env, HasMail env) type ForgotPasswordAPI = Summary "Forgot password POST API"
:> ReqBody '[JSON] ForgotPasswordRequest
:> Post '[JSON] ForgotPasswordResponse
:<|> Summary "Forgot password GET API"
:> QueryParam "uuid" Text
:> Get '[HTML] Text
forgotPassword :: GargServer ForgotPasswordAPI
-- => ForgotPasswordRequest -> Cmd' env err ForgotPasswordResponse
forgotPassword = forgotPasswordPost :<|> forgotPasswordGet
forgotPasswordPost :: (HasSettings env, HasConnectionPool env, HasJoseError err, HasConfig env, HasMail env)
=> ForgotPasswordRequest -> Cmd' env err ForgotPasswordResponse => ForgotPasswordRequest -> Cmd' env err ForgotPasswordResponse
forgotPassword (ForgotPasswordRequest email) = do forgotPasswordPost (ForgotPasswordRequest email) = do
us <- getUsersWithEmail email us <- getUsersWithEmail email
case us of case us of
[u] -> forgotUserPassword u [u] -> forgotUserPassword u
...@@ -150,6 +167,11 @@ forgotPassword (ForgotPasswordRequest email) = do ...@@ -150,6 +167,11 @@ forgotPassword (ForgotPasswordRequest email) = do
-- users' emails -- users' emails
pure $ ForgotPasswordResponse "ok" pure $ ForgotPasswordResponse "ok"
forgotPasswordGet :: (HasSettings env, HasConnectionPool env, HasJoseError err, HasConfig env, HasMail env)
=> Maybe Text -> Cmd' env err Text
forgotPasswordGet Nothing = pure ""
forgotPasswordGet (Just uuid) = pure uuid
forgotUserPassword :: (HasSettings env, HasConnectionPool env, HasJoseError err, HasConfig env, HasMail env) forgotUserPassword :: (HasSettings env, HasConnectionPool env, HasJoseError err, HasConfig env, HasMail env)
=> UserLight -> Cmd' env err () => UserLight -> Cmd' env err ()
forgotUserPassword user@(UserLight { .. }) = do forgotUserPassword user@(UserLight { .. }) = do
...@@ -160,7 +182,11 @@ forgotUserPassword user@(UserLight { .. }) = do ...@@ -160,7 +182,11 @@ forgotUserPassword user@(UserLight { .. }) = do
-- save user with that uuid -- save user with that uuid
_ <- updateUserForgotPasswordUUID user uuid _ <- updateUserForgotPasswordUUID user uuid
let userUUID = UserLight { userLight_forgot_password_uuid = Just $ toText uuid, .. }
-- send email with uuid link -- send email with uuid link
cfg <- view $ mailSettings
mail cfg (ForgotPassword { user = userUUID })
-- on uuid link enter: change user password and present it to the -- on uuid link enter: change user password and present it to the
-- user -- user
......
...@@ -64,7 +64,8 @@ getBackendVersion :: ClientM Text ...@@ -64,7 +64,8 @@ getBackendVersion :: ClientM Text
-- * auth API -- * auth API
postAuth :: AuthRequest -> ClientM AuthResponse postAuth :: AuthRequest -> ClientM AuthResponse
forgotPassword :: ForgotPasswordRequest -> ClientM ForgotPasswordResponse forgotPasswordPost :: ForgotPasswordRequest -> ClientM ForgotPasswordResponse
forgotPasswordGet :: Maybe Text -> ClientM Text
-- * admin api -- * admin api
getRoots :: Token -> ClientM [Node HyperdataUser] getRoots :: Token -> ClientM [Node HyperdataUser]
...@@ -439,7 +440,8 @@ clientApi = client (flatten apiGarg) ...@@ -439,7 +440,8 @@ clientApi = client (flatten apiGarg)
getMetricsSample :<|> getMetricSample :<|> _ = client (Proxy :: Proxy (Flat EkgAPI)) getMetricsSample :<|> getMetricSample :<|> _ = client (Proxy :: Proxy (Flat EkgAPI))
postAuth postAuth
:<|> forgotPassword :<|> forgotPasswordPost
:<|> forgotPasswordGet
:<|> getBackendVersion :<|> getBackendVersion
:<|> getRoots :<|> getRoots
:<|> putRoots :<|> putRoots
......
{-# OPTIONS_GHC -fprint-potential-instances #-}
{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DuplicateRecordFields #-} -- permit duplications for field names in multiple constructors {-# LANGUAGE DuplicateRecordFields #-} -- permit duplications for field names in multiple constructors
{-# LANGUAGE KindSignatures #-} -- for use of Endpoint (name :: Symbol) {-# LANGUAGE KindSignatures #-} -- for use of Endpoint (name :: Symbol)
...@@ -9,7 +11,6 @@ module Gargantext.API.GraphQL where ...@@ -9,7 +11,6 @@ module Gargantext.API.GraphQL where
import Data.ByteString.Lazy.Char8 import Data.ByteString.Lazy.Char8
( ByteString ( ByteString
) )
import Data.List.NonEmpty (NonEmpty ((:|)))
import Data.Map (Map) import Data.Map (Map)
import Data.Morpheus import Data.Morpheus
( App ( App
...@@ -41,19 +42,16 @@ import qualified Gargantext.API.GraphQL.User as GQLUser ...@@ -41,19 +42,16 @@ import qualified Gargantext.API.GraphQL.User as GQLUser
import qualified Gargantext.API.GraphQL.UserInfo as GQLUserInfo import qualified Gargantext.API.GraphQL.UserInfo as GQLUserInfo
import qualified Gargantext.API.GraphQL.TreeFirstLevel as GQLTree import qualified Gargantext.API.GraphQL.TreeFirstLevel as GQLTree
import Gargantext.API.Prelude (GargM, GargError) import Gargantext.API.Prelude (GargM, GargError)
import Gargantext.API.Types
import Gargantext.Core.Mail.Types (HasMail) import Gargantext.Core.Mail.Types (HasMail)
import Gargantext.Database.Prelude (HasConnectionPool, HasConfig) import Gargantext.Database.Prelude (HasConnectionPool, HasConfig)
import Gargantext.Prelude import Gargantext.Prelude
import GHC.Generics (Generic) import GHC.Generics (Generic)
import Network.HTTP.Media ((//), (/:))
import qualified Prelude
import Servant import Servant
( (:<|>) (..) ( (:<|>) (..)
, (:>) , (:>)
, Accept (..)
, Get , Get
, JSON , JSON
, MimeRender (..)
, Post , Post
, ReqBody , ReqBody
, ServerT , ServerT
...@@ -62,6 +60,7 @@ import qualified Servant.Auth as SA ...@@ -62,6 +60,7 @@ import qualified Servant.Auth as SA
import qualified Servant.Auth.Server as SAS import qualified Servant.Auth.Server as SAS
import Gargantext.API.Admin.Types (HasSettings) import Gargantext.API.Admin.Types (HasSettings)
-- | Represents possible GraphQL queries. -- | Represents possible GraphQL queries.
data Query m data Query m
= Query = Query
...@@ -124,13 +123,6 @@ app = deriveApp rootResolver ...@@ -124,13 +123,6 @@ app = deriveApp rootResolver
-- Now for some boilerplate to integrate the above GraphQL app with -- Now for some boilerplate to integrate the above GraphQL app with
-- servant. -- servant.
-- | HTML type is needed for the GraphQL Playground.
data HTML deriving (Typeable)
instance Accept HTML where
contentTypes _ = "text" // "html" /: ("charset", "utf-8") :| ["text" // "html"]
instance MimeRender HTML ByteString where
mimeRender _ = Prelude.id
-- | Servant route for the app we defined above. -- | Servant route for the app we defined above.
type GQAPI = ReqBody '[JSON] GQLRequest :> Post '[JSON] GQLResponse type GQAPI = ReqBody '[JSON] GQLRequest :> Post '[JSON] GQLResponse
-- type Schema = "schema" :> Get '[PlainText] Text -- type Schema = "schema" :> Get '[PlainText] Text
......
...@@ -16,7 +16,6 @@ module Gargantext.API.Ngrams.List ...@@ -16,7 +16,6 @@ module Gargantext.API.Ngrams.List
where where
import Control.Lens hiding (elements, Indexed) import Control.Lens hiding (elements, Indexed)
import Data.Aeson
import Data.Either (Either(..)) import Data.Either (Either(..))
import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict (HashMap)
import Data.Map (Map, toList) import Data.Map (Map, toList)
...@@ -31,6 +30,7 @@ import Gargantext.API.Ngrams.Prelude (getNgramsList) ...@@ -31,6 +30,7 @@ import Gargantext.API.Ngrams.Prelude (getNgramsList)
import Gargantext.API.Ngrams.Tools (getTermsWith) import Gargantext.API.Ngrams.Tools (getTermsWith)
import Gargantext.API.Ngrams.Types import Gargantext.API.Ngrams.Types
import Gargantext.API.Prelude (GargServer) import Gargantext.API.Prelude (GargServer)
import Gargantext.API.Types
import Gargantext.Core.NodeStory import Gargantext.Core.NodeStory
import Gargantext.Core.Text.Terms (ExtractedNgrams(..)) import Gargantext.Core.Text.Terms (ExtractedNgrams(..))
import Gargantext.Core.Text.Terms.WithList (buildPatterns, termsInText) import Gargantext.Core.Text.Terms.WithList (buildPatterns, termsInText)
...@@ -46,7 +46,6 @@ import Gargantext.Database.Schema.Ngrams ...@@ -46,7 +46,6 @@ import Gargantext.Database.Schema.Ngrams
import Gargantext.Database.Schema.Node (_node_parent_id) import Gargantext.Database.Schema.Node (_node_parent_id)
import Gargantext.Database.Types (Indexed(..)) import Gargantext.Database.Types (Indexed(..))
import Gargantext.Prelude import Gargantext.Prelude
import Network.HTTP.Media ((//), (/:))
import Servant import Servant
import Servant.Job.Async import Servant.Job.Async
import qualified Data.ByteString.Lazy as BSL import qualified Data.ByteString.Lazy as BSL
...@@ -67,12 +66,6 @@ type GETAPI = Summary "Get List" ...@@ -67,12 +66,6 @@ type GETAPI = Summary "Get List"
getApi :: GargServer GETAPI getApi :: GargServer GETAPI
getApi = get getApi = get
data HTML
instance Accept HTML where
contentType _ = "text" // "html" /: ("charset", "utf-8")
instance ToJSON a => MimeRender HTML a where
mimeRender _ = encode
---------------------- ----------------------
type JSONAPI = Summary "Update List" type JSONAPI = Summary "Update List"
:> "lists" :> "lists"
......
...@@ -28,8 +28,8 @@ import Servant.Auth.Swagger () ...@@ -28,8 +28,8 @@ import Servant.Auth.Swagger ()
import Servant.Job.Async import Servant.Job.Async
import Servant.Swagger.UI import Servant.Swagger.UI
import Gargantext.API.Admin.Auth (withAccess) import Gargantext.API.Admin.Auth (ForgotPasswordAPI, withAccess)
import Gargantext.API.Admin.Auth.Types (AuthRequest, AuthResponse, AuthenticatedUser(..), ForgotPasswordRequest, ForgotPasswordResponse, PathId(..)) import Gargantext.API.Admin.Auth.Types (AuthRequest, AuthResponse, AuthenticatedUser(..), PathId(..))
import Gargantext.API.Admin.FrontEnd (FrontEndAPI) import Gargantext.API.Admin.FrontEnd (FrontEndAPI)
import Gargantext.API.Context import Gargantext.API.Context
import Gargantext.API.Count (CountAPI, count, Query) import Gargantext.API.Count (CountAPI, count, Query)
...@@ -72,9 +72,7 @@ type GargAPI' = ...@@ -72,9 +72,7 @@ type GargAPI' =
"auth" :> Summary "AUTH API" "auth" :> Summary "AUTH API"
:> ReqBody '[JSON] AuthRequest :> ReqBody '[JSON] AuthRequest
:> Post '[JSON] AuthResponse :> Post '[JSON] AuthResponse
:<|> "forgotPassword" :> Summary "Forgot password API" :<|> "forgot-password" :> ForgotPasswordAPI
:> ReqBody '[JSON] ForgotPasswordRequest
:> Post '[JSON] ForgotPasswordResponse
:<|> GargVersion :<|> GargVersion
-- TODO-ACCESS here we want to request a particular header for -- TODO-ACCESS here we want to request a particular header for
-- auth and capabilities. -- auth and capabilities.
......
{-# OPTIONS_GHC -fprint-potential-instances #-}
module Gargantext.API.Types where
import Data.Aeson
import Data.ByteString.Lazy.Char8 (ByteString)
import Data.List.NonEmpty (NonEmpty ((:|)))
import Data.Typeable
import Network.HTTP.Media ((//), (/:))
import qualified Prelude
import Servant
( Accept(..)
, MimeRender(..) )
data HTML deriving (Typeable)
instance Accept HTML where
contentTypes _ = "text" // "html" /: ("charset", "utf-8") :| ["text" // "html"]
instance MimeRender HTML ByteString where
mimeRender _ = Prelude.id
instance ToJSON a => MimeRender HTML a where
mimeRender _ = encode
...@@ -14,6 +14,7 @@ module Gargantext.Core.Mail where ...@@ -14,6 +14,7 @@ module Gargantext.Core.Mail where
import Control.Lens (view) import Control.Lens (view)
import Data.Text (Text, unlines, splitOn) import Data.Text (Text, unlines, splitOn)
import Gargantext.Core.Types.Individu import Gargantext.Core.Types.Individu
import Gargantext.Database.Schema.User (UserLight(..))
import Gargantext.Prelude import Gargantext.Prelude
import Gargantext.Prelude.Config (gc_url) import Gargantext.Prelude.Config (gc_url)
import Gargantext.Database.Prelude import Gargantext.Database.Prelude
...@@ -39,6 +40,7 @@ data MailModel = Invitation { invitation_user :: NewUser GargPassword } ...@@ -39,6 +40,7 @@ data MailModel = Invitation { invitation_user :: NewUser GargPassword }
| MailInfo { mailInfo_username :: Name | MailInfo { mailInfo_username :: Name
, mailInfo_address :: EmailAddress , mailInfo_address :: EmailAddress
} }
| ForgotPassword { user :: UserLight }
------------------------------------------------------------------------ ------------------------------------------------------------------------
------------------------------------------------------------------------ ------------------------------------------------------------------------
mail :: (CmdM env err m) => MailConfig -> MailModel -> m () mail :: (CmdM env err m) => MailConfig -> MailModel -> m ()
...@@ -66,6 +68,7 @@ email_to :: MailModel -> (EmailAddress, Name) ...@@ -66,6 +68,7 @@ email_to :: MailModel -> (EmailAddress, Name)
email_to (Invitation user) = email_to' user email_to (Invitation user) = email_to' user
email_to (PassUpdate user) = email_to' user email_to (PassUpdate user) = email_to' user
email_to (MailInfo { .. }) = (mailInfo_address, mailInfo_username) email_to (MailInfo { .. }) = (mailInfo_address, mailInfo_username)
email_to (ForgotPassword { user = UserLight { .. }}) = (userLight_email, userLight_username)
email_to' :: NewUser GargPassword -> (EmailAddress, Name) email_to' :: NewUser GargPassword -> (EmailAddress, Name)
email_to' (NewUser u m _) = (m,u) email_to' (NewUser u m _) = (m,u)
...@@ -80,12 +83,21 @@ bodyWith server (PassUpdate u) = [ "Your account password have been updated on t ...@@ -80,12 +83,21 @@ bodyWith server (PassUpdate u) = [ "Your account password have been updated on t
] <> (email_credentials server u) ] <> (email_credentials server u)
bodyWith server (MailInfo _ _) = [ "Your last analysis is over on the server: " <> server] bodyWith server (MailInfo _ _) = [ "Your last analysis is over on the server: " <> server]
bodyWith _server (ForgotPassword { user = UserLight { userLight_forgot_password_uuid = Nothing }}) =
[ "Cannot send you link to forgot password, no UUID" ]
bodyWith server (ForgotPassword { user = UserLight { userLight_forgot_password_uuid = Just uuid }}) =
[ "Click on this link to restore your password: "
, forgot_password_link server uuid ]
forgot_password_link :: ServerAddress -> Text -> Text
forgot_password_link server uuid = server <> "/forgot-password?uuid=" <> uuid
------------------------------------------------------------------------ ------------------------------------------------------------------------
email_subject :: MailModel -> Text email_subject :: MailModel -> Text
email_subject (Invitation _) = "[GarganText] Invitation" email_subject (Invitation _) = "[GarganText] Invitation"
email_subject (PassUpdate _) = "[GarganText] Update" email_subject (PassUpdate _) = "[GarganText] Update"
email_subject (MailInfo _ _) = "[GarganText] Info" email_subject (MailInfo _ _) = "[GarganText] Info"
email_subject (ForgotPassword _) = "[GarganText] Forgot Password"
email_credentials :: ServerAddress -> NewUser GargPassword -> [Text] email_credentials :: ServerAddress -> NewUser GargPassword -> [Text]
......
...@@ -26,7 +26,8 @@ nix: ...@@ -26,7 +26,8 @@ nix:
allow-newer: true allow-newer: true
#ghc-options: ghc-options:
"$everything": -fprint-potential-instances
# "$everything": -haddock # "$everything": -haddock
extra-deps: extra-deps:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment