UNPKG

@okta/stormpath-migration

Version:

Migration tool to import Stormpath data into an Okta tenant

139 lines (128 loc) 4.84 kB
/*! * Copyright (c) 2017, Okta, Inc. and/or its affiliates. All rights reserved. * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.") * * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and limitations under the License. */ const Promise = require('bluebird'); const fs = require('fs'); const readFile = Promise.promisify(fs.readFile); const Base = require('./base'); const logger = require('../util/logger'); const config = require('../util/config'); function warn(policy, msg) { logger.warn(`Password policy id=${policy.id}: ${msg}`); } function mapToOktaPolicy(directory, policy) { const strength = policy.strength; if (strength.maxLength !== 100) { // Default value is 100. If they've changed it, warn them. warn(policy, 'maxLength policy is not supported'); } if (strength.minLowerCase > 1) { warn(policy, 'Okta minLowerCase only requires at least 1 lowercase character'); strength.minLowerCase = 1; } if (strength.minUpperCase > 1) { warn(policy, 'Okta minUpperCase only requires at least 1 uppercase character'); strength.minUpperCase = 1; } if (strength.minNumeric > 1) { warn(policy, 'Okta minNumber only requires at least 1 number'); strength.minNumeric = 1; } if (strength.minSymbol > 1) { warn(policy, 'Okta minSymbol only requires at least 1 symbol'); strength.minSymbol = 1; } if (strength.minDiacritic > 0) { warn(policy, 'minDiacritic policy is not supported'); } return { name: `${directory.name}-Policy`, description: `Imported from Stormpath passwordPolicy id=${policy.id}`, settings: { password: { complexity: { minLength: strength.minLength, minLowerCase: strength.minLowerCase, minUpperCase: strength.minUpperCase, minNumber: strength.minNumeric, minSymbol: strength.minSymbol, excludeUsername: false }, age: { maxAgeDays: -1, expireWarnDays: 0, minAgeMinutes: -1, historyCount: strength.preventReuse }, lockout: { maxAttempts: 10, autoUnlockMinutes: -1, showLockoutFailures: false } } } } } async function loadPasswordPolicy(directory) { if (!directory.passwordPolicy) { logger.verbose(`No passwordPolicy for directoryId=${directory.id}`); return; } const policyId = directory.passwordPolicy.id; const filePath = `${config.stormPathBaseDir}/passwordPolicies/${policyId}.json`; try { logger.verbose(`Loading passwordPolicy id=${policyId} for directoryId=${directory.id}`); const policy = await readFile(filePath, 'utf8'); return mapToOktaPolicy(directory, JSON.parse(policy)); } catch (err) { logger.error(`Failed to read passwordPolicy id=${policyId} for directoryId=${directory.id}: ${err}`); } } async function loadUserInfoMappingRules(directory) { const mappings = []; if (!directory.provider.userInfoMappingRules) { logger.verbose(`No userInfoMappingRules for directoryId=${directory.id}`); return mappings; } const filePath = `${config.stormPathBaseDir}/userInfoMappingRules/${directory.id}.json`; try { logger.verbose(`Loading userInfoMappingRules for directoryId=${directory.id}`); const content = await readFile(filePath, 'utf8'); const rules = JSON.parse(content); for (let item of rules.items) { for (let attribute of item.accountAttributes) { // Account attributes are mapped to custom user schema properties by: // 1. Flattening, i.e. replacing '.' with '_' // 2. Removing the customData prefix, which is not in the user profile const userAttribute = attribute.replace('customData.', '').replace(/\./g, '_'); mappings.push({ externalName: item.name, userAttribute }); } } return mappings; } catch (err) { logger.error(`Failed to read userInfoMappingRules for directoryId=${directory.id}: ${err}`); } } class Directory extends Base { async initializeFromExport() { this.passwordPolicy = await loadPasswordPolicy(this); this.attributeMappings = await loadUserInfoMappingRules(this); if (this.provider.providerId === 'saml') { this.signingCert = this.provider.encodedX509SigningCert .replace('-----BEGIN CERTIFICATE-----\n', '') .replace('\n-----END CERTIFICATE-----', ''); } } } module.exports = Directory;