You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by rl...@apache.org on 2016/03/23 22:55:03 UTC
[1/2] ambari git commit: AMBARI-6432. FreeIPA Support in Ambari
(Bolke de Bruin via rlevas)
Repository: ambari
Updated Branches:
refs/heads/trunk 2d7dffce5 -> 476d87b70
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/test/resources/kerberos/test_kerberos_descriptor_simple.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/resources/kerberos/test_kerberos_descriptor_simple.json b/ambari-server/src/test/resources/kerberos/test_kerberos_descriptor_simple.json
index 292ad25..fb884b7 100644
--- a/ambari-server/src/test/resources/kerberos/test_kerberos_descriptor_simple.json
+++ b/ambari-server/src/test/resources/kerberos/test_kerberos_descriptor_simple.json
@@ -25,7 +25,7 @@
{
"name": "hdfs",
"principal": {
- "value": "${hadoop-env/hdfs_user}-${cluster_name}@${realm}",
+ "value": "${hadoop-env/hdfs_user}-${cluster_name|toLower()}@${realm}",
"type": "user",
"configuration": "hadoop-env/hdfs_principal_name",
"local_username": "${hadoop-env/hdfs_user}"
@@ -46,7 +46,7 @@
{
"name": "smokeuser",
"principal": {
- "value": "${cluster-env/smokeuser}-${cluster_name}@${realm}",
+ "value": "${cluster-env/smokeuser}-${cluster_name|toLower()}@${realm}",
"type": "user",
"configuration": "cluster-env/smokeuser_principal_name",
"local_username": "${cluster-env/smokeuser}"
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/test/resources/stacks/HDP/2.0.8/services/HDFS/kerberos.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/resources/stacks/HDP/2.0.8/services/HDFS/kerberos.json b/ambari-server/src/test/resources/stacks/HDP/2.0.8/services/HDFS/kerberos.json
index c285234..d53205d 100644
--- a/ambari-server/src/test/resources/stacks/HDP/2.0.8/services/HDFS/kerberos.json
+++ b/ambari-server/src/test/resources/stacks/HDP/2.0.8/services/HDFS/kerberos.json
@@ -18,7 +18,7 @@
{
"name": "hdfs",
"principal": {
- "value": "${hadoop-env/hdfs_user}-${cluster_name}@${realm}",
+ "value": "${hadoop-env/hdfs_user}-${cluster_name|toLower()}@${realm}",
"type" : "user" ,
"configuration": "hadoop-env/hdfs_principal_name",
"local_username" : "${hadoop-env/hdfs_user}"
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-web/app/config.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/config.js b/ambari-web/app/config.js
index 5d3b27a..b1f6bd5 100644
--- a/ambari-web/app/config.js
+++ b/ambari-web/app/config.js
@@ -80,6 +80,7 @@ App.supports = {
serviceAutoStart: false,
logSearch: false,
redhatSatellite: false,
+ enableIpa: false,
addingNewRepository: false
};
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-web/app/controllers/main/admin/kerberos.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/admin/kerberos.js b/ambari-web/app/controllers/main/admin/kerberos.js
index 9512a51..5472413 100644
--- a/ambari-web/app/controllers/main/admin/kerberos.js
+++ b/ambari-web/app/controllers/main/admin/kerberos.js
@@ -32,6 +32,7 @@ App.MainAdminKerberosController = App.KerberosWizardStep4Controller.extend({
kdcTypesValues: {
'mit-kdc': Em.I18n.t('admin.kerberos.wizard.step1.option.kdc'),
'active-directory': Em.I18n.t('admin.kerberos.wizard.step1.option.ad'),
+ 'ipa': Em.I18n.t('admin.kerberos.wizard.step1.option.ipa'),
'none': Em.I18n.t('admin.kerberos.wizard.step1.option.manual')
},
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-web/app/controllers/main/admin/kerberos/step1_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/admin/kerberos/step1_controller.js b/ambari-web/app/controllers/main/admin/kerberos/step1_controller.js
index b9056ed..1b96b13 100644
--- a/ambari-web/app/controllers/main/admin/kerberos/step1_controller.js
+++ b/ambari-web/app/controllers/main/admin/kerberos/step1_controller.js
@@ -25,7 +25,30 @@ App.KerberosWizardStep1Controller = Em.Controller.extend({
isSubmitDisabled: Em.computed.someBy('selectedOption.preConditions', 'checked', false),
- options: [
+ ipaOption: Em.Object.create({
+ displayName: Em.I18n.t('admin.kerberos.wizard.step1.option.ipa'),
+ value: Em.I18n.t('admin.kerberos.wizard.step1.option.ipa'),
+ preConditions: [
+ Em.Object.create({
+ displayText: Em.I18n.t('admin.kerberos.wizard.step1.option.ipa.condition.1'),
+ checked: false
+ }),
+ Em.Object.create({
+ displayText: Em.I18n.t('admin.kerberos.wizard.step1.option.ipa.condition.2'),
+ checked: false
+ }),
+ Em.Object.create({
+ displayText: Em.I18n.t('admin.kerberos.wizard.step1.option.ipa.condition.3'),
+ checked: false
+ }),
+ Em.Object.create({
+ displayText: Em.I18n.t('admin.kerberos.wizard.step1.option.ipa.condition.4'),
+ checked: false
+ }),
+ ]
+ }),
+
+ options: Em.A([
Em.Object.create({
displayName: Em.I18n.t('admin.kerberos.wizard.step1.option.kdc'),
value: Em.I18n.t('admin.kerberos.wizard.step1.option.kdc'),
@@ -96,7 +119,7 @@ App.KerberosWizardStep1Controller = Em.Controller.extend({
})
]
})
- ],
+ ]),
/**
* precondition for the selected KDC option
@@ -108,9 +131,15 @@ App.KerberosWizardStep1Controller = Em.Controller.extend({
}.property('selectedItem'),
-
loadStep: function () {
this.set('selectedItem', Em.I18n.t('admin.kerberos.wizard.step1.option.kdc'));
+
+ if (App.get('supports.enableIpa')) {
+ var ipaOption = this.get('ipaOption');
+ var options = this.get('options');
+
+ options.pushObject(ipaOption);
+ }
},
next: function () {
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-web/app/controllers/main/admin/kerberos/step2_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/admin/kerberos/step2_controller.js b/ambari-web/app/controllers/main/admin/kerberos/step2_controller.js
index 36fb227..7f77c38 100644
--- a/ambari-web/app/controllers/main/admin/kerberos/step2_controller.js
+++ b/ambari-web/app/controllers/main/admin/kerberos/step2_controller.js
@@ -99,13 +99,15 @@ App.KerberosWizardStep2Controller = App.WizardStep7Controller.extend(App.KDCCred
},
/**
- * Make Active Directory specific configs visible if user has selected AD option
+ * Make Active Directory or IPA specific configs visible if user has selected AD or IPA option
* @param configs
*/
filterConfigs: function (configs) {
var kdcType = this.get('content.kerberosOption');
var adConfigNames = ['ldap_url', 'container_dn', 'ad_create_attributes_template'];
var mitConfigNames = ['kdc_create_attributes'];
+ var ipaConfigNames = ['group', 'set_password_expiry', 'password_chat_timeout'];
+
var kerberosWizardController = this.controllers.get('kerberosWizardController');
var manageIdentitiesConfig = configs.findProperty('name', 'manage_identities');
@@ -134,6 +136,13 @@ App.KerberosWizardStep2Controller = App.WizardStep7Controller.extend(App.KDCCred
config.isVisible = kdcType === Em.I18n.t('admin.kerberos.wizard.step1.option.kdc');
}
}, this);
+
+ ipaConfigNames.forEach(function (_configName) {
+ var config = configs.findProperty('name', _configName);
+ if (config) {
+ config.isVisible = kdcType === Em.I18n.t('admin.kerberos.wizard.step1.option.ipa');
+ }
+ }, this);
},
submit: function () {
@@ -215,6 +224,7 @@ App.KerberosWizardStep2Controller = App.WizardStep7Controller.extend(App.KDCCred
}, this);
this.tweakKdcTypeValue(properties);
this.tweakManualKdcProperties(properties);
+ this.tweakIpaKdcProperties(properties);
return {"type": site, "tag": tag, "properties": properties};
},
@@ -243,6 +253,20 @@ App.KerberosWizardStep2Controller = App.WizardStep7Controller.extend(App.KDCCred
}
},
+ tweakIpaKdcProperties: function (properties) {
+ if (typeof properties['kdc_type'] === 'undefined') {
+ return;
+ }
+ if (this.get('content.kerberosOption') === App.router.get('mainAdminKerberosController.kdcTypesValues')['ipa']) {
+ if (properties.hasOwnProperty('install_packages')) {
+ properties['install_packages'] = 'false';
+ }
+ if (properties.hasOwnProperty('manage_krb5_conf')) {
+ properties['manage_krb5_conf'] = 'false';
+ }
+ }
+ },
+
/**
* puts kerberos admin credentials in the live cluster session
* @returns {*} jqXHr
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-web/app/controllers/main/admin/kerberos/step5_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/admin/kerberos/step5_controller.js b/ambari-web/app/controllers/main/admin/kerberos/step5_controller.js
index 5aa4b8c..38150c8 100644
--- a/ambari-web/app/controllers/main/admin/kerberos/step5_controller.js
+++ b/ambari-web/app/controllers/main/admin/kerberos/step5_controller.js
@@ -130,6 +130,10 @@ App.KerberosWizardStep5Controller = App.KerberosProgressPageController.extend({
properties: ['kdc_type', 'kdc_host', 'realm', 'ldap_url', 'container_dn', 'executable_search_paths']
},
{
+ key: Em.I18n.t('admin.kerberos.wizard.step1.option.ipa'),
+ properties: ['kdc_type', 'kdc_host', 'realm', 'executable_search_paths']
+ },
+ {
key: Em.I18n.t('admin.kerberos.wizard.step1.option.manual'),
properties: ['kdc_type', 'realm', 'executable_search_paths']
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-web/app/controllers/main/service/info/configs.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/info/configs.js b/ambari-web/app/controllers/main/service/info/configs.js
index 8c855fc..b8a716c 100644
--- a/ambari-web/app/controllers/main/service/info/configs.js
+++ b/ambari-web/app/controllers/main/service/info/configs.js
@@ -372,6 +372,12 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.ConfigsLoader, A
} else if (kdc_type.get('value') === 'active-directory') {
configs.findProperty('name', 'container_dn').set('isVisible', true);
configs.findProperty('name', 'ldap_url').set('isVisible', true);
+ } else if (kdc_type.get('value') === 'ipa') {
+ configs.findProperty('name', 'group').set('isVisible', true);
+ configs.findProperty('name', 'manage_krb5_conf').set('value', false);
+ configs.findProperty('name', 'install_packages').set('value', false);
+ configs.findProperty('name', 'admin_server_host').set('isVisible', false);
+ configs.findProperty('name', 'domains').set('isVisible', false);
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-web/app/data/HDP2/site_properties.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/data/HDP2/site_properties.js b/ambari-web/app/data/HDP2/site_properties.js
index 5ad24fc..4b2dfb7 100644
--- a/ambari-web/app/data/HDP2/site_properties.js
+++ b/ambari-web/app/data/HDP2/site_properties.js
@@ -1498,6 +1498,14 @@ var hdp2properties = [
"index" : 14
},
{
+ "id": "puppet var",
+ "name": "group",
+ "serviceName": "KERBEROS",
+ "filename": "kerberos-env.xml",
+ "category": "Advanced kerberos-env",
+ "index": 15
+ },
+ {
"name": "admin_server_host",
"serviceName": "KERBEROS",
"filename": "kerberos-env.xml",
@@ -1505,6 +1513,7 @@ var hdp2properties = [
"index": 0
},
{
+ "id": "puppet var",
"name": "manage_krb5_conf",
"dependentConfigPattern": "CATEGORY",
"serviceName": "KERBEROS",
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 636f826..4fa6ba5 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -1122,6 +1122,11 @@ Em.I18n.translations = {
'admin.kerberos.wizard.step1.option.ad.condition.3': 'Active Directory User container for principals has been created and is on-hand (e.g. OU=Hadoop,OU=People,dc=apache,dc=org)',
'admin.kerberos.wizard.step1.option.ad.condition.4': 'Active Directory administrative credentials with delegated control of “Create, delete, and manage user accounts” on the previously mentioned User container are on-hand.',
'admin.kerberos.wizard.step1.option.ad.condition.5': 'The Java Cryptography Extensions (JCE) have been setup on the Ambari Server host and all hosts in the cluster.',
+ 'admin.kerberos.wizard.step1.option.ipa': 'Existing IPA',
+ 'admin.kerberos.wizard.step1.option.ipa.condition.1': 'Cluster hosts are joined to the IPA domain and hosts are registered in DNS',
+ 'admin.kerberos.wizard.step1.option.ipa.condition.2': 'A password policy in place that sets no expiry for created principals or krbPasswordExpiry attribute is writable',
+ 'admin.kerberos.wizard.step1.option.ipa.condition.3': 'The ipa managed krb5.conf sets default_ccache_name = /tmp/krb5cc_%{uid}',
+ 'admin.kerberos.wizard.step1.option.ipa.condition.4': 'The Java Cryptography Extensions (JCE) have been setup on the Ambari Server host and all hosts in the cluster.',
'admin.kerberos.wizard.step1.prerequisites.label': 'Following prerequisites needs to be checked to progress ahead in the wizard.',
'admin.kerberos.wizard.step2.info.body': 'Please configure kerberos related properties.',
'admin.kerberos.wizard.step3.task0.title': 'Install Kerberos Client',
@@ -1141,6 +1146,7 @@ Em.I18n.translations = {
'admin.kerberos.wizard.step5.executable_search_paths.label': 'Executable path',
'admin.kerberos.wizard.step5.exitWizard': 'Exit Wizard',
'admin.kerberos.wizard.step5.downloadCSV': 'Download CSV',
+ 'admin.kerberos.wizard.step5.group.label': 'Group user principals should be member of',
'admin.kerberos.wizard.step6.task0.title' : 'Stop Services',
'admin.kerberos.wizard.step6.task1.title' : 'Delete ATS',
'admin.kerberos.wizard.step6.notice.inProgress': 'Please wait while services are being stopped.',
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-web/app/views/common/controls_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/controls_view.js b/ambari-web/app/views/common/controls_view.js
index 19d3cc5..661ef54 100644
--- a/ambari-web/app/views/common/controls_view.js
+++ b/ambari-web/app/views/common/controls_view.js
@@ -1090,7 +1090,14 @@ App.CheckDBConnectionView = Ember.View.extend({
didInsertElement: function() {
var kdc = this.get('parentView.categoryConfigsAll').findProperty('name', 'kdc_type');
if (kdc) {
- var name = kdc.get('value') == 'Existing MIT KDC' ? 'KDC' : 'AD';
+ var name = kdc.get('value');
+ if (name == 'Existing MIT KDC') {
+ name = 'KDC';
+ } else if (name == 'Existing IPA') {
+ name = 'IPA';
+ } else {
+ name = 'AD';
+ }
App.popover(this.$(), {
title: Em.I18n.t('services.service.config.database.btn.idle'),
content: Em.I18n.t('installer.controls.checkConnection.popover').format(name),
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-web/test/utils/object_utils_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/utils/object_utils_test.js b/ambari-web/test/utils/object_utils_test.js
index 0f9723b..f70259c 100644
--- a/ambari-web/test/utils/object_utils_test.js
+++ b/ambari-web/test/utils/object_utils_test.js
@@ -486,7 +486,7 @@ describe('utils/object_utils', function() {
},
{
"principal" : {
- "value" : "${cluster-env/smokeuser}-${cluster_name}@${realm}",
+ "value" : "${cluster-env/smokeuser}-${cluster_name|toLower()}@${realm}",
"local_username" : "${cluster-env/smokeuser}",
"configuration" : "cluster-env/smokeuser_principal_name",
"type" : "user"
[2/2] ambari git commit: AMBARI-6432. FreeIPA Support in Ambari
(Bolke de Bruin via rlevas)
Posted by rl...@apache.org.
AMBARI-6432. FreeIPA Support in Ambari (Bolke de Bruin via rlevas)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/476d87b7
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/476d87b7
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/476d87b7
Branch: refs/heads/trunk
Commit: 476d87b70b42a58914c69c3ce8098531d9405e48
Parents: 2d7dffc
Author: Bolke de Bruin <bd...@gmail.com>
Authored: Wed Mar 23 17:55:01 2016 -0400
Committer: Robert Levas <rl...@hortonworks.com>
Committed: Wed Mar 23 17:55:16 2016 -0400
----------------------------------------------------------------------
.../HDP/2.0.8/services/HDFS/kerberos.json | 2 +-
.../server/controller/KerberosHelperImpl.java | 8 +-
.../kerberos/CreateKeytabFilesServerAction.java | 5 +-
.../kerberos/IPAKerberosOperationHandler.java | 1134 ++++++++++++++++++
.../server/serveraction/kerberos/KDCType.java | 7 +-
.../kerberos/KerberosOperationHandler.java | 41 +-
.../KerberosOperationHandlerFactory.java | 2 +
.../kerberos/VariableReplacementHelper.java | 15 +
.../ambari/server/utils/ShellCommandUtil.java | 29 +-
.../ACCUMULO/1.6.1.2.2.0/kerberos.json | 2 +-
.../HBASE/0.96.0.2.0/kerberos.json | 2 +-
.../HDFS/2.1.0.2.0/kerberos.json | 2 +-
.../1.10.3-10/configuration/kerberos-env.xml | 52 +-
.../SPARK/1.2.0.2.2/kerberos.json | 2 +-
.../SPARK/1.4.1.2.3/kerberos.json | 2 +-
.../STORM/0.9.1.2.1/kerberos.json | 2 +-
.../resources/stacks/HDP/2.0.6/kerberos.json | 2 +-
.../HDP/2.3.ECS/services/ECS/kerberos.json | 2 +-
.../HDP/2.3.ECS/services/HBASE/kerberos.json | 2 +-
.../services/ACCUMULO/kerberos.json | 2 +-
.../HDP/2.3/services/ACCUMULO/kerberos.json | 4 +-
.../IPAKerberosOperationHandlerTest.java | 154 +++
.../kerberos/VariableReplacementHelperTest.java | 2 +
.../test_kerberos_descriptor_2_1_3.json | 14 +-
.../test_kerberos_descriptor_no_hdfs.json | 4 +-
.../test_kerberos_descriptor_simple.json | 4 +-
.../HDP/2.0.8/services/HDFS/kerberos.json | 2 +-
ambari-web/app/config.js | 1 +
.../app/controllers/main/admin/kerberos.js | 1 +
.../main/admin/kerberos/step1_controller.js | 35 +-
.../main/admin/kerberos/step2_controller.js | 26 +-
.../main/admin/kerberos/step5_controller.js | 4 +
.../controllers/main/service/info/configs.js | 6 +
ambari-web/app/data/HDP2/site_properties.js | 9 +
ambari-web/app/messages.js | 6 +
ambari-web/app/views/common/controls_view.js | 9 +-
ambari-web/test/utils/object_utils_test.js | 2 +-
37 files changed, 1555 insertions(+), 43 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-funtest/src/test/resources/stacks/HDP/2.0.8/services/HDFS/kerberos.json
----------------------------------------------------------------------
diff --git a/ambari-funtest/src/test/resources/stacks/HDP/2.0.8/services/HDFS/kerberos.json b/ambari-funtest/src/test/resources/stacks/HDP/2.0.8/services/HDFS/kerberos.json
index c285234..d53205d 100644
--- a/ambari-funtest/src/test/resources/stacks/HDP/2.0.8/services/HDFS/kerberos.json
+++ b/ambari-funtest/src/test/resources/stacks/HDP/2.0.8/services/HDFS/kerberos.json
@@ -18,7 +18,7 @@
{
"name": "hdfs",
"principal": {
- "value": "${hadoop-env/hdfs_user}-${cluster_name}@${realm}",
+ "value": "${hadoop-env/hdfs_user}-${cluster_name|toLower()}@${realm}",
"type" : "user" ,
"configuration": "hadoop-env/hdfs_principal_name",
"local_username" : "${hadoop-env/hdfs_user}"
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
index be6edc9..f7326a0 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
@@ -1640,7 +1640,6 @@ public class KerberosHelperImpl implements KerberosHelper {
Role.KERBEROS_CLIENT.name().equals(componentName) &&
(sch.getState() == State.INSTALLED)) {
hostsWithValidKerberosClient.add(hostname);
-
int identitiesAdded = 0;
// Lazily create the KerberosIdentityDataFileWriter instance...
@@ -1671,6 +1670,10 @@ public class KerberosHelperImpl implements KerberosHelper {
String message = String.format("Failed to write index file - %s", identityDataFile.getAbsolutePath());
LOG.error(message);
throw new AmbariException(message, e);
+ } catch (Exception e) {
+ // make sure to log what is going wrong
+ LOG.error("Failed " + e);
+ throw e;
} finally {
if (kerberosIdentityDataFileWriter != null) {
// Make sure the data file is closed
@@ -1687,7 +1690,8 @@ public class KerberosHelperImpl implements KerberosHelper {
if (!serviceComponentHostsToProcess.isEmpty()) {
try {
validateKDCCredentials(kerberosDetails, cluster);
- } catch (KerberosOperationException e) {
+ } catch (Exception e) {
+ LOG.error("Cannot validate credentials: " + e);
try {
FileUtils.deleteDirectory(dataDirectory);
} catch (Throwable t) {
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java
index cadfe28..8aa816d 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java
@@ -298,6 +298,7 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction {
public Keytab createKeytab(String principal, String password, Integer keyNumber,
KerberosOperationHandler operationHandler, boolean checkCache,
boolean canCache, ActionLog actionLog) throws AmbariException {
+ LOG.debug("Creating keytab for " + principal + " with kvno " + keyNumber);
Keytab keytab = null;
// Possibly get the keytab from the cache
@@ -335,8 +336,8 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction {
File cachedKeytabFile = cacheKeytab(principal, keytab);
String previousCachedFilePath = principalEntity.getCachedKeytabPath();
String cachedKeytabFilePath = ((cachedKeytabFile == null) || !cachedKeytabFile.exists())
- ? null
- : cachedKeytabFile.getAbsolutePath();
+ ? null
+ : cachedKeytabFile.getAbsolutePath();
principalEntity.setCachedKeytabPath(cachedKeytabFilePath);
kerberosPrincipalDAO.merge(principalEntity);
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/IPAKerberosOperationHandler.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/IPAKerberosOperationHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/IPAKerberosOperationHandler.java
new file mode 100644
index 0000000..537a334
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/IPAKerberosOperationHandler.java
@@ -0,0 +1,1134 @@
+/*
+ * 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.
+ */
+
+package org.apache.ambari.server.serveraction.kerberos;
+
+import org.apache.ambari.server.security.credential.PrincipalKeyCredential;
+import org.apache.ambari.server.utils.ShellCommandUtil;
+import org.apache.commons.lang.StringUtils;
+import org.apache.directory.server.kerberos.shared.keytab.Keytab;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.TimeZone;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * IPAKerberosOperationHandler is an implementation of a KerberosOperationHandler providing
+ * functionality specifically for IPA managed KDC. See http://www.freeipa.org
+ * <p/>
+ * It is assumed that the IPA admin tools are installed and that the ipa shell command is
+ * available
+ */
+public class IPAKerberosOperationHandler extends KerberosOperationHandler {
+ private final static Logger LOG = LoggerFactory.getLogger(IPAKerberosOperationHandler.class);
+
+ private String adminServerHost = null;
+
+ private HashMap<String, Keytab> cachedKeytabs = null;
+ /**
+ * This is where user principals are members of. Important as the password should not expire
+ * and thus a separate password policy should apply to this group
+ */
+ private String userPrincipalGroup = null;
+
+ /**
+ * The format used for krbPasswordExpiry
+ */
+ private final SimpleDateFormat expiryFormat = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'");
+
+ /**
+ * Time zone for krbPasswordExpiry
+ */
+ private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
+
+ /**
+ * Years to add for password expiry
+ */
+ private static final int PASSWORD_EXPIRY_YEAR = 30;
+
+ /**
+ * A regular expression pattern to use to parse the key number from the text captured from the
+ * kvno command
+ */
+ private final static Pattern PATTERN_GET_KEY_NUMBER = Pattern.compile("^.*?: kvno = (\\d+).*$", Pattern.DOTALL);
+
+ /**
+ * A String containing the resolved path to the ipa executable
+ */
+ private String executableIpaGetKeytab = null;
+
+ /**
+ * A String containing the resolved path to the ipa executable
+ */
+ private String executableIpa = null;
+
+ /**
+ * A String containing the resolved path to the kinit executable
+ */
+ private String executableKinit = null;
+
+ /**
+ * A String containing the resolved path to the ipa-getkeytab executable
+ */
+ private String executableKvno = null;
+
+ /**
+ * A boolean indicating if password expiry should be set
+ */
+ private boolean usePasswordExpiry = false;
+
+ /**
+ * An int indicating the time out in seconds for the password chat;
+ */
+ private int timeout = DEFAULT_PASSWORD_CHAT_TIMEOUT;
+
+ /**
+ * Credentials context stores a handler to the ccache so it can be reused and removed on request
+ */
+ private CredentialsContext credentialsContext;
+
+ /**
+ * Prepares and creates resources to be used by this KerberosOperationHandler
+ * <p/>
+ * It is expected that this KerberosOperationHandler will not be used before this call.
+ * <p/>
+ * The kerberosConfiguration Map is not being used.
+ *
+ * @param administratorCredentials a KerberosCredential containing the administrative credentials
+ * for the relevant IPA KDC
+ * @param realm a String declaring the default Kerberos realm (or domain)
+ * @param kerberosConfiguration a Map of key/value pairs containing data from the kerberos-env configuration set
+ * @throws KerberosKDCConnectionException if a connection to the KDC cannot be made
+ * @throws KerberosAdminAuthenticationException if the administrator credentials fail to authenticate
+ * @throws KerberosRealmException if the realm does not map to a KDC
+ * @throws KerberosOperationException if an unexpected error occurred
+ */
+ @Override
+ public void open(PrincipalKeyCredential administratorCredentials, String realm,
+ Map<String, String> kerberosConfiguration)
+ throws KerberosOperationException {
+
+ setAdministratorCredential(administratorCredentials);
+ setDefaultRealm(realm);
+
+ if (kerberosConfiguration != null) {
+ // todo: ignore if ipa managed krb5.conf?
+ setKeyEncryptionTypes(translateEncryptionTypes(kerberosConfiguration.get(KERBEROS_ENV_ENCRYPTION_TYPES), "\\s+"));
+ setExecutableSearchPaths(kerberosConfiguration.get(KERBEROS_ENV_EXECUTABLE_SEARCH_PATHS));
+ setUserPrincipalGroup(kerberosConfiguration.get(KERBEROS_ENV_USER_PRINCIPAL_GROUP));
+ setAdminServerHost(kerberosConfiguration.get(KERBEROS_ENV_ADMIN_SERVER_HOST));
+ setUsePasswordExpiry(kerberosConfiguration.get(KERBEROS_ENV_SET_PASSWORD_EXPIRY));
+ setTimeout(kerberosConfiguration.get(KERBEROS_ENV_PASSWORD_CHAT_TIMEOUT));
+ } else {
+ setKeyEncryptionTypes(null);
+ setAdminServerHost(null);
+ setExecutableSearchPaths((String) null);
+ setUserPrincipalGroup(null);
+ setUsePasswordExpiry(null);
+ setTimeout(null);
+ }
+
+ // Pre-determine the paths to relevant Kerberos executables
+ executableIpa = getExecutable("ipa");
+ executableKvno = getExecutable("kvno");
+ executableKinit = getExecutable("kinit");
+ executableIpaGetKeytab = getExecutable("ipa-getkeytab");
+
+ credentialsContext = new CredentialsContext(administratorCredentials);
+ cachedKeytabs = new HashMap<>();
+ expiryFormat.setTimeZone(UTC);
+
+ setOpen(true);
+ }
+
+ private void setUsePasswordExpiry(String usePasswordExpiry) {
+ if (usePasswordExpiry == null) {
+ this.usePasswordExpiry = false;
+ return;
+ }
+
+ if (usePasswordExpiry.equalsIgnoreCase("true")) {
+ this.usePasswordExpiry = true;
+ } else {
+ this.usePasswordExpiry = false;
+ }
+ }
+
+ private void setTimeout(String timeout) {
+ if (timeout == null || timeout.isEmpty()) {
+ this.timeout = DEFAULT_PASSWORD_CHAT_TIMEOUT;
+ return;
+ }
+
+ try {
+ this.timeout = Integer.parseInt(timeout);
+ } catch (NumberFormatException e) {
+ this.timeout = DEFAULT_PASSWORD_CHAT_TIMEOUT;
+ }
+ }
+
+ @Override
+ public void close() throws KerberosOperationException {
+ if (isOpen()) {
+ credentialsContext.delete();
+ }
+
+ // There is nothing to do here.
+ setOpen(false);
+
+ executableIpa = null;
+ executableKvno = null;
+ executableIpaGetKeytab = null;
+ executableKinit = null;
+ credentialsContext = null;
+ cachedKeytabs = null;
+ }
+
+ /**
+ * Test to see if the specified principal exists in a previously configured IPA KDC
+ * <p/>
+ * This implementation creates a query to send to the ipa shell command and then interrogates
+ * the result from STDOUT to determine if the presence of the specified principal.
+ *
+ * @param principal a String containing the principal to test
+ * @return true if the principal exists; false otherwise
+ * @throws KerberosOperationException if an unexpected error occurred
+ */
+ @Override
+ public boolean principalExists(String principal)
+ throws KerberosOperationException {
+
+ LOG.debug("Entering principal exists");
+
+ if (!isOpen()) {
+ throw new KerberosOperationException("This operation handler has not been opened");
+ }
+
+ if (principal == null) {
+ return false;
+ } else if (isServicePrincipal(principal)) {
+ return true;
+ } else {
+ // TODO: fix exception check to only check for relevant exceptions
+ try {
+ DeconstructedPrincipal deconstructedPrincipal = createDeconstructPrincipal(principal);
+ LOG.debug("Running IPA command user-show");
+
+ // Create the ipa query to execute:
+ ShellCommandUtil.Result result = invokeIpa(String.format("user-show %s", deconstructedPrincipal.getPrincipalName()));
+ if (result.isSuccessful()) {
+ return true;
+ }
+ } catch (KerberosOperationException e) {
+ LOG.error("Cannot invoke IPA: " + e);
+ throw e;
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Creates a new principal in a previously configured IPA Realm
+ * <p/>
+ * This implementation creates a query to send to the kadmin shell command and then interrogates
+ * the result from STDOUT to determine if the operation executed successfully.
+ *
+ * @param principal a String containing the principal add
+ * @param password a String containing the password to use when creating the principal
+ * @param service a boolean value indicating whether the principal is to be created as a service principal or not
+ * @return an Integer declaring the generated key number
+ * @throws KerberosKDCConnectionException if a connection to the KDC cannot be made
+ */
+ @Override
+ public Integer createPrincipal(String principal, String password, boolean service)
+ throws KerberosOperationException {
+
+ if (!isOpen()) {
+ throw new KerberosOperationException("This operation handler has not been opened");
+ }
+
+ if ((principal == null) || principal.isEmpty()) {
+ throw new KerberosOperationException("Failed to create new principal - no principal specified");
+ } else if (((password == null) || password.isEmpty()) && service) {
+ throw new KerberosOperationException("Failed to create new user principal - no password specified");
+ } else {
+ DeconstructedPrincipal deconstructedPrincipal = createDeconstructPrincipal(principal);
+
+ if (service) {
+ // Create the ipa query: service-add --ok-as-delegate <principal>
+ ShellCommandUtil.Result result = invokeIpa(String.format("service-add %s", principal));
+ if (result.isSuccessful()) {
+ // IPA does not generate encryption types when no keytab has been generated
+ // So getKeyNumber(principal) cannot be used.
+ // createKeytabCredentials(principal, password);
+ // return getKeyNumber(principal);
+ return 0;
+ } else {
+ LOG.error("Failed to execute ipa query: service-add --ok-as-delegate=TRUE {}\nSTDOUT: {}\nSTDERR: {}",
+ principal, result.getStdout(), result.getStderr());
+ throw new KerberosOperationException(String.format("Failed to create service principal for %s\nSTDOUT: %s\nSTDERR: %s",
+ principal, result.getStdout(), result.getStderr()));
+ }
+ } else {
+ if (!StringUtils.isAllLowerCase(deconstructedPrincipal.getPrincipalName())) {
+ LOG.warn(deconstructedPrincipal.getPrincipalName() + " is not in lowercase. FreeIPA does not recognize user " +
+ "principals that are not entirely in lowercase. This can lead to issues with kinit and keytabs. Make " +
+ "sure users are in lowercase ");
+ }
+ // Create the ipa query: user-add <username> --principal=<principal_name> --first <primary> --last <primary>
+ // set-attr userPassword="<password>"
+ // first and last are required for IPA so we make it equal to the primary
+ // the --principal arguments makes sure that Kerberos keys are available for use in getKeyNumber
+ ShellCommandUtil.Result result = invokeIpa(String.format("user-add %s --principal=%s --first %s --last %s --setattr userPassword=%s",
+ deconstructedPrincipal.getPrimary(), deconstructedPrincipal.getPrincipalName(),
+ deconstructedPrincipal.getPrimary(), deconstructedPrincipal.getPrimary(), password));
+
+ if (!result.isSuccessful()) {
+ throw new KerberosOperationException(String.format("Failed to create user principal for %s\nSTDOUT: %s\nSTDERR: %s",
+ principal, result.getStdout(), result.getStderr()));
+ }
+
+ if (getUserPrincipalGroup() != null && !getUserPrincipalGroup().isEmpty()) {
+ result = invokeIpa(String.format("group-add-member %s --users=%s",
+ getUserPrincipalGroup(), deconstructedPrincipal.getPrimary()));
+ if (!result.isSuccessful()) {
+ throw new KerberosOperationException(String.format("Failed to create user principal for %s\nSTDOUT: %s\nSTDERR: %s",
+ principal, result.getStdout(), result.getStderr()));
+ }
+ }
+
+ if (!usePasswordExpiry) {
+ updatePassword(deconstructedPrincipal.getPrimary(), password);
+ return getKeyNumber(principal);
+ }
+
+ Calendar calendar = Calendar.getInstance();
+ calendar.add(Calendar.YEAR, PASSWORD_EXPIRY_YEAR);
+
+ result = invokeIpa(String.format("user-mod %s --setattr krbPasswordExpiration=%s",
+ deconstructedPrincipal.getPrimary(), expiryFormat.format(calendar.getTime())));
+
+ if (result.isSuccessful()) {
+ return getKeyNumber(principal);
+ }
+
+ throw new KerberosOperationException(String.format("Unknown error while creating principal for %s\n" +
+ "STDOUT: %s\n" +
+ "STDERR: %s\n",
+ principal, result.getStdout(), result.getStderr()));
+ }
+ }
+ }
+
+ /**
+ * Updates the password for an existing user principal in a previously configured IPA KDC
+ * <p/>
+ * This implementation creates a query to send to the ipa shell command and then interrogates
+ * the exit code to determine if the operation executed successfully.
+ *
+ * @param principal a String containing the principal to update
+ * @param password a String containing the password to set
+ * @return an Integer declaring the new key number
+ * @throws KerberosOperationException if an unexpected error occurred
+ */
+ @Override
+ public Integer setPrincipalPassword(String principal, String password) throws KerberosOperationException {
+ if (!isOpen()) {
+ throw new KerberosOperationException("This operation handler has not been opened");
+ }
+
+ if ((principal == null) || principal.isEmpty()) {
+ throw new KerberosOperationException("Failed to set password - no principal specified");
+ } else if ((password == null) || password.isEmpty()) {
+ throw new KerberosOperationException("Failed to set password - no password specified");
+ } else if (!isServicePrincipal(principal)) {
+ DeconstructedPrincipal deconstructedPrincipal = createDeconstructPrincipal(principal);
+
+ if (usePasswordExpiry) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.add(Calendar.YEAR, PASSWORD_EXPIRY_YEAR);
+
+ // Create the ipa query: user-mod <user> --setattr userPassword=<password>
+ invokeIpa(String.format("user-mod %s --setattr userPassword=%s", deconstructedPrincipal.getPrimary(), password));
+
+ List<String> command = new ArrayList<>();
+ command.add(executableIpa);
+ command.add("user-mod");
+ command.add(deconstructedPrincipal.getPrimary());
+ command.add("--setattr");
+ command.add(String.format("krbPasswordExpiration=%s", expiryFormat.format(calendar.getTime())));
+ ShellCommandUtil.Result result = executeCommand(command.toArray(new String[command.size()]));
+ if (!result.isSuccessful()) {
+ throw new KerberosOperationException("Failed to set password expiry");
+ }
+ } else {
+ updatePassword(deconstructedPrincipal.getPrimary(), password);
+ }
+ } else {
+ ShellCommandUtil.Result result = invokeIpa(String.format("service-show %s", principal));
+ // ignore the keytab but set the password for this principal
+ if (result.isSuccessful() && result.getStdout().contains("Keytab: False")) {
+ LOG.debug("Found service principal " + principal + " without password/keytab. Setting one");
+ createKeytab(principal, password, 0);
+ }
+ }
+ return getKeyNumber(principal);
+ }
+
+ /**
+ * Removes an existing principal in a previously configured KDC
+ * <p/>
+ * The implementation is specific to a particular type of KDC.
+ *
+ * @param principal a String containing the principal to remove
+ * @return true if the principal was successfully removed; otherwise false
+ * @throws KerberosKDCConnectionException if a connection to the KDC cannot be made
+ * @throws KerberosAdminAuthenticationException if the administrator credentials fail to authenticate
+ * @throws KerberosRealmException if the realm does not map to a KDC
+ * @throws KerberosOperationException if an unexpected error occurred
+ */
+ @Override
+ public boolean removePrincipal(String principal) throws KerberosOperationException {
+ if (!isOpen()) {
+ throw new KerberosOperationException("This operation handler has not been opened");
+ }
+
+ if ((principal == null) || principal.isEmpty()) {
+ throw new KerberosOperationException("Failed to remove new principal - no principal specified");
+ } else {
+ ShellCommandUtil.Result result = null;
+ if (isServicePrincipal(principal)) {
+ result = invokeIpa(String.format("service-del %s", principal));
+ } else {
+ DeconstructedPrincipal deconstructedPrincipal = createDeconstructPrincipal(principal);
+ result = invokeIpa(String.format("user-del %s", deconstructedPrincipal.getPrincipalName()));
+ }
+ return result.isSuccessful();
+ }
+ }
+
+ /**
+ * Sets the name of the group where user principals should be members of
+ *
+ * @param userPrincipalGroup the name of the group
+ */
+ public void setUserPrincipalGroup(String userPrincipalGroup) {
+ this.userPrincipalGroup = userPrincipalGroup;
+ }
+
+ /**
+ * Gets the name of the group where user principals should be members of
+ *
+ * @return name of the group where user principals should be members of
+ */
+ public String getUserPrincipalGroup() {
+ return this.userPrincipalGroup;
+ }
+
+ /**
+ * Sets the KDC administrator server host address
+ *
+ * @param adminServerHost the ip address or FQDN of the IPA administrator server
+ */
+ public void setAdminServerHost(String adminServerHost) {
+ this.adminServerHost = adminServerHost;
+ }
+
+ /**
+ * Gets the IP address or FQDN of the IPA administrator server
+ *
+ * @return the IP address or FQDN of the IPA administrator server
+ */
+ public String getAdminServerHost() {
+ return this.adminServerHost;
+ }
+
+ /**
+ * Reads data from a stream without blocking and when available. Allows some time for the
+ * stream to become ready.
+ *
+ * @param stdin the stdin BufferedReader to read from
+ * @param stderr the stderr BufferedReader in case something goes wrong
+ * @return a String with available data
+ * @throws KerberosOperationException if a timeout happens
+ * @throws IOException when somethings goes wrong with the underlying stream
+ * @throws InterruptedException if the thread is interrupted
+ */
+ private String readData(BufferedReader stdin, BufferedReader stderr) throws KerberosOperationException, IOException, InterruptedException {
+ char[] data = new char[1024];
+ StringBuilder sb = new StringBuilder();
+
+ int count = 0;
+ while (!stdin.ready()) {
+ Thread.sleep(1000L);
+ if (count >= timeout) {
+ char[] err_data = new char[1024];
+ StringBuilder err = new StringBuilder();
+ while (stderr.ready()) {
+ stderr.read(err_data);
+ err.append(err_data);
+ }
+ throw new KerberosOperationException("No answer data available from stdin stream. STDERR: " + err.toString());
+ }
+ count++;
+ }
+
+ while (stdin.ready()) {
+ stdin.read(data);
+ sb.append(data);
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Updates a password for a (user) principal. This is done by first setting a random password and
+ * then invoking kInit to directly set the password. This is done to circumvent issues with expired
+ * password in IPA, as IPA needs passwords set by the admin to be set again by the user. Note that
+ * this resets the current principal to the principal specified here. To invoke further administrative
+ * commands a new kInit to admin is required.
+ *
+ * @param principal The principal user name that needs to be updated
+ * @param password The new password
+ * @throws KerberosOperationException if something is not as expected
+ */
+ private void updatePassword(String principal, String password) throws KerberosOperationException {
+ BufferedReader reader = null;
+ BufferedReader stderr = null;
+ OutputStreamWriter out = null;
+
+ LOG.debug("Updating password for: " + principal);
+
+ UUID uuid = UUID.randomUUID();
+ String fileName = System.getProperty("java.io.tmpdir") +
+ File.pathSeparator +
+ "krb5cc_" + uuid.toString();
+
+ try {
+ ShellCommandUtil.Result result = invokeIpa(String.format("user-mod %s --random", principal));
+ if (!result.isSuccessful()) {
+ throw new KerberosOperationException(result.getStderr());
+ }
+ Pattern pattern = Pattern.compile("password: (.*)");
+ Matcher matcher = pattern.matcher(result.getStdout());
+ if (!matcher.find()) {
+ throw new KerberosOperationException("Unexpected response from ipa: " + result.getStdout());
+ }
+ String old_password = matcher.group(1);
+
+ String credentialsCache = String.format("FILE:%s", fileName);
+ Process process = Runtime.getRuntime().exec(new String[]{executableKinit, "-c", credentialsCache, principal});
+ reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8));
+ stderr = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8));
+ out = new OutputStreamWriter(process.getOutputStream());
+
+ String data = readData(reader, stderr);
+ if (!data.startsWith("Password")) {
+ process.destroy();
+ throw new KerberosOperationException("Unexpected response from kinit while trying to password for "
+ + principal + " got: " + data);
+ }
+ LOG.debug("Sending old password");
+ out.write(old_password);
+ out.write('\n');
+ out.flush();
+
+ data = readData(reader, stderr);
+ if (!data.contains("Enter")) {
+ process.destroy();
+ throw new KerberosOperationException("Unexpected response from kinit while trying to password for "
+ + principal + " got: " + data);
+ }
+ LOG.debug("Sending new password");
+ out.write(password);
+ out.write('\n');
+ out.flush();
+
+ data = readData(reader, stderr);
+ if (!data.contains("again")) {
+ process.destroy();
+ throw new KerberosOperationException("Unexpected response from kinit while trying to password for "
+ + principal + " got: " + data);
+ }
+ LOG.debug("Sending new password again");
+ out.write(password);
+ out.write('\n');
+ out.flush();
+
+ process.waitFor();
+ } catch (IOException e) {
+ LOG.error("Cannot read stream: " + e);
+ throw new KerberosOperationException(e.getMessage());
+ } catch (InterruptedException e) {
+ LOG.error("Process interrupted: " + e);
+ throw new KerberosOperationException(e.getMessage());
+ } finally {
+ try {
+ if (out != null)
+ out.close();
+ } catch (IOException e) {
+ LOG.warn("Cannot close out stream: " + e);
+ }
+ try {
+ if (reader != null)
+ reader.close();
+ } catch (IOException e) {
+ LOG.warn("Cannot close stdin stream: " + e);
+ }
+ try {
+ if (stderr != null)
+ stderr.close();
+ } catch (IOException e) {
+ LOG.warn("Cannot close stderr stream: " + e);
+ }
+ File ccache = new File(fileName);
+ ccache.delete();
+ }
+
+ }
+
+ /**
+ * Invokes the ipa shell command with administrative credentials to issue queries
+ *
+ * @param query a String containing the query to send to the kdamin command
+ * @return a ShellCommandUtil.Result containing the result of the operation
+ * @throws KerberosOperationException if an unexpected error occurred
+ */
+ protected ShellCommandUtil.Result invokeIpa(String query)
+ throws KerberosOperationException {
+ LOG.debug("Entering invokeipa");
+
+ ShellCommandUtil.Result result = null;
+
+ if ((query == null) || query.isEmpty()) {
+ throw new KerberosOperationException("Missing ipa query");
+ }
+ PrincipalKeyCredential administratorCredentials = getAdministratorCredential();
+ String defaultRealm = getDefaultRealm();
+
+ List<String> command = new ArrayList<String>();
+ List<String> kinit = new ArrayList<String>();
+
+ String adminPrincipal = (administratorCredentials == null)
+ ? null
+ : administratorCredentials.getPrincipal();
+
+ if ((adminPrincipal == null) || adminPrincipal.isEmpty()) {
+ throw new KerberosOperationException("No admin principal for ipa available - " +
+ "this KerberosOperationHandler may not have been opened.");
+ }
+
+ if ((executableIpa == null) || executableIpa.isEmpty()) {
+ throw new KerberosOperationException("No path for ipa is available - " +
+ "this KerberosOperationHandler may not have been opened.");
+ }
+
+ // Set the ipa interface to be ipa
+ command.add(executableIpa);
+ command.add(query);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Executing: %s", createCleanCommand(command)));
+ }
+
+ List<String> fixedCommand = fixCommandList(command);
+ result = executeCommand(fixedCommand.toArray(new String[fixedCommand.size()]));
+
+
+ LOG.debug("Done invokeipa");
+ return result;
+ }
+
+ /**
+ * Executes a shell command in a credentials context
+ * <p/>
+ * See {@link org.apache.ambari.server.utils.ShellCommandUtil#runCommand(String[])}
+ *
+ * @param command an array of String value representing the command and its arguments
+ * @return a ShellCommandUtil.Result declaring the result of the operation
+ * @throws KerberosOperationException
+ */
+ @Override
+ protected ShellCommandUtil.Result executeCommand(String[] command)
+ throws KerberosOperationException {
+ return credentialsContext.executeCommand(command);
+ }
+
+ /**
+ * Rebuilds the command line to make sure space are converted to arguments
+ *
+ * @param command a List of items making up the command
+ * @return the fixed command
+ */
+ private List<String> fixCommandList(List<String> command) {
+ List<String> fixedCommandList = new ArrayList<>();
+ Iterator<String> iterator = command.iterator();
+
+ if (iterator.hasNext()) {
+ fixedCommandList.add(iterator.next());
+ }
+
+ while (iterator.hasNext()) {
+ String part = iterator.next();
+
+ // split arguments
+ if (part.contains(" ")) {
+ StringTokenizer st = new StringTokenizer(part, " ");
+ while (st.hasMoreElements()) {
+ fixedCommandList.add(st.nextToken());
+ }
+ } else {
+ fixedCommandList.add(part);
+ }
+ }
+
+ return fixedCommandList;
+ }
+
+ /**
+ * Build the ipa command string, replacing administrator password with "********"
+ *
+ * @param command a List of items making up the command
+ * @return the cleaned command string
+ */
+ private String createCleanCommand(List<String> command) {
+ StringBuilder cleanedCommand = new StringBuilder();
+ Iterator<String> iterator = command.iterator();
+
+ if (iterator.hasNext()) {
+ cleanedCommand.append(iterator.next());
+ }
+
+ while (iterator.hasNext()) {
+ String part = iterator.next();
+
+ cleanedCommand.append(' ');
+ cleanedCommand.append(part);
+
+ if ("--setattr".equals(part)) {
+ // Skip the password and use "********" instead
+ String arg= null;
+ if (iterator.hasNext()) {
+ arg = iterator.next();
+ if (arg.contains("userPassword")) {
+ cleanedCommand.append("userPassword=******");
+ } else {
+ cleanedCommand.append(arg);
+ }
+ }
+ }
+ }
+
+ return cleanedCommand.toString();
+ }
+
+ /**
+ * Determine is a principal is a service principal
+ *
+ * @param principal
+ * @return true if the principal is a (existing) service principal
+ * @throws KerberosOperationException
+ */
+ private boolean isServicePrincipal(String principal)
+ throws KerberosOperationException {
+
+ if ((principal == null) || principal.isEmpty()) {
+ throw new KerberosOperationException("Failed to determine principal type- no principal specified");
+ } else if (!principal.contains("/")) {
+ return false;
+ }
+
+ try {
+ ShellCommandUtil.Result result = invokeIpa(String.format("service-show %s", principal));
+
+ // TODO: unfortunately we can be in limbo if the "Keytab: False" is present
+ if (result.isSuccessful()) {
+ return true;
+ }
+ } catch (KerberosOperationException e) {
+ LOG.warn("Exception while invoking ipa service-show: " + e);
+ return false;
+ }
+
+ return false;
+ }
+
+ /**
+ * Retrieves the current key number assigned to the identity identified by the specified principal
+ *
+ * @param principal a String declaring the principal to look up
+ * @return an Integer declaring the current key number
+ * @throws KerberosKDCConnectionException if a connection to the KDC cannot be made
+ * @throws KerberosAdminAuthenticationException if the administrator credentials fail to authenticate
+ * @throws KerberosRealmException if the realm does not map to a KDC
+ * @throws KerberosOperationException if an unexpected error occurred
+ */
+ private Integer getKeyNumber(String principal) throws KerberosOperationException {
+ if (!isOpen()) {
+ throw new KerberosOperationException("This operation handler has not been opened");
+ }
+
+ if ((principal == null) || principal.isEmpty()) {
+ throw new KerberosOperationException("Failed to get key number for principal - no principal specified");
+ } else {
+ // Create the kvno query: <principal>
+ List<String> command = new ArrayList<>();
+ command.add(executableKvno);
+ command.add(principal);
+
+ ShellCommandUtil.Result result = executeCommand(command.toArray(new String[command.size()]));
+ String stdOut = result.getStdout();
+ if (stdOut == null) {
+ String message = String.format("Failed to get key number for %s:\n\tExitCode: %s\n\tSTDOUT: NULL\n\tSTDERR: %s",
+ principal, result.getExitCode(), result.getStderr());
+ LOG.warn(message);
+ throw new KerberosOperationException(message);
+ }
+
+ Matcher matcher = PATTERN_GET_KEY_NUMBER.matcher(stdOut);
+ if (matcher.matches()) {
+ NumberFormat numberFormat = NumberFormat.getIntegerInstance();
+ String keyNumber = matcher.group(1);
+
+ numberFormat.setGroupingUsed(false);
+ try {
+ Number number = numberFormat.parse(keyNumber);
+ return (number == null) ? 0 : number.intValue();
+ } catch (ParseException e) {
+ String message = String.format("Failed to get key number for %s - invalid key number value (%s):\n\tExitCode: %s\n\tSTDOUT: NULL\n\tSTDERR: %s",
+ principal, keyNumber, result.getExitCode(), result.getStderr());
+ LOG.warn(message);
+ throw new KerberosOperationException(message);
+ }
+ } else {
+ String message = String.format("Failed to get key number for %s - unexpected STDOUT data:\n\tExitCode: %s\n\tSTDOUT: NULL\n\tSTDERR: %s",
+ principal, result.getExitCode(), result.getStderr());
+ LOG.warn(message);
+ throw new KerberosOperationException(message);
+ }
+
+ }
+ }
+
+ /**
+ * Creates a key tab by using the ipa commandline utilities.
+ *
+ * @param principal a String containing the principal to test
+ * @param password a String containing the password to use when creating the principal
+ * @return
+ * @throws KerberosOperationException
+ */
+ /*private Keytab createKeytabCredentials(String principal, String password)
+ throws KerberosOperationException {
+
+ if ((principal == null) || principal.isEmpty()) {
+ throw new KerberosOperationException("Failed to create keytab file, missing principal");
+ }
+
+ BufferedReader reader = null;
+ BufferedReader stderr = null;
+ OutputStreamWriter out = null;
+
+ UUID uuid = UUID.randomUUID();
+ String fileName = System.getProperty("java.io.tmpdir") +
+ File.pathSeparator +
+ "ambari." + uuid.toString();
+
+ try {
+ // TODO: add ciphers
+ Process p = credentialsContext.exec(new String[]{executableIpaGetKeytab, "-s",
+ getAdminServerHost(), "-p", principal, "-k", fileName, "-P"});
+ reader = new BufferedReader(new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8));
+ stderr = new BufferedReader(new InputStreamReader(p.getErrorStream(), StandardCharsets.UTF_8));
+ out = new OutputStreamWriter(p.getOutputStream());
+
+ String data = readData(reader, stderr);
+ if (!data.startsWith("New")) {
+ p.destroy();
+ throw new KerberosOperationException("Unexpected response from ipa-getkeytab while trying to password for "
+ + principal + " got: " + data);
+ }
+ LOG.debug("Sending password");
+ out.write(password);
+ out.write('\n');
+ out.flush();
+
+ data = readData(reader, stderr);
+ if (!data.contains("Verify")) {
+ p.destroy();
+ throw new KerberosOperationException("Unexpected response from ipa-getkeytab while trying to password for "
+ + principal + " got: " + data);
+ }
+ LOG.debug("Sending new password");
+ out.write(password);
+ out.write('\n');
+ out.flush();
+
+ p.waitFor();
+ } catch (IOException e) {
+ LOG.error("Cannot read stream: " + e);
+ throw new KerberosOperationException(e.getMessage());
+ } catch (InterruptedException e) {
+ LOG.error("Process interrupted: " + e);
+ throw new KerberosOperationException(e.getMessage());
+ } finally {
+ try {
+ if (out != null)
+ out.close();
+ } catch (IOException e) {
+ LOG.warn("Cannot close out stream: " + e);
+ }
+ try {
+ if (reader != null)
+ reader.close();
+ } catch (IOException e) {
+ LOG.warn("Cannot close stdin stream: " + e);
+ }
+ try {
+ if (stderr != null)
+ stderr.close();
+ } catch (IOException e) {
+ LOG.warn("Cannot close stderr stream: " + e);
+ }
+ }
+
+ File keytabFile = new File(fileName);
+ Keytab keytab = readKeytabFile(keytabFile);
+ keytabFile.delete();
+
+ return keytab;
+ }*/
+
+ /**
+ * Creates a key tab by using the ipa commandline utilities. It ignores key number and password
+ * as this will be handled by IPA
+ *
+ * @param principal a String containing the principal to test
+ * @param password (IGNORED) a String containing the password to use when creating the principal
+ * @param keyNumber (IGNORED) a Integer indicating the key number for the keytab entries
+ * @return
+ * @throws KerberosOperationException
+ */
+ @Override
+ protected Keytab createKeytab(String principal, String password, Integer keyNumber)
+ throws KerberosOperationException {
+
+ if ((principal == null) || principal.isEmpty()) {
+ throw new KerberosOperationException("Failed to create keytab file, missing principal");
+ }
+
+ // use cache if available
+ if (cachedKeytabs.containsKey(principal)) {
+ return cachedKeytabs.get(principal);
+ }
+
+ UUID uuid = UUID.randomUUID();
+ String fileName = System.getProperty("java.io.tmpdir") +
+ File.pathSeparator +
+ "ambari." + uuid.toString();
+
+ // TODO: add ciphers
+ List<String> command = new ArrayList<>();
+ command.add(executableIpaGetKeytab);
+ command.add("-s");
+ command.add(getAdminServerHost());
+ command.add("-p");
+ command.add(principal);
+ command.add("-k");
+ command.add(fileName);
+
+ // TODO: is it really required to set the password?
+ ShellCommandUtil.Result result = executeCommand(command.toArray(new String[command.size()]));
+ if (!result.isSuccessful()) {
+ String message = String.format("Failed to get key number for %s:\n\tExitCode: %s\n\tSTDOUT: %s\n\tSTDERR: %s",
+ principal, result.getExitCode(), result.getStdout(), result.getStderr());
+ LOG.warn(message);
+ throw new KerberosOperationException(message);
+ }
+
+ File keytabFile = new File(fileName);
+ Keytab keytab = readKeytabFile(keytabFile);
+ keytabFile.delete();
+
+ cachedKeytabs.put(principal, keytab);
+ return keytab;
+ }
+
+
+ /**
+ * Credentials context executes commands wrapped with kerberos credentials
+ */
+ class CredentialsContext {
+ private PrincipalKeyCredential credentials;
+ Map<String, String> env = new HashMap();
+ private String fileName;
+ private List<Process> processes = new ArrayList<>();
+
+ public CredentialsContext(PrincipalKeyCredential credentials) throws KerberosOperationException {
+ this.credentials = credentials;
+
+ UUID uuid = UUID.randomUUID();
+ fileName = System.getProperty("java.io.tmpdir") +
+ File.pathSeparator +
+ "krb5cc_" + uuid.toString();
+ env.put("KRB5CCNAME", String.format("FILE:%s", fileName));
+
+ init(credentials, fileName);
+ }
+
+ protected ShellCommandUtil.Result executeCommand(String[] command)
+ throws KerberosOperationException {
+
+ if ((command == null) || (command.length == 0)) {
+ return null;
+ } else {
+ try {
+ return ShellCommandUtil.runCommand(command, env);
+ } catch (IOException e) {
+ String message = String.format("Failed to execute the command: %s", e.getLocalizedMessage());
+ LOG.error(message, e);
+ throw new KerberosOperationException(message, e);
+ } catch (InterruptedException e) {
+ String message = String.format("Failed to wait for the command to complete: %s", e.getLocalizedMessage());
+ LOG.error(message, e);
+ throw new KerberosOperationException(message, e);
+ }
+ }
+ }
+
+ /**
+ * Does a kinit to obtain a ticket for the specified principal and stores it in the specified cache
+ *
+ * @param credentials Credentials to be used to obtain the ticket
+ * @param fileName Filename where to store the credentials
+ * @throws KerberosOperationException In case the ticket cannot be obtained
+ */
+ private void init(PrincipalKeyCredential credentials, String fileName) throws KerberosOperationException {
+ Process process;
+ BufferedReader reader = null;
+ OutputStreamWriter osw = null;
+
+ LOG.debug("Entering doKinit");
+ try {
+ String credentialsCache = String.format("FILE:%s", fileName);
+
+ LOG.debug("start subprocess " + executableKinit + " " + credentials.getPrincipal());
+ process = Runtime.getRuntime().exec(new String[]{executableKinit, "-c", credentialsCache, credentials.getPrincipal()});
+ reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8));
+ osw = new OutputStreamWriter(process.getOutputStream());
+
+ char[] data = new char[1024];
+ StringBuilder sb = new StringBuilder();
+
+ int count = 0;
+ while (!reader.ready()) {
+ Thread.sleep(1000L);
+ if (count >= 5) {
+ process.destroy();
+ throw new KerberosOperationException("No answer from kinit");
+ }
+ count++;
+ }
+
+ while (reader.ready()) {
+ reader.read(data);
+ sb.append(data);
+ }
+
+ String line = sb.toString();
+ LOG.debug("Reading a line: " + line);
+ if (!line.startsWith("Password")) {
+ throw new KerberosOperationException("Unexpected response from kinit while trying to get ticket for "
+ + credentials.getPrincipal() + " got: " + line);
+ }
+ osw.write(credentials.getKey());
+ osw.write('\n');
+ osw.close();
+
+ process.waitFor();
+
+ LOG.debug("done subprocess");
+ } catch (IOException e) {
+ String message = String.format("Failed to execute the command: %s", e.getLocalizedMessage());
+ LOG.error(message, e);
+ throw new KerberosOperationException(message, e);
+ } catch (InterruptedException e) {
+ String message = String.format("Failed to execute the command: %s", e.getLocalizedMessage());
+ LOG.error(message, e);
+ throw new KerberosOperationException(message, e);
+ } finally {
+ if (osw != null) {
+ try {
+ osw.close();
+ } catch (IOException e) {
+ }
+ }
+
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ if (process.exitValue() != 0) {
+ throw new KerberosOperationException("kinit failed for " + credentials.getPrincipal() + ". Wrong password?");
+ }
+
+ }
+
+ public Process exec(String[] args) throws IOException {
+ Process process = Runtime.getRuntime().exec(args);
+ processes.add(process);
+
+ return process;
+ }
+
+ public void delete() {
+ File ccache = new File(fileName);
+ ccache.delete();
+ for (Process p : processes) {
+ p.destroy();
+ }
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KDCType.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KDCType.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KDCType.java
index 5b1372a..f56ccdc 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KDCType.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KDCType.java
@@ -37,7 +37,12 @@ public enum KDCType {
/**
* Indicates a Microsoft Active Directory
*/
- ACTIVE_DIRECTORY;
+ ACTIVE_DIRECTORY,
+
+ /**
+ * Indicates an IPA KDC
+ */
+ IPA;
/**
* Translates a String to a KDCType.
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandler.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandler.java
index 4cd050e..139fd7a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandler.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandler.java
@@ -66,6 +66,26 @@ public abstract class KerberosOperationHandler {
public final static String KERBEROS_ENV_PRINCIPAL_CONTAINER_DN = "container_dn";
/**
+ * Kerberos-env configuration property name: group
+ */
+ public final static String KERBEROS_ENV_USER_PRINCIPAL_GROUP = "group";
+
+ /**
+ * Kerberos-env configuration property name: password_chat_timeout
+ */
+ public final static String KERBEROS_ENV_PASSWORD_CHAT_TIMEOUT = "password_chat_timeout";
+
+ /**
+ * Default timeout for password chat
+ */
+ public final static int DEFAULT_PASSWORD_CHAT_TIMEOUT = 5;
+
+ /**
+ * Kerberos-env configuration property name: set_password_expiry
+ */
+ public final static String KERBEROS_ENV_SET_PASSWORD_EXPIRY = "set_password_expiry";
+
+ /**
* Kerberos-env configuration property name: ad_create_attributes_template
*/
public final static String KERBEROS_ENV_AD_CREATE_ATTRIBUTES_TEMPLATE = "ad_create_attributes_template";
@@ -695,20 +715,21 @@ public abstract class KerberosOperationHandler {
/**
* Executes a shell command.
* <p/>
- * See {@link org.apache.ambari.server.utils.ShellCommandUtil#runCommand(String[])}
+ * See {@link org.apache.ambari.server.utils.ShellCommandUtil#runCommand(String[], Map<String,String>)}
*
* @param command an array of String value representing the command and its arguments
+ * @param envp a map of string, string of environment variables
* @return a ShellCommandUtil.Result declaring the result of the operation
* @throws KerberosOperationException
*/
- protected ShellCommandUtil.Result executeCommand(String[] command)
+ protected ShellCommandUtil.Result executeCommand(String[] command, Map<String, String> envp)
throws KerberosOperationException {
if ((command == null) || (command.length == 0)) {
return null;
} else {
try {
- return ShellCommandUtil.runCommand(command);
+ return ShellCommandUtil.runCommand(command, envp);
} catch (IOException e) {
String message = String.format("Failed to execute the command: %s", e.getLocalizedMessage());
LOG.error(message, e);
@@ -722,6 +743,20 @@ public abstract class KerberosOperationHandler {
}
/**
+ * Executes a shell command.
+ * <p/>
+ * See {@link org.apache.ambari.server.utils.ShellCommandUtil#runCommand(String[])}
+ *
+ * @param command an array of String value representing the command and its arguments
+ * @return a ShellCommandUtil.Result declaring the result of the operation
+ * @throws KerberosOperationException
+ */
+ protected ShellCommandUtil.Result executeCommand(String[] command)
+ throws KerberosOperationException {
+ return executeCommand(command, null);
+ }
+
+ /**
* Given a principal, attempt to create a new DeconstructedPrincipal
*
* @param principal a String containing the principal to deconstruct
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandlerFactory.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandlerFactory.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandlerFactory.java
index bfd45b7..d8ca38f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandlerFactory.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandlerFactory.java
@@ -51,6 +51,8 @@ public class KerberosOperationHandlerFactory {
return injector.getInstance(MITKerberosOperationHandler.class);
case ACTIVE_DIRECTORY:
return injector.getInstance(ADKerberosOperationHandler.class);
+ case IPA:
+ return injector.getInstance(IPAKerberosOperationHandler.class);
default:
throw new IllegalArgumentException(String.format("Unexpected kdcType value: %s", kdcType.name()));
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/VariableReplacementHelper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/VariableReplacementHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/VariableReplacementHelper.java
index 42eea14..66be3bf 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/VariableReplacementHelper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/VariableReplacementHelper.java
@@ -48,6 +48,7 @@ public class VariableReplacementHelper {
private static final Map<String, Function> FUNCTIONS = new HashMap<String, Function>() {
{
put("each", new EachFunction());
+ put("toLower", new ToLowerFunction());
}
};
@@ -225,4 +226,18 @@ public class VariableReplacementHelper {
return "";
}
}
+
+ /**
+ * ToLowerFunction is a Function implementation that converts a String to lowercase
+ */
+ private static class ToLowerFunction implements Function {
+ @Override
+ public String perform(String[] args, String data) {
+ if (data != null) {
+ return data.toLowerCase();
+ }
+
+ return "";
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/main/java/org/apache/ambari/server/utils/ShellCommandUtil.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/utils/ShellCommandUtil.java b/ambari-server/src/main/java/org/apache/ambari/server/utils/ShellCommandUtil.java
index 947b336..b5d2b70 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/utils/ShellCommandUtil.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/utils/ShellCommandUtil.java
@@ -24,6 +24,7 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.util.Map;
/**
* Logs OpenSsl command exit code with description
@@ -164,9 +165,23 @@ public class ShellCommandUtil {
}
}
- public static Result runCommand(String [] args) throws IOException,
+ /**
+ * Runs a command with a given set of environment variables
+ * @param args a String[] of the command and its arguments
+ * @param vars a Map of String,String setting an environment variable to run the command with
+ * @return Result
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ public static Result runCommand(String [] args, Map<String, String> vars) throws IOException,
InterruptedException {
ProcessBuilder builder = new ProcessBuilder(args);
+
+ if (vars != null) {
+ Map<String, String> env = builder.environment();
+ env.putAll(vars);
+ }
+
Process process;
if (WINDOWS) {
synchronized (WindowsProcessLaunchLock) {
@@ -189,6 +204,18 @@ public class ShellCommandUtil {
return new Result(exitCode, stdout, stderr);
}
+ /**
+ * Run a command
+ * @param args A String[] of the command and its arguments
+ * @return Result
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ public static Result runCommand(String [] args) throws IOException,
+ InterruptedException {
+ return runCommand(args, null);
+ }
+
private static String streamToString(InputStream is) throws IOException {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader reader = new BufferedReader(isr);
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/main/resources/common-services/ACCUMULO/1.6.1.2.2.0/kerberos.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/ACCUMULO/1.6.1.2.2.0/kerberos.json b/ambari-server/src/main/resources/common-services/ACCUMULO/1.6.1.2.2.0/kerberos.json
index e76f809..caef123 100644
--- a/ambari-server/src/main/resources/common-services/ACCUMULO/1.6.1.2.2.0/kerberos.json
+++ b/ambari-server/src/main/resources/common-services/ACCUMULO/1.6.1.2.2.0/kerberos.json
@@ -6,7 +6,7 @@
{
"name": "accumulo",
"principal": {
- "value": "${accumulo-env/accumulo_user}-${cluster_name}@${realm}",
+ "value": "${accumulo-env/accumulo_user}-${cluster_name|toLower()}@${realm}",
"type" : "user",
"configuration": "accumulo-env/accumulo_principal_name",
"local_username": "${accumulo-env/accumulo_user}"
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/main/resources/common-services/HBASE/0.96.0.2.0/kerberos.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/HBASE/0.96.0.2.0/kerberos.json b/ambari-server/src/main/resources/common-services/HBASE/0.96.0.2.0/kerberos.json
index dc5ef2e..c9536f8 100644
--- a/ambari-server/src/main/resources/common-services/HBASE/0.96.0.2.0/kerberos.json
+++ b/ambari-server/src/main/resources/common-services/HBASE/0.96.0.2.0/kerberos.json
@@ -9,7 +9,7 @@
{
"name": "hbase",
"principal": {
- "value": "${hbase-env/hbase_user}-${cluster_name}@${realm}",
+ "value": "${hbase-env/hbase_user}-${cluster_name|toLower()}@${realm}",
"type" : "user",
"configuration": "hbase-env/hbase_principal_name",
"local_username": "${hbase-env/hbase_user}"
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/kerberos.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/kerberos.json b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/kerberos.json
index c9c738e..3d6e25c 100644
--- a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/kerberos.json
+++ b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/kerberos.json
@@ -43,7 +43,7 @@
{
"name": "hdfs",
"principal": {
- "value": "${hadoop-env/hdfs_user}-${cluster_name}@${realm}",
+ "value": "${hadoop-env/hdfs_user}-${cluster_name|toLower()}@${realm}",
"type" : "user" ,
"configuration": "hadoop-env/hdfs_principal_name",
"local_username" : "${hadoop-env/hdfs_user}"
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/main/resources/common-services/KERBEROS/1.10.3-10/configuration/kerberos-env.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/KERBEROS/1.10.3-10/configuration/kerberos-env.xml b/ambari-server/src/main/resources/common-services/KERBEROS/1.10.3-10/configuration/kerberos-env.xml
index a03dea6..b56bcbf 100644
--- a/ambari-server/src/main/resources/common-services/KERBEROS/1.10.3-10/configuration/kerberos-env.xml
+++ b/ambari-server/src/main/resources/common-services/KERBEROS/1.10.3-10/configuration/kerberos-env.xml
@@ -24,7 +24,7 @@
<property require-input="true">
<name>kdc_type</name>
<description>
- The type of KDC being used. Either mit-kdc or active-directory
+ The type of KDC being used. Either mit-kdc, ipa, or active-directory
</description>
<value>mit-kdc</value>
<display-name>KDC type</display-name>
@@ -161,7 +161,7 @@
<name>executable_search_paths</name>
<display-name>Executable Search Paths</display-name>
<description>
- A comma-delimited list of search paths to use to find Kerberos utilities like kadmin and kinit.
+ A comma-delimited list of search paths to use to find Kerberos utilities like kadmin, kinit and ipa.
</description>
<value>/usr/bin, /usr/kerberos/bin, /usr/sbin, /usr/lib/mit/bin, /usr/lib/mit/sbin</value>
<value-attributes>
@@ -253,7 +253,7 @@
<description>
The principal name to use when executing the Kerberos service check
</description>
- <value>${cluster_name}-${short_date}</value>
+ <value>${cluster_name|toLower()}-${short_date}</value>
<value-attributes>
<overridable>false</overridable>
</value-attributes>
@@ -315,4 +315,50 @@
<overridable>false</overridable>
</value-attributes>
</property>
+
+ <property>
+ <name>group</name>
+ <display-name>IPA Group</display-name>
+ <description>
+ The group in IPA user principals should be member of
+ </description>
+ <value>ambari-managed-principals</value>
+ <value-attributes>
+ <empty-value-valid>true</empty-value-valid>
+ <visible>false</visible>
+ </value-attributes>
+ </property>
+
+ <property>
+ <name>set_password_expiry</name>
+ <display-name>Set IPA principal password expiry</display-name>
+ <description>
+ Indicates whether Ambari should set the password expiry for the principals it creates. By default
+ IPA does not allow this. It requires write permission of the admin principal to the krbPasswordExpiry
+ attribute. If set IPA principal password expiry is not true it is assumed that a suitable password
+ policy is in place for the IPA Group principals are added to.
+ </description>
+ <value>false</value>
+ <value-attributes>
+ <type>boolean</type>
+ <overridable>false</overridable>
+ <visible>false</visible>
+ </value-attributes>
+ </property>
+
+ <property>
+ <name>password_chat_timeout</name>
+ <display-name>Set IPA kinit password chat timeout</display-name>
+ <description>
+ Indicates the timeout in seconds that Ambari should wait for a response during a password chat. This is
+ because it can take some time due to lookups before a response is there.
+ </description>
+ <value>5</value>
+ <value-attributes>
+ <visible>false</visible>
+ <type>int</type>
+ <overridable>false</overridable>
+ </value-attributes>
+ </property>
+
</configuration>
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/main/resources/common-services/SPARK/1.2.0.2.2/kerberos.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/SPARK/1.2.0.2.2/kerberos.json b/ambari-server/src/main/resources/common-services/SPARK/1.2.0.2.2/kerberos.json
index 5354f69..fa6af33 100644
--- a/ambari-server/src/main/resources/common-services/SPARK/1.2.0.2.2/kerberos.json
+++ b/ambari-server/src/main/resources/common-services/SPARK/1.2.0.2.2/kerberos.json
@@ -9,7 +9,7 @@
{
"name": "sparkuser",
"principal": {
- "value": "${spark-env/spark_user}-${cluster_name}@${realm}",
+ "value": "${spark-env/spark_user}-${cluster_name|toLower()}@${realm}",
"type" : "user",
"configuration": "spark-defaults/spark.history.kerberos.principal",
"local_username" : "${spark-env/spark_user}"
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/main/resources/common-services/SPARK/1.4.1.2.3/kerberos.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/SPARK/1.4.1.2.3/kerberos.json b/ambari-server/src/main/resources/common-services/SPARK/1.4.1.2.3/kerberos.json
index 90d9090..21ba259 100644
--- a/ambari-server/src/main/resources/common-services/SPARK/1.4.1.2.3/kerberos.json
+++ b/ambari-server/src/main/resources/common-services/SPARK/1.4.1.2.3/kerberos.json
@@ -9,7 +9,7 @@
{
"name": "sparkuser",
"principal": {
- "value": "${spark-env/spark_user}-${cluster_name}@${realm}",
+ "value": "${spark-env/spark_user}-${cluster_name|toLower()}@${realm}",
"type" : "user",
"configuration": "spark-defaults/spark.history.kerberos.principal",
"local_username" : "${spark-env/spark_user}"
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/main/resources/common-services/STORM/0.9.1.2.1/kerberos.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/STORM/0.9.1.2.1/kerberos.json b/ambari-server/src/main/resources/common-services/STORM/0.9.1.2.1/kerberos.json
index 5c2133c..fcfe524 100644
--- a/ambari-server/src/main/resources/common-services/STORM/0.9.1.2.1/kerberos.json
+++ b/ambari-server/src/main/resources/common-services/STORM/0.9.1.2.1/kerberos.json
@@ -12,7 +12,7 @@
{
"name": "storm_components",
"principal": {
- "value": "${storm-env/storm_user}-${cluster_name}@${realm}",
+ "value": "${storm-env/storm_user}-${cluster_name|toLower()}@${realm}",
"type": "user",
"configuration": "storm-env/storm_principal_name"
},
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/main/resources/stacks/HDP/2.0.6/kerberos.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/stacks/HDP/2.0.6/kerberos.json b/ambari-server/src/main/resources/stacks/HDP/2.0.6/kerberos.json
index 52e7ee0..7b1888b 100644
--- a/ambari-server/src/main/resources/stacks/HDP/2.0.6/kerberos.json
+++ b/ambari-server/src/main/resources/stacks/HDP/2.0.6/kerberos.json
@@ -26,7 +26,7 @@
{
"name": "smokeuser",
"principal": {
- "value": "${cluster-env/smokeuser}-${cluster_name}@${realm}",
+ "value": "${cluster-env/smokeuser}-${cluster_name|toLower()}@${realm}",
"type" : "user",
"configuration": "cluster-env/smokeuser_principal_name",
"local_username" : "${cluster-env/smokeuser}"
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/main/resources/stacks/HDP/2.3.ECS/services/ECS/kerberos.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/stacks/HDP/2.3.ECS/services/ECS/kerberos.json b/ambari-server/src/main/resources/stacks/HDP/2.3.ECS/services/ECS/kerberos.json
index 213c964..9668354 100644
--- a/ambari-server/src/main/resources/stacks/HDP/2.3.ECS/services/ECS/kerberos.json
+++ b/ambari-server/src/main/resources/stacks/HDP/2.3.ECS/services/ECS/kerberos.json
@@ -9,7 +9,7 @@
{
"name": "hdfs",
"principal": {
- "value": "${hadoop-env/hdfs_user}-${cluster_name}@${realm}",
+ "value": "${hadoop-env/hdfs_user}-${cluster_name|toLower()}@${realm}",
"type" : "user" ,
"configuration": "hadoop-env/hdfs_principal_name",
"local_username" : "${hadoop-env/hdfs_user}"
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/main/resources/stacks/HDP/2.3.ECS/services/HBASE/kerberos.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/stacks/HDP/2.3.ECS/services/HBASE/kerberos.json b/ambari-server/src/main/resources/stacks/HDP/2.3.ECS/services/HBASE/kerberos.json
index 1db82a3..20b10f7 100644
--- a/ambari-server/src/main/resources/stacks/HDP/2.3.ECS/services/HBASE/kerberos.json
+++ b/ambari-server/src/main/resources/stacks/HDP/2.3.ECS/services/HBASE/kerberos.json
@@ -12,7 +12,7 @@
{
"name": "hbase",
"principal": {
- "value": "${hbase-env/hbase_user}-${cluster_name}@${realm}",
+ "value": "${hbase-env/hbase_user}-${cluster_name|toLower()}@${realm}",
"type" : "user",
"configuration": "hbase-env/hbase_principal_name",
"local_username": "${hbase-env/hbase_user}"
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/main/resources/stacks/HDP/2.3.GlusterFS/services/ACCUMULO/kerberos.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/stacks/HDP/2.3.GlusterFS/services/ACCUMULO/kerberos.json b/ambari-server/src/main/resources/stacks/HDP/2.3.GlusterFS/services/ACCUMULO/kerberos.json
index d621e05..678a2b5 100644
--- a/ambari-server/src/main/resources/stacks/HDP/2.3.GlusterFS/services/ACCUMULO/kerberos.json
+++ b/ambari-server/src/main/resources/stacks/HDP/2.3.GlusterFS/services/ACCUMULO/kerberos.json
@@ -6,7 +6,7 @@
{
"name": "accumulo",
"principal": {
- "value": "${accumulo-env/accumulo_user}-${cluster_name}@${realm}",
+ "value": "${accumulo-env/accumulo_user}-${cluster_name|toLower()}@${realm}",
"type" : "user",
"configuration": "accumulo-env/accumulo_principal_name",
"local_username": "${accumulo-env/accumulo_user}"
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/main/resources/stacks/HDP/2.3/services/ACCUMULO/kerberos.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/stacks/HDP/2.3/services/ACCUMULO/kerberos.json b/ambari-server/src/main/resources/stacks/HDP/2.3/services/ACCUMULO/kerberos.json
index 61fe31e..0fec0ab 100644
--- a/ambari-server/src/main/resources/stacks/HDP/2.3/services/ACCUMULO/kerberos.json
+++ b/ambari-server/src/main/resources/stacks/HDP/2.3/services/ACCUMULO/kerberos.json
@@ -6,7 +6,7 @@
{
"name": "accumulo",
"principal": {
- "value": "${accumulo-env/accumulo_user}-${cluster_name}@${realm}",
+ "value": "${accumulo-env/accumulo_user}-${cluster_name|toLower()}@${realm}",
"type" : "user",
"configuration": "accumulo-env/accumulo_principal_name",
"local_username": "${accumulo-env/accumulo_user}"
@@ -48,7 +48,7 @@
{
"name": "accumulo_tracer",
"principal": {
- "value": "tracer-${cluster_name}@${realm}",
+ "value": "tracer-${cluster_name|toLower()}@${realm}",
"type" : "user",
"configuration": "accumulo-site/trace.user",
"local_username": "${accumulo-env/accumulo_user}"
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/IPAKerberosOperationHandlerTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/IPAKerberosOperationHandlerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/IPAKerberosOperationHandlerTest.java
new file mode 100644
index 0000000..f877e85
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/IPAKerberosOperationHandlerTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+package org.apache.ambari.server.serveraction.kerberos;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import junit.framework.Assert;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.security.credential.PrincipalKeyCredential;
+import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.stack.OsFamily;
+import org.easymock.EasyMock;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+
+public class IPAKerberosOperationHandlerTest extends KerberosOperationHandlerTest {
+ private static final String DEFAULT_ADMIN_PRINCIPAL = "admin";
+ private static final String DEFAULT_ADMIN_PASSWORD = "Hadoop12345";
+
+ private static final String DEFAULT_REALM = "IPA01.LOCAL";
+
+ private static Injector injector;
+
+ private static boolean hasIpa = false;
+
+ private static final Map<String, String> KERBEROS_ENV_MAP = new HashMap<String, String>() {
+ {
+ put(IPAKerberosOperationHandler.KERBEROS_ENV_ENCRYPTION_TYPES, null);
+ put(IPAKerberosOperationHandler.KERBEROS_ENV_KDC_HOST, "localhost");
+ put(IPAKerberosOperationHandler.KERBEROS_ENV_ADMIN_SERVER_HOST, "localhost");
+ put(IPAKerberosOperationHandler.KERBEROS_ENV_USER_PRINCIPAL_GROUP, "");
+ }
+ };
+
+ @BeforeClass
+ public static void beforeClass() throws AmbariException {
+ injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ Configuration configuration = EasyMock.createNiceMock(Configuration.class);
+ expect(configuration.getServerOsFamily()).andReturn("redhat6").anyTimes();
+ replay(configuration);
+
+ bind(Clusters.class).toInstance(EasyMock.createNiceMock(Clusters.class));
+ bind(Configuration.class).toInstance(configuration);
+ bind(OsFamily.class).toInstance(EasyMock.createNiceMock(OsFamily.class));
+ }
+ });
+ if (System.getenv("HAS_IPA") != null) {
+ hasIpa = true;
+ }
+ }
+
+ @Test
+ public void testSetPrincipalPasswordExceptions() throws Exception {
+ if (!hasIpa) {
+ return;
+ }
+
+ IPAKerberosOperationHandler handler = injector.getInstance(IPAKerberosOperationHandler.class);
+ handler.open(new PrincipalKeyCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD), DEFAULT_REALM, KERBEROS_ENV_MAP);
+ try {
+ handler.setPrincipalPassword(DEFAULT_ADMIN_PRINCIPAL, null);
+ Assert.fail("KerberosOperationException not thrown for null password");
+ } catch (Throwable t) {
+ Assert.assertEquals(KerberosOperationException.class, t.getClass());
+ }
+
+ try {
+ handler.setPrincipalPassword(DEFAULT_ADMIN_PRINCIPAL, "");
+ Assert.fail("KerberosOperationException not thrown for empty password");
+ handler.createPrincipal("", "1234", false);
+ Assert.fail("AmbariException not thrown for empty principal");
+ } catch (Throwable t) {
+ Assert.assertEquals(KerberosOperationException.class, t.getClass());
+ }
+
+ try {
+ handler.setPrincipalPassword(null, DEFAULT_ADMIN_PASSWORD);
+ Assert.fail("KerberosOperationException not thrown for null principal");
+ } catch (Throwable t) {
+ Assert.assertEquals(KerberosOperationException.class, t.getClass());
+ }
+
+ try {
+ handler.setPrincipalPassword("", DEFAULT_ADMIN_PASSWORD);
+ Assert.fail("KerberosOperationException not thrown for empty principal");
+ } catch (Throwable t) {
+ Assert.assertEquals(KerberosOperationException.class, t.getClass());
+ }
+ }
+
+ @Test
+ public void testCreateServicePrincipal_Exceptions() throws Exception {
+ if (!hasIpa) {
+ return;
+ }
+
+ IPAKerberosOperationHandler handler = new IPAKerberosOperationHandler();
+ handler.open(new PrincipalKeyCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD), DEFAULT_REALM, KERBEROS_ENV_MAP);
+
+ try {
+ handler.createPrincipal(DEFAULT_ADMIN_PRINCIPAL, null, false);
+ Assert.fail("KerberosOperationException not thrown for null password");
+ } catch (Throwable t) {
+ Assert.fail("KerberosOperationException thrown on null password with IPA");
+ }
+
+ try {
+ handler.createPrincipal(DEFAULT_ADMIN_PRINCIPAL, "", false);
+ } catch (Throwable t) {
+ Assert.fail("KerberosOperationException thrown for empty password");
+ }
+
+ try {
+ handler.createPrincipal(null, DEFAULT_ADMIN_PASSWORD, false);
+ Assert.fail("KerberosOperationException not thrown for null principal");
+ } catch (Throwable t) {
+ Assert.assertEquals(KerberosOperationException.class, t.getClass());
+ }
+
+ try {
+ handler.createPrincipal("", DEFAULT_ADMIN_PASSWORD, false);
+ Assert.fail("KerberosOperationException not thrown for empty principal");
+ } catch (Throwable t) {
+ Assert.assertEquals(KerberosOperationException.class, t.getClass());
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/test/java/org/apache/ambari/server/state/kerberos/VariableReplacementHelperTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/kerberos/VariableReplacementHelperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/kerberos/VariableReplacementHelperTest.java
index cbfa4a3..ee2a671 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/kerberos/VariableReplacementHelperTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/kerberos/VariableReplacementHelperTest.java
@@ -169,6 +169,8 @@ public class VariableReplacementHelperTest {
Assert.assertEquals("hive.metastore.local=false,hive.metastore.uris=thrift://host1.unit.test:9083\\,thrift://host2.unit.test:9083\\,thrift://host3.unit.test:9083,hive.metastore.sasl.enabled=true,hive.metastore.execute.setugi=true,hive.metastore.warehouse.dir=/apps/hive/warehouse,hive.exec.mode.local.auto=false,hive.metastore.kerberos.principal=hive/_HOST@UNIT.TEST",
helper.replaceVariables("hive.metastore.local=false,hive.metastore.uris=${clusterHostInfo/hive_metastore_host | each(thrift://%s:9083, \\\\,, \\s*\\,\\s*)},hive.metastore.sasl.enabled=true,hive.metastore.execute.setugi=true,hive.metastore.warehouse.dir=/apps/hive/warehouse,hive.exec.mode.local.auto=false,hive.metastore.kerberos.principal=hive/_HOST@${realm}", configurations));
+
+ Assert.assertEquals("test=unit.test", helper.replaceVariables("test=${realm|toLower()}", configurations));
}
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/test/resources/kerberos/test_kerberos_descriptor_2_1_3.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/resources/kerberos/test_kerberos_descriptor_2_1_3.json b/ambari-server/src/test/resources/kerberos/test_kerberos_descriptor_2_1_3.json
index 09d1d0c..f3dbd38 100644
--- a/ambari-server/src/test/resources/kerberos/test_kerberos_descriptor_2_1_3.json
+++ b/ambari-server/src/test/resources/kerberos/test_kerberos_descriptor_2_1_3.json
@@ -21,7 +21,7 @@
"configuration": "cluster-env/smokeuser_principal_name",
"type": "user",
"local_username": "${cluster-env/smokeuser}",
- "value": "${cluster-env/smokeuser}-${cluster_name}@${realm}"
+ "value": "${cluster-env/smokeuser}-${cluster_name|toLower()}@${realm}"
},
"name": "smokeuser",
"keytab": {
@@ -318,7 +318,7 @@
"configuration": "hadoop-env/hdfs_principal_name",
"type": "user",
"local_username": "${hadoop-env/hdfs_user}",
- "value": "${hadoop-env/hdfs_user}-${cluster_name}@${realm}"
+ "value": "${hadoop-env/hdfs_user}-${cluster_name|toLower()}@${realm}"
},
"name": "hdfs",
"keytab": {
@@ -370,7 +370,7 @@
"configuration": "spark-defaults/spark.history.kerberos.principal",
"type": "user",
"local_username": "${spark-env/spark_user}",
- "value": "${spark-env/spark_user}-${cluster_name}@${realm}"
+ "value": "${spark-env/spark_user}-${cluster_name|toLower()}@${realm}"
},
"name": "sparkuser",
"keytab": {
@@ -411,7 +411,7 @@
"configuration": "accumulo-env/accumulo_principal_name",
"type": "user",
"local_username": "${accumulo-env/accumulo_user}",
- "value": "${accumulo-env/accumulo_user}-${cluster_name}@${realm}"
+ "value": "${accumulo-env/accumulo_user}-${cluster_name|toLower()}@${realm}"
},
"name": "accumulo",
"keytab": {
@@ -451,7 +451,7 @@
"configuration": "accumulo-site/trace.user",
"type": "user",
"local_username": "${accumulo-env/accumulo_user}",
- "value": "tracer-${cluster_name}@${realm}"
+ "value": "tracer-${cluster_name|toLower()}@${realm}"
},
"name": "accumulo_tracer",
"keytab": {
@@ -591,7 +591,7 @@
"configuration": "hbase-env/hbase_principal_name",
"type": "user",
"local_username": "${hbase-env/hbase_user}",
- "value": "${hbase-env/hbase_user}-${cluster_name}@${realm}"
+ "value": "${hbase-env/hbase_user}-${cluster_name|toLower()}@${realm}"
},
"name": "hbase",
"keytab": {
@@ -940,7 +940,7 @@
"principal": {
"configuration": "storm-env/storm_principal_name",
"type": "user",
- "value": "${storm-env/storm_user}-${cluster_name}@${realm}"
+ "value": "${storm-env/storm_user}-${cluster_name|toLower()}@${realm}"
},
"name": "storm_components",
"keytab": {
http://git-wip-us.apache.org/repos/asf/ambari/blob/476d87b7/ambari-server/src/test/resources/kerberos/test_kerberos_descriptor_no_hdfs.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/resources/kerberos/test_kerberos_descriptor_no_hdfs.json b/ambari-server/src/test/resources/kerberos/test_kerberos_descriptor_no_hdfs.json
index 8f1d075..dcf5bbc 100644
--- a/ambari-server/src/test/resources/kerberos/test_kerberos_descriptor_no_hdfs.json
+++ b/ambari-server/src/test/resources/kerberos/test_kerberos_descriptor_no_hdfs.json
@@ -25,7 +25,7 @@
{
"name": "hdfs",
"principal": {
- "value": "${hadoop-env/hdfs_user}-${cluster_name}@${realm}",
+ "value": "${hadoop-env/hdfs_user}-${cluster_name|toLower()}@${realm}",
"type": "user",
"configuration": "hadoop-env/hdfs_principal_name",
"local_username": "${hadoop-env/hdfs_user}"
@@ -46,7 +46,7 @@
{
"name": "smokeuser",
"principal": {
- "value": "${cluster-env/smokeuser}-${cluster_name}@${realm}",
+ "value": "${cluster-env/smokeuser}-${cluster_name|toLower()}@${realm}",
"type": "user",
"configuration": "cluster-env/smokeuser_principal_name",
"local_username": "${cluster-env/smokeuser}"