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 ScopedTypeVariables #-}
25 module Gargantext.API.Admin.Auth
31 import Control.Lens (view)
32 import Data.Text.Lazy (toStrict)
33 import Data.Text.Lazy.Encoding (decodeUtf8)
35 import Servant.Auth.Server
36 import qualified Gargantext.Prelude.Crypto.Auth as Auth
38 import Gargantext.API.Admin.Types
39 import Gargantext.API.Admin.Auth.Types
40 import Gargantext.API.Prelude (HasJoseError(..), joseError, HasServerError, GargServerC)
41 import Gargantext.Core.Types.Individu (User(..), Username, GargPassword(..))
42 import Gargantext.Database.Query.Tree (isDescendantOf, isIn)
43 import Gargantext.Database.Query.Tree.Root (getRoot)
44 import Gargantext.Database.Schema.Node (NodePoly(_node_id))
45 import Gargantext.Database.Admin.Types.Node (NodeId(..), UserId)
46 import Gargantext.Database.Prelude (Cmd', CmdM, HasConnectionPool, HasConfig)
47 import Gargantext.Prelude hiding (reverse)
48 import Gargantext.Database.Query.Table.User
50 ---------------------------------------------------
52 -- | Main functions of authorization
54 makeTokenForUser :: (HasSettings env, HasJoseError err)
55 => NodeId -> Cmd' env err Token
56 makeTokenForUser uid = do
57 jwtS <- view $ settings . jwtSettings
58 e <- liftBase $ makeJWT (AuthenticatedUser uid) jwtS Nothing
59 -- TODO-SECURITY here we can implement token expiration ^^.
60 either joseError (pure . toStrict . decodeUtf8) e
61 -- TODO not sure about the encoding...
63 checkAuthRequest :: (HasSettings env, HasConnectionPool env, HasJoseError err, HasConfig env)
66 -> Cmd' env err CheckAuth
67 checkAuthRequest u (GargPassword p) = do
68 candidate <- head <$> getUsersWith u
70 Nothing -> pure InvalidUser
71 Just (UserLight _id _u _email h) ->
72 case Auth.checkPassword (Auth.mkPassword p) (Auth.PasswordHash h) of
73 Auth.PasswordCheckFail -> pure InvalidPassword
74 Auth.PasswordCheckSuccess -> do
75 muId <- head <$> getRoot (UserName u)
76 case _node_id <$> muId of
77 Nothing -> pure InvalidUser
79 token <- makeTokenForUser uid
80 pure $ Valid token uid
82 auth :: (HasSettings env, HasConnectionPool env, HasJoseError err, HasConfig env)
83 => AuthRequest -> Cmd' env err AuthResponse
84 auth (AuthRequest u p) = do
85 checkAuthRequest' <- checkAuthRequest u p
86 case checkAuthRequest' of
87 InvalidUser -> pure $ AuthResponse Nothing (Just $ AuthInvalid "Invalid user")
88 InvalidPassword -> pure $ AuthResponse Nothing (Just $ AuthInvalid "Invalid password")
89 Valid to trId -> pure $ AuthResponse (Just $ AuthValid to trId) Nothing
91 --type instance BasicAuthCfg = BasicAuthData -> IO (AuthResult AuthenticatedUser)
94 instance FromBasicAuthData AuthenticatedUser where
95 fromBasicAuthData authData authCheckFunction = authCheckFunction authData
97 authCheck :: forall env. env
99 -> IO (AuthResult AuthenticatedUser)
100 authCheck _env (BasicAuthData login password) = pure $
101 maybe Indefinite Authenticated $ TODO
104 withAccessM :: (CmdM env err m, HasServerError err)
109 withAccessM uId (PathNode id) m = do
110 d <- id `isDescendantOf` NodeId uId
111 if d then m else m -- serverError err401
113 withAccessM uId (PathNodeNode cId docId) m = do
114 _a <- isIn cId docId -- TODO use one query for all ?
115 _d <- cId `isDescendantOf` NodeId uId
120 withAccess :: forall env err m api.
121 (GargServerC env err m, HasServer api '[]) =>
122 Proxy api -> Proxy m -> UserId -> PathId ->
123 ServerT api m -> ServerT api m
124 withAccess p _ uId id = hoistServer p f
126 f :: forall a. m a -> m a
127 f = withAccessM uId id
129 {- | Collaborative Schema
130 User at his root can create Teams Folder
131 User can create Team in Teams Folder.
132 User can invite User in Team as NodeNode only if Team in his parents.
133 All users can access to the Team folder as if they were owner.