Skip to content

Instantly share code, notes, and snippets.

@dukuo
Created June 12, 2018 06:50
Show Gist options
  • Save dukuo/351f581e54b0af863f7155110e8f39c6 to your computer and use it in GitHub Desktop.
Save dukuo/351f581e54b0af863f7155110e8f39c6 to your computer and use it in GitHub Desktop.
GraphQL Shield Rules class
import { shield, and, or, not } from 'graphql-shield'
import {rules} from './rules'
export const permissions = shield({
Mutation: {
createResource: and( rules.isAuthenticated(), rules.hasRoles(["ADMIN", "USER"]), rules.hasScopes(["admin:god", "create:resource"]) ),
deleteResource: and( rules.isAuthenticated(), or( rules.hasRole("ADMIN"), rules.hasRole("EDITOR") ) )
}
}, {
debug:true
})
import { rule } from 'graphql-shield'
import { getUserId } from '../../lib/utils'
import * as BBPromise from 'bluebird'
/**
* Collection of useful methods for checking roles and role scopes from an authenticated user
*/
class Rules {
/**
* Check user roles against Prisma
* @param userId (string) User ID from Context
* @param role (string) Required Role
* @param context
*/
private async _hasRole (userId:string, role:string, context:any) {
const checkIfUserHasRole = await context.prisma.query.users({ where: {
id: userId,
roles_some: {
type:role
}
}
})
console.log("HAS ROLE ", role, "?", checkIfUserHasRole.length > 0)
if(Object.keys(checkIfUserHasRole).length > 0) return true
return false
}
/**
* Check scopes from user roles against Prisma
* @param userId (string) User ID from Context
* @param requiredScope (string)
* @param context
*/
private async _hasScope (userId:string, requiredScope:string, context:any) {
const checkIfUserHasScope = await context.prisma.query.users({ where: {
id: userId,
roles_some: {
scopes_some: {
scope: requiredScope
}
}
}
})
console.log("HAS SCOPE ", requiredScope, "?", checkIfUserHasScope.length > 0)
if(Object.keys(checkIfUserHasScope).length > 0) return true
return false
}
/**
* Check whether user is authenticated and if the user exists
*/
isAuthenticated() {
return rule()(async ( parent, args, context, info ) => {
const id = getUserId(context)
if(!id) throw Error('User not found')
return await context.prisma.exists.User({ id })
})
}
/**
* Check scopes from user roles in parallel
* @param scopes (string[])
*/
hasScopes (scopes:string[]) {
return rule()(async ( parent, args, context, info) => {
const id = getUserId(context)
await BBPromise.all(scopes.map(async (scope) => {
if(await this._hasScope(id, scope, context) === false) throw Error('User not found or does not have required permissions')
return true
}))
return true
})
}
/**
* Check if user is authenticated and has the required role scope
* @param scope (string)
*/
hasScope (scope:string) {
return rule()(async ( parent, args, context, info) => {
const id = getUserId(context)
if ( await this._hasScope(id, scope, context) === false ) throw Error('User not found or does not have required permissions')
return true
})
}
/**
* Check user roles in parallel
* @param roles (string[])
*/
hasRoles (roles:string[]) {
return rule()(async ( parent, args, context, info) => {
const id = getUserId(context)
await BBPromise.all(roles.map(async (role) => {
if(await this._hasRole(id, role, context) === false) throw Error('User not found or doesn\'t have necessary authorization')
return true
}))
return true
})
}
/**
* Check if user is authenticated and has the required role
* @param role (string)
*/
hasRole (role:string) {
return rule()(async ( parent, args, context, info) => {
const id = getUserId(context)
if(!await this._hasRole(id, role, context)) throw Error('User not found or doesn\'t have necessary authorization')
return true
})
}
}
export let rules = new Rules
@maticzav
Copy link

Hey @dukou, I see you've taken one interesting approach! I've never thought of anything like this.

I like the way you pull together all your rules in one class, really interesting. I am not the biggest fan of classes myself, but I see great benefit in how you do it. Would love to see more of this examples! 🎉 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment