Commit 6d776767 authored by Alfredo Di Napoli's avatar Alfredo Di Napoli

Bolt-on ownership check for /remote/export

parent 58d9fcb0
...@@ -33,6 +33,7 @@ module Gargantext.API.Auth.PolicyCheck ( ...@@ -33,6 +33,7 @@ module Gargantext.API.Auth.PolicyCheck (
, nodePublishedEdit , nodePublishedEdit
, moveChecks , moveChecks
, publishChecks , publishChecks
, remoteExportChecks
, userMe , userMe
, alwaysAllow , alwaysAllow
, alwaysDeny , alwaysDeny
...@@ -211,7 +212,7 @@ nodeNotDescendant :: AccessPolicyErrorReason ...@@ -211,7 +212,7 @@ nodeNotDescendant :: AccessPolicyErrorReason
nodeNotDescendant = AccessPolicyErrorReason "Node is not a direct descendant." nodeNotDescendant = AccessPolicyErrorReason "Node is not a direct descendant."
invalidUserPermissions :: AccessPolicyErrorReason invalidUserPermissions :: AccessPolicyErrorReason
invalidUserPermissions = AccessPolicyErrorReason "User not authorized to perform the operation." invalidUserPermissions = AccessPolicyErrorReason "User not authorized to perform the operation (typically due to wrong ownership)."
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Smart constructors of access checks -- Smart constructors of access checks
...@@ -274,6 +275,11 @@ publishChecks :: NodeId -> BoolExpr AccessCheck ...@@ -274,6 +275,11 @@ publishChecks :: NodeId -> BoolExpr AccessCheck
publishChecks nodeId = publishChecks nodeId =
(nodeUser nodeId `BOr` nodeSuper nodeId) (nodeUser nodeId `BOr` nodeSuper nodeId)
-- | A user can export a node if he/she owns it, or if that's a super.
remoteExportChecks :: NodeId -> BoolExpr AccessCheck
remoteExportChecks nodeId =
(nodeUser nodeId `BOr` nodeSuper nodeId)
alwaysAllow :: BoolExpr AccessCheck alwaysAllow :: BoolExpr AccessCheck
alwaysAllow = BConst . Positive $ AC_always_allow alwaysAllow = BConst . Positive $ AC_always_allow
......
...@@ -16,6 +16,7 @@ import Data.ByteString qualified as BS ...@@ -16,6 +16,7 @@ import Data.ByteString qualified as BS
import Data.Proxy import Data.Proxy
import Data.Swagger hiding (Http) import Data.Swagger hiding (Http)
import Gargantext.API.Admin.Auth.Types (Token) import Gargantext.API.Admin.Auth.Types (Token)
import Gargantext.API.Auth.PolicyCheck (PolicyChecked)
import Gargantext.Database.Admin.Types.Node ( NodeId (..) ) import Gargantext.Database.Admin.Types.Node ( NodeId (..) )
import GHC.Generics import GHC.Generics
import Prelude import Prelude
...@@ -76,7 +77,7 @@ instance ToSchema RemoteBinaryData where ...@@ -76,7 +77,7 @@ instance ToSchema RemoteBinaryData where
declareNamedSchema _ = pure $ NamedSchema (Just "RemoteExportRequest") binarySchema declareNamedSchema _ = pure $ NamedSchema (Just "RemoteExportRequest") binarySchema
data RemoteAPI' mode = RemoteAPI' data RemoteAPI' mode = RemoteAPI'
{ remoteExportEp :: mode :- "export" :> ReqBody '[JSON] RemoteExportRequest :> Post '[JSON] () { remoteExportEp :: mode :- "export" :> ReqBody '[JSON] RemoteExportRequest :> PolicyChecked (Post '[JSON] ())
, remoteImportEp :: mode :- "import" :> StreamBody NoFraming OctetStream (C.ConduitT () RemoteBinaryData IO ()) , remoteImportEp :: mode :- "import" :> StreamBody NoFraming OctetStream (C.ConduitT () RemoteBinaryData IO ())
:> Post '[JSON] () :> Post '[JSON] ()
} deriving Generic } deriving Generic
...@@ -67,5 +67,5 @@ serverPrivateGargAPI' authenticatedUser@(AuthenticatedUser userNodeId userId) ...@@ -67,5 +67,5 @@ serverPrivateGargAPI' authenticatedUser@(AuthenticatedUser userNodeId userId)
, listJsonAPI = List.jsonAPI , listJsonAPI = List.jsonAPI
, listTsvAPI = List.tsvAPI , listTsvAPI = List.tsvAPI
, shareUrlAPI = shareURL , shareUrlAPI = shareURL
, remoteAPI = Remote.remoteAPI , remoteAPI = Remote.remoteAPI authenticatedUser
} }
...@@ -19,6 +19,9 @@ import Data.ByteString.Char8 qualified as C8 ...@@ -19,6 +19,9 @@ import Data.ByteString.Char8 qualified as C8
import Data.ByteString.Lazy qualified as BL import Data.ByteString.Lazy qualified as BL
import Data.Conduit.Combinators qualified as C import Data.Conduit.Combinators qualified as C
import Data.Conduit.List qualified as CL import Data.Conduit.List qualified as CL
import Gargantext.API.Admin.Auth.Types (AuthenticatedUser)
import Gargantext.API.Admin.Auth
import Gargantext.API.Auth.PolicyCheck (remoteExportChecks)
import Gargantext.API.Errors.Types import Gargantext.API.Errors.Types
import Gargantext.API.Prelude (IsGargServer) import Gargantext.API.Prelude (IsGargServer)
import Gargantext.API.Routes.Client (remoteImportClient) import Gargantext.API.Routes.Client (remoteImportClient)
...@@ -29,10 +32,13 @@ import Prelude ...@@ -29,10 +32,13 @@ import Prelude
import Servant.Client.Streaming (mkClientEnv, withClientM, ClientError) import Servant.Client.Streaming (mkClientEnv, withClientM, ClientError)
import Servant.Server.Generic (AsServerT) import Servant.Server.Generic (AsServerT)
remoteAPI :: (MonadIO m, IsGargServer env err m) => Named.RemoteAPI (AsServerT m) remoteAPI :: (MonadIO m, IsGargServer env BackendInternalError m)
remoteAPI = Named.RemoteAPI $ => AuthenticatedUser
-> Named.RemoteAPI (AsServerT m)
remoteAPI authenticatedUser = Named.RemoteAPI $
Named.RemoteAPI' Named.RemoteAPI'
{ remoteExportEp = remoteExportHandler { remoteExportEp = \payload@Named.RemoteExportRequest{..} mgr ->
withPolicy authenticatedUser (remoteExportChecks _rer_node_id) (remoteExportHandler payload) mgr
, remoteImportEp = remoteImportHandler , remoteImportEp = remoteImportHandler
} }
...@@ -45,7 +51,7 @@ remoteImportHandler c = do ...@@ -45,7 +51,7 @@ remoteImportHandler c = do
chunks <- liftIO $ sourceToList $ c .| C.map (B.byteString . Named.getRemoteBinaryData) chunks <- liftIO $ sourceToList $ c .| C.map (B.byteString . Named.getRemoteBinaryData)
case deserialiseOrFail @ExpectedPayload (B.toLazyByteString $ mconcat chunks) of case deserialiseOrFail @ExpectedPayload (B.toLazyByteString $ mconcat chunks) of
Left err -> throwError $ _BackendInternalError # InternalUnexpectedError (toException $ userError $ "Deserialization error: " ++ show err) Left err -> throwError $ _BackendInternalError # InternalUnexpectedError (toException $ userError $ "Deserialization error: " ++ show err)
Right value -> liftIO $ putStrLn $ show $ value Right value -> liftIO $ putStrLn $ "Received from outside: " ++ show value
remoteExportHandler :: ( MonadIO m, Safe.MonadCatch m remoteExportHandler :: ( MonadIO m, Safe.MonadCatch m
, IsGargServer err env m , IsGargServer err env m
......
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