-
Notifications
You must be signed in to change notification settings - Fork 90
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SALTO-4062: Support Custom Roles in Okta (#6933)
- Loading branch information
1 parent
769c1c6
commit 8139dea
Showing
12 changed files
with
516 additions
and
4 deletions.
There are no files selected for viewing
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
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
30 changes: 30 additions & 0 deletions
30
packages/okta-adapter/src/change_validators/standard_role_deployments.ts
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,30 @@ | ||
/* | ||
* Copyright 2024 Salto Labs Ltd. | ||
* Licensed under the Salto Terms of Use (the "License"); | ||
* You may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.salto.io/terms-of-use | ||
* | ||
* CERTAIN THIRD PARTY SOFTWARE MAY BE CONTAINED IN PORTIONS OF THE SOFTWARE. See NOTICE FILE AT https://github.com/salto-io/salto/blob/main/NOTICES | ||
*/ | ||
import { ChangeValidator, getChangeData, isInstanceChange, InstanceElement, Change } from '@salto-io/adapter-api' | ||
import { ROLE_TYPE_NAME } from '../constants' | ||
import { ROLE_TYPE_TO_LABEL } from '../filters/standard_roles' | ||
|
||
const STANDARD_ROLE_TYPES = new Set(Object.keys(ROLE_TYPE_TO_LABEL)) | ||
const isStandardRoleChange = (change: Change<InstanceElement>): boolean => | ||
getChangeData(change).value.type !== undefined && STANDARD_ROLE_TYPES.has(getChangeData(change).value.type) | ||
|
||
/** | ||
* Block deployments of standard Okta roles | ||
*/ | ||
export const standardRoleDeployments: ChangeValidator = async changes => | ||
changes | ||
.filter(isInstanceChange) | ||
.filter(change => getChangeData(change).elemID.typeName === ROLE_TYPE_NAME) | ||
.filter(isStandardRoleChange) | ||
.map(getChangeData) | ||
.map(instance => ({ | ||
elemID: instance.elemID, | ||
severity: 'Error' as const, | ||
message: 'Standard roles cannot be deployed', | ||
detailedMessage: 'Okta does not support deployments of standard roles.', | ||
})) |
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
83 changes: 83 additions & 0 deletions
83
packages/okta-adapter/src/definitions/deploy/types/roles.ts
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,83 @@ | ||
/* | ||
* Copyright 2024 Salto Labs Ltd. | ||
* Licensed under the Salto Terms of Use (the "License"); | ||
* You may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.salto.io/terms-of-use | ||
* | ||
* CERTAIN THIRD PARTY SOFTWARE MAY BE CONTAINED IN PORTIONS OF THE SOFTWARE. See NOTICE FILE AT https://github.com/salto-io/salto/blob/main/NOTICES | ||
*/ | ||
|
||
import _ from 'lodash' | ||
import { definitions } from '@salto-io/adapter-components' | ||
import { getParents, safeJsonStringify, validatePlainObject } from '@salto-io/adapter-utils' | ||
import { | ||
Change, | ||
ChangeGroup, | ||
InstanceElement, | ||
getChangeData, | ||
isAdditionChange, | ||
isReferenceExpression, | ||
} from '@salto-io/adapter-api' | ||
import { logger } from '@salto-io/logging' | ||
import { ROLE_TYPE_NAME } from '../../../constants' | ||
|
||
const log = logger(module) | ||
|
||
/** | ||
* When adding a custom role, we must specify permissions for the role. | ||
* In the role POST request, the permission object must be "flattened" to a list of permission labels. | ||
* permission conditions, if exist, must be added in a separate request, therefore we store those in the shared context. | ||
*/ | ||
export const adjustRoleAdditionChange: definitions.AdjustFunction< | ||
definitions.deploy.ChangeAndExtendedContext | ||
> = async ({ value, context }) => { | ||
const { sharedContext, change } = context | ||
validatePlainObject(value, ROLE_TYPE_NAME) | ||
const permissions = _.get(value, 'permissions') | ||
if (!_.isArray(permissions)) { | ||
log.error('expected permissions to be an array, instead got %s', safeJsonStringify(permissions)) | ||
throw new Error('missing permissions for role') | ||
} | ||
const mappedPermissions = permissions.map(permission => { | ||
if (_.isPlainObject(permission?.conditions)) { | ||
_.set(sharedContext, [getChangeData(change).elemID.getFullName(), permission.label], true) | ||
} | ||
return permission.label | ||
}) | ||
return { | ||
value: { | ||
...value, | ||
permissions: mappedPermissions, | ||
}, | ||
} | ||
} | ||
|
||
export const isPermissionChangeOfAddedRole = ( | ||
change: Change<InstanceElement>, | ||
changeGroup: Readonly<ChangeGroup>, | ||
): boolean => { | ||
const parent = getParents(getChangeData(change))[0] | ||
const parentName = isReferenceExpression(parent) ? parent.elemID.getFullName() : undefined | ||
const parentChange = changeGroup.changes.find(c => getChangeData(c).elemID.getFullName() === parentName) | ||
return parentChange !== undefined && isAdditionChange(parentChange) | ||
} | ||
|
||
/** | ||
* Condition to determine if we should update the role permission. | ||
* role permission should be updated on Role modification changes, or on Role addition changes that contains permissions with conditions. | ||
*/ | ||
export const shouldUpdateRolePermission: definitions.deploy.DeployRequestCondition['custom'] = | ||
() => | ||
({ change, changeGroup, sharedContext }) => { | ||
const inst = getChangeData(change) | ||
const parent = getParents(inst)[0] | ||
const parentName = isReferenceExpression(parent) ? parent.elemID.getFullName() : undefined | ||
if (parentName !== undefined && isPermissionChangeOfAddedRole(change, changeGroup)) { | ||
// only make request for permission that their "conditions" were not deployed yet | ||
if (_.get(sharedContext, [parentName, inst.value.label]) === true) { | ||
log.debug('deploying permission condition for %s', getChangeData(change).elemID.getFullName()) | ||
return true | ||
} | ||
return false | ||
} | ||
return true | ||
} |
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
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
Oops, something went wrong.