Skip to content

Commit

Permalink
refactor: 🚧 add verifyied as custom directive to api
Browse files Browse the repository at this point in the history
Signed-off-by: Manuel Ruck <[email protected]>
  • Loading branch information
Manuel Ruck committed Jul 15, 2024
1 parent 9048e2a commit 2f1d2ce
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 4 deletions.
4 changes: 3 additions & 1 deletion src/graphql/schemas/Device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ export default `
tags: [String]
}
directive @veryfied on OBJECT | FIELD_DEFINITION
type Query {
notificationSettings: NotificationSettings
notificationSettings: NotificationSettings @veryfied
}
type Mutation {
Expand Down
50 changes: 50 additions & 0 deletions src/services/graphql/authDirective.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { defaultFieldResolver, GraphQLSchema } from 'graphql';
import { getDirective, MapperKind, mapSchema } from '@graphql-tools/utils';

export const authDirective = (
directiveName: string,
getUserFn: (token: string) => { hasRole: (role: string) => boolean },
) => {
const typeDirectiveArgumentMaps: Record<string, any> = {};
return {
authDirectiveTypeDefs: `directive @${directiveName}(
requires: Role = ADMIN,
) on OBJECT | FIELD_DEFINITION
enum Role {
ADMIN
REVIEWER
USER
UNKNOWN
}`,
authDirectiveTransformer: (schema: GraphQLSchema) =>
mapSchema(schema, {
[MapperKind.TYPE]: (type) => {
const authDirective = getDirective(schema, type, directiveName)?.[0];
if (authDirective) {
typeDirectiveArgumentMaps[type.name] = authDirective;
}
return undefined;
},
[MapperKind.OBJECT_FIELD]: (fieldConfig, _fieldName, typeName) => {
const authDirective =
getDirective(schema, fieldConfig, directiveName)?.[0] ??
typeDirectiveArgumentMaps[typeName];
if (authDirective) {
const { requires } = authDirective;
if (requires) {
const { resolve = defaultFieldResolver } = fieldConfig;
fieldConfig.resolve = function (source, args, context, info) {
const user = getUserFn(context.headers.authToken);
if (!user.hasRole(requires)) {
throw new Error('not authorized');
}
return resolve(source, args, context, info);
};
return fieldConfig;
}
}
},
}),
};
};
27 changes: 24 additions & 3 deletions src/services/graphql/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import resolvers from '../../graphql/resolvers';
import DataLoader from 'dataloader';
import { votedLoader } from '../../graphql/resolvers/dataLoaders';
import express from 'express';
import { makeExecutableSchema } from '@graphql-tools/schema';

// Models
import {
Expand All @@ -20,17 +21,37 @@ import {
DeputyModel,
} from '@democracy-deutschland/democracy-common';
import { Types } from 'mongoose';
import { authDirective } from './authDirective';

const app = express();

app.get('/test', () => 'hallo');

function getUser(token: string) {
const roles = ['UNKNOWN', 'USER', 'REVIEWER', 'ADMIN'];
return {
hasRole: (role: string) => {
const tokenIndex = roles.indexOf(token);
const roleIndex = roles.indexOf(role);
return roleIndex >= 0 && tokenIndex >= roleIndex;
},
};
}

const { authDirectiveTypeDefs, authDirectiveTransformer } = authDirective('auth', getUser);

let schema = makeExecutableSchema({ typeDefs: [authDirectiveTypeDefs, typeDefs], resolvers });
schema = authDirectiveTransformer(schema);

export const server = new ApolloServer<BaseContext>({
resolvers,
typeDefs,
introspection: true, // process.env.NODE_ENV !== 'production',
// resolvers,
// typeDefs: [authDirectiveTypeDefs, typeDefs],
schema: schema,
introspection: process.env.NODE_ENV !== 'production',
});

console.log('process.env.NODE_ENV', process.env.NODE_ENV);

export const context = async ({ req, res }) => ({
// Connection
res,
Expand Down

0 comments on commit 2f1d2ce

Please sign in to comment.