@cran/gql.core
Version:
Cran/GraphQL Core Utilities
127 lines (126 loc) • 5.88 kB
JavaScript
;
/* 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;
}