2 Module : Gargantext.API.Admin.Auth
3 Description : Server API Auth Module
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
10 Main authorization of Gargantext are managed in this module
12 -- 1: Implement the Server / Client JWT authentication
13 -> Client towards Python Backend
14 -> Server towards Purescript Front-End
16 -- 2: Implement the Auth API backend
17 https://github.com/haskell-servant/servant-auth
23 {-# LANGUAGE MonoLocalBinds #-}
24 {-# LANGUAGE ScopedTypeVariables #-}
26 module Gargantext.API.Admin.Auth
32 import Control.Lens (view)
33 import Data.Text.Lazy (toStrict)
34 import Data.Text.Lazy.Encoding (decodeUtf8)
36 import Servant.Auth.Server
37 import qualified Gargantext.Prelude.Crypto.Auth as Auth
39 import Gargantext.API.Admin.Auth.Types
40 import Gargantext.API.Admin.Types
41 import Gargantext.API.Prelude (HasJoseError(..), joseError, HasServerError, GargServerC)
42 import Gargantext.Core.Mail.Types (HasMail)
43 import Gargantext.Core.Types.Individu (User(..), Username, GargPassword(..))
44 import Gargantext.Database.Admin.Types.Node (NodeId(..), UserId)
45 import Gargantext.Database.Prelude (Cmd', CmdM, HasConnectionPool, HasConfig)
46 import Gargantext.Database.Query.Table.User
47 import Gargantext.Database.Query.Tree (isDescendantOf, isIn)
48 import Gargantext.Database.Query.Tree.Root (getRoot)
49 import Gargantext.Database.Schema.Node (NodePoly(_node_id))
50 import Gargantext.Prelude hiding (reverse)
52 ---------------------------------------------------
54 -- | Main functions of authorization
56 makeTokenForUser :: (HasSettings env, HasJoseError err)
57 => NodeId -> Cmd' env err Token
58 makeTokenForUser uid = do
59 jwtS <- view $ settings . jwtSettings
60 e <- liftBase $ makeJWT (AuthenticatedUser uid) jwtS Nothing
61 -- TODO-SECURITY here we can implement token expiration ^^.
62 either joseError (pure . toStrict . decodeUtf8) e
63 -- TODO not sure about the encoding...
65 checkAuthRequest :: (HasSettings env, HasConnectionPool env, HasJoseError err, HasConfig env, HasMail env)
68 -> Cmd' env err CheckAuth
69 checkAuthRequest u (GargPassword p) = do
70 candidate <- head <$> getUsersWith u
72 Nothing -> pure InvalidUser
73 Just (UserLight id _u _email (GargPassword h)) ->
74 case Auth.checkPassword (Auth.mkPassword p) (Auth.PasswordHash h) of
75 Auth.PasswordCheckFail -> pure InvalidPassword
76 Auth.PasswordCheckSuccess -> do
77 muId <- head <$> getRoot (UserName u)
78 case _node_id <$> muId of
79 Nothing -> pure InvalidUser
81 token <- makeTokenForUser uid
82 pure $ Valid token uid id
84 auth :: (HasSettings env, HasConnectionPool env, HasJoseError err, HasConfig env, HasMail env)
85 => AuthRequest -> Cmd' env err AuthResponse
86 auth (AuthRequest u p) = do
87 checkAuthRequest' <- checkAuthRequest u p
88 case checkAuthRequest' of
89 InvalidUser -> pure $ AuthResponse Nothing (Just $ AuthInvalid "Invalid user")
90 InvalidPassword -> pure $ AuthResponse Nothing (Just $ AuthInvalid "Invalid password")
91 Valid to trId uId -> pure $ AuthResponse (Just $ AuthValid to trId uId) Nothing
93 --type instance BasicAuthCfg = BasicAuthData -> IO (AuthResult AuthenticatedUser)
96 instance FromBasicAuthData AuthenticatedUser where
97 fromBasicAuthData authData authCheckFunction = authCheckFunction authData
99 authCheck :: forall env. env
101 -> IO (AuthResult AuthenticatedUser)
102 authCheck _env (BasicAuthData login password) = pure $
103 maybe Indefinite Authenticated $ TODO
106 withAccessM :: (CmdM env err m, HasServerError err)
111 withAccessM uId (PathNode id) m = do
112 d <- id `isDescendantOf` NodeId uId
113 if d then m else m -- serverError err401
115 withAccessM uId (PathNodeNode cId docId) m = do
116 _a <- isIn cId docId -- TODO use one query for all ?
117 _d <- cId `isDescendantOf` NodeId uId
122 withAccess :: forall env err m api.
123 (GargServerC env err m, HasServer api '[]) =>
124 Proxy api -> Proxy m -> UserId -> PathId ->
125 ServerT api m -> ServerT api m
126 withAccess p _ uId id = hoistServer p f
128 f :: forall a. m a -> m a
129 f = withAccessM uId id
131 {- | Collaborative Schema
132 User at his root can create Teams Folder
133 User can create Team in Teams Folder.
134 User can invite User in Team as NodeNode only if Team in his parents.
135 All users can access to the Team folder as if they were owner.