import { ApolloServer } from 'apollo-server' ; import { GraphQLEnumType , GraphQLID , GraphQLList , GraphQLNonNull , GraphQLObjectType , GraphQLSchema , GraphQLString } from 'graphql' ; import { preExecRule , postExecRule , IAuthConfig } from '@graphql-authz/core' ; import { authZApolloPlugin } from '@graphql-authz/apollo-server-plugin' ; const users = [ { id : '1' , username : 'user01' , email : 'user01@gmail.com' , role : 'Customer' } , { id : '2' , username : 'user02' , email : 'user02@gmail.com' , role : 'Admin' } ] ; const posts = [ { id : '1' , title : 'Post01 title' , body : 'Post01 body' , status : 'draft' , authorId : '1' } , { id : '2' , title : 'Post02 title' , body : 'Post02 body' , status : 'public' , authorId : '1' } ] ; interface IContext { user ? : { id : string ; role : string ; } ; } const IsAuthenticated = preExecRule ( { error : 'User is not authenticated' } ) ( ( context ) => ! ! context . user ) ; const IsAdmin = preExecRule ( { error : 'User is not admin' } ) ( ( context ) => context . user ?. role === 'Admin' ) ; const CanReadPost = postExecRule ( { error : 'Access denied' , selectionSet : '{ status author { id } }' } ) ( ( context , fieldArgs , post ) => post . status === 'public' || context . user ?. id === post . author . id ) ; const CanPublishPost = preExecRule ( ) ( async ( context , fieldArgs ) => { const post = await Promise . resolve ( posts . find ( ( { id } ) => id === fieldArgs . postId ) ) ; return ! post || post . authorId === context . user ?. id ; } ) ; const authZRules = { IsAuthenticated , IsAdmin , CanReadPost , CanPublishPost } as const ; function createAuthZExtensions ( args ) { return { authz : { directives : [ { name : 'authz' , arguments : args } ] } } ; } const Status = new GraphQLEnumType ( { name : 'Status' , values : { draft : { value : 'draft' } , public : { value : 'public' } } } ) ; const Post = new GraphQLObjectType ( { name : 'Post' , extensions : createAuthZExtensions ( { rules : [ 'CanReadPost' ] } ) , fields : ( ) => ( { id : { type : new GraphQLNonNull ( GraphQLID ) } , title : { type : new GraphQLNonNull ( GraphQLString ) } , body : { type : new GraphQLNonNull ( GraphQLString ) } , status : { type : new GraphQLNonNull ( Status ) } , author : { type : new GraphQLNonNull ( User ) , resolve : ( parent ) => users . find ( ( { id } ) => id === parent . authorId ) } } ) } ) ; const User = new GraphQLObjectType ( { name : 'User' , fields : ( ) => ( { id : { type : new GraphQLNonNull ( GraphQLID ) } , username : { type : new GraphQLNonNull ( GraphQLString ) } , email : { type : new GraphQLNonNull ( GraphQLString ) , extensions : createAuthZExtensions ( { rules : [ 'IsAdmin' ] } ) } , posts : { type : new GraphQLNonNull ( new GraphQLList ( new GraphQLNonNull ( Post ) ) ) , resolve : ( parent ) => posts . filter ( ( { authorId } ) => authorId === parent . id ) } } ) } ) ; const Query = new GraphQLObjectType ( { name : 'Query' , fields : { users : { type : new GraphQLNonNull ( new GraphQLList ( new GraphQLNonNull ( User ) ) ) , extensions : createAuthZExtensions ( { rules : [ 'IsAuthenticated' ] } ) , resolve : ( ) => users } , posts : { type : new GraphQLNonNull ( new GraphQLList ( new GraphQLNonNull ( Post ) ) ) , resolve : ( ) => posts } , post : { type : Post , args : { id : { type : new GraphQLNonNull ( GraphQLID ) } } , resolve : ( parent , args ) => posts . find ( ( { id } ) => id === args . id ) } } } ) ; const Mutation = new GraphQLObjectType ( { name : 'Mutation' , fields : { publishPost : { type : new GraphQLNonNull ( Post ) , extensions : createAuthZExtensions ( { rules : [ 'CanPublishPost' ] } ) , args : { postId : { type : new GraphQLNonNull ( GraphQLID ) } } , resolve : ( parent , args ) => { const post = posts . find ( ( { id } ) => id === args . postId ) ; if ( ! post ) { throw new Error ( 'Not Found' ) ; } post . status = 'public' ; return post ; } } } } ) ; const schema = new GraphQLSchema ( { query : Query , mutation : Mutation } ) ; const server = new ApolloServer ( { schema , plugins : [ authZApolloPlugin ( { rules : authZRules } ) ] , context : ( { req } ) => ( { user : users . find ( ( { id } ) => id === req . get ( 'x-user-id' ) ) || null } ) } ) ; server . listen ( ) . then ( ( { url } ) => { console . log ( `🚀  Server ready at ${ url }` ) ; } ) ;