Skip to content

Commit 6a8cafc

Browse files
authored
Feature/security (#21)
* Add security management
1 parent 6d252fe commit 6a8cafc

File tree

13 files changed

+190
-48
lines changed

13 files changed

+190
-48
lines changed

.env

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
NEO4J_HOST='bolt://localhost'
2+
DEBUG=@neo4j/graphql:*
3+
GRAPH_SECURITY_ENABLED=true
4+
GRAPH_TOKEN_ROLES_PATH=resource_access.graph-graphql.roles
5+
GRAPH_TOKEN_AUTHORITY=http://localhost:9080/auth/realms/dependencies

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,13 @@ This image is base on **Linux**.
3131

3232
You can configure container by setting environment variables.
3333

34-
| Environment variable | Comment | default value |
34+
| Environment variable | Comment | sample value |
3535
|------------------------- | :--------------------------|-------------------- |
3636
| NEO4J_HOST | Noe4j database uri | bolt://localhost |
37+
| GRAPH_SECURITY_ENABLED | Enable jwt validation | bolt://localhost |
38+
| GRAPH_TOKEN_ROLES_PATH | Roles path inside jwt | resource_access.graph-graphql.roles |
39+
| GRAPH_TOKEN_AUTHORITY | Authority for token validation | http://localhost:9080/auth/realms/dependencies |
40+
| DEBUG | Activate @neo4j/graphql traces | @neo4j/graphql:* |
3741

3842
Port exposed by Container:
3943

doc/releases/v2.0.0.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
### Changes
2+
- Add security management (token validation)
3+
- Docker image: ARM architecture support (linux/arm64, linux/arm/v7)
4+
- Migrating from "neo4j-graphql-js" (deprecated) to "@neo4j/graphql"
5+
6+
Docker image available on:
7+
- [github](https://github.com/xclemence/dependencies-graph-graphql/packages)
8+
- [dockerhub](https://hub.docker.com/r/xclemence/dependencies-graph-graphql)

package.json

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
22
"name": "dependencies-graph-graphql",
3-
"version": "1.1.0",
3+
"version": "2.0.0",
44
"description": "",
5-
"main": "index.js",
5+
"main": "server.js",
66
"scripts": {
77
"start": "node dotenv/config 'dist/server.js'",
8-
"build": "tsc -p . && ncp src/definitions dist/definitions",
8+
"build": "tsc -p . && ncp src/definitions dist/definitions && ncp src/definitions-rights dist/definitions-rights",
99
"dev": "nodemon --exec \"yarn ts-node\" src/server.ts -e ts,graphql ",
10-
"ts-node": "ts-node"
10+
"ts-node": "ts-node "
1111
},
1212
"repository": {
1313
"type": "git",
@@ -24,6 +24,7 @@
2424
"@types/compression": "^1.7.0",
2525
"@types/dotenv": "^8.2.0",
2626
"@types/express": "^4.17.12",
27+
"@types/express-jwt": "^6.0.1",
2728
"@types/graphql-depth-limit": "^1.1.2",
2829
"@types/lodash": "^4.14.170",
2930
"@types/node": "^15.12.1",
@@ -40,6 +41,7 @@
4041
"@neo4j/graphql-ogm": "^1.0.2",
4142
"apollo-server": "^2.25.0",
4243
"apollo-server-express": "^2.25.0",
44+
"axios": "^0.21.1",
4345
"compression": "^1.7.4",
4446
"cors": "^2.8.5",
4547
"dotenv": "^10.0.0",

src/apollo-server.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import './env';
2+
3+
import depthLimit from 'graphql-depth-limit';
4+
import { driver } from 'neo4j-driver';
5+
import { OGM } from '@neo4j/graphql-ogm';
6+
7+
import { getTypesFiles, getResolvers } from './schema';
8+
import { Context } from './types/context';
9+
import { Neo4jGraphQL } from '@neo4j/graphql';
10+
import { Neo4jGraphQLConfig } from '@neo4j/graphql/dist/classes';
11+
import { ApolloServer } from 'apollo-server-express';
12+
13+
export function createApolloServerWithToken(
14+
neo4jHost: string,
15+
tokenValidation: { publicKey: string, rolesPath: string | undefined }
16+
): ApolloServer {
17+
18+
return createApolloServerBase(
19+
neo4jHost,
20+
true,
21+
{
22+
jwt: {
23+
secret: tokenValidation.publicKey,
24+
rolesPath: tokenValidation.rolesPath
25+
}
26+
}
27+
);
28+
}
29+
30+
export function createApolloServerNoToken(neo4jHost: string): ApolloServer {
31+
return createApolloServerBase(neo4jHost, false);
32+
}
33+
34+
35+
function createApolloServerBase(
36+
neo4jHost: string,
37+
enabledSecurity: boolean,
38+
config?: Neo4jGraphQLConfig
39+
): ApolloServer {
40+
41+
const driverInstance = driver(neo4jHost);
42+
const typesFiles = getTypesFiles(enabledSecurity);
43+
44+
const ogm = new OGM({
45+
typeDefs: typesFiles,
46+
driver: driverInstance,
47+
});
48+
49+
const neo4jGraphQL = new Neo4jGraphQL({
50+
typeDefs: typesFiles,
51+
resolvers: getResolvers(),
52+
config
53+
});
54+
55+
return new ApolloServer({
56+
schema: neo4jGraphQL.schema,
57+
validationRules: [depthLimit(10)],
58+
context: ({ req }) => ({ req, ogm, driver: driverInstance } as Context)
59+
});
60+
}

src/app-process-env.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ declare global {
33
interface ProcessEnv {
44
NEO4J_HOST: string;
55
NODE_ENV: 'development' | 'production';
6+
GRAPH_TOKEN_ROLES_PATH: string;
7+
GRAPH_SECURITY_ENABLED: string;
8+
GRAPH_TOKEN_AUTHORITY: string;
69
}
710
}
811
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
type Mutation {
2+
removeAssembly(assemblyName: String!): Assembly @auth(rules: [{ operations: [DELETE], roles: ["write"] }])
3+
}

src/keycloak.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import axios from 'axios';
2+
3+
export async function getPublicKey(server: string): Promise<string> {
4+
const response = await axios.get(server);
5+
return `-----BEGIN PUBLIC KEY-----\r\n${response.data.public_key}\r\n-----END PUBLIC KEY-----`;
6+
}
7+

src/resolvers/main-resolver.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ const mainResolver = {
22
Query: {
33
isAlive() {
44
return true;
5-
},
5+
}
66
},
77
Mutation: {
8-
hello: (parent: any, parameters: { name: string }): string => {
8+
hello: (_: any, parameters: { name: string }): string => {
99
return `Hello ${parameters.name}!`;
1010
}
1111
}

src/schema.ts

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,28 @@
11
import { loadFilesSync } from '@graphql-tools/load-files';
22
import { mergeResolvers } from '@graphql-tools/merge';
33
import path from 'path';
4-
import { Neo4jGraphQL } from '@neo4j/graphql';
54

6-
export const typesFiles = loadFilesSync(path.join(__dirname, 'definitions/*.graphql'))
5+
export function getTypesFiles(enabledSecurity: boolean): any[] {
6+
let typesFilesPatterns = [
7+
path.join(__dirname, 'definitions/*.graphql'),
8+
];
79

8-
const resolverPatterns = [
9-
path.join(__dirname, 'resolvers/*.ts'),
10-
path.join(__dirname, 'resolvers/*.js')
11-
];
12-
13-
const resolversFiles = loadFilesSync(resolverPatterns);
14-
const resolvers = mergeResolvers(resolversFiles);
10+
if (enabledSecurity) {
11+
typesFilesPatterns = [
12+
...typesFilesPatterns,
13+
path.join(__dirname, 'definitions-rights/*.graphql')
14+
];
15+
}
1516

16-
const neo4jGraphQL = new Neo4jGraphQL({
17-
typeDefs: typesFiles,
18-
resolvers,
19-
});
17+
return loadFilesSync(typesFilesPatterns);
18+
}
2019

21-
const schema = neo4jGraphQL.schema;
20+
export function getResolvers(): any {
21+
const resolverPatterns = [
22+
path.join(__dirname, 'resolvers/*.ts'),
23+
path.join(__dirname, 'resolvers/*.js')
24+
];
2225

23-
export default schema;
26+
const resolversFiles = loadFilesSync(resolverPatterns);
27+
return mergeResolvers(resolversFiles);
28+
}

0 commit comments

Comments
 (0)