addSeccurityConfigs_test.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  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. var stackDescriptorData = require('test/mock_data_setup/stack_descriptors');
  20. var stackDescriptor = stackDescriptorData.Versions.kerberos_descriptor;
  21. require('mixins/wizard/addSecurityConfigs');
  22. describe('App.AddSecurityConfigs', function () {
  23. var controller = Em.Object.extend(App.AddSecurityConfigs,{}).create({
  24. content: {},
  25. enableSubmit: function () {
  26. this._super();
  27. },
  28. secureMapping: [],
  29. secureProperties: []
  30. });
  31. describe('#secureServices', function() {
  32. it('content.services is correct', function() {
  33. controller.set('content.services', [{}]);
  34. expect(controller.get('secureServices')).to.eql([{}]);
  35. controller.reopen({
  36. secureServices: []
  37. });
  38. });
  39. });
  40. describe('#loadUiSideSecureConfigs()', function() {
  41. beforeEach(function(){
  42. sinon.stub(controller, 'checkServiceForConfigValue', function() {
  43. return 'value2';
  44. });
  45. sinon.stub(controller, 'setConfigValue', Em.K);
  46. sinon.stub(controller, 'formatConfigName', Em.K);
  47. sinon.stub(App.Service, 'find').returns([{serviceName: 'SOME_SERVICE'}]);
  48. });
  49. afterEach(function(){
  50. controller.checkServiceForConfigValue.restore();
  51. controller.setConfigValue.restore();
  52. controller.formatConfigName.restore();
  53. App.Service.find.restore();
  54. });
  55. it('secureMapping is empty', function() {
  56. controller.set('secureMapping', []);
  57. expect(controller.loadUiSideSecureConfigs()).to.be.empty;
  58. });
  59. it('Config does not have dependedServiceName', function() {
  60. controller.set('secureMapping', [{
  61. name: 'config1',
  62. value: 'value1',
  63. filename: 'file1',
  64. serviceName: 'SOME_SERVICE',
  65. foreignKey: null
  66. }]);
  67. expect(controller.loadUiSideSecureConfigs()).to.eql([{
  68. "id": "site property",
  69. "name": 'config1',
  70. "value": 'value1',
  71. "filename": 'file1'
  72. }]);
  73. });
  74. it('Config has dependedServiceName', function() {
  75. controller.set('secureMapping', [{
  76. name: 'config1',
  77. value: 'value1',
  78. filename: 'file1',
  79. foreignKey: null,
  80. serviceName: 'SOME_SERVICE',
  81. dependedServiceName: 'SOME_SERVICE'
  82. }]);
  83. expect(controller.loadUiSideSecureConfigs()).to.eql([{
  84. "id": "site property",
  85. "name": 'config1',
  86. "value": 'value2',
  87. "filename": 'file1'
  88. }]);
  89. });
  90. it('Config has non-existent serviceName', function() {
  91. controller.set('secureMapping', [{
  92. name: 'config1',
  93. value: 'value1',
  94. filename: 'file1',
  95. foreignKey: true,
  96. serviceName: 'NO_SERVICE'
  97. }]);
  98. expect(controller.loadUiSideSecureConfigs()).to.be.empty;
  99. });
  100. it('Config has correct serviceName', function() {
  101. controller.set('secureMapping', [{
  102. name: 'config1',
  103. value: 'value1',
  104. filename: 'file1',
  105. foreignKey: true,
  106. serviceName: 'SOME_SERVICE'
  107. }]);
  108. expect(controller.loadUiSideSecureConfigs()).to.eql([{
  109. "id": "site property",
  110. "name": 'config1',
  111. "value": 'value1',
  112. "filename": 'file1'
  113. }]);
  114. expect(controller.setConfigValue.calledOnce).to.be.true;
  115. expect(controller.formatConfigName.calledOnce).to.be.true;
  116. });
  117. });
  118. describe('#checkServiceForConfigValue()', function() {
  119. it('services is empty', function() {
  120. var services = [];
  121. expect(controller.checkServiceForConfigValue('value1', services)).to.equal('value1');
  122. });
  123. it('Service is loaded', function() {
  124. var services = [{}];
  125. sinon.stub(App.Service, 'find', function () {
  126. return Em.Object.create({isLoaded: false});
  127. });
  128. expect(controller.checkServiceForConfigValue('value1', services)).to.equal('value1');
  129. App.Service.find.restore();
  130. });
  131. it('Service is not loaded', function() {
  132. var services = [{
  133. replace: 'val'
  134. }];
  135. sinon.stub(App.Service, 'find', function () {
  136. return Em.Object.create({isLoaded: false});
  137. });
  138. expect(controller.checkServiceForConfigValue('value1', services)).to.equal('ue1');
  139. App.Service.find.restore();
  140. });
  141. });
  142. describe('#formatConfigName()', function() {
  143. it('config.value is null', function() {
  144. var config = {
  145. value: null
  146. };
  147. expect(controller.formatConfigName([], config)).to.be.false;
  148. });
  149. it('config.name does not contain foreignKey', function() {
  150. var config = {
  151. value: 'value1',
  152. name: 'config1'
  153. };
  154. expect(controller.formatConfigName([], config)).to.be.false;
  155. });
  156. it('globalProperties is empty, use uiConfig', function() {
  157. var config = {
  158. value: 'value1',
  159. name: '<foreignKey[0]>',
  160. foreignKey: ['key1']
  161. };
  162. controller.set('globalProperties', []);
  163. var uiConfig = [{
  164. name: 'key1',
  165. value: 'globalValue1'
  166. }];
  167. expect(controller.formatConfigName(uiConfig, config)).to.be.true;
  168. expect(config._name).to.equal('globalValue1');
  169. });
  170. });
  171. describe('#setConfigValue()', function() {
  172. it('config.value is null', function() {
  173. var config = {
  174. value: null
  175. };
  176. expect(controller.setConfigValue(config)).to.be.false;
  177. });
  178. it('config.value does not match "templateName"', function() {
  179. var config = {
  180. value: ''
  181. };
  182. expect(controller.setConfigValue(config)).to.be.false;
  183. });
  184. it('No such property in global configs', function() {
  185. var config = {
  186. value: '<templateName[0]>',
  187. templateName: ['config1']
  188. };
  189. controller.set('globalProperties', []);
  190. controller.set('configs', []);
  191. expect(controller.setConfigValue(config)).to.be.true;
  192. expect(config.value).to.be.null;
  193. });
  194. it('Hive Metastore hostname array is converted to string', function () {
  195. var config = {
  196. value: '<templateName[0]>',
  197. templateName: ['hive_metastore']
  198. };
  199. controller.set('globalProperties', []);
  200. controller.set('configs', [
  201. {
  202. name: 'hive_metastore',
  203. value: ['h0', 'h1', 'h2']
  204. }
  205. ]);
  206. expect(controller.setConfigValue(config)).to.be.true;
  207. expect(config.value).to.equal('h0,h1,h2');
  208. });
  209. });
  210. describe('#addHostConfig()', function() {
  211. afterEach(function () {
  212. App.Service.find.restore();
  213. });
  214. it('No such service loaded', function() {
  215. sinon.stub(App.Service, 'find', function(){
  216. return Em.Object.create({isLoaded: false});
  217. });
  218. expect(controller.addHostConfig('service1', 'comp1', 'config1')).to.be.false;
  219. });
  220. it('No such service in secureServices', function() {
  221. sinon.stub(App.Service, 'find', function(){
  222. return Em.Object.create({isLoaded: true});
  223. });
  224. controller.set('secureServices', []);
  225. expect(controller.addHostConfig('service1', 'comp1', 'config1')).to.be.false;
  226. });
  227. it('Service does not have such host-component', function() {
  228. sinon.stub(App.Service, 'find', function(){
  229. return Em.Object.create({
  230. isLoaded: true,
  231. hostComponents: []
  232. });
  233. });
  234. controller.set('secureServices', [{
  235. serviceName: 'service1'
  236. }]);
  237. expect(controller.addHostConfig('service1', 'comp1', 'config1')).to.be.false;
  238. });
  239. });
  240. describe('#getPrincipalNames()', function() {
  241. beforeEach(function () {
  242. controller.set('globalProperties', []);
  243. controller.set('secureProperties', []);
  244. });
  245. it('globalProperties and secureProperties are empty', function() {
  246. expect(controller.getPrincipalNames()).to.be.empty;
  247. });
  248. it('global property name does not match "principal_name"', function() {
  249. controller.set('globalProperties', [{
  250. name: 'config1'
  251. }]);
  252. expect(controller.getPrincipalNames()).to.be.empty;
  253. });
  254. it('secure property name does not match "principal_name"', function() {
  255. controller.set('secureProperties', [{
  256. name: 'config1'
  257. }]);
  258. expect(controller.getPrincipalNames()).to.be.empty;
  259. });
  260. it('property with such name already exists', function() {
  261. controller.set('globalProperties', [{
  262. name: 'principal_name'
  263. }]);
  264. controller.set('secureProperties', [{
  265. name: 'principal_name'
  266. }]);
  267. expect(controller.getPrincipalNames().mapProperty('name')).to.eql(['principal_name']);
  268. });
  269. });
  270. describe('#loadUsersFromServer()', function() {
  271. it('testMode = true', function() {
  272. controller.set('testModeUsers', [{
  273. name: 'user1',
  274. value: 'value1'
  275. }]);
  276. controller.set('serviceUsers', []);
  277. sinon.stub(App, 'get', function(k) {
  278. if ('testMode' === k) return true;
  279. return Em.get(App, k);
  280. });
  281. controller.loadUsersFromServer();
  282. expect(controller.get('serviceUsers')).to.eql([{
  283. name: 'user1',
  284. value: 'value1',
  285. id: 'puppet var'
  286. }]);
  287. App.get.restore();
  288. });
  289. it('testMode = false', function() {
  290. sinon.stub(App.router, 'set', Em.K);
  291. sinon.stub(App.db, 'getSecureUserInfo', function(){
  292. return [];
  293. });
  294. sinon.stub(App, 'get', function(k) {
  295. if ('testMode' === k) return false;
  296. return Em.get(App, k);
  297. });
  298. controller.loadUsersFromServer();
  299. expect(App.db.getSecureUserInfo.calledOnce).to.be.true;
  300. expect(App.router.set.calledWith('mainAdminSecurityController.serviceUsers', [])).to.be.true;
  301. App.router.set.restore();
  302. App.get.restore();
  303. App.db.getSecureUserInfo.restore();
  304. });
  305. });
  306. describe('#createServicesStackDescriptorConfigs', function() {
  307. var result = controller.createServicesStackDescriptorConfigs(stackDescriptorData);
  308. var propertyValidationTests = [
  309. {
  310. property: 'spnego_keytab',
  311. e: [
  312. { key: 'value', value: '${keytab_dir}/spnego.service.keytab' },
  313. { key: 'serviceName', value: 'Cluster' },
  314. ]
  315. },
  316. // principal name inherited from /spnego with predefined value
  317. {
  318. property: 'oozie.authentication.kerberos.principal',
  319. e: [
  320. { key: 'value', value: 'HTTP/${host}@${realm}' },
  321. { key: 'isEditable', value: true },
  322. ]
  323. },
  324. // keytab inherited from /spnego without predefined file value
  325. {
  326. property: 'oozie.authentication.kerberos.keytab',
  327. e: [
  328. { key: 'value', value: null },
  329. { key: 'isEditable', value: false },
  330. { key: 'referenceProperty', value: 'spnego:keytab' },
  331. { key: 'observesValueFrom', value: 'spnego_keytab' }
  332. ]
  333. }
  334. ];
  335. propertyValidationTests.forEach(function(test) {
  336. it('property {0} should be created'.format(test.property), function() {
  337. expect(result.findProperty('name', test.property)).to.be.ok;
  338. });
  339. test.e.forEach(function(expected) {
  340. it('property `{0}` should have `{1}` with value `{2}`'.format(test.property, expected.key, expected.value), function() {
  341. expect(result.findProperty('name', test.property)).to.have.deep.property(expected.key, expected.value);
  342. });
  343. });
  344. });
  345. });
  346. describe('#expandKerberosStackDescriptorProps', function() {
  347. var serviceName = 'Cluster';
  348. var result = controller.expandKerberosStackDescriptorProps(stackDescriptor.properties, serviceName);
  349. var testCases = [
  350. {
  351. property: 'realm',
  352. e: [
  353. { key: 'isEditable', value: false },
  354. { key: 'serviceName', value: 'Cluster' },
  355. ]
  356. },
  357. {
  358. property: 'keytab_dir',
  359. e: [
  360. { key: 'isEditable', value: true },
  361. { key: 'serviceName', value: 'Cluster' },
  362. ]
  363. }
  364. ];
  365. testCases.forEach(function(test) {
  366. it('property {0} should be created'.format(test.property), function() {
  367. expect(result.findProperty('name', test.property)).to.be.ok;
  368. });
  369. test.e.forEach(function(expected) {
  370. it('property `{0}` should have `{1}` with value `{2}`'.format(test.property, expected.key, expected.value), function() {
  371. expect(result.findProperty('name', test.property)).to.have.deep.property(expected.key, expected.value);
  372. });
  373. });
  374. });
  375. });
  376. describe('#createConfigsByIdentity', function() {
  377. var identitiesData = stackDescriptor.services[0].components[0].identities;
  378. var tests = [
  379. {
  380. property: 'dfs.namenode.kerberos.principal',
  381. e: [
  382. { key: 'value', value: 'nn/_HOST@${realm}' },
  383. ]
  384. },
  385. {
  386. property: 'dfs.web.authentication.kerberos.principal',
  387. e: [
  388. { key: 'referenceProperty', value: 'spnego:principal' },
  389. { key: 'isEditable', value: false }
  390. ]
  391. }
  392. ];
  393. var properties = controller.createConfigsByIdentities(identitiesData, 'NAMENODE');
  394. tests.forEach(function(test) {
  395. it('property {0} should be created'.format(test.property), function() {
  396. expect(properties.findProperty('name', test.property)).to.be.ok;
  397. });
  398. test.e.forEach(function(expected) {
  399. it('property `{0}` should have `{1}` with value `{2}`'.format(test.property, expected.key, expected.value), function() {
  400. expect(properties.findProperty('name', test.property)).to.have.deep.property(expected.key, expected.value);
  401. });
  402. });
  403. });
  404. });
  405. describe('#parseIdentityObject', function() {
  406. var testCases = [
  407. {
  408. identity: stackDescriptor.services[0].components[0].identities[0],
  409. tests: [
  410. {
  411. property: 'dfs.namenode.kerberos.principal',
  412. e: [
  413. { key: 'filename', value: 'hdfs-site' },
  414. ]
  415. },
  416. {
  417. property: 'dfs.namenode.keytab.file',
  418. e: [
  419. { key: 'value', value: '${keytab_dir}/nn.service.keytab' }
  420. ]
  421. }
  422. ]
  423. },
  424. {
  425. identity: stackDescriptor.services[0].components[0].identities[1],
  426. tests: [
  427. {
  428. property: 'dfs.namenode.kerberos.https.principal',
  429. e: [
  430. { key: 'filename', value: 'hdfs-site' }
  431. ]
  432. }
  433. ]
  434. },
  435. {
  436. identity: stackDescriptor.identities[0],
  437. tests: [
  438. {
  439. property: 'spnego_principal',
  440. e: [
  441. { key: 'displayName', value: 'Spnego Principal' },
  442. { key: 'filename', value: 'cluster-env' }
  443. ]
  444. }
  445. ]
  446. },
  447. {
  448. identity: stackDescriptor.identities[0],
  449. tests: [
  450. {
  451. property: 'spnego_keytab',
  452. e: [
  453. { key: 'displayName', value: 'Spnego Keytab' },
  454. { key: 'filename', value: 'cluster-env' }
  455. ]
  456. }
  457. ]
  458. }
  459. ];
  460. testCases.forEach(function(testCase) {
  461. testCase.tests.forEach(function(test) {
  462. var result = controller.parseIdentityObject(testCase.identity);
  463. it('property `{0}` should be present'.format(test.property), function() {
  464. expect(result.findProperty('name', test.property)).to.be.ok;
  465. });
  466. test.e.forEach(function(expected) {
  467. it('property `{0}` should have `{1}` with value `{2}`'.format(test.property, expected.key, expected.value), function() {
  468. expect(result.findProperty('name', test.property)).to.have.deep.property(expected.key, expected.value);
  469. });
  470. });
  471. });
  472. });
  473. });
  474. describe('#processConfigReferences', function() {
  475. var generateProperty = function(name, reference) {
  476. return Em.Object.create({ name: name, referenceProperty: reference});
  477. };
  478. var descriptor = {
  479. identities: [
  480. { name: 'spnego', principal: { value: 'spnego_value' }, keytab: { file: 'spnego_file'} },
  481. { name: 'hdfs',
  482. principal: { value: 'hdfs_value', configuration: "hadoop-env/hdfs_user_principal_name" },
  483. keytab: { file: 'hdfs_file', configuration: "hadoop-env/hdfs_user_keytab"} }
  484. ],
  485. services: [
  486. {
  487. name: 'SERVICE',
  488. identities: [
  489. { name: '/spnego' },
  490. { name: '/hdfs' }
  491. ]
  492. },
  493. {
  494. name: 'SERVICE2',
  495. components: [
  496. {
  497. name: 'COMPONENT',
  498. identities: [
  499. {
  500. name: 'component_prop1',
  501. keytab: { configuration: 'service2-site/component.keytab' },
  502. principal: { configuration: null }
  503. },
  504. {
  505. name: 'component_prop2',
  506. keytab: { configuration: 'service2-site/component2.keytab' },
  507. principal: { configuration: 'service2-site/component2.principal' }
  508. }
  509. ]
  510. }
  511. ]
  512. }
  513. ]
  514. };
  515. var configs = Em.A([
  516. generateProperty('spnego_inherited_keytab', 'spnego:keytab'),
  517. generateProperty('spnego_inherited_principal', 'spnego:principal'),
  518. generateProperty('hdfs_inherited_keytab', 'hdfs:keytab'),
  519. generateProperty('hdfs_inherited_principal', 'hdfs:principal'),
  520. generateProperty('component_prop1_inherited_principal', 'component_prop1:principal'),
  521. generateProperty('component_prop1_inherited_keytab', 'component_prop1:keytab'),
  522. generateProperty('component_prop2_inherited_keytab', 'component_prop2:keytab'),
  523. generateProperty('component_prop2_inherited_principal', 'component_prop2:principal'),
  524. ]);
  525. var tests = [
  526. { name: 'spnego_inherited_keytab', e: 'spnego_keytab' },
  527. { name: 'spnego_inherited_principal', e: 'spnego_principal' },
  528. { name: 'hdfs_inherited_keytab', e: 'hdfs_user_keytab' },
  529. { name: 'hdfs_inherited_principal', e: 'hdfs_user_principal_name' },
  530. { name: 'component_prop1_inherited_keytab', e: 'component.keytab' },
  531. { name: 'component_prop1_inherited_principal', e: 'component_prop1_principal' },
  532. { name: 'component_prop2_inherited_keytab', e: 'component2.keytab' },
  533. { name: 'component_prop2_inherited_principal', e: 'component2.principal' }
  534. ];
  535. before(function() {
  536. controller.processConfigReferences(descriptor, configs);
  537. });
  538. tests.forEach(function(test) {
  539. it('`{0}` should observe value from `{1}` property'.format(test.name, test.e), function() {
  540. expect(configs.findProperty('name', test.name).get('observesValueFrom')).to.be.eql(test.e);
  541. });
  542. });
  543. });
  544. });