Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 132 additions & 0 deletions frontend/common/services/useScimConfiguration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { Res } from 'common/types/responses'
import { Req } from 'common/types/requests'
import { service } from 'common/service'

export const scimConfigurationService = service
.enhanceEndpoints({
addTagTypes: ['ScimConfiguration'],
})
.injectEndpoints({
endpoints: (builder) => ({
createScimConfiguration: builder.mutation<
Res['scimConfigurationWithToken'],
Req['createScimConfiguration']
>({
invalidatesTags: (_res, _err, query) => [
{ id: query.organisation_id, type: 'ScimConfiguration' },
],
query: (query: Req['createScimConfiguration']) => ({
method: 'POST',
url: `organisations/${query.organisation_id}/scim/`,
}),
}),
deleteScimConfiguration: builder.mutation<
void,
Req['deleteScimConfiguration']
>({
invalidatesTags: (_res, _err, query) => [
{ id: query.organisation_id, type: 'ScimConfiguration' },
],
query: (query: Req['deleteScimConfiguration']) => ({
method: 'DELETE',
url: `organisations/${query.organisation_id}/scim/`,
}),
}),
getScimConfiguration: builder.query<
Res['scimConfiguration'],
Req['getScimConfiguration']
>({
providesTags: (_res, _err, query) => [
{ id: query.organisation_id, type: 'ScimConfiguration' },
],
query: (query: Req['getScimConfiguration']) => ({
url: `organisations/${query.organisation_id}/scim/`,
}),
}),
regenerateScimToken: builder.mutation<
Res['scimConfigurationWithToken'],
Req['regenerateScimToken']
>({
invalidatesTags: (_res, _err, query) => [
{ id: query.organisation_id, type: 'ScimConfiguration' },
],
query: (query: Req['regenerateScimToken']) => ({
method: 'POST',
url: `organisations/${query.organisation_id}/scim/regenerate-token/`,
}),
}),
// END OF ENDPOINTS
}),
})

export async function createScimConfiguration(
store: any,
data: Req['createScimConfiguration'],
options?: Parameters<
typeof scimConfigurationService.endpoints.createScimConfiguration.initiate
>[1],
) {
return store.dispatch(
scimConfigurationService.endpoints.createScimConfiguration.initiate(
data,
options,
),
)
}
export async function deleteScimConfiguration(
store: any,
data: Req['deleteScimConfiguration'],
options?: Parameters<
typeof scimConfigurationService.endpoints.deleteScimConfiguration.initiate
>[1],
) {
return store.dispatch(
scimConfigurationService.endpoints.deleteScimConfiguration.initiate(
data,
options,
),
)
}
export async function getScimConfiguration(
store: any,
data: Req['getScimConfiguration'],
options?: Parameters<
typeof scimConfigurationService.endpoints.getScimConfiguration.initiate
>[1],
) {
return store.dispatch(
scimConfigurationService.endpoints.getScimConfiguration.initiate(
data,
options,
),
)
}
export async function regenerateScimToken(
store: any,
data: Req['regenerateScimToken'],
options?: Parameters<
typeof scimConfigurationService.endpoints.regenerateScimToken.initiate
>[1],
) {
return store.dispatch(
scimConfigurationService.endpoints.regenerateScimToken.initiate(
data,
options,
),
)
}
// END OF FUNCTION_EXPORTS

export const {
useCreateScimConfigurationMutation,
useDeleteScimConfigurationMutation,
useGetScimConfigurationQuery,
useRegenerateScimTokenMutation,
// END OF EXPORTS
} = scimConfigurationService

/* Usage examples:
const { data, isLoading } = useGetScimConfigurationQuery({ organisation_id: 2 }) //get hook
const [createScimConfiguration, { isLoading, data, isSuccess }] = useCreateScimConfigurationMutation() //create hook
scimConfigurationService.endpoints.getScimConfiguration.select({organisation_id: 2})(store.getState()) //access data from any function
*/
4 changes: 4 additions & 0 deletions frontend/common/types/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,10 @@ export type Req = {
idp_attribute_name: string
}
}
getScimConfiguration: { organisation_id: number }
createScimConfiguration: { organisation_id: number }
deleteScimConfiguration: { organisation_id: number }
regenerateScimToken: { organisation_id: number }
updateIdentity: {
environmentId: string
data: Identity
Expand Down
12 changes: 12 additions & 0 deletions frontend/common/types/responses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,16 @@ export type SAMLAttributeMapping = {
idp_attribute_name: string
}

export type ScimConfiguration = {
created_at: string
token_rotated_at: string
base_url: string
}

export type ScimConfigurationWithToken = ScimConfiguration & {
token: string
}

export type HealthEventType = 'HEALTHY' | 'UNHEALTHY'

export type FeatureHealthEventReasonTextBlock = {
Expand Down Expand Up @@ -1215,6 +1225,8 @@ export type Res = {
metadata_xml: string
}
samlAttributeMapping: PagedResponse<SAMLAttributeMapping>
scimConfiguration: ScimConfiguration
scimConfigurationWithToken: ScimConfigurationWithToken
identitySegments: PagedResponse<Segment>
organisationWebhooks: PagedResponse<Webhook>
projectChangeRequests: PagedResponse<ChangeRequestSummary>
Expand Down
10 changes: 8 additions & 2 deletions frontend/common/utils/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export type PaidFeature =
| 'METADATA'
| 'REALTIME'
| 'SAML'
| 'SCIM'
| 'SCHEDULE_FLAGS'
| 'CREATE_ADDITIONAL_PROJECT'
| '2FA'
Expand Down Expand Up @@ -221,7 +222,11 @@ const Utils = Object.assign({}, BaseUtils, {
flagsmithFeatureExists(flag: string) {
return Object.prototype.hasOwnProperty.call(flagsmith.getAllFlags(), flag)
},
getContentType(contentTypes: ContentType[] | undefined, model: string, type: string) {
getContentType(
contentTypes: ContentType[] | undefined,
model: string,
type: string,
) {
return contentTypes?.find((c: ContentType) => c[model] === type) || null
},
getCreateProjectPermission(organisation: Organisation) {
Expand Down Expand Up @@ -522,7 +527,8 @@ const Utils = Object.assign({}, BaseUtils, {
case 'AUDIT':
case '4_EYES_PROJECT':
case '4_EYES':
case 'SAML': {
case 'SAML':
case 'SCIM': {
plan = 'scale-up'
break
}
Expand Down
6 changes: 6 additions & 0 deletions frontend/web/components/PlanBasedAccess.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ export const featureDescriptions: Record<PaidFeature, any> = {
docs: 'https://docs.flagsmith.com/advanced-use/scheduled-flags',
title: 'Scheduled Flags',
},
'SCIM': {
description:
'Provision and de-provision users and groups automatically from your identity provider.',
docs: 'https://docs.flagsmith.com/system-administration/authentication/',
title: 'SCIM user provisioning',
},
'STALE_FLAGS': {
description:
'Add automatic stale flag detection, prompting your team to clean up old flags.',
Expand Down
Loading
Loading