From 1843643c7487dc85671bc95b134698cef2f504c6 Mon Sep 17 00:00:00 2001 From: Phil Freeman Date: Tue, 1 Dec 2020 15:23:27 -0800 Subject: [PATCH] Disallow caching for remote joins with forwarded headers (master) (#58) GitOrigin-RevId: 76eb061534fd2a068965b8b22517a0729d9e3020 --- server/src-lib/Hasura/App.hs | 2 +- server/src-lib/Hasura/GraphQL/Execute/Prepare.hs | 1 + server/src-lib/Hasura/GraphQL/Transport/HTTP.hs | 13 +++++++++---- .../src-lib/Hasura/GraphQL/Transport/WebSocket.hs | 3 ++- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/server/src-lib/Hasura/App.hs b/server/src-lib/Hasura/App.hs index bdaef51247d36..878bfb8e0d533 100644 --- a/server/src-lib/Hasura/App.hs +++ b/server/src-lib/Hasura/App.hs @@ -648,7 +648,7 @@ instance HttpLog PGMetadataStorageApp where mkHttpAccessLogContext userInfoM reqId waiReq compressedResponse qTime cType headers instance MonadExecuteQuery PGMetadataStorageApp where - cacheLookup _ _ = pure ([], Nothing) + cacheLookup _ _ _ = pure ([], Nothing) cacheStore _ _ = pure () instance UserAuthentication (Tracing.TraceT PGMetadataStorageApp) where diff --git a/server/src-lib/Hasura/GraphQL/Execute/Prepare.hs b/server/src-lib/Hasura/GraphQL/Execute/Prepare.hs index 9a26d894fc582..943fc22af5529 100644 --- a/server/src-lib/Hasura/GraphQL/Execute/Prepare.hs +++ b/server/src-lib/Hasura/GraphQL/Execute/Prepare.hs @@ -59,6 +59,7 @@ data ExecutionStep db -- ^ A query to execute against a remote schema | ExecStepRaw J.Value -- ^ Output a plain JSON object + deriving (Functor, Foldable, Traversable) data PlanningSt = PlanningSt diff --git a/server/src-lib/Hasura/GraphQL/Transport/HTTP.hs b/server/src-lib/Hasura/GraphQL/Transport/HTTP.hs index 9d8c290e0dd0b..a43c100b63aec 100644 --- a/server/src-lib/Hasura/GraphQL/Transport/HTTP.hs +++ b/server/src-lib/Hasura/GraphQL/Transport/HTTP.hs @@ -21,6 +21,7 @@ import Control.Monad.Morph (hoist) import Hasura.EncJSON import Hasura.GraphQL.Context +import Hasura.GraphQL.Execute.Prepare (ExecutionPlan) import Hasura.GraphQL.Logging (MonadQueryLog (..)) import Hasura.GraphQL.Parser.Column (UnpreparedValue) import Hasura.GraphQL.Transport.HTTP.Protocol @@ -40,6 +41,7 @@ import qualified Data.Environment as Env import qualified Data.HashMap.Strict.InsOrd as OMap import qualified Data.Text as T import qualified Database.PG.Query as Q +import qualified Hasura.Backends.Postgres.Execute.RemoteJoin as RJ import qualified Hasura.GraphQL.Execute as E import qualified Hasura.GraphQL.Execute.Query as EQ import qualified Hasura.Logging as L @@ -67,6 +69,8 @@ class Monad m => MonadExecuteQuery m where cacheLookup :: [QueryRootField (UnpreparedValue 'Postgres)] -- ^ Used to check that the query is cacheable + -> ExecutionPlan (Maybe (Maybe (RJ.RemoteJoins 'Postgres))) + -- ^ Used to check if the elaborated query supports caching -> QueryCacheKey -- ^ Key that uniquely identifies the result of a query execution -> TraceT (ExceptT QErr m) (HTTP.ResponseHeaders, Maybe EncJSON) @@ -93,15 +97,15 @@ class Monad m => MonadExecuteQuery m where -- ^ Always succeeds instance MonadExecuteQuery m => MonadExecuteQuery (ReaderT r m) where - cacheLookup a b = hoist (hoist lift) $ cacheLookup a b + cacheLookup a b c = hoist (hoist lift) $ cacheLookup a b c cacheStore a b = hoist (hoist lift) $ cacheStore a b instance MonadExecuteQuery m => MonadExecuteQuery (ExceptT r m) where - cacheLookup a b = hoist (hoist lift) $ cacheLookup a b + cacheLookup a b c = hoist (hoist lift) $ cacheLookup a b c cacheStore a b = hoist (hoist lift) $ cacheStore a b instance MonadExecuteQuery m => MonadExecuteQuery (TraceT m) where - cacheLookup a b = hoist (hoist lift) $ cacheLookup a b + cacheLookup a b c = hoist (hoist lift) $ cacheLookup a b c cacheStore a b = hoist (hoist lift) $ cacheStore a b data ResultsFragment = ResultsFragment @@ -147,7 +151,8 @@ runGQ env logger reqId userInfo ipAddress reqHeaders queryType reqUnparsed = do (telemCacheHit,) <$> case execPlan of E.QueryExecutionPlan queryPlans asts -> trace "Query" $ do let cacheKey = QueryCacheKey reqParsed $ _uiRole userInfo - (responseHeaders, cachedValue) <- Tracing.interpTraceT (liftEitherM . runExceptT) $ cacheLookup asts cacheKey + redactedPlan = fmap (fmap (fmap EQ._psRemoteJoins . snd)) queryPlans + (responseHeaders, cachedValue) <- Tracing.interpTraceT (liftEitherM . runExceptT) $ cacheLookup asts redactedPlan cacheKey case cachedValue of Just cachedResponseData -> pure (Telem.Query, 0, Telem.Local, HttpResponse cachedResponseData responseHeaders) diff --git a/server/src-lib/Hasura/GraphQL/Transport/WebSocket.hs b/server/src-lib/Hasura/GraphQL/Transport/WebSocket.hs index bb2407dab2087..4cc02140257f0 100644 --- a/server/src-lib/Hasura/GraphQL/Transport/WebSocket.hs +++ b/server/src-lib/Hasura/GraphQL/Transport/WebSocket.hs @@ -367,9 +367,10 @@ onStart env serverEnv wsConn (StartMsg opId q) = catchAndIgnore $ do case execPlan of E.QueryExecutionPlan queryPlan asts -> Tracing.trace "Query" $ do let cacheKey = QueryCacheKey reqParsed $ _uiRole userInfo + redactedPlan = fmap (fmap (fmap EQ._psRemoteJoins . snd)) queryPlan -- We ignore the response headers (containing TTL information) because -- WebSockets don't support them. - (_responseHeaders, cachedValue) <- Tracing.interpTraceT (withExceptT mempty) $ cacheLookup asts cacheKey + (_responseHeaders, cachedValue) <- Tracing.interpTraceT (withExceptT mempty) $ cacheLookup asts redactedPlan cacheKey case cachedValue of Just cachedResponseData -> do sendSuccResp cachedResponseData $ LQ.LiveQueryMetadata 0