addSecurityConfigs.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  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: function () {
  27. if (App.get('isHadoop2Stack')) {
  28. return require('data/HDP2/secure_properties').configProperties;
  29. } else {
  30. return require('data/secure_properties').configProperties;
  31. }
  32. }.property('App.isHadoop2Stack'),
  33. secureMapping: function () {
  34. return (App.get('isHadoop2Stack')) ? require('data/HDP2/secure_mapping') : require('data/secure_mapping');
  35. }.property('App.isHadoop2Stack'),
  36. serviceUsersBinding: 'App.router.mainAdminSecurityController.serviceUsers',
  37. componentsConfig: [
  38. {
  39. serviceName: 'OOZIE',
  40. componentName: 'OOZIE_SERVER',
  41. configName: 'oozieserver_host'
  42. },
  43. {
  44. serviceName: 'HIVE',
  45. componentName: 'HIVE_METASTORE',
  46. configName: 'hivemetastore_host'
  47. },
  48. {
  49. serviceName: 'HIVE',
  50. componentName: 'WEBHCAT_SERVER',
  51. configName: 'webhcat_server'
  52. }
  53. ],
  54. /**
  55. * mock users used in testMode
  56. */
  57. testModeUsers: [
  58. {
  59. name: 'hdfs_user',
  60. value: 'hdfs'
  61. },
  62. {
  63. name: 'mapred_user',
  64. value: 'mapred'
  65. },
  66. {
  67. name: 'hbase_user',
  68. value: 'hbase'
  69. },
  70. {
  71. name: 'hive_user',
  72. value: 'hive'
  73. }
  74. ],
  75. /**
  76. * security configs, which values should be modified after APPLY CONFIGURATIONS stage
  77. */
  78. secureConfigs: function () {
  79. var configs = [
  80. {
  81. name: 'nagios_principal_name',
  82. serviceName: 'NAGIOS'
  83. },
  84. {
  85. name: 'zookeeper_principal_name',
  86. serviceName: 'ZOOKEEPER'
  87. },
  88. {
  89. name: 'knox_principal_name',
  90. serviceName: 'KNOX'
  91. },
  92. {
  93. name: 'storm_principal_name',
  94. serviceName: 'STORM'
  95. }
  96. ];
  97. if (App.get('isHadoop22Stack')) {
  98. configs.push({
  99. name: 'nimbus_principal_name',
  100. serviceName: 'STORM'
  101. });
  102. }
  103. return configs;
  104. }.property('App.isHadoop22Stack'),
  105. secureServices: function() {
  106. return this.get('content.services');
  107. }.property('content.services'),
  108. /**
  109. * prepare secure configs
  110. */
  111. prepareSecureConfigs: function () {
  112. var configs = this.get('content.serviceConfigProperties');
  113. this.set('configs', configs);
  114. this.loadStaticConfigs(); //Hack for properties which are declared in site_properties.js and not able to retrieve values declared in secure_properties.js
  115. this.loadUsersToConfigs();
  116. this.loadHostNames();
  117. this.loadPrimaryNames();
  118. var uiConfigs = this.loadUiSideSecureConfigs();
  119. this.set('configs', this.get('configs').concat(uiConfigs));
  120. },
  121. /**
  122. * push users to configs
  123. */
  124. loadUsersToConfigs: function () {
  125. if (!this.get('serviceUsers').length) {
  126. this.loadUsersFromServer();
  127. }
  128. App.router.get('mainAdminSecurityController.serviceUsers').forEach(function (_user) {
  129. this.get('configs').pushObject(_user);
  130. }, this);
  131. },
  132. /**
  133. * add component config that contain host name as value
  134. * @param serviceName
  135. * @param componentName
  136. * @param configName
  137. * @return {Boolean}
  138. */
  139. addHostConfig: function (serviceName, componentName, configName) {
  140. var service = App.Service.find(serviceName);
  141. var isServiceSecure = this.get('secureServices').someProperty('serviceName', serviceName);
  142. if (service.get('isLoaded') && isServiceSecure) {
  143. var hostComponent = service.get('hostComponents').findProperty('componentName', componentName);
  144. if (hostComponent) {
  145. var hostName = hostComponent.get('hostName');
  146. this.get('configs').push({
  147. id: 'puppet var',
  148. name: configName,
  149. value: hostName
  150. });
  151. return true;
  152. }
  153. }
  154. return false;
  155. },
  156. /**
  157. * add hosts' names to configs
  158. */
  159. loadHostNames: function () {
  160. var componentsConfig = this.get('componentsConfig');
  161. componentsConfig.forEach(function (host) {
  162. this.addHostConfig(host.serviceName, host.componentName, host.configName);
  163. }, this);
  164. },
  165. /**
  166. * load static configs
  167. */
  168. loadStaticConfigs: function () {
  169. this.get('configs').forEach(function (_property) {
  170. switch (_property.name) {
  171. case 'security_enabled':
  172. _property.value = 'true';
  173. break;
  174. }
  175. }, this);
  176. },
  177. /**
  178. * add principals to properties
  179. */
  180. loadPrimaryNames: function () {
  181. var principalProperties = this.getPrincipalNames();
  182. principalProperties.forEach(function (_principalProperty) {
  183. var name = _principalProperty.name.replace('principal', 'primary');
  184. var value = _principalProperty.value.split('/')[0];
  185. this.get('configs').push({name: name, value: value});
  186. }, this);
  187. },
  188. /**
  189. * gather and return properties with "principal_name"
  190. * @return {Array}
  191. */
  192. getPrincipalNames: function () {
  193. var principalNames = [];
  194. this.get('configs').forEach(function (_property) {
  195. if (/principal_name?$/.test(_property.name)) {
  196. principalNames.push(_property);
  197. }
  198. }, this);
  199. this.get('secureProperties').forEach(function (_secureProperty) {
  200. if (/principal_name?$/.test(_secureProperty.name)) {
  201. var principalName = principalNames.findProperty('name', _secureProperty.name);
  202. if (!principalName) {
  203. _secureProperty.value = _secureProperty.defaultValue;
  204. principalNames.push(_secureProperty);
  205. }
  206. }
  207. }, this);
  208. return principalNames;
  209. },
  210. /**
  211. * load users from server
  212. */
  213. loadUsersFromServer: function () {
  214. if (App.get('testMode')) {
  215. var serviceUsers = this.get('serviceUsers');
  216. this.get('testModeUsers').forEach(function (user) {
  217. user.id = 'puppet var';
  218. serviceUsers.push(user);
  219. }, this);
  220. } else {
  221. App.router.set('mainAdminSecurityController.serviceUsers', App.db.getSecureUserInfo());
  222. }
  223. },
  224. /**
  225. * load configs from UI side
  226. * @return {Array}
  227. */
  228. loadUiSideSecureConfigs: function () {
  229. var uiConfig = [];
  230. var configs = this.get('secureMapping').filterProperty('foreignKey', null).filter(function(_configProperty){
  231. return (App.Service.find().mapProperty('serviceName').contains(_configProperty.serviceName));
  232. },this);
  233. configs.forEach(function (_config) {
  234. var value = _config.value;
  235. if (_config.hasOwnProperty('dependedServiceName')) {
  236. value = this.checkServiceForConfigValue(value, _config.dependedServiceName);
  237. }
  238. value = this.getConfigValue(_config.templateName, value);
  239. uiConfig.push({
  240. "id": "site property",
  241. "name": _config.name,
  242. "value": value,
  243. "filename": _config.filename
  244. });
  245. }, this);
  246. var dependentConfig = this.get('secureMapping').filterProperty('foreignKey');
  247. dependentConfig.forEach(function (_config) {
  248. if (App.Service.find().mapProperty('serviceName').contains(_config.serviceName)) {
  249. this.setConfigValue(_config);
  250. this.formatConfigName(uiConfig, _config);
  251. uiConfig.push({
  252. "id": "site property",
  253. "name": _config._name || _config.name,
  254. "value": _config.value,
  255. "filename": _config.filename
  256. });
  257. }
  258. }, this);
  259. return uiConfig;
  260. },
  261. /**
  262. * erase template rules from config value if service is not loaded
  263. * @param value
  264. * @param services
  265. * @return {*}
  266. */
  267. checkServiceForConfigValue: function (value, services) {
  268. services.forEach(function (_service) {
  269. if (!App.Service.find(_service.name).get('isLoaded')) {
  270. value = value.replace(_service.replace, '');
  271. }
  272. }, this);
  273. return value;
  274. },
  275. /**
  276. * Set all site property that are derived from other puppet-variable
  277. * @param templateName
  278. * @param expression
  279. * @return {String|null}
  280. */
  281. getConfigValue: function (templateName, expression) {
  282. var express = expression.match(/<(.*?)>/g);
  283. var value = expression;
  284. if (Em.isNone(express)) return expression;
  285. express.forEach(function (_express) {
  286. var index = parseInt(_express.match(/\[([\d]*)(?=\])/)[1]);
  287. var configs = this.get('configs').findProperty('name', templateName[index]);
  288. if (!!value) {
  289. value = (configs) ? value.replace(_express, configs.value) : null;
  290. }
  291. }, this);
  292. return value;
  293. },
  294. /**
  295. * format name of config values of configs which match foreignKey
  296. * @param uiConfig
  297. * @param config
  298. * @return {Boolean}
  299. */
  300. formatConfigName: function (uiConfig, config) {
  301. if (Em.isNone(config.value)) return false;
  302. var fkValue = config.name.match(/<(foreignKey.*?)>/g);
  303. if (fkValue) {
  304. fkValue.forEach(function (_fkValue) {
  305. var index = parseInt(_fkValue.match(/\[([\d]*)(?=\])/)[1]);
  306. var value;
  307. if (uiConfig.someProperty('name', config.foreignKey[index])) {
  308. value = uiConfig.findProperty('name', config.foreignKey[index]).value;
  309. config._name = config.name.replace(_fkValue, value);
  310. } else if (this.get('configs').someProperty('name', config.foreignKey[index])) {
  311. value = this.get('configs').findProperty('name', config.foreignKey[index]).value;
  312. config._name = config.name.replace(_fkValue, value);
  313. }
  314. }, this);
  315. return true;
  316. }
  317. return false;
  318. },
  319. /**
  320. * Set config value with values of configs which match template
  321. * @param config
  322. * @return {Boolean}
  323. */
  324. setConfigValue: function (config) {
  325. if (Em.isNone(config.value)) return false;
  326. //For properties in the configMapping file having foreignKey and templateName properties.
  327. var templateValue = config.value.match(/<(templateName.*?)>/g);
  328. if (templateValue) {
  329. templateValue.forEach(function (_value) {
  330. var index = parseInt(_value.match(/\[([\d]*)(?=\])/)[1]);
  331. var cfgValue = this.get('configs').findProperty('name', config.templateName[index]);
  332. config.value = (cfgValue) ? config.value.replace(_value, cfgValue.value) : null;
  333. }, this);
  334. return true;
  335. }
  336. return false;
  337. },
  338. /**
  339. * set value of principal property
  340. * @param serviceName
  341. * @param principalName
  342. * @return {Boolean}
  343. */
  344. setPrincipalValue: function (serviceName, principalName) {
  345. var siteProperties = this.get('configs');
  346. var realmName = siteProperties.findProperty('name', 'kerberos_domain');
  347. if (this.get('secureServices').someProperty('serviceName', serviceName)) {
  348. var principalProperty = siteProperties.findProperty('name', principalName);
  349. principalProperty.value = principalProperty.value + '@' + realmName.value;
  350. return true;
  351. }
  352. return false;
  353. },
  354. /**
  355. * Generate stack descriptor configs.
  356. *
  357. * @returns {$.Deferred}
  358. */
  359. getStackDescriptorConfigs: function() {
  360. return this.loadStackDescriptorConfigs().pipe(this.createServicesStackDescriptorConfigs.bind(this));
  361. },
  362. /**
  363. *
  364. * @param {object[]} items - stack descriptor json response
  365. * @returns {App.ServiceConfigProperty[]}
  366. */
  367. createServicesStackDescriptorConfigs: function(items) {
  368. var self = this;
  369. var configs = [];
  370. var clusterConfigs = [];
  371. var kerberosDescriptor = items.Versions.kerberos_descriptor;
  372. this.set('kerberosDescriptor', kerberosDescriptor);
  373. // generate configs for root level properties object, currently realm, keytab_dir
  374. clusterConfigs = clusterConfigs.concat(this.expandKerberosStackDescriptorProps(kerberosDescriptor.properties));
  375. // generate configs for root level identities object, currently spnego property
  376. clusterConfigs = clusterConfigs.concat(this.createConfigsByIdentities(kerberosDescriptor.identities, 'Cluster'));
  377. clusterConfigs.setEach('serviceName', 'Cluster');
  378. // generate properties for services object
  379. kerberosDescriptor.services.forEach(function(service) {
  380. var serviceName = service.name;
  381. service.components.forEach(function(component) {
  382. var componentName = component.name;
  383. var identityConfigs = self.createConfigsByIdentities(component.identities, componentName);
  384. identityConfigs.setEach('serviceName', serviceName);
  385. configs = configs.concat(identityConfigs);
  386. });
  387. });
  388. // unite cluster and service configs
  389. configs = configs.concat(clusterConfigs);
  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. if (identity.name == '/spnego') {
  415. defaultObject.isEditable = false;
  416. }
  417. self.parseIdentityObject(identity).forEach(function(item) {
  418. configs.push(App.ServiceConfigProperty.create($.extend({}, defaultObject, item)));
  419. });
  420. });
  421. return configs;
  422. },
  423. /**
  424. * Bootstrap base object according to identity info. Generate objects will be converted to
  425. * App.ServiceConfigProperty model class instances.
  426. *
  427. * @param {object} identity
  428. * @returns {object[]}
  429. */
  430. parseIdentityObject: function(identity) {
  431. var result = [];
  432. var name = identity.name;
  433. var keys = Em.keys(identity).without('name');
  434. keys.forEach(function(item) {
  435. var configObject = {};
  436. var prop = identity[item];
  437. if (name == '/spnego') configObject.observesValueFrom = 'spnego_' + item;
  438. configObject.defaultValue = configObject.value = item == 'principal' ? prop.value : prop.file;
  439. configObject.filename = prop.configuration ? prop.configuration.split('/')[0] : 'cluster-env';
  440. configObject.name = configObject.displayName = prop.configuration ? prop.configuration.split('/')[1] : name + '_' + item;
  441. result.push(configObject);
  442. });
  443. return result;
  444. },
  445. /**
  446. * Wrap kerberos properties to App.ServiceConfigProperty model class instances.
  447. *
  448. * @param {object} kerberosProperties
  449. * @returns {App.ServiceConfigProperty[]}
  450. */
  451. expandKerberosStackDescriptorProps: function(kerberosProperties) {
  452. var configs = [];
  453. for (var propertyName in kerberosProperties) {
  454. var propertyObject = {
  455. name: propertyName,
  456. value: kerberosProperties[propertyName],
  457. defaultValue: kerberosProperties[propertyName],
  458. serviceName: 'Cluster',
  459. displayName: propertyName,
  460. isOverridable: false,
  461. isEditable: propertyName != 'realm',
  462. isSecureConfig: true
  463. };
  464. configs.push(App.ServiceConfigProperty.create(propertyObject));
  465. }
  466. return configs;
  467. },
  468. /**
  469. * update the kerberos descriptor to be put on cluster resource with user customizations
  470. * @param kerberosDescriptor {Object}
  471. * @param configs {Object}
  472. */
  473. updateKerberosDescriptor: function(kerberosDescriptor, configs) {
  474. configs.forEach(function(_config){
  475. if (Object.keys(kerberosDescriptor.properties).contains(_config.name)) {
  476. for (var key in kerberosDescriptor.properties) {
  477. if (key === _config.name) {
  478. kerberosDescriptor.properties[key] = _config.value;
  479. }
  480. }
  481. } else if (_config.name.endsWith('_principal') || _config.name.endsWith('_keytab')) {
  482. var identities = kerberosDescriptor.identities;
  483. identities.forEach(function(_identity){
  484. if (_config.name.startsWith(_identity.name)) {
  485. if (_config.name.endsWith('_principal')) {
  486. _identity.principal.value = _config.value;
  487. } else if (_config.name.endsWith('_keytab')) {
  488. _identity.keytab.file = _config.value;
  489. }
  490. }
  491. },this);
  492. } else {
  493. kerberosDescriptor.services.forEach(function(_service) {
  494. _service.components.forEach(function(_component){
  495. _component.identities.forEach(function(_identity){
  496. if (_identity.principal && _identity.principal.configuration && _identity.principal.configuration.endsWith(_config.name)) {
  497. _identity.principal.value = _config.value;
  498. } else if (_identity.keytab && _identity.keytab.configuration && _identity.keytab.configuration.endsWith(_config.name)) {
  499. _identity.keytab.file = _config.value;
  500. }
  501. },this);
  502. }, this);
  503. },this);
  504. }
  505. }, this);
  506. },
  507. /**
  508. * Make request for stack descriptor configs.
  509. *
  510. * @returns {$.ajax}
  511. */
  512. loadStackDescriptorConfigs: function() {
  513. return App.ajax.send({
  514. sender: this,
  515. name: 'admin.kerberize.stack_descriptor',
  516. data: {
  517. stackName: App.get('currentStackName'),
  518. stackVersionNumber: App.get('currentStackVersionNumber')
  519. }
  520. });
  521. }
  522. });