-
-
Notifications
You must be signed in to change notification settings - Fork 739
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: take into account project segments permission #5304
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,8 @@ | ||
import FormTemplate from 'component/common/FormTemplate/FormTemplate'; | ||
import { UPDATE_SEGMENT } from 'component/providers/AccessProvider/permissions'; | ||
import { | ||
UPDATE_PROJECT_SEGMENT, | ||
UPDATE_SEGMENT, | ||
} from 'component/providers/AccessProvider/permissions'; | ||
import { useSegmentsApi } from 'hooks/api/actions/useSegmentsApi/useSegmentsApi'; | ||
import { useConstraintsValidation } from 'hooks/api/getters/useConstraintsValidation/useConstraintsValidation'; | ||
import { useSegment } from 'hooks/api/getters/useSegment/useSegment'; | ||
|
@@ -135,7 +138,8 @@ export const EditSegment = ({ modal }: IEditSegmentProps) => { | |
mode='edit' | ||
> | ||
<UpdateButton | ||
permission={UPDATE_SEGMENT} | ||
permission={[UPDATE_SEGMENT, UPDATE_PROJECT_SEGMENT]} | ||
projectId={projectId} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These 2 lines fix the bug on the UI, where the "Save" button was shown as being disabled. |
||
disabled={!hasValidConstraints || overSegmentValuesLimit} | ||
data-testid={SEGMENT_SAVE_BTN_ID} | ||
> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -194,14 +194,14 @@ const useAPI = ({ | |
loadingOn: boolean = true, | ||
) => { | ||
const start = timeApiCallStart( | ||
requestId || `Uknown request happening on ${apiCaller}`, | ||
requestId || `Unknown request happening on ${apiCaller}`, | ||
); | ||
|
||
const res = await requestFunction(apiCaller, requestId, loadingOn); | ||
|
||
timeApiCallEnd( | ||
start, | ||
requestId || `Uknown request happening on ${apiCaller}`, | ||
requestId || `Unknown request happening on ${apiCaller}`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Boyscouting: Noticed the typo and fixed it. |
||
); | ||
|
||
return res; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,7 @@ class PermissionError extends UnleashError { | |
const permissionsMessage = | ||
permissions.length === 1 | ||
? `the "${permissions[0]}" permission` | ||
: `all of the following permissions: ${permissions | ||
: `one of the following permissions: ${permissions | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Boyscouting: I think "one" instead of "all" is more honest and reflective of the real behavior. |
||
.map((perm) => `"${perm}"`) | ||
.join(', ')}`; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,7 @@ import { | |
IFlagResolver, | ||
NONE, | ||
UPDATE_FEATURE_STRATEGY, | ||
UPDATE_PROJECT_SEGMENT, | ||
UPDATE_SEGMENT, | ||
serializeDates, | ||
} from '../../types'; | ||
|
@@ -165,7 +166,7 @@ export class SegmentsController extends Controller { | |
method: 'delete', | ||
path: '/:id', | ||
handler: this.removeSegment, | ||
permission: DELETE_SEGMENT, | ||
permission: [DELETE_SEGMENT, UPDATE_PROJECT_SEGMENT], | ||
acceptAnyContentType: true, | ||
middleware: [ | ||
openApiService.validPath({ | ||
|
@@ -186,7 +187,7 @@ export class SegmentsController extends Controller { | |
method: 'put', | ||
path: '/:id', | ||
handler: this.updateSegment, | ||
permission: UPDATE_SEGMENT, | ||
permission: [UPDATE_SEGMENT, UPDATE_PROJECT_SEGMENT], | ||
middleware: [ | ||
openApiService.validPath({ | ||
summary: 'Update segment by id', | ||
|
@@ -225,7 +226,7 @@ export class SegmentsController extends Controller { | |
method: 'post', | ||
path: '', | ||
handler: this.createSegment, | ||
permission: CREATE_SEGMENT, | ||
permission: [CREATE_SEGMENT, UPDATE_PROJECT_SEGMENT], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These changes add the missing permission on the API level. |
||
middleware: [ | ||
openApiService.validPath({ | ||
summary: 'Create a new segment', | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,12 +7,16 @@ import ApiUser from '../types/api-user'; | |
import { IFeatureToggleStore } from '../features/feature-toggle/types/feature-toggle-store-type'; | ||
import FakeFeatureToggleStore from '../features/feature-toggle/fakes/fake-feature-toggle-store'; | ||
import { ApiTokenType } from '../types/models/api-token'; | ||
import { ISegmentStore } from '../types'; | ||
import FakeSegmentStore from '../../test/fixtures/fake-segment-store'; | ||
|
||
let config: IUnleashConfig; | ||
let featureToggleStore: IFeatureToggleStore; | ||
let segmentStore: ISegmentStore; | ||
|
||
beforeEach(() => { | ||
featureToggleStore = new FakeFeatureToggleStore(); | ||
segmentStore = new FakeSegmentStore(); | ||
config = createTestConfig(); | ||
}); | ||
|
||
|
@@ -21,7 +25,11 @@ test('should add checkRbac to request', () => { | |
hasPermission: jest.fn(), | ||
}; | ||
|
||
const func = rbacMiddleware(config, { featureToggleStore }, accessService); | ||
const func = rbacMiddleware( | ||
config, | ||
{ featureToggleStore, segmentStore }, | ||
accessService, | ||
); | ||
|
||
const cb = jest.fn(); | ||
|
||
|
@@ -40,7 +48,11 @@ test('should give api-user ADMIN permission', async () => { | |
hasPermission: jest.fn(), | ||
}; | ||
|
||
const func = rbacMiddleware(config, { featureToggleStore }, accessService); | ||
const func = rbacMiddleware( | ||
config, | ||
{ featureToggleStore, segmentStore }, | ||
accessService, | ||
); | ||
|
||
const cb = jest.fn(); | ||
const req: any = { | ||
|
@@ -66,7 +78,11 @@ test('should not give api-user ADMIN permission', async () => { | |
hasPermission: jest.fn(), | ||
}; | ||
|
||
const func = rbacMiddleware(config, { featureToggleStore }, accessService); | ||
const func = rbacMiddleware( | ||
config, | ||
{ featureToggleStore, segmentStore }, | ||
accessService, | ||
); | ||
|
||
const cb = jest.fn(); | ||
const req: any = { | ||
|
@@ -94,7 +110,11 @@ test('should not allow user to miss userId', async () => { | |
hasPermission: jest.fn(), | ||
}; | ||
|
||
const func = rbacMiddleware(config, { featureToggleStore }, accessService); | ||
const func = rbacMiddleware( | ||
config, | ||
{ featureToggleStore, segmentStore }, | ||
accessService, | ||
); | ||
|
||
const cb = jest.fn(); | ||
const req: any = { | ||
|
@@ -116,7 +136,11 @@ test('should return false for missing user', async () => { | |
hasPermission: jest.fn(), | ||
}; | ||
|
||
const func = rbacMiddleware(config, { featureToggleStore }, accessService); | ||
const func = rbacMiddleware( | ||
config, | ||
{ featureToggleStore, segmentStore }, | ||
accessService, | ||
); | ||
|
||
const cb = jest.fn(); | ||
const req: any = {}; | ||
|
@@ -134,7 +158,11 @@ test('should verify permission for root resource', async () => { | |
hasPermission: jest.fn(), | ||
}; | ||
|
||
const func = rbacMiddleware(config, { featureToggleStore }, accessService); | ||
const func = rbacMiddleware( | ||
config, | ||
{ featureToggleStore, segmentStore }, | ||
accessService, | ||
); | ||
|
||
const cb = jest.fn(); | ||
const req: any = { | ||
|
@@ -163,7 +191,11 @@ test('should lookup projectId from params', async () => { | |
hasPermission: jest.fn(), | ||
}; | ||
|
||
const func = rbacMiddleware(config, { featureToggleStore }, accessService); | ||
const func = rbacMiddleware( | ||
config, | ||
{ featureToggleStore, segmentStore }, | ||
accessService, | ||
); | ||
|
||
const cb = jest.fn(); | ||
const req: any = { | ||
|
@@ -198,7 +230,11 @@ test('should lookup projectId from feature toggle', async () => { | |
|
||
featureToggleStore.getProjectId = jest.fn().mockReturnValue(projectId); | ||
|
||
const func = rbacMiddleware(config, { featureToggleStore }, accessService); | ||
const func = rbacMiddleware( | ||
config, | ||
{ featureToggleStore, segmentStore }, | ||
accessService, | ||
); | ||
|
||
const cb = jest.fn(); | ||
const req: any = { | ||
|
@@ -231,7 +267,11 @@ test('should lookup projectId from data', async () => { | |
hasPermission: jest.fn(), | ||
}; | ||
|
||
const func = rbacMiddleware(config, { featureToggleStore }, accessService); | ||
const func = rbacMiddleware( | ||
config, | ||
{ featureToggleStore, segmentStore }, | ||
accessService, | ||
); | ||
|
||
const cb = jest.fn(); | ||
const req: any = { | ||
|
@@ -266,7 +306,11 @@ test('Does not double check permission if not changing project when updating tog | |
}; | ||
featureToggleStore.getProjectId = jest.fn().mockReturnValue(oldProjectId); | ||
|
||
const func = rbacMiddleware(config, { featureToggleStore }, accessService); | ||
const func = rbacMiddleware( | ||
config, | ||
{ featureToggleStore, segmentStore }, | ||
accessService, | ||
); | ||
const cb = jest.fn(); | ||
const req: any = { | ||
user: new User({ username: 'user', id: 1 }), | ||
|
@@ -290,7 +334,11 @@ test('UPDATE_TAG_TYPE does not need projectId', async () => { | |
hasPermission: jest.fn().mockReturnValue(true), | ||
}; | ||
|
||
const func = rbacMiddleware(config, { featureToggleStore }, accessService); | ||
const func = rbacMiddleware( | ||
config, | ||
{ featureToggleStore, segmentStore }, | ||
accessService, | ||
); | ||
const cb = jest.fn(); | ||
const req: any = { | ||
user: new User({ username: 'user', id: 1 }), | ||
|
@@ -314,7 +362,11 @@ test('DELETE_TAG_TYPE does not need projectId', async () => { | |
hasPermission: jest.fn().mockReturnValue(true), | ||
}; | ||
|
||
const func = rbacMiddleware(config, { featureToggleStore }, accessService); | ||
const func = rbacMiddleware( | ||
config, | ||
{ featureToggleStore, segmentStore }, | ||
accessService, | ||
); | ||
const cb = jest.fn(); | ||
const req: any = { | ||
user: new User({ username: 'user', id: 1 }), | ||
|
@@ -340,7 +392,11 @@ test('should not expect featureName for UPDATE_FEATURE when projectId specified' | |
hasPermission: jest.fn(), | ||
}; | ||
|
||
const func = rbacMiddleware(config, { featureToggleStore }, accessService); | ||
const func = rbacMiddleware( | ||
config, | ||
{ featureToggleStore, segmentStore }, | ||
accessService, | ||
); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Change requires injecting a |
||
|
||
const cb = jest.fn(); | ||
const req: any = { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,8 @@ import { | |
DELETE_FEATURE, | ||
ADMIN, | ||
UPDATE_FEATURE, | ||
DELETE_SEGMENT, | ||
UPDATE_PROJECT_SEGMENT, | ||
} from '../types/permissions'; | ||
import { IUnleashConfig } from '../types/option'; | ||
import { IUnleashStores } from '../types/stores'; | ||
|
@@ -32,7 +34,10 @@ export function findParam( | |
|
||
const rbacMiddleware = ( | ||
config: Pick<IUnleashConfig, 'getLogger'>, | ||
{ featureToggleStore }: Pick<IUnleashStores, 'featureToggleStore'>, | ||
{ | ||
featureToggleStore, | ||
segmentStore, | ||
}: Pick<IUnleashStores, 'featureToggleStore' | 'segmentStore'>, | ||
accessService: PermissionChecker, | ||
): any => { | ||
const logger = config.getLogger('/middleware/rbac-middleware.ts'); | ||
|
@@ -87,6 +92,17 @@ const rbacMiddleware = ( | |
projectId = 'default'; | ||
} | ||
|
||
// DELETE segment does not include information about the segment's project | ||
// This is needed to check if the user has the right permissions on a project level | ||
if ( | ||
!projectId && | ||
permissionsArray.includes(UPDATE_PROJECT_SEGMENT) | ||
) { | ||
const { id } = params; | ||
const { project } = await segmentStore.get(id); | ||
projectId = project; | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is what I mean by:
|
||
return accessService.hasPermission( | ||
user, | ||
permissionsArray, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These 2 lines fix the bug on the UI, where the "Create" button was shown as being disabled.