Skip to content
This repository was archived by the owner on Sep 13, 2021. It is now read-only.

Commit

Permalink
api handles chart access authorization only, the rest is done by plug…
Browse files Browse the repository at this point in the history
…ins (#155)

* api handles chart access authorization only, the rest is done by plugins

* bump orm to 3.14.2 and chart-core to 8.8.1

* load plugins before routes (so they can influence the Joi schema)

* only allow formats that are registered by plugins

* find first results with data
  • Loading branch information
gka authored Apr 29, 2020
1 parent 6e40a69 commit 0dea280
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 60 deletions.
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@
"url": "git+https://github.com/datawrapper/datawrapper-api.git"
},
"dependencies": {
"@datawrapper/chart-core": "8.8.1",
"@datawrapper/chart-core": "^8.8.1",
"@datawrapper/locales": "1.0.0",
"@datawrapper/orm": "^3.14.0",
"@datawrapper/orm": "^3.14.2",
"@datawrapper/schemas": "^1.3.0",
"@datawrapper/shared": "0.25.1",
"@hapi/boom": "9.1.0",
Expand Down
63 changes: 33 additions & 30 deletions src/routes/charts.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const { Op } = require('@datawrapper/orm').db;
const { decamelizeKeys, decamelize } = require('humps');
const set = require('lodash/set');
const mime = require('mime');
const { Chart, ChartPublic, User, Folder, Plugin } = require('@datawrapper/orm/models');
const { Chart, ChartPublic, User, Folder } = require('@datawrapper/orm/models');
const { prepareChart } = require('../utils/index.js');
const assignWithEmptyObjects = require('../utils/assignWithEmptyObjects');

Expand Down Expand Up @@ -235,9 +235,9 @@ function register(server, options) {
.required()
.description('5 character long chart ID.'),
format: Joi.string()
.lowercase()
.required()
.description('Export format (pdf, png, svg, zip)')
.valid(...server.app.exportFormats.values())
.description('Export format')
}),
payload: Joi.object({
unit: Joi.string().default('px'),
Expand Down Expand Up @@ -279,9 +279,9 @@ function register(server, options) {
.required()
.description('5 character long chart ID.'),
format: Joi.string()
.lowercase()
.required()
.description('Export format (pdf, png, svg, zip)')
.valid(...server.app.exportFormats.values())
.description('Export format')
}),
query: Joi.object({
unit: Joi.string().default('px'),
Expand Down Expand Up @@ -786,37 +786,40 @@ async function exportChart(request, h) {
const { events, event } = server.app;
const user = auth.artifacts;

const userPlugins = await user.getUserPluginCache();
const plugins = userPlugins && userPlugins.plugins ? userPlugins.plugins.split(',') : [];

if (params.format !== 'png' && !plugins.includes('export-pdf')) {
const pdfPlugin = await Plugin.findByPk('export-pdf');

if (pdfPlugin && pdfPlugin.is_private) {
return Boom.forbidden();
// authorize user
const chart = await Chart.findOne({
where: {
id: params.id,
deleted: { [Op.not]: true }
}
}
});

if (params.format === 'zip' && !plugins.includes('export-zip')) {
const zipPlugin = await Plugin.findByPk('export-zip');
if (!chart) return Boom.notFound();
const mayEdit = await user.mayEditChart(chart);
if (!mayEdit) return Boom.notFound();

if (zipPlugin && zipPlugin.is_private) {
return Boom.forbidden();
}
}
// user is authorized to access chart
// further authoritzation is handled by plugins

Object.assign(payload, params);
try {
const result = await events.emit(
event.CHART_EXPORT,
{
data: payload,
userId: user.id,
auth,
logger
},
{ filter: 'first' }
);
const result = (
await events.emit(
event.CHART_EXPORT,
{
chart,
user,
data: payload,
auth,
logger
},
{
filter: 'success'
}
)
).find(res => res.data);

if (!result) return Boom.badImplementation();

await request.server.methods.logAction(user.id, `chart/export/${params.format}`, params.id);

Expand Down
23 changes: 0 additions & 23 deletions src/routes/visualizations.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,6 @@ module.exports = {
};

async function register(server, options) {
server.app.visualizations = new Map();
server.method('registerVisualization', registerVisualization);

function registerVisualization(plugin, visualizations = []) {
visualizations.forEach(vis => {
const visualization = server.app.visualizations.get(vis.id);

if (visualization) {
server
.logger()
.warn(
{ status: 'skipping', registeredBy: plugin },
`[Visualization] "${vis.id}" already registered.`
);
return;
}

vis.__plugin = plugin;
vis.libraries = vis.libraries || [];
server.app.visualizations.set(vis.id, vis);
});
}

server.route({
method: 'GET',
path: '/{id}',
Expand Down
24 changes: 22 additions & 2 deletions src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,33 @@ async function configure(options = { usePlugins: true, useOpenAPI: true }) {

server.app.event = eventList;
server.app.events = new ApiEventEmitter({ logger: server.logger });
server.app.visualizations = new Map();
server.app.exportFormats = new Set();

server.method('getModel', name => ORM.db.models[name]);
server.method('config', key => (key ? config[key] : config));
server.method('generateToken', generateToken);
server.method('logAction', require('@datawrapper/orm/utils/action').logAction);
server.method('createChartWebsite', require('./publish/create-chart-website.js'));
server.method('registerVisualization', function(plugin, visualizations = []) {
visualizations.forEach(vis => {
const visualization = server.app.visualizations.get(vis.id);

if (visualization) {
server
.logger()
.warn(
{ status: 'skipping', registeredBy: plugin },
`[Visualization] "${vis.id}" already registered.`
);
return;
}

vis.__plugin = plugin;
vis.libraries = vis.libraries || [];
server.app.visualizations.set(vis.id, vis);
});
});

const { validateThemeData } = schemas.initialize({
getSchema: config.api.schemaBaseUrl
Expand All @@ -186,11 +207,10 @@ async function configure(options = { usePlugins: true, useOpenAPI: true }) {
await server.register(OpenAPI, routeOptions);
}

await server.register([require('./routes')], routeOptions);

if (options.usePlugins) {
await server.register([require('./plugin-loader')], routeOptions);
}
await server.register([require('./routes')], routeOptions);

const { events, event } = server.app;
const { general, frontend } = server.methods.config();
Expand Down

0 comments on commit 0dea280

Please sign in to comment.