From 69623d2046de2a193953a1f5dc2f34675b004104 Mon Sep 17 00:00:00 2001 From: Darrell Warde Date: Wed, 30 Aug 2023 18:11:53 +0100 Subject: [PATCH] Add guide on impersonation and user switching --- modules/ROOT/content-nav.adoc | 1 + .../impersonation-and-user-switching.adoc | 198 ++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 modules/ROOT/pages/authentication-and-authorization/impersonation-and-user-switching.adoc diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index a7c335d7..33a9959c 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -31,6 +31,7 @@ *** xref:authentication-and-authorization/configuration.adoc[] *** xref:authentication-and-authorization/authentication.adoc[] *** xref:authentication-and-authorization/authorization.adoc[] +*** xref:authentication-and-authorization/impersonation-and-user-switching.adoc[] ** xref:introspector.adoc[Introspector] diff --git a/modules/ROOT/pages/authentication-and-authorization/impersonation-and-user-switching.adoc b/modules/ROOT/pages/authentication-and-authorization/impersonation-and-user-switching.adoc new file mode 100644 index 00000000..5ec4da36 --- /dev/null +++ b/modules/ROOT/pages/authentication-and-authorization/impersonation-and-user-switching.adoc @@ -0,0 +1,198 @@ += Impersonation and user switching + +Impersonation and user switching are features of the Neo4j database and driver which allow for query execution in a different context to the initial connection. + +== Impersonation + +Impersonation still authenticates with the database as the original configured user, but runs the query in the context of an impersonated user. +When impersonating a user, the query is run within the complete security context of the impersonated user and not the authenticated user (home database, permissions, etc.). + +An example of how to impersonate a different user per request can be found below. In this example, the user to impersonate is taken from a HTTP header `User`: + +.TypeScript +[%collapsible] +==== +[source, typescript, indent=0] +---- +import { ApolloServer } from "@apollo/server"; +import { startStandaloneServer } from "@apollo/server/standalone"; +import { Neo4jGraphQL, Neo4jGraphQLContext } from "@neo4j/graphql"; +import neo4j from "neo4j-driver"; + +const typeDefs = `#graphql + type Movie { + title: String! + } +`; + +const driver = neo4j.driver( + "neo4j://localhost:7687", + neo4j.auth.basic("neo4j", "password") +); + +const neo4jgraphql = new Neo4jGraphQL({ + typeDefs, + driver, +}); + +const schema = await neo4jgraphql.getSchema(); + +const server = new ApolloServer({ + schema, +}); + +const { url } = await startStandaloneServer(server, { + // Your async context function should async and return an object + context: async ({ req }) => ({ + sessionConfig: { + impersonatedUser: req.headers.user, + }, + }), +}); + +console.log(`🚀 Server ready at: ${url}`); +---- +==== + +.JavaScript +[%collapsible] +==== +[source, javascript, indent=0] +---- +import { ApolloServer } from "@apollo/server"; +import { startStandaloneServer } from "@apollo/server/standalone"; +import { Neo4jGraphQL } from "@neo4j/graphql"; +import neo4j from "neo4j-driver"; + +const typeDefs = `#graphql + type Movie { + title: String! + } +`; + +const driver = neo4j.driver( + "neo4j://localhost:7687", + neo4j.auth.basic("neo4j", "password") +); + +const neo4jgraphql = new Neo4jGraphQL({ + typeDefs, + driver, +}); + +const schema = await neo4jgraphql.getSchema(); + +const server = new ApolloServer({ + schema, +}); + +const { url } = await startStandaloneServer(server, { + // Your async context function should async and return an object + context: async ({ req }) => ({ + sessionConfig: { + impersonatedUser: req.headers.user, + }, + }), +}); + +console.log(`🚀 Server ready at: ${url}`); +---- +==== + +== User switching + +User switching completely switches the user authenticating with the database for the given session, without the performance cost of instantiating an entire new driver instance. + +An example of configuring user switching on a per request basis can be found in the example below. Note that the username and password are provided in HTTP headers `User` and `Password`, but this would not be recommended for production use: + +.TypeScript +[%collapsible] +==== +[source, typescript, indent=0] +---- +import { ApolloServer } from "@apollo/server"; +import { startStandaloneServer } from "@apollo/server/standalone"; +import { Neo4jGraphQL, Neo4jGraphQLContext } from "@neo4j/graphql"; +import neo4j from "neo4j-driver"; + +const typeDefs = `#graphql + type Movie { + title: String! + } +`; + +const driver = neo4j.driver( + "neo4j://localhost:7687", + neo4j.auth.basic("neo4j", "password") +); + +const neo4jgraphql = new Neo4jGraphQL({ + typeDefs, + driver, +}); + +const schema = await neo4jgraphql.getSchema(); + +const server = new ApolloServer({ + schema, +}); + +const { url } = await startStandaloneServer(server, { + // Your async context function should async and return an object + context: async ({ req }) => ({ + sessionConfig: { + auth: neo4j.auth.basic(req.headers.user, req.headers.password), + }, + }), +}); + +console.log(`🚀 Server ready at: ${url}`); +---- +==== + +.JavaScript +[%collapsible] +==== +[source, javascript, indent=0] +---- +import { ApolloServer } from "@apollo/server"; +import { startStandaloneServer } from "@apollo/server/standalone"; +import { Neo4jGraphQL } from "@neo4j/graphql"; +import neo4j from "neo4j-driver"; + +const typeDefs = `#graphql + type Movie { + title: String! + } +`; + +const driver = neo4j.driver( + "neo4j://localhost:7687", + neo4j.auth.basic("neo4j", "password") +); + +const neo4jgraphql = new Neo4jGraphQL({ + typeDefs, + driver, +}); + +const schema = await neo4jgraphql.getSchema(); + +const server = new ApolloServer({ + schema, +}); + +const { url } = await startStandaloneServer(server, { + // Your async context function should async and return an object + context: async ({ req }) => ({ + sessionConfig: { + auth: neo4j.auth.basic(req.headers.user, req.headers.password), + }, + }), +}); + +console.log(`🚀 Server ready at: ${url}`); +---- +==== + +