UNPKG

@cran/gql.core

Version:

Cran/GraphQL Core Utilities

127 lines (126 loc) 5.88 kB
"use strict"; /* eslint-disable max-lines */ Object.defineProperty(exports, "__esModule", { value: true }); exports.withRbac = void 0; const Plugin_1 = require("./Plugin"); const createDirective_1 = require("../utilities/createDirective"); const reportMissing_1 = require("../utilities/reportMissing"); const withSchemaFiltering_1 = require("../addons/withSchemaFiltering"); const utils_1 = require("@graphql-tools/utils"); const graphql_1 = require("graphql"); const extensions_1 = require("../utilities/extensions"); // eslint-disable-next-line max-lines-per-function function withRbac(options) { const { name = "rbac", enumName = "role", publicName = "public", rolesParameter = "roles", defaultGrants = {}, } = options; const roles = [...options.roles, publicName,].map(upper); if (!options.denyPublic) { defaultGrants[publicName] = true; } withSchemaFiltering_1.withSchemaFiltering.filter.types.push(function filterType(type, context, schema) { if (!context) { return true; } // internal call const usedTypes = (0, extensions_1.getExtension)(schema, "usedTypes"); const userRoles = (context[rolesParameter] || [publicName,]).map(upper); for (const role of userRoles) { const types = usedTypes[role] || usedTypes[publicName]; if (types[type.name]) { return true; } } return false; }); withSchemaFiltering_1.withSchemaFiltering.filter.fields.push(function filterField(fieldConfig, context) { if (!context) { return true; } // internal call return isAuthorized((0, extensions_1.getExtension)(fieldConfig, "granted"), context[rolesParameter]); }); return [{ typeDefs: [ `enum ${enumName}{${roles.map(upper).join(" ")}}`, ], transformer: { // eslint-disable-next-line max-statements, max-lines-per-function [Plugin_1.PluginPhase.FINALIZE](schema) { const directives = schema.getDirectives() .filter(function filterDirective(directive) { return withSchemaFiltering_1.withSchemaFiltering .filterDirectives(directive, null, schema); }); for (const directive of directives) { for (const arg of directive.args) { (0, extensions_1.setExtension)((0, graphql_1.getNamedType)(arg.type), "authRequired", true); } } const emptyAuth = { grant: [], revoke: [], }; const usedTypes = {}; for (const role of roles) { usedTypes[role] = {}; } (0, extensions_1.setExtension)(schema, "usedTypes", usedTypes); return (0, utils_1.mapSchema)(schema, { [utils_1.MapperKind.COMPOSITE_FIELD](fieldConfig, _fieldName, typeName) { const { resolve = graphql_1.defaultFieldResolver, } = fieldConfig; const typeExt = (0, extensions_1.getExtension)(schema.getType(typeName), "auth", emptyAuth); const fieldExt = (0, extensions_1.getExtension)(fieldConfig, "auth", emptyAuth); const granted = grants(fieldExt.grant, fieldExt.revoke, grants(typeExt.grant, typeExt.revoke, defaultGrants)); (0, extensions_1.setExtension)(fieldConfig, "granted", granted); for (const grant in granted) { usedTypes[grant][(0, graphql_1.getNamedType)(fieldConfig.type).name] = true; for (const arg in (fieldConfig.args || {})) { usedTypes[grant][(0, graphql_1.getNamedType)(fieldConfig.args[arg].type).name] = true; } } fieldConfig.resolve = function resolver(source, args, context, info) { if (!isAuthorized(granted, context[rolesParameter])) { return void (0, reportMissing_1.reportMissing)(info); } return resolve.call(this, source, args, context, info); }; return fieldConfig; }, }); }, }, }, (0, createDirective_1.createDirective)(name, { grant: `[${enumName}!]`, revoke: `[${enumName}!]`, }, { [utils_1.MapperKind.TYPE]([directive,], type) { (0, extensions_1.setExtension)(type, "auth", { grant: directive.grant || [], revoke: directive.revoke || [], }); }, [utils_1.MapperKind.COMPOSITE_FIELD]([directive,], fieldConfig) { (0, extensions_1.setExtension)(fieldConfig, "auth", { grant: directive.grant || [], revoke: directive.revoke || [], }); }, }),]; } exports.withRbac = withRbac; function isAuthorized(granted, roles) { if (roles) { for (const role of roles) { if (granted[role]) { return true; } } } // TODO else public return false; } function upper(value) { return value.toUpperCase(); } function grants(grant, revoke, initial) { const roles = { ...initial, }; for (const role of grant) { roles[role] = true; } for (const role of revoke) { delete roles[role]; } return roles; }