graphql-shield
Version:
GraphQL Server permissions as another layer of abstraction!
110 lines (109 loc) • 4.84 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.shield = exports.getFragmentReplacements = exports.normalizeOptions = void 0;
const tslib_1 = require("tslib");
const object_hash_1 = tslib_1.__importDefault(require("object-hash"));
const resolvers_composition_1 = require("@graphql-tools/resolvers-composition");
const schema_1 = require("@graphql-tools/schema");
const wrap_1 = require("@graphql-tools/wrap");
const constructors_js_1 = require("./constructors.js");
const utils_js_1 = require("./utils.js");
const getResolversFromSchema_js_1 = require("./getResolversFromSchema.js");
const validation_js_1 = require("./validation.js");
const generator_js_1 = require("./generator.js");
const replaceFieldWithFragment_js_1 = require("./replaceFieldWithFragment.js");
const fragments_js_1 = require("./fragments.js");
/**
*
* @param options
*
* Makes sure all of defined rules are in accord with the options
* shield can process.
*
*/
function normalizeOptions(options) {
if (typeof options.fallbackError === 'string') {
options.fallbackError = new Error(options.fallbackError);
}
return {
debug: options.debug !== undefined ? options.debug : false,
allowExternalErrors: (0, utils_js_1.withDefault)(false)(options.allowExternalErrors),
fallbackRule: (0, utils_js_1.withDefault)(constructors_js_1.allow)(options.fallbackRule),
fallbackError: (0, utils_js_1.withDefault)(new Error('Not Authorised!'))(options.fallbackError),
hashFunction: (0, utils_js_1.withDefault)(object_hash_1.default)(options.hashFunction),
disableFragmentsAndPostExecRules: (0, utils_js_1.withDefault)(false)(options.disableFragmentsAndPostExecRules),
};
}
exports.normalizeOptions = normalizeOptions;
function middlewareToCompositionResolver(middlewareWithOptions) {
const { resolve } = middlewareWithOptions;
if (resolve) {
return (next) => (root, args, context, info) => resolve(next, root, args, context, info);
}
return (next) => (root, args, context, info) => next(root, args, context, info);
}
function getFragmentReplacements(middleware) {
const fragmentReplacements = Object.entries(middleware).reduce((result, [objectName, objectFields]) => {
Object.entries(objectFields).forEach(([fieldName, middlewareFunction]) => {
const { fragment, fragments } = middlewareFunction;
if (fragment) {
result.push({
field: fieldName,
fragment,
});
}
if (fragments) {
for (const fragment of fragments) {
result.push({
field: fieldName,
fragment: fragment,
});
}
}
});
return result;
}, []);
return (0, fragments_js_1.prepareFragmentReplacements)(fragmentReplacements);
}
exports.getFragmentReplacements = getFragmentReplacements;
function applyComposition(schema, middleware) {
const compositionRules = Object.entries(middleware).reduce((result, [objectName, objectFields]) => {
Object.entries(objectFields).forEach(([fieldName, middlewareFunction]) => {
const compositionResolver = middlewareToCompositionResolver(middlewareFunction);
result[`${objectName}.${fieldName}`] = [compositionResolver];
});
return result;
}, {});
const originalResolvers = (0, getResolversFromSchema_js_1.getResolversFromSchema)(schema, true, true);
const resolvers = (0, resolvers_composition_1.composeResolvers)(originalResolvers, compositionRules);
return (0, schema_1.addResolversToSchema)({ schema, resolvers });
}
/**
*
* @param schema
* @param ruleTree
* @param options
*
* Validates rules and applies defined rule tree to the schema.
*
*/
function shield(schema, ruleTree, options = {}) {
const normalizedOptions = normalizeOptions(options);
const ruleTreeValidity = (0, validation_js_1.validateRuleTree)(ruleTree);
if (ruleTreeValidity.status === 'ok') {
const middleware = (0, generator_js_1.generateMiddlewareFromSchemaAndRuleTree)(schema, ruleTree, normalizedOptions);
if (normalizedOptions.disableFragmentsAndPostExecRules) {
return applyComposition(schema, middleware);
}
const fragmentReplacements = getFragmentReplacements(middleware);
const wrappedSchema = (0, wrap_1.wrapSchema)({
schema,
transforms: [new replaceFieldWithFragment_js_1.ReplaceFieldWithFragment(fragmentReplacements || [])],
});
return applyComposition(wrappedSchema, middleware);
}
else {
throw new validation_js_1.ValidationError(ruleTreeValidity.message);
}
}
exports.shield = shield;