mirror of
https://github.com/strapi/strapi.git
synced 2025-12-06 03:52:38 +00:00
Refactor to check permissions on entities before passing to updateMany
This commit is contained in:
parent
119b88a1b1
commit
f2e769cd69
@ -184,7 +184,7 @@ module.exports = {
|
|||||||
async bulkPublish(ctx) {
|
async bulkPublish(ctx) {
|
||||||
const { userAbility } = ctx.state;
|
const { userAbility } = ctx.state;
|
||||||
const { model } = ctx.params;
|
const { model } = ctx.params;
|
||||||
const { query, body } = ctx.request;
|
const { body } = ctx.request;
|
||||||
const { ids } = body;
|
const { ids } = body;
|
||||||
|
|
||||||
await validateBulkActionInput(body);
|
await validateBulkActionInput(body);
|
||||||
@ -196,24 +196,27 @@ module.exports = {
|
|||||||
return ctx.forbidden();
|
return ctx.forbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
const permissionQuery = await permissionChecker.sanitizedQuery.publish(query);
|
const entityPromises = ids.map((id) => entityManager.findOneWithCreatorRoles(id, model));
|
||||||
|
const entities = await Promise.all(entityPromises);
|
||||||
|
|
||||||
const idsWhereClause = { id: { $in: ids } };
|
for (const entity of entities) {
|
||||||
const params = {
|
if (!entity) {
|
||||||
...permissionQuery,
|
return ctx.notFound();
|
||||||
filters: {
|
}
|
||||||
$and: [idsWhereClause].concat(permissionQuery.filters || []),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const { count } = await entityManager.publishMany(params, model);
|
if (permissionChecker.cannot.publish(entity)) {
|
||||||
|
return ctx.forbidden();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { count } = await entityManager.publishMany(entities, model);
|
||||||
ctx.body = { count };
|
ctx.body = { count };
|
||||||
},
|
},
|
||||||
|
|
||||||
async bulkUnpublish(ctx) {
|
async bulkUnpublish(ctx) {
|
||||||
const { userAbility } = ctx.state;
|
const { userAbility } = ctx.state;
|
||||||
const { model } = ctx.params;
|
const { model } = ctx.params;
|
||||||
const { query, body } = ctx.request;
|
const { body } = ctx.request;
|
||||||
const { ids } = body;
|
const { ids } = body;
|
||||||
|
|
||||||
await validateBulkActionInput(body);
|
await validateBulkActionInput(body);
|
||||||
@ -225,17 +228,20 @@ module.exports = {
|
|||||||
return ctx.forbidden();
|
return ctx.forbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
const permissionQuery = await permissionChecker.sanitizedQuery.unpublish(query);
|
const entityPromises = ids.map((id) => entityManager.findOneWithCreatorRoles(id, model));
|
||||||
|
const entities = await Promise.all(entityPromises);
|
||||||
|
|
||||||
const idsWhereClause = { id: { $in: ids } };
|
for (const entity of entities) {
|
||||||
const params = {
|
if (!entity) {
|
||||||
...permissionQuery,
|
return ctx.notFound();
|
||||||
filters: {
|
}
|
||||||
$and: [idsWhereClause].concat(permissionQuery.filters || []),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const { count } = await entityManager.unpublishMany(params, model);
|
if (permissionChecker.cannot.publish(entity)) {
|
||||||
|
return ctx.forbidden();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { count } = await entityManager.unpublishMany(entities, model);
|
||||||
ctx.body = { count };
|
ctx.body = { count };
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -17,11 +17,11 @@ describe('Content-Manager', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
global.strapi = {
|
global.strapi = {
|
||||||
entityService: {
|
entityService: {
|
||||||
|
findMany: jest.fn().mockResolvedValue([{ id: 1 }, { id: 2 }]),
|
||||||
update: jest.fn().mockReturnValue({ id: 1, publishedAt: new Date() }),
|
update: jest.fn().mockReturnValue({ id: 1, publishedAt: new Date() }),
|
||||||
},
|
},
|
||||||
db: {
|
db: {
|
||||||
query: jest.fn(() => ({
|
query: jest.fn(() => ({
|
||||||
findMany: jest.fn().mockResolvedValue([{ id: 1 }, { id: 2 }]),
|
|
||||||
updateMany: queryUpdateMock,
|
updateMany: queryUpdateMock,
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
@ -55,13 +55,16 @@ describe('Content-Manager', () => {
|
|||||||
|
|
||||||
test('Publish many content-types', async () => {
|
test('Publish many content-types', async () => {
|
||||||
const uid = 'api::test.test';
|
const uid = 'api::test.test';
|
||||||
const params = { filters: { $and: [1, 2] } };
|
const entities = [
|
||||||
|
{ id: 1, publishedAt: null },
|
||||||
|
{ id: 2, publishedAt: null },
|
||||||
|
];
|
||||||
|
|
||||||
await entityManager.publishMany(params, uid);
|
await entityManager.publishMany(entities, uid);
|
||||||
|
|
||||||
expect(strapi.db.query().updateMany).toBeCalledWith({
|
expect(strapi.db.query().updateMany).toHaveBeenCalledWith({
|
||||||
where: {
|
where: {
|
||||||
$and: [1, 2],
|
id: { $in: [1, 2] },
|
||||||
},
|
},
|
||||||
data: { publishedAt: expect.any(Date) },
|
data: { publishedAt: expect.any(Date) },
|
||||||
});
|
});
|
||||||
@ -74,11 +77,11 @@ describe('Content-Manager', () => {
|
|||||||
global.strapi = {
|
global.strapi = {
|
||||||
db: {
|
db: {
|
||||||
query: jest.fn(() => ({
|
query: jest.fn(() => ({
|
||||||
findMany: jest.fn().mockResolvedValue([{ id: 1 }, { id: 2 }]),
|
|
||||||
updateMany: queryUpdateMock,
|
updateMany: queryUpdateMock,
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
entityService: {
|
entityService: {
|
||||||
|
findMany: jest.fn().mockResolvedValue([{ id: 1 }, { id: 2 }]),
|
||||||
update: jest.fn().mockReturnValue({ id: 1, publishedAt: null }),
|
update: jest.fn().mockReturnValue({ id: 1, publishedAt: null }),
|
||||||
},
|
},
|
||||||
eventHub: { emit: jest.fn(), sanitizeEntity: (entity) => entity },
|
eventHub: { emit: jest.fn(), sanitizeEntity: (entity) => entity },
|
||||||
@ -107,13 +110,16 @@ describe('Content-Manager', () => {
|
|||||||
|
|
||||||
test('Unpublish many content-types', async () => {
|
test('Unpublish many content-types', async () => {
|
||||||
const uid = 'api::test.test';
|
const uid = 'api::test.test';
|
||||||
const params = { filters: { $and: [1, 2] } };
|
const entities = [
|
||||||
|
{ id: 1, publishedAt: new Date() },
|
||||||
|
{ id: 2, publishedAt: new Date() },
|
||||||
|
];
|
||||||
|
|
||||||
await entityManager.unpublishMany(params, uid);
|
await entityManager.unpublishMany(entities, uid);
|
||||||
|
|
||||||
expect(strapi.db.query().updateMany).toBeCalledWith({
|
expect(strapi.db.query().updateMany).toHaveBeenCalledWith({
|
||||||
where: {
|
where: {
|
||||||
$and: [1, 2],
|
id: { $in: [1, 2] },
|
||||||
},
|
},
|
||||||
data: { publishedAt: null },
|
data: { publishedAt: null },
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,7 +3,6 @@
|
|||||||
const { assoc, has, prop, omit } = require('lodash/fp');
|
const { assoc, has, prop, omit } = require('lodash/fp');
|
||||||
const strapiUtils = require('@strapi/utils');
|
const strapiUtils = require('@strapi/utils');
|
||||||
const { mapAsync } = require('@strapi/utils');
|
const { mapAsync } = require('@strapi/utils');
|
||||||
const { transformParamsToQuery } = require('@strapi/utils').convertQueryParams;
|
|
||||||
const { ApplicationError } = require('@strapi/utils').errors;
|
const { ApplicationError } = require('@strapi/utils').errors;
|
||||||
const { getDeepPopulate, getDeepPopulateDraftCount } = require('./utils/populate');
|
const { getDeepPopulate, getDeepPopulateDraftCount } = require('./utils/populate');
|
||||||
const { getDeepRelationsCount } = require('./utils/count');
|
const { getDeepRelationsCount } = require('./utils/count');
|
||||||
@ -267,41 +266,44 @@ module.exports = ({ strapi }) => ({
|
|||||||
return mappedEntity;
|
return mappedEntity;
|
||||||
},
|
},
|
||||||
|
|
||||||
async publishMany(opts, uid) {
|
async publishMany(entities, uid) {
|
||||||
const params = {
|
if (!entities.length) {
|
||||||
...opts,
|
|
||||||
data: {
|
|
||||||
[PUBLISHED_AT_ATTRIBUTE]: new Date(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const query = transformParamsToQuery(uid, params);
|
|
||||||
const entitiesToUpdate = await strapi.db.query(uid).findMany(query);
|
|
||||||
// No entities to update, return early
|
|
||||||
if (!entitiesToUpdate.length) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate entities before publishing, throw if invalid
|
// Validate entities before publishing, throw if invalid
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
entitiesToUpdate.map((entityToUpdate) =>
|
entities.map((entityToUpdate) => {
|
||||||
strapi.entityValidator.validateEntityCreation(
|
if (entityToUpdate[PUBLISHED_AT_ATTRIBUTE]) {
|
||||||
|
throw new ApplicationError('already.published');
|
||||||
|
}
|
||||||
|
|
||||||
|
return strapi.entityValidator.validateEntityCreation(
|
||||||
strapi.getModel(uid),
|
strapi.getModel(uid),
|
||||||
entityToUpdate,
|
entityToUpdate,
|
||||||
{
|
{
|
||||||
isDraft: true,
|
isDraft: true,
|
||||||
},
|
},
|
||||||
entityToUpdate
|
entityToUpdate
|
||||||
)
|
);
|
||||||
)
|
})
|
||||||
);
|
);
|
||||||
// Everything is valid, publish
|
|
||||||
const publishedEntitiesCount = await strapi.db
|
|
||||||
.query(uid)
|
|
||||||
.updateMany({ ...query, data: params.data });
|
|
||||||
|
|
||||||
|
const where = { id: { $in: entities.map((entity) => entity.id) } };
|
||||||
|
const data = {
|
||||||
|
[PUBLISHED_AT_ATTRIBUTE]: new Date(),
|
||||||
|
};
|
||||||
|
const populate = isRelationsPopulateEnabled(uid)
|
||||||
|
? getDeepPopulate(uid, {})
|
||||||
|
: getDeepPopulate(uid, { countMany: true, countOne: true });
|
||||||
|
|
||||||
|
// Everything is valid, publish
|
||||||
|
const publishedEntitiesCount = await strapi.db.query(uid).updateMany({
|
||||||
|
where,
|
||||||
|
data,
|
||||||
|
});
|
||||||
// Get the updated entities since updateMany only returns the count
|
// Get the updated entities since updateMany only returns the count
|
||||||
const publishedEntities = await strapi.db.query(uid).findMany(query);
|
const publishedEntities = await strapi.entityService.findMany(uid, { where, populate });
|
||||||
// Emit the publish event for all updated entities
|
// Emit the publish event for all updated entities
|
||||||
await Promise.all(publishedEntities.map((entity) => emitEvent(ENTRY_PUBLISH, entity, uid)));
|
await Promise.all(publishedEntities.map((entity) => emitEvent(ENTRY_PUBLISH, entity, uid)));
|
||||||
|
|
||||||
@ -309,28 +311,32 @@ module.exports = ({ strapi }) => ({
|
|||||||
return publishedEntitiesCount;
|
return publishedEntitiesCount;
|
||||||
},
|
},
|
||||||
|
|
||||||
async unpublishMany(opts, uid) {
|
async unpublishMany(entities, uid) {
|
||||||
const params = {
|
if (!entities.length) {
|
||||||
...opts,
|
|
||||||
data: {
|
|
||||||
[PUBLISHED_AT_ATTRIBUTE]: null,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const query = transformParamsToQuery(uid, params);
|
|
||||||
const entitiesToUpdate = await strapi.db.query(uid).findMany(query);
|
|
||||||
// No entities to update, return early
|
|
||||||
if (!entitiesToUpdate.length) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No need to validate, unpublish
|
entities.forEach((entity) => {
|
||||||
const unpublishedEntitiesCount = await strapi.db
|
if (!entity[PUBLISHED_AT_ATTRIBUTE]) {
|
||||||
.query(uid)
|
throw new ApplicationError('already.draft');
|
||||||
.updateMany({ ...query, data: params.data });
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const where = { id: { $in: entities.map((entity) => entity.id) } };
|
||||||
|
const data = {
|
||||||
|
[PUBLISHED_AT_ATTRIBUTE]: null,
|
||||||
|
};
|
||||||
|
const populate = isRelationsPopulateEnabled(uid)
|
||||||
|
? getDeepPopulate(uid, {})
|
||||||
|
: getDeepPopulate(uid, { countMany: true, countOne: true });
|
||||||
|
|
||||||
|
// No need to validate, unpublish
|
||||||
|
const unpublishedEntitiesCount = await strapi.db.query(uid).updateMany({
|
||||||
|
where,
|
||||||
|
data,
|
||||||
|
});
|
||||||
// Get the updated entities since updateMany only returns the count
|
// Get the updated entities since updateMany only returns the count
|
||||||
const unpublishedEntities = await strapi.db.query(uid).findMany(query);
|
const unpublishedEntities = await strapi.entityService.findMany(uid, { where, populate });
|
||||||
// Emit the unpublish event for all updated entities
|
// Emit the unpublish event for all updated entities
|
||||||
await Promise.all(unpublishedEntities.map((entity) => emitEvent(ENTRY_UNPUBLISH, entity, uid)));
|
await Promise.all(unpublishedEntities.map((entity) => emitEvent(ENTRY_UNPUBLISH, entity, uid)));
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user