blob: cc9fe5ac2f04248cd2ef8f9885c7554ab81cd1e0 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with 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.
*/
var App = require('app');
/**
* Mixin for loading and setting secure configs
*
* @type {Ember.Mixin}
*/
App.AddSecurityConfigs = Em.Mixin.create({
kerberosDescriptor: {},
kerberosDescriptorProperties: require('data/HDP2/kerberos_descriptor_properties'),
/**
* security configs, which values should be modified after APPLY CONFIGURATIONS stage
*/
secureConfigs: [
{
name: 'zookeeper_principal_name',
serviceName: 'ZOOKEEPER'
},
{
name: 'knox_principal_name',
serviceName: 'KNOX'
},
{
name: 'storm_principal_name',
serviceName: 'STORM'
},
{
name: 'nimbus_principal_name',
serviceName: 'STORM'
}
],
/**
* Store status of kerberos descriptor located in cluster artifacts.
* This status needed for Add Service Wizard to select appropriate method to create
* or update descriptor.
*
* @param {Boolean} isExists <code>true</code> if cluster descriptor present
*/
storeClusterDescriptorStatus: function(isExists) {
if (this.get('isWithinAddService')) {
this.get('wizardController').setDBProperty('isClusterDescriptorExists', isExists);
}
},
/**
*
* @param {object[]} items - stack descriptor json response
* @returns {App.ServiceConfigProperty[]}
*/
createServicesStackDescriptorConfigs: function (items) {
var self = this;
var configs = [];
var clusterConfigs = [];
var kerberosDescriptor = Em.get(items, 'KerberosDescriptor.kerberos_descriptor');
this.set('kerberosDescriptor', kerberosDescriptor);
// generate configs for root level properties object, currently realm, keytab_dir
clusterConfigs = clusterConfigs.concat(this.expandKerberosStackDescriptorProps(kerberosDescriptor.properties, 'Cluster', 'stackConfigs'));
// generate configs for root level identities object, currently spnego property
clusterConfigs = clusterConfigs.concat(this.createConfigsByIdentities(kerberosDescriptor.identities, 'Cluster'));
kerberosDescriptor.services.forEach(function (service) {
var serviceName = service.name;
// generate configs for service level identity objects
configs = configs.concat(self.createResourceConfigs(service, serviceName));
// generate configs for service component level identity object
Em.getWithDefault(service, 'components', []).forEach(function (component) {
configs = configs.concat(self.createResourceConfigs(component, serviceName));
});
});
// unite cluster, service and component configs
configs = configs.concat(clusterConfigs);
self.processConfigReferences(kerberosDescriptor, configs);
return configs;
},
/**
*
* @param {Object} resource
* @param {String} serviceName
* @return {Array}
*/
createResourceConfigs: function (resource, serviceName) {
var identityConfigs = [];
var resourceConfigs = [];
if (resource.identities) {
identityConfigs = this.createConfigsByIdentities(resource.identities, serviceName);
}
if (resource.configurations) {
resource.configurations.forEach(function (_configuration) {
for (var key in _configuration) {
resourceConfigs = resourceConfigs.concat(this.expandKerberosStackDescriptorProps(_configuration[key], serviceName, key));
}
}, this);
}
return identityConfigs.concat(resourceConfigs);
},
/**
* Create service properties based on component identity
*
* @param {object[]} identities
* @param {string} serviceName
* @returns {App.ServiceConfigProperty[]}
*/
createConfigsByIdentities: function (identities, serviceName) {
var self = this;
var configs = [];
identities.forEach(function (identity) {
var defaultObject = {
isConfigIdentity: true,
isOverridable: false,
isVisible: true,
isSecureConfig: true,
serviceName: serviceName,
name: identity.name,
identityType: identity.principal && identity.principal.type
};
self.parseIdentityObject(identity).forEach(function (item) {
configs.push(App.ServiceConfigProperty.create($.extend({}, defaultObject, item)));
});
});
return configs;
},
/**
* Bootstrap base object according to identity info. Generate objects will be converted to
* App.ServiceConfigProperty model class instances.
*
* @param {object} identity
* @returns {object[]}
*/
parseIdentityObject: function (identity) {
var result = [];
var name = identity.name;
var self = this;
Em.keys(identity).without('name').forEach(function (item) {
var configObject = {};
var prop = identity[item];
var itemValue = prop[{keytab: 'file', principal: 'value'}[item]];
var predefinedProperty;
// skip inherited property without `configuration` and `keytab` or `file` values
if (!prop.configuration && !itemValue) return;
// inherited property with value should not observe value from reference
if (name.startsWith('/') && !itemValue) {
configObject.referenceProperty = name.substring(1) + ':' + item;
configObject.isEditable = false;
}
Em.setProperties(configObject, {
recommendedValue: itemValue,
initialValue: itemValue,
defaultValue: itemValue,
value: itemValue
});
configObject.filename = prop.configuration ? prop.configuration.split('/')[0] : 'cluster-env';
configObject.name = prop.configuration ? prop.configuration.split('/')[1] : name + '_' + item;
predefinedProperty = self.get('kerberosDescriptorProperties').findProperty('name', configObject.name);
configObject.displayName = self._getDisplayNameForConfig(configObject.name, configObject.filename);
configObject.index = predefinedProperty && !Em.isNone(predefinedProperty.index) ? predefinedProperty.index : Infinity;
result.push(configObject);
});
return result;
},
/**
* Get new config display name basing on its name and filename
* If config <code>fileName</code> is `cluster-env`, normalizing for its <code>name</code> is used (@see App.format.normalizeName)
* If config is predefined in the <code>secureProperties</code> (and it's displayName isn't empty there), predefined displayName is used
* Otherwise - config <code>name</code> is returned
*
* @param {string} name config name
* @param {string} fileName config filename
* @returns {String} new config display name
* @method _getDisplayNameForConfig
* @private
*/
_getDisplayNameForConfig: function (name, fileName) {
var predefinedConfig = App.config.get('kerberosIdentitiesMap')[App.config.configId(name, fileName)];
return (predefinedConfig && predefinedConfig.displayName)
? predefinedConfig.displayName
: fileName == 'cluster-env' ? App.format.normalizeName(name) : name;
},
/**
* Wrap kerberos properties to App.ServiceConfigProperty model class instances.
*
* @param {object} kerberosProperties
* @param {string} serviceName
* @param {string} filename
* @returns {App.ServiceConfigProperty[]}
*/
expandKerberosStackDescriptorProps: function (kerberosProperties, serviceName, filename) {
var configs = [];
for (var propertyName in kerberosProperties) {
var predefinedProperty = this.get('kerberosDescriptorProperties').findProperty('name', propertyName);
var value = kerberosProperties[propertyName];
var isRequired = ['additional_realms', 'principal_suffix'].contains(propertyName) ? false : value !== "";
var propertyObject = {
name: propertyName,
value: value,
defaultValue: value,
recommendedValue: value,
initialValue: value,
serviceName: serviceName,
filename: filename,
displayName: serviceName == "Cluster" ? App.format.normalizeName(propertyName) : propertyName,
isOverridable: false,
isEditable: propertyName != 'realm',
isRequired: isRequired,
isSecureConfig: true,
placeholderText: predefinedProperty && !Em.isNone(predefinedProperty.index) ? predefinedProperty.placeholderText : '',
index: predefinedProperty && !Em.isNone(predefinedProperty.index) ? predefinedProperty.index : Infinity
};
configs.push(App.ServiceConfigProperty.create(propertyObject));
}
return configs;
},
/**
* Take care about configs that should observe value from referenced configs.
* Reference is set with `referenceProperty` key.
*
* @param {object[]} kerberosDescriptor
* @param {App.ServiceConfigProperty[]} configs
*/
processConfigReferences: function (kerberosDescriptor, configs) {
var identities = kerberosDescriptor.identities;
/**
* Returns indentity object with additional attribute `referencePath`.
* Reference path depends on how deep identity is. Each level separated by `/` sign.
*
* @param {object} identity
* @param {string} [prefix=false] prefix to append e.g. 'SERVICE_NAME'
* @returns {object} identity object
*/
var setReferencePath = function(identity, prefix) {
var name = Em.getWithDefault(identity, 'name', false);
if (name) {
if (prefix) {
name = prefix + '/' + name;
}
identity.referencePath = name;
}
return identity;
};
// map all identities and add attribute `referencePath`
// `referencePath` is a path to identity it can be 1-3 levels
// 1 for "/global" identity e.g. `/spnego`
// 2 for "/SERVICE/identity"
// 3 for "/SERVICE/COMPONENT/identity"
identities = identities.map(function(i) {
return setReferencePath(i);
})
.concat(kerberosDescriptor.services.map(function (service) {
var serviceName = Em.getWithDefault(service, 'name', false);
var serviceIdentities = Em.getWithDefault(service, 'identities', []).map(function(i) {
return setReferencePath(i, serviceName);
});
var componentIdentities = Em.getWithDefault(service || {}, 'components', []).map(function(i) {
var componentName = Em.getWithDefault(i, 'name', false);
return Em.getWithDefault(i, 'identities', []).map(function(componentIdentity) {
return setReferencePath(componentIdentity, serviceName + '/' + componentName);
});
}).reduce(function(p, c) {
return p.concat(c);
}, []);
serviceIdentities.pushObjects(componentIdentities);
return serviceIdentities;
}).reduce(function (p, c) {
return p.concat(c);
}, []));
// clean up array
identities = identities.compact().without(undefined);
configs.forEach(function (item) {
var reference = item.get('referenceProperty');
if (!!reference) {
// first find identity by `name`
// if not found try to find by `referencePath`
var identity = Em.getWithDefault(identities.findProperty('name', reference.split(':')[0]) || {}, reference.split(':')[1], false) ||
Em.getWithDefault(identities.findProperty('referencePath', reference.split(':')[0]) || {}, reference.split(':')[1], false);
if (identity && !!identity.configuration) {
item.set('observesValueFrom', identity.configuration.split('/')[1]);
} else {
item.set('observesValueFrom', reference.replace(':', '_'));
}
}
});
},
/**
* update the kerberos descriptor to be put on cluster resource with user customizations
* @param kerberosDescriptor {Object}
* @param configs {Object}
*/
updateKerberosDescriptor: function (kerberosDescriptor, configs) {
configs.forEach(function (_config) {
var isConfigUpdated;
var isStackResouce = true;
isConfigUpdated = this.updateResourceIdentityConfigs(kerberosDescriptor, _config, isStackResouce);
if (!isConfigUpdated) {
kerberosDescriptor.services.forEach(function (_service) {
isConfigUpdated = this.updateResourceIdentityConfigs(_service, _config);
if (!isConfigUpdated) {
Em.getWithDefault(_service, 'components', []).forEach(function (_component) {
isConfigUpdated = this.updateResourceIdentityConfigs(_component, _config);
}, this);
}
}, this);
}
}, this);
},
/**
* Updates the identity configs or configurations at a resource. A resource could be
* 1) Stack
* 2) Service
* 3) Component
* @param resource
* @param config
* @param isStackResource
* @return boolean
*/
updateResourceIdentityConfigs: function (resource, config, isStackResource) {
var isConfigUpdated;
var identities = resource.identities;
var properties = !!isStackResource ? resource.properties : resource.configurations;
isConfigUpdated = this.updateDescriptorConfigs(properties, config);
if (!isConfigUpdated) {
if (identities) {
isConfigUpdated = this.updateDescriptorIdentityConfig(identities, config);
}
}
return isConfigUpdated;
},
/**
* This function updates stack/service/component level configurations of the kerberos descriptor
* with the values entered by the user on the rendered ui
* @param configurations
* @param config
* @return boolean
*/
updateDescriptorConfigs: function (configurations, config) {
var isConfigUpdated;
if (!!configurations) {
if (Array.isArray(configurations)) {
configurations.forEach(function (_configuration) {
for (var key in _configuration) {
if (Object.keys(_configuration[key]).contains(config.name) && config.filename === key) {
_configuration[key][config.name] = config.value;
isConfigUpdated = true
}
}
}, this);
} else if (Object.keys(configurations).contains(config.name) && config.filename === 'stackConfigs') {
configurations[config.name] = config.value;
isConfigUpdated = true;
}
}
return isConfigUpdated;
},
/**
* This function updates stack/service/component level kerberos descriptor identities (principal and keytab)
* with the values entered by the user on the rendered ui
* @param identities
* @param config
* @return boolean
*/
updateDescriptorIdentityConfig: function (identities, config) {
var isConfigUpdated = false;
identities.forEach(function (identity) {
var keys = Em.keys(identity).without('name');
keys.forEach(function (item) {
var prop = identity[item];
// compare ui rendered config against identity with `configuration attribute` (Most of the identities have `configuration attribute`)
var isIdentityWithConfig = (prop.configuration && prop.configuration.split('/')[0] === config.filename && prop.configuration.split('/')[1] === config.name);
// compare ui rendered config against identity without `configuration attribute` (For example spnego principal and keytab)
var isIdentityWithoutConfig = (!prop.configuration && identity.name === config.name.split('_')[0] && item === config.name.split('_')[1]);
if (isIdentityWithConfig || isIdentityWithoutConfig) {
prop[{keytab: 'file', principal: 'value'}[item]] = config.value;
isConfigUpdated = true;
}
});
}, this);
return isConfigUpdated;
},
/**
* Make request for cluster descriptor configs.
*
* @param {string[]|null} [services=null] services to be added
* @returns {$.ajax}
* @method loadClusterDescriptorConfigs
*/
loadClusterDescriptorConfigs: function (services) {
var servicesParam = services ? 'additional_services=' + services.join(',') : null,
queryParams = ['evaluate_when=true'].concat(servicesParam).compact().join('&');
return App.ajax.send({
sender: this,
name: 'admin.kerberize.cluster_descriptor',
data: {
queryParams: '?' + queryParams
}
});
}
});