@voidkey/broker-core
Version:
Core credential minting logic for the voidkey zero-trust credential broker
301 lines • 15.4 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.IdpConfigLoader = void 0;
const yaml = __importStar(require("js-yaml"));
const fs = __importStar(require("fs"));
class IdpConfigLoader {
static loadFromFile(configPath) {
try {
const configContent = fs.readFileSync(configPath, 'utf8');
const config = yaml.load(configContent);
// Handle empty or null config
if (!config) {
return {};
}
// Validate client IdP configurations if present
if (config.clientIdps) {
if (!Array.isArray(config.clientIdps)) {
throw new Error('Invalid configuration: "clientIdps" must be an array');
}
config.clientIdps.forEach((idp, index) => {
this.validateIdpConfig(idp, index);
});
}
// Validate broker IdP configuration if present
if (config.brokerIdp) {
this.validateBrokerIdpConfig(config.brokerIdp);
}
// Validate access providers if present
if (config.accessProviders) {
if (!Array.isArray(config.accessProviders)) {
throw new Error('Invalid configuration: "accessProviders" must be an array');
}
config.accessProviders.forEach((provider, index) => {
this.validateAccessProvider(provider, index);
});
}
// Validate client identities if present
if (config.clientIdentities) {
if (!Array.isArray(config.clientIdentities)) {
throw new Error('Invalid configuration: "clientIdentities" must be an array');
}
config.clientIdentities.forEach((identity, index) => {
this.validateClientIdentity(identity, index, config.clientIdps || [], config.accessProviders || []);
});
}
return config;
}
catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to load IdP configuration from ${configPath}: ${error.message}`);
}
throw error;
}
}
static loadFromString(yamlContent) {
try {
const config = yaml.load(yamlContent);
// Handle empty or null config
if (!config) {
return {};
}
// Validate client IdP configurations if present
if (config.clientIdps) {
if (!Array.isArray(config.clientIdps)) {
throw new Error('Invalid configuration: "clientIdps" must be an array');
}
config.clientIdps.forEach((idp, index) => {
this.validateIdpConfig(idp, index);
});
}
// Validate broker IdP configuration if present
if (config.brokerIdp) {
this.validateBrokerIdpConfig(config.brokerIdp);
}
// Validate access providers if present
if (config.accessProviders) {
if (!Array.isArray(config.accessProviders)) {
throw new Error('Invalid configuration: "accessProviders" must be an array');
}
config.accessProviders.forEach((provider, index) => {
this.validateAccessProvider(provider, index);
});
}
// Validate client identities if present
if (config.clientIdentities) {
if (!Array.isArray(config.clientIdentities)) {
throw new Error('Invalid configuration: "clientIdentities" must be an array');
}
config.clientIdentities.forEach((identity, index) => {
this.validateClientIdentity(identity, index, config.clientIdps || [], config.accessProviders || []);
});
}
return config;
}
catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to parse IdP configuration: ${error.message}`);
}
throw error;
}
}
static validateIdpConfig(idp, index) {
const requiredFields = ['name', 'issuer', 'jwksUri'];
for (const field of requiredFields) {
if (!idp[field] || typeof idp[field] !== 'string') {
throw new Error(`Invalid IdP configuration at index ${index}: missing or invalid "${field}" field`);
}
}
// Validate optional audience field - can be string, array of strings, or undefined
if (idp.audience !== undefined) {
if (typeof idp.audience === 'string') {
// Valid single audience
}
else if (Array.isArray(idp.audience)) {
// Validate each audience in array
if (idp.audience.length === 0) {
throw new Error(`Invalid IdP configuration at index ${index}: "audience" array cannot be empty`);
}
for (let i = 0; i < idp.audience.length; i++) {
if (typeof idp.audience[i] !== 'string') {
throw new Error(`Invalid IdP configuration at index ${index}: "audience" array must contain only strings`);
}
}
}
else {
throw new Error(`Invalid IdP configuration at index ${index}: "audience" must be a string or array of strings`);
}
}
// Validate optional validateAudience field
if (idp.validateAudience !== undefined && typeof idp.validateAudience !== 'boolean') {
throw new Error(`Invalid IdP configuration at index ${index}: "validateAudience" must be a boolean`);
}
// Validate optional algorithms field
if (idp.algorithms && !Array.isArray(idp.algorithms)) {
throw new Error(`Invalid IdP configuration at index ${index}: "algorithms" must be an array`);
}
}
static validateBrokerIdpConfig(brokerIdp) {
// For now, broker IdP is a placeholder - no required fields
if (brokerIdp.name && typeof brokerIdp.name !== 'string') {
throw new Error('Invalid broker IdP configuration: "name" must be a string');
}
if (brokerIdp.issuer && typeof brokerIdp.issuer !== 'string') {
throw new Error('Invalid broker IdP configuration: "issuer" must be a string');
}
// Validate optional audience field - can be string, array of strings, or undefined
if (brokerIdp.audience !== undefined) {
if (typeof brokerIdp.audience === 'string') {
// Valid single audience
}
else if (Array.isArray(brokerIdp.audience)) {
// Validate each audience in array
if (brokerIdp.audience.length === 0) {
throw new Error('Invalid broker IdP configuration: "audience" array cannot be empty');
}
for (let i = 0; i < brokerIdp.audience.length; i++) {
if (typeof brokerIdp.audience[i] !== 'string') {
throw new Error('Invalid broker IdP configuration: "audience" array must contain only strings');
}
}
}
else {
throw new Error('Invalid broker IdP configuration: "audience" must be a string or array of strings');
}
}
// Validate optional validateAudience field
if (brokerIdp.validateAudience !== undefined && typeof brokerIdp.validateAudience !== 'boolean') {
throw new Error('Invalid broker IdP configuration: "validateAudience" must be a boolean');
}
if (brokerIdp.jwksUri && typeof brokerIdp.jwksUri !== 'string') {
throw new Error('Invalid broker IdP configuration: "jwksUri" must be a string');
}
if (brokerIdp.algorithms && !Array.isArray(brokerIdp.algorithms)) {
throw new Error('Invalid broker IdP configuration: "algorithms" must be an array');
}
}
static validateAccessProvider(provider, index) {
const requiredFields = ['name', 'type', 'endpoint'];
for (const field of requiredFields) {
if (!provider[field] || typeof provider[field] !== 'string') {
throw new Error(`Invalid access provider at index ${index}: missing or invalid "${field}" field`);
}
}
// Validate supported provider types
const supportedTypes = ['minio-sts', 'aws-sts'];
if (!supportedTypes.includes(provider.type)) {
throw new Error(`Invalid access provider at index ${index}: unsupported type "${provider.type}". Supported types: ${supportedTypes.join(', ')}`);
}
// Validate optional defaultDuration field
if (provider.defaultDuration !== undefined) {
if (typeof provider.defaultDuration !== 'number' || provider.defaultDuration <= 0) {
throw new Error(`Invalid access provider at index ${index}: "defaultDuration" must be a positive number`);
}
}
// Validate optional brokerAuth field
if (provider.brokerAuth) {
if (typeof provider.brokerAuth !== 'object' || Array.isArray(provider.brokerAuth)) {
throw new Error(`Invalid access provider at index ${index}: "brokerAuth" must be an object`);
}
if (!provider.brokerAuth.tokenSource || typeof provider.brokerAuth.tokenSource !== 'string') {
throw new Error(`Invalid access provider at index ${index}: "brokerAuth.tokenSource" is required and must be a string`);
}
}
}
static validateClientIdentity(identity, index, clientIdps, accessProviders = []) {
if (!identity.subject || typeof identity.subject !== 'string') {
throw new Error(`Invalid client identity at index ${index}: missing or invalid "subject" field`);
}
if (!identity.idp || typeof identity.idp !== 'string') {
throw new Error(`Invalid client identity at index ${index}: missing or invalid "idp" field`);
}
// Verify the referenced IdP exists
const idpNames = clientIdps.map(idp => idp.name);
if (!idpNames.includes(identity.idp)) {
throw new Error(`Invalid client identity at index ${index}: IdP "${identity.idp}" not found in clientIdps`);
}
// Keys are required and must be an object with key configurations
if (!identity.keys || typeof identity.keys !== 'object' || Array.isArray(identity.keys)) {
throw new Error(`Invalid client identity at index ${index}: "keys" is required and must be an object with key configurations`);
}
if (Object.keys(identity.keys).length === 0) {
throw new Error(`Invalid client identity at index ${index}: "keys" must define at least one key configuration`);
}
const providerNames = accessProviders.map(p => p.name);
// Validate each key configuration
Object.entries(identity.keys).forEach(([keyName, keyConfig]) => {
if (typeof keyName !== 'string' || keyName.trim() === '') {
throw new Error(`Invalid client identity at index ${index}: key name must be a non-empty string`);
}
if (typeof keyConfig !== 'object' || Array.isArray(keyConfig)) {
throw new Error(`Invalid client identity at index ${index}: key "${keyName}" must be an object with configuration`);
}
const config = keyConfig;
// Validate required provider field
if (!config.provider || typeof config.provider !== 'string') {
throw new Error(`Invalid client identity at index ${index}: key "${keyName}" must have a valid "provider" field`);
}
// Verify the referenced provider exists
if (!providerNames.includes(config.provider)) {
throw new Error(`Invalid client identity at index ${index}: key "${keyName}" references provider "${config.provider}" which is not defined in accessProviders`);
}
// Validate optional duration field
if (config.duration !== undefined) {
if (typeof config.duration !== 'number' || config.duration <= 0) {
throw new Error(`Invalid client identity at index ${index}: key "${keyName}" duration must be a positive number`);
}
}
// Validate required outputs field
if (!config.outputs || typeof config.outputs !== 'object' || Array.isArray(config.outputs)) {
throw new Error(`Invalid client identity at index ${index}: key "${keyName}" must have an "outputs" object mapping env vars to field paths`);
}
if (Object.keys(config.outputs).length === 0) {
throw new Error(`Invalid client identity at index ${index}: key "${keyName}" outputs must define at least one environment variable mapping`);
}
// Validate each output mapping
Object.entries(config.outputs).forEach(([envVar, fieldPath]) => {
if (typeof envVar !== 'string' || envVar.trim() === '') {
throw new Error(`Invalid client identity at index ${index}: key "${keyName}" output environment variable names must be non-empty strings`);
}
if (typeof fieldPath !== 'string' || fieldPath.trim() === '') {
throw new Error(`Invalid client identity at index ${index}: key "${keyName}" output field paths must be non-empty strings`);
}
});
});
}
}
exports.IdpConfigLoader = IdpConfigLoader;
//# sourceMappingURL=idp-config.js.map