-
-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #17611 from strapi/feature/rw-stage-rbac
Feature: Stage permissions for review workflows
- Loading branch information
Showing
64 changed files
with
3,168 additions
and
806 deletions.
There are no files selected for viewing
255 changes: 255 additions & 0 deletions
255
api-tests/core/admin/ee/review-workflows-stage-permissions.test.api.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,255 @@ | ||
'use strict'; | ||
|
||
const { mapAsync } = require('@strapi/utils'); | ||
|
||
const { createStrapiInstance } = require('api-tests/strapi'); | ||
const { createAuthRequest, createRequest } = require('api-tests/request'); | ||
const { createTestBuilder } = require('api-tests/builder'); | ||
const { describeOnCondition } = require('api-tests/utils'); | ||
|
||
const { | ||
STAGE_MODEL_UID, | ||
WORKFLOW_MODEL_UID, | ||
ENTITY_STAGE_ATTRIBUTE, | ||
} = require('../../../../packages/core/admin/ee/server/constants/workflows'); | ||
const { create } = require('lodash'); | ||
|
||
const edition = process.env.STRAPI_DISABLE_EE === 'true' ? 'CE' : 'EE'; | ||
|
||
const productUID = 'api::product.product'; | ||
const model = { | ||
draftAndPublish: true, | ||
pluginOptions: {}, | ||
singularName: 'product', | ||
pluralName: 'products', | ||
displayName: 'Product', | ||
kind: 'collectionType', | ||
attributes: { | ||
name: { | ||
type: 'string', | ||
}, | ||
}, | ||
options: { | ||
reviewWorkflows: true, | ||
}, | ||
}; | ||
|
||
const baseWorkflow = { | ||
contentTypes: [productUID], | ||
stages: [{ name: 'Stage 1' }, { name: 'Stage 2' }], | ||
}; | ||
|
||
const getStageTransitionPermissions = (roleIds) => { | ||
return roleIds.map((roleId) => ({ | ||
action: 'admin::review-workflows.stage.transition', | ||
role: roleId, | ||
})); | ||
}; | ||
|
||
describeOnCondition(edition === 'EE')('Review workflows', () => { | ||
const builder = createTestBuilder(); | ||
|
||
let strapi; | ||
let workflow; | ||
let rq; | ||
let roles; | ||
|
||
const createWorkflow = async (data) => { | ||
const name = `workflow-${Math.random().toString(36)}`; | ||
const req = await rq.post('/admin/review-workflows/workflows?populate=*', { | ||
body: { data: { name, ...data } }, | ||
}); | ||
|
||
const status = req.statusCode; | ||
const error = req.body.error; | ||
const workflow = req.body.data; | ||
|
||
return { workflow, status, error }; | ||
}; | ||
|
||
const updateWorkflow = async (id, data) => { | ||
const req = await rq.put(`/admin/review-workflows/workflows/${id}?populate=stages`, { | ||
body: { data }, | ||
}); | ||
|
||
const status = req.statusCode; | ||
const error = req.body.error; | ||
const workflow = req.body.data; | ||
|
||
return { workflow, status, error }; | ||
}; | ||
|
||
const deleteWorkflow = async (id) => { | ||
const req = await rq.delete(`/admin/review-workflows/workflows/${id}`); | ||
|
||
const status = req.statusCode; | ||
const error = req.body.error; | ||
const workflow = req.body.data; | ||
|
||
return { workflow, status, error }; | ||
}; | ||
|
||
beforeAll(async () => { | ||
await builder.addContentTypes([model]).build(); | ||
|
||
strapi = await createStrapiInstance(); | ||
rq = await createAuthRequest({ strapi }); | ||
|
||
workflow = await createWorkflow(baseWorkflow); | ||
|
||
// Get default roles | ||
const { body } = await rq.get('/admin/roles'); | ||
roles = body.data; | ||
}); | ||
|
||
afterAll(async () => { | ||
await strapi.destroy(); | ||
await builder.cleanup(); | ||
}); | ||
|
||
describe('Assign workflow permissions', () => { | ||
// Create stage with permissions | ||
test('Can assign new stage permissions', async () => { | ||
const { workflow } = await createWorkflow({ | ||
...baseWorkflow, | ||
stages: [ | ||
{ | ||
...baseWorkflow.stages[0], | ||
permissions: getStageTransitionPermissions([roles[0].id, roles[1].id]), | ||
}, | ||
baseWorkflow.stages[1], | ||
], | ||
}); | ||
|
||
expect(workflow.stages[0].permissions).toHaveLength(2); | ||
}); | ||
|
||
// Can unassign a role | ||
test('Can remove stage permissions', async () => { | ||
// Create workflow with permissions to transition to role 0 and 1 | ||
const { workflow } = await createWorkflow({ | ||
...baseWorkflow, | ||
stages: [ | ||
{ | ||
...baseWorkflow.stages[0], | ||
permissions: getStageTransitionPermissions([roles[0].id, roles[1].id]), | ||
}, | ||
baseWorkflow.stages[1], | ||
], | ||
}); | ||
|
||
// Update workflow to remove role 1 | ||
const { workflow: updatedWorkflow } = await updateWorkflow(workflow.id, { | ||
stages: [ | ||
{ | ||
...workflow.stages[0], | ||
permissions: getStageTransitionPermissions([roles[0].id]), | ||
}, | ||
workflow.stages[1], | ||
], | ||
}); | ||
|
||
// Validate that permissions have been removed from database | ||
const deletedPermission = await strapi.query('admin::permission').findOne({ | ||
where: { | ||
id: workflow.stages[0].permissions[1].id, | ||
}, | ||
}); | ||
|
||
expect(updatedWorkflow.stages[0].permissions).toHaveLength(1); | ||
expect(deletedPermission).toBeNull(); | ||
}); | ||
|
||
test('Deleting stage removes permissions', async () => { | ||
const { workflow } = await createWorkflow({ | ||
...baseWorkflow, | ||
stages: [ | ||
{ | ||
...baseWorkflow.stages[0], | ||
permissions: getStageTransitionPermissions([roles[0].id, roles[1].id]), | ||
}, | ||
baseWorkflow.stages[1], | ||
], | ||
}); | ||
|
||
const { workflow: updatedWorkflow } = await updateWorkflow(workflow.id, { | ||
...workflow, | ||
stages: [workflow.stages[1]], | ||
}); | ||
|
||
// Deleted stage permissions should be removed from database | ||
const permissions = await strapi.query('admin::permission').findMany({ | ||
where: { | ||
id: { $in: workflow.stages[0].permissions.map((p) => p.id) }, | ||
}, | ||
}); | ||
|
||
expect(permissions).toHaveLength(0); | ||
}); | ||
|
||
test('Deleting workflow removes permissions', async () => { | ||
const { workflow } = await createWorkflow({ | ||
...baseWorkflow, | ||
stages: [ | ||
{ | ||
...baseWorkflow.stages[0], | ||
permissions: getStageTransitionPermissions([roles[0].id, roles[1].id]), | ||
}, | ||
], | ||
}); | ||
|
||
await deleteWorkflow(workflow.id); | ||
|
||
// Deleted workflow permissions should be removed from database | ||
const permissions = await strapi.query('admin::permission').findMany({ | ||
where: { | ||
id: { $in: workflow.stages[0].permissions.map((p) => p.id) }, | ||
}, | ||
}); | ||
|
||
expect(permissions).toHaveLength(0); | ||
}); | ||
|
||
test('Fails when using invalid action', async () => { | ||
const { status, error } = await createWorkflow({ | ||
...baseWorkflow, | ||
stages: [ | ||
{ | ||
...baseWorkflow.stages[0], | ||
permissions: [{ action: 'invalid-action', role: roles[0].id }], | ||
}, | ||
], | ||
}); | ||
|
||
expect(status).toBe(400); | ||
expect(error.name).toBe('ValidationError'); | ||
}); | ||
|
||
// TODO | ||
test.skip('Can send permissions as undefined to apply partial update', async () => { | ||
// Creates workflow with permissions | ||
const { workflow } = await createWorkflow({ | ||
...baseWorkflow, | ||
stages: [ | ||
{ | ||
...baseWorkflow.stages[0], | ||
permissions: [{ action: 'invalid-action', role: roles[0].id }], | ||
}, | ||
], | ||
}); | ||
|
||
const { workflow: updatedWorkflow } = await updateWorkflow(workflow.id, { | ||
...workflow, | ||
stages: [ | ||
{ | ||
...workflow.stages[0], | ||
permissions: undefined, | ||
}, | ||
], | ||
}); | ||
|
||
// Permissions should be kept | ||
expect(updatedWorkflow.stages[0].permissions).toHaveLength(1); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.