kerberos.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  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. require('controllers/main/admin/kerberos/step4_controller');
  20. App.MainAdminKerberosController = App.KerberosWizardStep4Controller.extend({
  21. name: 'mainAdminKerberosController',
  22. securityEnabled: false,
  23. defaultKerberosLoaded: false,
  24. dataIsLoaded: false,
  25. isRecommendedLoaded: true,
  26. isEditMode: false,
  27. kdc_type: '',
  28. kdcTypesValues: {
  29. 'mit-kdc': Em.I18n.t('admin.kerberos.wizard.step1.option.kdc'),
  30. 'active-directory': Em.I18n.t('admin.kerberos.wizard.step1.option.ad'),
  31. 'ipa': Em.I18n.t('admin.kerberos.wizard.step1.option.ipa'),
  32. 'none': Em.I18n.t('admin.kerberos.wizard.step1.option.manual')
  33. },
  34. // use cluster descriptor instead of stack
  35. loadStackDescriptorConfigs: Em.alias('loadClusterDescriptorConfigs'),
  36. getAddSecurityWizardStatus: function () {
  37. return App.db.getSecurityWizardStatus();
  38. },
  39. setAddSecurityWizardStatus: function (status) {
  40. App.db.setSecurityWizardStatus(status);
  41. },
  42. setDisableSecurityStatus: function (status) {
  43. App.db.setDisableSecurityStatus(status);
  44. },
  45. getDisableSecurityStatus: function (status) {
  46. return App.db.getDisableSecurityStatus();
  47. },
  48. notifySecurityOff: false,
  49. notifySecurityAdd: false,
  50. notifySecurityOffPopup: function () {
  51. var self = this;
  52. this.checkServiceWarnings().then(function() {
  53. App.ModalPopup.show({
  54. header: Em.I18n.t('popup.confirmation.commonHeader'),
  55. primary: Em.I18n.t('ok'),
  56. onPrimary: function () {
  57. App.db.setSecurityDeployCommands(undefined);
  58. self.setDisableSecurityStatus("RUNNING");
  59. App.router.transitionTo('disableSecurity');
  60. this.hide();
  61. },
  62. bodyClass: Ember.View.extend({
  63. templateName: require('templates/main/admin/kerberos/notify_security_off_popup')
  64. })
  65. });
  66. });
  67. },
  68. /**
  69. * Show confirmation popup for regenerate keytabs
  70. * @method regenerateKeytabs
  71. * @param callback function (optional)
  72. * @return {App.ModalPopup}
  73. */
  74. regenerateKeytabs: function (callback) {
  75. var self = this;
  76. return App.ModalPopup.show({
  77. /**
  78. * True - regenerate keytabs only for missing hosts and components, false - regenerate for all hosts and components
  79. * @type {boolean}
  80. */
  81. regenerateKeytabsOnlyForMissing: false,
  82. header: Em.I18n.t('admin.kerberos.button.regenerateKeytabs'),
  83. bodyClass: Em.View.extend({
  84. templateName: require('templates/main/admin/kerberos/regenerate_keytabs_popup_body')
  85. }),
  86. onPrimary: function () {
  87. this._super();
  88. return self.restartServicesAfterRegenerate(this.get('regenerateKeytabsOnlyForMissing'), callback);
  89. }
  90. });
  91. },
  92. /**
  93. * Show confirmation popup for restarting all services and after confirmation regenerate keytabs
  94. *
  95. * @param regenerateKeytabsOnlyForMissing {Boolean}
  96. * @param callback (optional)
  97. * @returns {*}
  98. */
  99. restartServicesAfterRegenerate: function (regenerateKeytabsOnlyForMissing, callback) {
  100. var self = this;
  101. return App.ModalPopup.show({
  102. /**
  103. * True - automatically restart services, false - user will have to restart required services manually
  104. * @type {boolean}
  105. */
  106. restartComponents: false,
  107. header: Em.I18n.t('admin.kerberos.button.regenerateKeytabs'),
  108. bodyClass: Em.View.extend({
  109. templateName: require('templates/main/admin/kerberos/restart_services_after_regenerate_body')
  110. }),
  111. onPrimary: function () {
  112. this._super();
  113. var popupContext = this;
  114. // Keytabs can either be regenerated directly or after updating kerberos descriptor in the callback function
  115. if (Em.typeOf(callback) === 'function') {
  116. callback().done(function () {
  117. self.regenerateKeytabsRequest(regenerateKeytabsOnlyForMissing, popupContext.get('restartComponents'));
  118. });
  119. } else {
  120. self.regenerateKeytabsRequest(regenerateKeytabsOnlyForMissing, popupContext.get('restartComponents'));
  121. }
  122. }
  123. });
  124. },
  125. /**
  126. * Send request to regenerate keytabs
  127. * @param {boolean} missingOnly determines type of regeneration - missing|all
  128. * @param {boolean} withAutoRestart determines if the system should automatically restart all services or not after regeneration
  129. * @returns {$.ajax}
  130. */
  131. regenerateKeytabsRequest: function (missingOnly, withAutoRestart) {
  132. missingOnly = missingOnly || false;
  133. return App.ajax.send({
  134. name: "admin.kerberos_security.regenerate_keytabs",
  135. sender: this,
  136. data: {
  137. type: missingOnly ? 'missing' : 'all',
  138. withAutoRestart: withAutoRestart || false
  139. },
  140. success: "regenerateKeytabsSuccess"
  141. });
  142. },
  143. /**
  144. * Success callback of <code>regenerateKeytabs</code>
  145. * show background operations popup if appropriate option is set
  146. *
  147. * @param data
  148. * @param opt
  149. * @param params
  150. * @param request
  151. */
  152. regenerateKeytabsSuccess: function (data, opt, params, request) {
  153. var self = this;
  154. App.router.get('userSettingsController').dataLoading('show_bg').done(function (initValue) {
  155. if (initValue) {
  156. App.router.get('backgroundOperationsController').showPopup();
  157. }
  158. self.set('needsRestartAfterRegenerate', params.withAutoRestart);
  159. });
  160. },
  161. /**
  162. * Do request to server for restarting all services
  163. * @method restartAllServices
  164. * @return {$.ajax}
  165. */
  166. restartAllServices: function () {
  167. if (!App.router.get('backgroundOperationsController.allOperationsCount')) {
  168. if (this.get('needsRestartAfterRegenerate')) {
  169. this.set('needsRestartAfterRegenerate', false);
  170. App.router.get('mainServiceController').restartAllServices();
  171. }
  172. }
  173. }.observes('controllers.backgroundOperationsController.allOperationsCount'),
  174. /**
  175. * performs cluster check before kerbefos security
  176. * wizard starts if <code>preKerberizeCheck<code> supports is true
  177. * otherwise runs <code>startKerberosWizard<code>
  178. * @method checkAndStartKerberosWizard
  179. */
  180. checkAndStartKerberosWizard: function () {
  181. if (App.get('supports.preKerberizeCheck')) {
  182. App.ajax.send({
  183. name: "admin.kerberos_security.checks",
  184. sender: this,
  185. success: "runSecurityCheckSuccess"
  186. });
  187. } else {
  188. this.startKerberosWizard();
  189. }
  190. },
  191. /**
  192. * success callback of <code>checkAndStartKerberosWizard()</code>
  193. * if there are some fails - it shows popup else open security wizard
  194. * @param data {object}
  195. * @param opt {object}
  196. * @param params {object}
  197. */
  198. runSecurityCheckSuccess: function (data, opt, params) {
  199. //TODO correct check
  200. if (data.items.someProperty('UpgradeChecks.status', "FAIL")) {
  201. var
  202. hasFails = data.items.someProperty('UpgradeChecks.status', 'FAIL'),
  203. header = Em.I18n.t('popup.clusterCheck.Security.header').format(params.label),
  204. title = Em.I18n.t('popup.clusterCheck.Security.title'),
  205. alert = Em.I18n.t('popup.clusterCheck.Security.alert');
  206. App.showClusterCheckPopup(data, {
  207. header: header,
  208. failTitle: title,
  209. failAlert: alert,
  210. noCallbackCondition: hasFails
  211. });
  212. } else {
  213. this.startKerberosWizard();
  214. }
  215. },
  216. startKerberosWizard: function () {
  217. var self = this;
  218. this.checkServiceWarnings().then(function() {
  219. self.setAddSecurityWizardStatus('RUNNING');
  220. App.router.get('kerberosWizardController').setDBProperty('onClosePath', 'main.admin.adminKerberos.index');
  221. App.router.transitionTo('adminKerberos.adminAddKerberos');
  222. });
  223. },
  224. /**
  225. * Loads the security status from server (security_enabled property in cluster-env configuration)
  226. */
  227. loadSecurityStatusFromServer: function () {
  228. if (App.get('testMode')) {
  229. this.set('securityEnabled', !App.get('testEnableSecurity'));
  230. this.set('dataIsLoaded', true);
  231. } else {
  232. //get Security Status From Server
  233. this.getSecurityType();
  234. return this.getSecurityStatus();
  235. }
  236. },
  237. /**
  238. * Load security status from server.
  239. * @returns {$.Deferred}
  240. */
  241. getSecurityStatus: function () {
  242. var self = this;
  243. var dfd = $.Deferred();
  244. if (App.get('testMode')) {
  245. this.set('securityEnabled', !App.get('testEnableSecurity'));
  246. this.set('dataIsLoaded', true);
  247. dfd.resolve();
  248. } else {
  249. //get Security Status From Server
  250. App.ajax.send({
  251. name: 'admin.security_status',
  252. sender: this,
  253. success: 'getSecurityStatusSuccessCallback',
  254. error: 'errorCallback'
  255. })
  256. .always(function() {
  257. self.getSecurityType(function() {
  258. dfd.resolve();
  259. });
  260. });
  261. }
  262. return dfd.promise();
  263. },
  264. getSecurityStatusSuccessCallback: function (data) {
  265. this.set('dataIsLoaded', true);
  266. var securityType = data.Clusters.security_type;
  267. this.set('securityEnabled', securityType === 'KERBEROS');
  268. },
  269. errorCallback: function (jqXHR) {
  270. this.set('dataIsLoaded', true);
  271. // Show the error popup if the API call received a response from the server.
  272. // jqXHR.status will be empty when browser cancels the request. Refer to AMBARI-5921 for more info
  273. if (!!jqXHR.status) {
  274. this.showSecurityErrorPopup();
  275. }
  276. },
  277. showSecurityErrorPopup: function () {
  278. App.ModalPopup.show({
  279. header: Em.I18n.t('common.error'),
  280. secondary: false,
  281. bodyClass: Ember.View.extend({
  282. template: Ember.Handlebars.compile('<p>{{t admin.security.status.error}}</p>')
  283. })
  284. });
  285. },
  286. /**
  287. * Override <code>App.KerberosWizardStep4Controller</code>
  288. */
  289. clearStep: function() {
  290. this.set('isEditMode', false);
  291. this._super();
  292. },
  293. /**
  294. * Override <code>App.KerberosWizardStep4Controller</code>
  295. *
  296. * @param {App.ServiceConfigProperty[]} properties
  297. */
  298. setStepConfigs: function (properties) {
  299. this.get('stepConfigs').clear();
  300. this._super(properties);
  301. this.get('stepConfigs').forEach(function (serviceConfig) {
  302. serviceConfig.set('initConfigsLength', serviceConfig.get('configs.length'));
  303. });
  304. },
  305. /**
  306. * Override <code>App.KerberosWizardStep4Controller</code>
  307. *
  308. * @param {App.ServiceConfigProperty[]} configs
  309. * @returns {App.ServiceConfigProperty[]}
  310. */
  311. prepareConfigProperties: function (configs) {
  312. var self = this;
  313. var configProperties = configs.slice(0);
  314. var siteProperties = App.configsCollection.getAll();
  315. var installedServiceNames = ['Cluster'].concat(App.Service.find().mapProperty('serviceName'));
  316. configProperties = configProperties.filter(function (item) {
  317. return installedServiceNames.contains(item.get('serviceName'));
  318. });
  319. configProperties.setEach('isSecureConfig', false);
  320. configProperties.forEach(function (property, item, allConfigs) {
  321. if (['spnego_keytab', 'spnego_principal'].contains(property.get('name'))) {
  322. property.addObserver('value', self, 'spnegoPropertiesObserver');
  323. }
  324. if (property.get('observesValueFrom')) {
  325. var observedValue = allConfigs.findProperty('name', property.get('observesValueFrom')).get('value');
  326. property.set('value', observedValue);
  327. property.set('recommendedValue', observedValue);
  328. }
  329. if (property.get('serviceName') == 'Cluster') {
  330. property.set('category', 'Global');
  331. } else {
  332. property.set('category', property.get('serviceName'));
  333. }
  334. // All user identity should be grouped under "Ambari Principals" category
  335. if (property.get('identityType') == 'user') property.set('category', 'Ambari Principals');
  336. var siteProperty = siteProperties.findProperty('name', property.get('name'));
  337. if (siteProperty) {
  338. if (siteProperty.category === property.get('category')) {
  339. property.set('displayName', siteProperty.displayName);
  340. if (siteProperty.index) {
  341. property.set('index', siteProperty.index);
  342. }
  343. }
  344. if (siteProperty.displayType) {
  345. property.set('displayType', siteProperty.displayType);
  346. }
  347. }
  348. });
  349. configProperties.setEach('isEditable', false);
  350. return configProperties;
  351. },
  352. getKDCSessionState: function (callback, kdcCancelHandler) {
  353. var self = this;
  354. if (this.get('securityEnabled') || App.get('isKerberosEnabled')) {
  355. this.getSecurityType(function () {
  356. if (!self.get('isManualKerberos')) {
  357. App.ajax.send({
  358. name: 'kerberos.session.state',
  359. sender: self,
  360. data: {
  361. callback: callback
  362. },
  363. success: 'checkState',
  364. kdcCancelHandler: kdcCancelHandler
  365. })
  366. } else {
  367. callback();
  368. }
  369. });
  370. } else {
  371. callback();
  372. }
  373. },
  374. /**
  375. * Determines security type.
  376. *
  377. * @param {function} [callback] callback function to execute
  378. * @returns {$.Deferred|null}
  379. */
  380. getSecurityType: function (callback) {
  381. if (this.get('securityEnabled') || App.get('isKerberosEnabled')) {
  382. if (!this.get('kdc_type')) {
  383. return App.ajax.send({
  384. name: 'admin.security.cluster_configs.kerberos',
  385. sender: this,
  386. data: {
  387. clusterName: App.get('clusterName'),
  388. additionalCallback: callback
  389. },
  390. success: 'getSecurityTypeSuccess'
  391. });
  392. } else {
  393. if (Em.typeOf(callback) === 'function') {
  394. callback();
  395. }
  396. return $.Deferred().resolve().promise;
  397. }
  398. } else if (Em.typeOf(callback) === 'function') {
  399. callback();
  400. } else {
  401. return $.Deferred().resolve().promise;
  402. }
  403. },
  404. getSecurityTypeSuccess: function (data, opt, params) {
  405. var kdcType = data.items && data.items[0] &&
  406. Em.getWithDefault(Em.getWithDefault(data.items[0], 'configurations', []).findProperty('type', 'kerberos-env') || {}, 'properties.kdc_type', 'none') || 'none';
  407. this.set('kdc_type', kdcType);
  408. if (Em.typeOf(params.additionalCallback) === 'function') {
  409. params.additionalCallback();
  410. }
  411. },
  412. isManualKerberos: Em.computed.equal('kdc_type', 'none'),
  413. checkState: function (data, opt, params) {
  414. var res = Em.get(data, 'Services.attributes.kdc_validation_result');
  415. var message = Em.get(data, 'Services.attributes.kdc_validation_failure_details');
  416. if (res.toUpperCase() === "OK") {
  417. params.callback();
  418. } else {
  419. App.showInvalidKDCPopup(opt, App.format.kdcErrorMsg(message, false));
  420. }
  421. },
  422. /**
  423. * Determines if some config value is changed
  424. * @type {boolean}
  425. */
  426. isPropertiesChanged: Em.computed.someBy('stepConfigs', 'isPropertiesChanged', true),
  427. /**
  428. * Determines if the save button is disabled
  429. */
  430. isSaveButtonDisabled: Em.computed.or('isSubmitDisabled', '!isPropertiesChanged'),
  431. /**
  432. * Determines if the `Disbale Kerberos` and `Regenerate Keytabs` button are disabled
  433. */
  434. isKerberosButtonsDisabled: Em.computed.not('isSaveButtonDisabled'),
  435. makeConfigsEditable: function () {
  436. if (this.get('stepConfigs') && this.get('stepConfigs.length')) {
  437. this.set('isEditMode', true);
  438. this.get('stepConfigs').forEach(function (_stepConfig) {
  439. _stepConfig.get('configs').setEach('isEditable', true);
  440. _stepConfig.get('configs').forEach(function (_config) {
  441. _config.set('isEditable', _config.get('name') != 'realm');
  442. });
  443. }, this);
  444. }
  445. },
  446. _updateConfigs: function () {
  447. this.makeConfigsUneditable(true);
  448. },
  449. makeConfigsUneditable: function (configsUpdated) {
  450. this.set('isEditMode', false);
  451. this.get('stepConfigs').forEach(function (_stepConfig) {
  452. _stepConfig.get('configs').forEach(function (_config) {
  453. if (configsUpdated === true) { // configsUpdated should be checked for boolean true
  454. _config.set('savedValue', _config.get('value'));
  455. _config.set('defaultValue', _config.get('value'));
  456. } else {
  457. _config.set('value', _config.get('savedValue'));
  458. }
  459. _config.set('isEditable', false);
  460. });
  461. }, this);
  462. },
  463. /**
  464. * Update kerberos descriptor and regenerate keytabs
  465. */
  466. submit: function (context) {
  467. var callback;
  468. var self = this;
  469. var kerberosDescriptor = this.get('kerberosDescriptor');
  470. var configs = [];
  471. this.get('stepConfigs').forEach(function (_stepConfig) {
  472. configs = configs.concat(_stepConfig.get('configs'));
  473. });
  474. callback = function () {
  475. return App.ajax.send({
  476. name: 'admin.kerberos.cluster.artifact.update',
  477. sender: self,
  478. data: {
  479. artifactName: 'kerberos_descriptor',
  480. data: {
  481. artifact_data: kerberosDescriptor
  482. }
  483. },
  484. success: '_updateConfigs'
  485. });
  486. };
  487. this.updateKerberosDescriptor(kerberosDescriptor, configs);
  488. if (this.get('isManualKerberos')) {
  489. callback().done(function () {
  490. self.regenerateKeytabsRequest(false,false);
  491. });
  492. } else {
  493. this.restartServicesAfterRegenerate(false, callback);
  494. }
  495. },
  496. /**
  497. * List of the warnings regarding specific services before enabling/disabling Kerberos.
  498. *
  499. * @type {String[]}
  500. */
  501. serviceAlerts: function() {
  502. var messages = [];
  503. var serviceAlertMap = {
  504. YARN: Em.I18n.t('admin.kerberos.service.alert.yarn')
  505. };
  506. var installedServices = App.Service.find().mapProperty('serviceName');
  507. Em.keys(serviceAlertMap).forEach(function(serviceName) {
  508. if (installedServices.contains(serviceName)) {
  509. messages.push(serviceAlertMap[serviceName]);
  510. }
  511. });
  512. return messages;
  513. }.property(),
  514. /**
  515. * Check for additional info to display before enabling/disabling kerberos and show appropriate
  516. * messages in popup if needed.
  517. * @returns {$.Deferred} - promise
  518. */
  519. checkServiceWarnings: function() {
  520. var dfd = $.Deferred();
  521. this.displayServiceWarnings(this.get('serviceAlerts'), dfd);
  522. return dfd.promise();
  523. },
  524. /**
  525. * Show appropriate message regarding changes affected after enabling/disabling Kerberos
  526. *
  527. * @param {String[]} messages - list of the messages to display
  528. * @param {$.Deferred} dfd - used to break recursive calls and reject/resolve promise returned by <code>checkServiceWarnings</code>
  529. */
  530. displayServiceWarnings: function(messages, dfd) {
  531. var self = this;
  532. if (!messages.get('length')) {
  533. dfd.resolve();
  534. } else {
  535. App.showConfirmationPopup(function() {
  536. self.displayServiceWarnings(messages.slice(1), dfd);
  537. }, messages[0], function() {
  538. dfd.reject();
  539. }, Em.I18n.t('common.warning'), Em.I18n.t('common.proceedAnyway'));
  540. }
  541. },
  542. showManageKDCCredentialsPopup: function() {
  543. return App.showManageCredentialsPopup();
  544. }
  545. });