addSecurityConfigs.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. var App = require('app');
  19. /**
  20. * Mixin for loading and setting secure configs
  21. *
  22. * @type {Ember.Mixin}
  23. */
  24. App.AddSecurityConfigs = Em.Mixin.create({
  25. kerberosDescriptor: {},
  26. secureProperties: require('data/HDP2/secure_properties').configProperties,
  27. secureMapping: require('data/HDP2/secure_mapping'),
  28. serviceUsersBinding: 'App.router.mainAdminSecurityController.serviceUsers',
  29. componentsConfig: [
  30. {
  31. serviceName: 'OOZIE',
  32. componentName: 'OOZIE_SERVER',
  33. configName: 'oozieserver_host'
  34. },
  35. {
  36. serviceName: 'HIVE',
  37. componentName: 'WEBHCAT_SERVER',
  38. configName: 'webhcat_server'
  39. }
  40. ],
  41. /**
  42. * mock users used in testMode
  43. */
  44. testModeUsers: [
  45. {
  46. name: 'hdfs_user',
  47. value: 'hdfs'
  48. },
  49. {
  50. name: 'mapred_user',
  51. value: 'mapred'
  52. },
  53. {
  54. name: 'hbase_user',
  55. value: 'hbase'
  56. },
  57. {
  58. name: 'hive_user',
  59. value: 'hive'
  60. }
  61. ],
  62. /**
  63. * security configs, which values should be modified after APPLY CONFIGURATIONS stage
  64. */
  65. secureConfigs: function () {
  66. var configs = [
  67. {
  68. name: 'nagios_principal_name',
  69. serviceName: 'NAGIOS'
  70. },
  71. {
  72. name: 'zookeeper_principal_name',
  73. serviceName: 'ZOOKEEPER'
  74. },
  75. {
  76. name: 'knox_principal_name',
  77. serviceName: 'KNOX'
  78. },
  79. {
  80. name: 'storm_principal_name',
  81. serviceName: 'STORM'
  82. }
  83. ];
  84. if (App.get('isHadoop22Stack')) {
  85. configs.push({
  86. name: 'nimbus_principal_name',
  87. serviceName: 'STORM'
  88. });
  89. }
  90. return configs;
  91. }.property('App.isHadoop22Stack'),
  92. secureServices: function () {
  93. return this.get('content.services');
  94. }.property('content.services'),
  95. /**
  96. * prepare secure configs
  97. */
  98. prepareSecureConfigs: function () {
  99. var configs = this.get('content.serviceConfigProperties');
  100. this.set('configs', configs);
  101. this.loadStaticConfigs(); //Hack for properties which are declared in site_properties.js and not able to retrieve values declared in secure_properties.js
  102. this.loadUsersToConfigs();
  103. this.loadHostNames();
  104. this.loadPrimaryNames();
  105. var uiConfigs = this.loadUiSideSecureConfigs();
  106. this.set('configs', this.get('configs').concat(uiConfigs));
  107. },
  108. /**
  109. * push users to configs
  110. */
  111. loadUsersToConfigs: function () {
  112. if (!this.get('serviceUsers').length) {
  113. this.loadUsersFromServer();
  114. }
  115. App.router.get('mainAdminSecurityController.serviceUsers').forEach(function (_user) {
  116. this.get('configs').pushObject(_user);
  117. }, this);
  118. },
  119. /**
  120. * add component config that contain host name as value
  121. * @param serviceName
  122. * @param componentName
  123. * @param configName
  124. * @return {Boolean}
  125. */
  126. addHostConfig: function (serviceName, componentName, configName) {
  127. var service = App.Service.find(serviceName);
  128. var isServiceSecure = this.get('secureServices').someProperty('serviceName', serviceName);
  129. if (service.get('isLoaded') && isServiceSecure) {
  130. var hostComponent = service.get('hostComponents').findProperty('componentName', componentName);
  131. if (hostComponent) {
  132. var hostName = hostComponent.get('hostName');
  133. this.get('configs').push({
  134. id: 'puppet var',
  135. name: configName,
  136. value: hostName
  137. });
  138. return true;
  139. }
  140. }
  141. return false;
  142. },
  143. /**
  144. * add hosts' names to configs
  145. */
  146. loadHostNames: function () {
  147. var componentsConfig = this.get('componentsConfig');
  148. componentsConfig.forEach(function (host) {
  149. this.addHostConfig(host.serviceName, host.componentName, host.configName);
  150. }, this);
  151. },
  152. /**
  153. * load static configs
  154. */
  155. loadStaticConfigs: function () {
  156. this.get('configs').forEach(function (_property) {
  157. switch (_property.name) {
  158. case 'security_enabled':
  159. _property.value = 'true';
  160. break;
  161. }
  162. }, this);
  163. },
  164. /**
  165. * add principals to properties
  166. */
  167. loadPrimaryNames: function () {
  168. var principalProperties = this.getPrincipalNames();
  169. principalProperties.forEach(function (_principalProperty) {
  170. var name = _principalProperty.name.replace('principal', 'primary');
  171. var value = _principalProperty.value.split('/')[0];
  172. this.get('configs').push({name: name, value: value});
  173. }, this);
  174. },
  175. /**
  176. * gather and return properties with "principal_name"
  177. * @return {Array}
  178. */
  179. getPrincipalNames: function () {
  180. var principalNames = [];
  181. this.get('configs').forEach(function (_property) {
  182. if (/principal_name?$/.test(_property.name)) {
  183. principalNames.push(_property);
  184. }
  185. }, this);
  186. this.get('secureProperties').forEach(function (_secureProperty) {
  187. if (/principal_name?$/.test(_secureProperty.name)) {
  188. var principalName = principalNames.findProperty('name', _secureProperty.name);
  189. if (!principalName) {
  190. _secureProperty.value = _secureProperty.defaultValue;
  191. principalNames.push(_secureProperty);
  192. }
  193. }
  194. }, this);
  195. return principalNames;
  196. },
  197. /**
  198. * load users from server
  199. */
  200. loadUsersFromServer: function () {
  201. if (App.get('testMode')) {
  202. var serviceUsers = this.get('serviceUsers');
  203. this.get('testModeUsers').forEach(function (user) {
  204. user.id = 'puppet var';
  205. serviceUsers.push(user);
  206. }, this);
  207. } else {
  208. App.router.set('mainAdminSecurityController.serviceUsers', App.db.getSecureUserInfo());
  209. }
  210. },
  211. /**
  212. * load configs from UI side
  213. * @return {Array}
  214. */
  215. loadUiSideSecureConfigs: function () {
  216. var uiConfig = [];
  217. var configs = this.get('secureMapping').filterProperty('foreignKey', null).filter(function (_configProperty) {
  218. return (App.Service.find().mapProperty('serviceName').contains(_configProperty.serviceName));
  219. }, this);
  220. configs.forEach(function (_config) {
  221. var value = _config.value;
  222. if (_config.hasOwnProperty('dependedServiceName')) {
  223. value = this.checkServiceForConfigValue(value, _config.dependedServiceName);
  224. }
  225. value = this.getConfigValue(_config.templateName, value, _config.name);
  226. uiConfig.push({
  227. "id": "site property",
  228. "name": _config.name,
  229. "value": value,
  230. "filename": _config.filename
  231. });
  232. }, this);
  233. var dependentConfig = this.get('secureMapping').filterProperty('foreignKey');
  234. dependentConfig.forEach(function (_config) {
  235. if (App.Service.find().mapProperty('serviceName').contains(_config.serviceName)) {
  236. this.setConfigValue(_config);
  237. this.formatConfigName(uiConfig, _config);
  238. uiConfig.push({
  239. "id": "site property",
  240. "name": _config._name || _config.name,
  241. "value": _config.value,
  242. "filename": _config.filename
  243. });
  244. }
  245. }, this);
  246. return uiConfig;
  247. },
  248. /**
  249. * erase template rules from config value if service is not loaded
  250. * @param value
  251. * @param services
  252. * @return {*}
  253. */
  254. checkServiceForConfigValue: function (value, services) {
  255. services.forEach(function (_service) {
  256. if (!App.Service.find(_service.name).get('isLoaded')) {
  257. value = value.replace(_service.replace, '');
  258. }
  259. }, this);
  260. return value;
  261. },
  262. /**
  263. * Set all site property that are derived from other puppet-variable
  264. * @param templateName
  265. * @param expression
  266. * @param name
  267. * @return {String|null}
  268. */
  269. getConfigValue: function (templateName, expression, name) {
  270. var express = expression.match(/<(.*?)>/g);
  271. var value = expression;
  272. if (Em.isNone(express)) return expression;
  273. express.forEach(function (_express) {
  274. var index = parseInt(_express.match(/\[([\d]*)(?=\])/)[1]);
  275. var configs = this.get('configs').findProperty('name', templateName[index]);
  276. var configValue = templateName[index] == 'hive_metastore' ?
  277. configs.value.map(function (hostName) {
  278. return 'thrift://' + hostName + ':9083';
  279. }).join(',') : configs.value;
  280. if (!!value) {
  281. value = (configs) ? App.config.replaceConfigValues(name, _express, value, configValue) : null;
  282. }
  283. }, this);
  284. return value;
  285. },
  286. /**
  287. * format name of config values of configs which match foreignKey
  288. * @param uiConfig
  289. * @param config
  290. * @return {Boolean}
  291. */
  292. formatConfigName: function (uiConfig, config) {
  293. if (Em.isNone(config.value)) return false;
  294. var fkValue = config.name.match(/<(foreignKey.*?)>/g);
  295. if (fkValue) {
  296. fkValue.forEach(function (_fkValue) {
  297. var index = parseInt(_fkValue.match(/\[([\d]*)(?=\])/)[1]);
  298. var value;
  299. if (uiConfig.someProperty('name', config.foreignKey[index])) {
  300. value = uiConfig.findProperty('name', config.foreignKey[index]).value;
  301. config._name = config.name.replace(_fkValue, value);
  302. } else if (this.get('configs').someProperty('name', config.foreignKey[index])) {
  303. value = this.get('configs').findProperty('name', config.foreignKey[index]).value;
  304. config._name = config.name.replace(_fkValue, value);
  305. }
  306. }, this);
  307. return true;
  308. }
  309. return false;
  310. },
  311. /**
  312. * Set config value with values of configs which match template
  313. * @param config
  314. * @return {Boolean}
  315. */
  316. setConfigValue: function (config) {
  317. if (Em.isNone(config.value)) return false;
  318. //For properties in the configMapping file having foreignKey and templateName properties.
  319. var templateValue = config.value.match(/<(templateName.*?)>/g);
  320. if (templateValue) {
  321. templateValue.forEach(function (_value) {
  322. var index = parseInt(_value.match(/\[([\d]*)(?=\])/)[1]);
  323. var cfg = this.get('configs').findProperty('name', config.templateName[index]);
  324. if (cfg) {
  325. var cfgValue = config.templateName[index] == 'hive_metastore' ? cfg.value.join(',') : cfg.value;
  326. config.value = config.value.replace(_value, cfgValue);
  327. } else {
  328. config.value = null;
  329. }
  330. }, this);
  331. return true;
  332. }
  333. return false;
  334. },
  335. /**
  336. * set value of principal property
  337. * @param serviceName
  338. * @param principalName
  339. * @return {Boolean}
  340. */
  341. setPrincipalValue: function (serviceName, principalName) {
  342. var siteProperties = this.get('configs');
  343. var realmName = siteProperties.findProperty('name', 'kerberos_domain');
  344. if (this.get('secureServices').someProperty('serviceName', serviceName)) {
  345. var principalProperty = siteProperties.findProperty('name', principalName);
  346. principalProperty.value = principalProperty.value + '@' + realmName.value;
  347. return true;
  348. }
  349. return false;
  350. },
  351. /**
  352. * Generate stack descriptor configs.
  353. *
  354. * @returns {$.Deferred}
  355. */
  356. getStackDescriptorConfigs: function () {
  357. return this.loadStackDescriptorConfigs().pipe(this.createServicesStackDescriptorConfigs.bind(this));
  358. },
  359. /**
  360. *
  361. * @param {object[]} items - stack descriptor json response
  362. * @returns {App.ServiceConfigProperty[]}
  363. */
  364. createServicesStackDescriptorConfigs: function (items) {
  365. var self = this;
  366. var configs = [];
  367. var clusterConfigs = [];
  368. var kerberosDescriptor = items.Versions.kerberos_descriptor;
  369. this.set('kerberosDescriptor', kerberosDescriptor);
  370. // generate configs for root level properties object, currently realm, keytab_dir
  371. clusterConfigs = clusterConfigs.concat(this.expandKerberosStackDescriptorProps(kerberosDescriptor.properties));
  372. // generate configs for root level identities object, currently spnego property
  373. clusterConfigs = clusterConfigs.concat(this.createConfigsByIdentities(kerberosDescriptor.identities, 'Cluster'));
  374. clusterConfigs.setEach('serviceName', 'Cluster');
  375. // generate properties for services object
  376. kerberosDescriptor.services.forEach(function (service) {
  377. var serviceName = service.name;
  378. service.components.forEach(function (component) {
  379. var componentName = component.name;
  380. if (component.identities) {
  381. var identityConfigs = self.createConfigsByIdentities(component.identities, componentName);
  382. identityConfigs.setEach('serviceName', serviceName);
  383. configs = configs.concat(identityConfigs);
  384. }
  385. });
  386. });
  387. // unite cluster and service configs
  388. configs = configs.concat(clusterConfigs);
  389. self.processConfigReferences(kerberosDescriptor, configs);
  390. // return configs with uniq names
  391. return configs.reduce(function (p, c) {
  392. if (!p.findProperty('name', c.get('name'))) p.push(c);
  393. return p;
  394. }, []);
  395. },
  396. /**
  397. * Create service properties based on component identity
  398. *
  399. * @param {object[]} identities
  400. * @param {string} componentName
  401. * @returns {App.ServiceConfigProperty[]}
  402. */
  403. createConfigsByIdentities: function (identities, componentName) {
  404. var self = this;
  405. var configs = [];
  406. identities.forEach(function (identity) {
  407. var defaultObject = {
  408. isOverridable: false,
  409. isVisible: true,
  410. isSecureConfig: true,
  411. componentName: componentName,
  412. name: identity.name
  413. };
  414. self.parseIdentityObject(identity).forEach(function (item) {
  415. configs.push(App.ServiceConfigProperty.create($.extend({}, defaultObject, item)));
  416. });
  417. });
  418. return configs;
  419. },
  420. /**
  421. * Bootstrap base object according to identity info. Generate objects will be converted to
  422. * App.ServiceConfigProperty model class instances.
  423. *
  424. * @param {object} identity
  425. * @returns {object[]}
  426. */
  427. parseIdentityObject: function (identity) {
  428. var result = [];
  429. var name = identity.name;
  430. var keys = Em.keys(identity).without('name');
  431. keys.forEach(function (item) {
  432. var configObject = {};
  433. var prop = identity[item];
  434. var itemValue = prop[{keytab: 'file', principal: 'value'}[item]];
  435. // skip inherited property without `configuration` and `keytab` or `file` values
  436. if (!prop.configuration && !itemValue) return;
  437. // inherited property with value should not observe value from reference
  438. if (name.startsWith('/') && !itemValue) {
  439. configObject.referenceProperty = name.substring(1) + ':' + item;
  440. configObject.isEditable = false;
  441. }
  442. configObject.defaultValue = configObject.value = itemValue;
  443. configObject.filename = prop.configuration ? prop.configuration.split('/')[0] : 'cluster-env';
  444. configObject.name = configObject.displayName = prop.configuration ? prop.configuration.split('/')[1] : name + '_' + item;
  445. result.push(configObject);
  446. });
  447. return result;
  448. },
  449. /**
  450. * Wrap kerberos properties to App.ServiceConfigProperty model class instances.
  451. *
  452. * @param {object} kerberosProperties
  453. * @returns {App.ServiceConfigProperty[]}
  454. */
  455. expandKerberosStackDescriptorProps: function (kerberosProperties) {
  456. var configs = [];
  457. for (var propertyName in kerberosProperties) {
  458. var propertyObject = {
  459. name: propertyName,
  460. value: kerberosProperties[propertyName],
  461. defaultValue: kerberosProperties[propertyName],
  462. serviceName: 'Cluster',
  463. displayName: propertyName,
  464. isOverridable: false,
  465. isEditable: propertyName != 'realm',
  466. isSecureConfig: true
  467. };
  468. configs.push(App.ServiceConfigProperty.create(propertyObject));
  469. }
  470. return configs;
  471. },
  472. /**
  473. * Take care about configs that should observe value from referenced configs.
  474. * Reference is set with `referenceProperty` key.
  475. *
  476. * @param {object[]} kerberosDescriptor
  477. * @param {App.ServiceConfigProperty[]} configs
  478. */
  479. processConfigReferences: function (kerberosDescriptor, configs) {
  480. var identities = kerberosDescriptor.identities;
  481. identities = identities.concat(kerberosDescriptor.services.map(function (service) {
  482. var _identities = service.identities || [];
  483. if (service.components && !!service.components.length) {
  484. identities = identities.concat(service.components.mapProperty('identities').reduce(function (p, c) {
  485. return p.concat(c);
  486. }, []));
  487. return identities;
  488. }
  489. }).reduce(function (p, c) {
  490. return p.concat(c);
  491. }, []));
  492. // clean up array
  493. identities = identities.compact().without(undefined);
  494. configs.forEach(function (item) {
  495. var reference = item.get('referenceProperty');
  496. if (!!reference) {
  497. var identity = identities.findProperty('name', reference.split(':')[0])[reference.split(':')[1]];
  498. if (identity && !!identity.configuration) {
  499. item.set('observesValueFrom', identity.configuration.split('/')[1]);
  500. } else {
  501. item.set('observesValueFrom', reference.replace(':', '_'));
  502. }
  503. }
  504. });
  505. },
  506. /**
  507. * update the kerberos descriptor to be put on cluster resource with user customizations
  508. * @param kerberosDescriptor {Object}
  509. * @param configs {Object}
  510. */
  511. updateKerberosDescriptor: function (kerberosDescriptor, configs) {
  512. configs.forEach(function (_config) {
  513. if (Object.keys(kerberosDescriptor.properties).contains(_config.name)) {
  514. for (var key in kerberosDescriptor.properties) {
  515. if (key === _config.name) {
  516. kerberosDescriptor.properties[key] = _config.value;
  517. }
  518. }
  519. } else if (_config.name.endsWith('_principal') || _config.name.endsWith('_keytab')) {
  520. var identities = kerberosDescriptor.identities;
  521. identities.forEach(function (_identity) {
  522. if (_config.name.startsWith(_identity.name)) {
  523. if (_config.name.endsWith('_principal')) {
  524. _identity.principal.value = _config.value;
  525. } else if (_config.name.endsWith('_keytab')) {
  526. _identity.keytab.file = _config.value;
  527. }
  528. }
  529. }, this);
  530. } else {
  531. kerberosDescriptor.services.forEach(function (_service) {
  532. _service.components.forEach(function (_component) {
  533. if (_component.identities) {
  534. _component.identities.forEach(function (_identity) {
  535. if (_identity.principal && _identity.principal.configuration && _identity.principal.configuration.endsWith(_config.name)) {
  536. _identity.principal.value = _config.value;
  537. } else if (_identity.keytab && _identity.keytab.configuration && _identity.keytab.configuration.endsWith(_config.name)) {
  538. _identity.keytab.file = _config.value;
  539. }
  540. }, this);
  541. }
  542. }, this);
  543. }, this);
  544. }
  545. }, this);
  546. },
  547. /**
  548. * Make request for stack descriptor configs.
  549. *
  550. * @returns {$.ajax}
  551. */
  552. loadStackDescriptorConfigs: function () {
  553. return App.ajax.send({
  554. sender: this,
  555. name: 'admin.kerberize.stack_descriptor',
  556. data: {
  557. stackName: App.get('currentStackName'),
  558. stackVersionNumber: App.get('currentStackVersionNumber')
  559. }
  560. });
  561. }
  562. });