Add missing authorization on views endpoints
Updated ApiKeys authorization to match elsewhere
This commit is contained in:
parent
e84fb5e6ba
commit
83f32be6f7
10
server/api/__snapshots__/views.test.js.snap
Normal file
10
server/api/__snapshots__/views.test.js.snap
Normal file
@ -0,0 +1,10 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`#views.list should require authentication 1`] = `
|
||||
Object {
|
||||
"error": "authentication_required",
|
||||
"message": "Authentication required",
|
||||
"ok": false,
|
||||
"status": 401,
|
||||
}
|
||||
`;
|
@ -6,7 +6,9 @@ import auth from './middlewares/authentication';
|
||||
import pagination from './middlewares/pagination';
|
||||
import { presentApiKey } from '../presenters';
|
||||
import { ApiKey } from '../models';
|
||||
import policy from '../policies';
|
||||
|
||||
const { authorize } = policy;
|
||||
const router = new Router();
|
||||
|
||||
router.post('apiKeys.create', auth(), async ctx => {
|
||||
@ -14,6 +16,7 @@ router.post('apiKeys.create', auth(), async ctx => {
|
||||
ctx.assertPresent(name, 'name is required');
|
||||
|
||||
const user = ctx.state.user;
|
||||
authorize(user, 'create', ApiKey);
|
||||
|
||||
const key = await ApiKey.create({
|
||||
name,
|
||||
@ -36,9 +39,7 @@ router.post('apiKeys.list', auth(), pagination(), async ctx => {
|
||||
limit: ctx.state.pagination.limit,
|
||||
});
|
||||
|
||||
const data = keys.map(key => {
|
||||
return presentApiKey(ctx, key);
|
||||
});
|
||||
const data = keys.map(key => presentApiKey(ctx, key));
|
||||
|
||||
ctx.body = {
|
||||
pagination: ctx.state.pagination,
|
||||
@ -52,10 +53,8 @@ router.post('apiKeys.delete', auth(), async ctx => {
|
||||
|
||||
const user = ctx.state.user;
|
||||
const key = await ApiKey.findById(id);
|
||||
authorize(user, 'delete', ApiKey);
|
||||
|
||||
if (!key || key.userId !== user.id) throw httpErrors.BadRequest();
|
||||
|
||||
// Delete the actual document
|
||||
try {
|
||||
await key.destroy();
|
||||
} catch (e) {
|
||||
|
@ -3,7 +3,7 @@ import TestServer from 'fetch-test-server';
|
||||
import app from '..';
|
||||
import { flushdb, seed } from '../test/support';
|
||||
import { buildUser } from '../test/factories';
|
||||
import Collection from '../models/Collection';
|
||||
import { Collection } from '../models';
|
||||
const server = new TestServer(app.callback());
|
||||
|
||||
beforeEach(flushdb);
|
||||
|
@ -1,10 +1,9 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from 'fetch-test-server';
|
||||
import app from '..';
|
||||
import { View, Star } from '../models';
|
||||
import { Document, View, Star } from '../models';
|
||||
import { flushdb, seed } from '../test/support';
|
||||
import { buildUser } from '../test/factories';
|
||||
import Document from '../models/Document';
|
||||
|
||||
const server = new TestServer(app.callback());
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from 'fetch-test-server';
|
||||
import app from '..';
|
||||
import Authentication from '../models/Authentication';
|
||||
import { Authentication } from '../models';
|
||||
import { flushdb, seed } from '../test/support';
|
||||
import * as Slack from '../slack';
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import { flushdb, seed } from '../../test/support';
|
||||
import ApiKey from '../../models/ApiKey';
|
||||
import { ApiKey } from '../../models';
|
||||
import randomstring from 'randomstring';
|
||||
import auth from './authentication';
|
||||
|
||||
|
@ -2,8 +2,7 @@
|
||||
import Router from 'koa-router';
|
||||
import httpErrors from 'http-errors';
|
||||
|
||||
import Team from '../models/Team';
|
||||
import User from '../models/User';
|
||||
import { Team, User } from '../models';
|
||||
|
||||
import auth from './middlewares/authentication';
|
||||
import pagination from './middlewares/pagination';
|
||||
|
@ -2,7 +2,7 @@
|
||||
import uuid from 'uuid';
|
||||
import Router from 'koa-router';
|
||||
import { makePolicy, signPolicy, publicS3Endpoint } from '../utils/s3';
|
||||
import Event from '../models/Event';
|
||||
import { Event } from '../models';
|
||||
import auth from './middlewares/authentication';
|
||||
import { presentUser } from '../presenters';
|
||||
|
||||
|
@ -1,24 +1,26 @@
|
||||
// @flow
|
||||
import Router from 'koa-router';
|
||||
import httpErrors from 'http-errors';
|
||||
import auth from './middlewares/authentication';
|
||||
import { presentView } from '../presenters';
|
||||
import { View, Document } from '../models';
|
||||
import policy from '../policies';
|
||||
|
||||
const { authorize } = policy;
|
||||
const router = new Router();
|
||||
|
||||
router.post('views.list', auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertPresent(id, 'id is required');
|
||||
|
||||
const user = ctx.state.user;
|
||||
const document = await Document.findById(id);
|
||||
authorize(user, 'read', document);
|
||||
|
||||
const views = await View.findAll({
|
||||
where: {
|
||||
documentId: id,
|
||||
},
|
||||
where: { documentId: id },
|
||||
order: [['updatedAt', 'DESC']],
|
||||
});
|
||||
|
||||
// Collectiones
|
||||
let users = [];
|
||||
let count = 0;
|
||||
await Promise.all(
|
||||
@ -42,11 +44,13 @@ router.post('views.create', auth(), async ctx => {
|
||||
|
||||
const user = ctx.state.user;
|
||||
const document = await Document.findById(id);
|
||||
|
||||
if (!document || document.teamId !== user.teamId)
|
||||
throw httpErrors.BadRequest();
|
||||
authorize(user, 'read', document);
|
||||
|
||||
await View.increment({ documentId: document.id, userId: user.id });
|
||||
|
||||
ctx.body = {
|
||||
success: true,
|
||||
};
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
73
server/api/views.test.js
Normal file
73
server/api/views.test.js
Normal file
@ -0,0 +1,73 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from 'fetch-test-server';
|
||||
import app from '..';
|
||||
import { flushdb, seed } from '../test/support';
|
||||
import { buildUser } from '../test/factories';
|
||||
|
||||
const server = new TestServer(app.callback());
|
||||
|
||||
beforeEach(flushdb);
|
||||
afterAll(server.close);
|
||||
|
||||
describe('#views.list', async () => {
|
||||
it('should return views for a document', async () => {
|
||||
const { user, document } = await seed();
|
||||
const res = await server.post('/api/views.list', {
|
||||
body: { token: user.getJwtToken(), id: document.id },
|
||||
});
|
||||
expect(res.status).toEqual(200);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const { document } = await seed();
|
||||
const res = await server.post('/api/views.list', {
|
||||
body: { id: document.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(401);
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should require authorization', async () => {
|
||||
const { document } = await seed();
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/views.list', {
|
||||
body: { token: user.getJwtToken(), id: document.id },
|
||||
});
|
||||
expect(res.status).toEqual(403);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#views.create', async () => {
|
||||
it('should allow creating a view record for document', async () => {
|
||||
const { user, document } = await seed();
|
||||
const res = await server.post('/api/views.create', {
|
||||
body: { token: user.getJwtToken(), id: document.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.success).toBe(true);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const { document } = await seed();
|
||||
const res = await server.post('/api/views.create', {
|
||||
body: { id: document.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(401);
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should require authorization', async () => {
|
||||
const { document } = await seed();
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/views.create', {
|
||||
body: { token: user.getJwtToken(), id: document.id },
|
||||
});
|
||||
expect(res.status).toEqual(403);
|
||||
});
|
||||
});
|
@ -2,8 +2,7 @@
|
||||
import Queue from 'bull';
|
||||
import debug from 'debug';
|
||||
import services from '../services';
|
||||
import Document from './models/Document';
|
||||
import Collection from './models/Collection';
|
||||
import { Collection, Document } from './models';
|
||||
|
||||
type DocumentEvent = {
|
||||
name: 'documents.create',
|
||||
|
@ -1,7 +1,6 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import { flushdb, seed } from '../test/support';
|
||||
import Collection from '../models/Collection';
|
||||
import Document from '../models/Document';
|
||||
import { Collection, Document } from '../models';
|
||||
|
||||
beforeEach(flushdb);
|
||||
beforeEach(jest.resetAllMocks);
|
||||
|
14
server/policies/apiKey.js
Normal file
14
server/policies/apiKey.js
Normal file
@ -0,0 +1,14 @@
|
||||
// @flow
|
||||
import policy from './policy';
|
||||
import { ApiKey, User } from '../models';
|
||||
|
||||
const { allow } = policy;
|
||||
|
||||
allow(User, 'create', ApiKey);
|
||||
|
||||
allow(
|
||||
User,
|
||||
['read', 'update', 'delete'],
|
||||
ApiKey,
|
||||
(user, apiKey) => user && user.id === apiKey.userId
|
||||
);
|
@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
import policy from './policy';
|
||||
import Collection from '../models/Collection';
|
||||
import User from '../models/User';
|
||||
import { Collection, User } from '../models';
|
||||
|
||||
const { allow } = policy;
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
import policy from './policy';
|
||||
import Document from '../models/Document';
|
||||
import User from '../models/User';
|
||||
import { Document, User } from '../models';
|
||||
|
||||
const { allow } = policy;
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
// @flow
|
||||
import policy from './policy';
|
||||
import './document';
|
||||
import './apiKey';
|
||||
import './collection';
|
||||
import './document';
|
||||
import './user';
|
||||
|
||||
export default policy;
|
||||
|
@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
import policy from './policy';
|
||||
import User from '../models/User';
|
||||
import { User } from '../models';
|
||||
|
||||
const { allow } = policy;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// @flow
|
||||
import User from '../models/User';
|
||||
import { User } from '../models';
|
||||
|
||||
type Options = {
|
||||
includeDetails?: boolean,
|
||||
|
@ -1,6 +1,5 @@
|
||||
// @flow
|
||||
import Team from '../models/Team';
|
||||
import User from '../models/User';
|
||||
import { Team, User } from '../models';
|
||||
import uuid from 'uuid';
|
||||
|
||||
let count = 0;
|
||||
|
Reference in New Issue
Block a user