@roadiehq/catalog-backend-module-okta
Version:
A set of Backstage catalog providers for Okta
191 lines (185 loc) • 6.78 kB
JavaScript
'use strict';
var OktaEntityProvider = require('./OktaEntityProvider.cjs.js');
require('lodash');
var groupNamingStrategyFactory = require('./groupNamingStrategies/groupNamingStrategyFactory.cjs.js');
var userNamingStrategyFactory = require('./userNamingStrategies/userNamingStrategyFactory.cjs.js');
require('slugify');
var userEntityFromOktaUser = require('./userEntityFromOktaUser.cjs.js');
var groupEntityFromOktaGroup = require('./groupEntityFromOktaGroup.cjs.js');
var accountConfig = require('./accountConfig.cjs.js');
var errors = require('@backstage/errors');
var getOktaGroups = require('./getOktaGroups.cjs.js');
var getParentGroup = require('./getParentGroup.cjs.js');
var GroupTree = require('./GroupTree.cjs.js');
var types = require('./types.cjs.js');
var chunk = require('lodash/chunk');
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
var chunk__default = /*#__PURE__*/_interopDefaultCompat(chunk);
const DEFAULT_CHUNK_SIZE = 250;
class OktaOrgEntityProvider extends OktaEntityProvider.OktaEntityProvider {
groupNamingStrategy;
userNamingStrategy;
groupEntityFromOktaGroup;
userEntityFromOktaUser;
includeEmptyGroups;
hierarchyConfig;
customAttributesToAnnotationAllowlist;
chunkSize;
static fromConfig(config, options) {
const oktaConfig = accountConfig.getAccountConfig(config);
if (options.parentGroupField && !options.hierarchyConfig?.parentKey) {
options.hierarchyConfig = {
parentKey: `profile.${options.parentGroupField}`
};
}
return new OktaOrgEntityProvider(oktaConfig, options);
}
constructor(accountConfig, options) {
super(accountConfig, options);
this.groupNamingStrategy = groupNamingStrategyFactory.groupNamingStrategyFactory(
options.groupNamingStrategy
);
this.userNamingStrategy = userNamingStrategyFactory.userNamingStrategyFactory(
options.userNamingStrategy
);
this.groupEntityFromOktaGroup = options?.groupTransformer || groupEntityFromOktaGroup.groupEntityFromOktaGroup;
this.userEntityFromOktaUser = options.userTransformer || userEntityFromOktaUser.userEntityFromOktaUser;
this.includeEmptyGroups = !!options.includeEmptyGroups;
this.hierarchyConfig = options.hierarchyConfig;
this.customAttributesToAnnotationAllowlist = options.customAttributesToAnnotationAllowlist || [];
this.chunkSize = options.chunkSize || DEFAULT_CHUNK_SIZE;
}
getProviderName() {
return `okta-org:all`;
}
async run() {
if (!this.connection) {
throw new Error("Not initialized");
}
this.logger.info("Providing user and group resources from okta");
let groupResources = [];
const userResources = {};
let providedUserCount = 0;
let providedGroupCount = 0;
const client = this.getClient(this.account.orgUrl, [
"okta.groups.read",
"okta.users.read"
]);
const defaultAnnotations = await this.buildDefaultAnnotations();
const allUsers = await client.userApi.listUsers({
search: this.account.userFilter
});
await allUsers.each((rawUser) => {
const user = types.asOktaUser(rawUser);
try {
const userName = this.userNamingStrategy(user);
userResources[userName] = this.userEntityFromOktaUser(
user,
this.userNamingStrategy,
{
annotations: defaultAnnotations
}
);
} catch (e) {
this.logger.warn(
`Failed to add user: ${errors.isError(e) ? e.message : "unknown error"}`
);
}
});
providedUserCount = Object.values(userResources).length;
const oktaGroups = await getOktaGroups.getOktaGroups({
client,
groupFilter: this.account.groupFilter,
key: this.hierarchyConfig?.key,
groupNamingStrategy: this.groupNamingStrategy,
logger: this.logger
});
for (const chunkOfGroups of chunk__default.default(
Object.values(oktaGroups),
this.chunkSize
)) {
const promiseResults = await Promise.allSettled(
chunkOfGroups.map(async (group) => {
const members = [];
const groupUsers = await client.groupApi.listGroupUsers({
groupId: group.id
});
await groupUsers.each((rawUser) => {
const user = types.asOktaUser(rawUser);
try {
const userName = this.userNamingStrategy(user);
if (userResources[userName]) {
members.push(userName);
}
} catch (e) {
this.logger.warn(
`failed to add user to group: ${errors.isError(e) ? e.message : "unknown error"}`
);
}
});
const parentGroup = getParentGroup.getParentGroup({
parentKey: this.hierarchyConfig?.parentKey,
group,
oktaGroups
});
const profileAnnotations = this.getCustomAnnotations(
group,
this.customAttributesToAnnotationAllowlist
);
const annotations = {
...defaultAnnotations,
...profileAnnotations
};
try {
const groupEntity = this.groupEntityFromOktaGroup(
group,
this.groupNamingStrategy,
{
annotations,
members
},
parentGroup
);
return groupEntity;
} catch (e) {
throw new Error(
`failed to add group: ${errors.isError(e) ? e.message : "unknown error"}`
);
}
})
);
for (const promise of promiseResults) {
if (promise.status === "fulfilled") {
groupResources.push(promise.value);
} else {
this.logger.info(
errors.isError(promise.reason) ? promise.reason.message : "unknown error"
);
}
}
}
if (!this.includeEmptyGroups) {
this.logger.info(
`Found ${groupResources.length} groups in okta, pruning the empty ones`
);
groupResources = new GroupTree.GroupTree(groupResources).getGroups({
pruneEmptyMembers: true
});
}
providedGroupCount = groupResources.length;
await this.connection.applyMutation({
type: "full",
entities: [...Object.values(userResources), ...groupResources].map(
(entity) => ({
entity,
locationKey: this.getProviderName()
})
)
});
this.logger.info(
`Finished providing ${providedUserCount} user and ${providedGroupCount} group resources from okta`
);
}
}
exports.OktaOrgEntityProvider = OktaOrgEntityProvider;
//# sourceMappingURL=OktaOrgEntityProvider.cjs.js.map