You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by pr...@apache.org on 2026/08/28 07:20:29 UTC

(ranger) branch ranger-2.10 updated: RANGER-5742: Inconsistent handling of service config fields by name vs. declared type (backport) to ranger-2.10 (#1189)

This is an automated email from the ASF dual-hosted git repository.

pradeepagrawal8184 pushed a commit to branch ranger-2.10
in repository https://gitbox.apache.org/repos/asf/ranger.git


The following commit(s) were added to refs/heads/ranger-2.10 by this push:
     new 54cb4f18a RANGER-5742: Inconsistent handling of service config fields by name vs. declared type (backport) to ranger-2.10 (#1189)
54cb4f18a is described below

commit 54cb4f18afa30f788d228dbbdc7e9a470a0812cd
Author: Vyom Mani Tiwari <vy...@gmail.com>
AuthorDate: Fri Aug 28 12:50:24 2026 +0530

    RANGER-5742: Inconsistent handling of service config fields by name vs. declared type (backport) to ranger-2.10 (#1189)
---
 .../java/org/apache/ranger/biz/ServiceDBStore.java |  37 ++-
 .../apache/ranger/db/XXServiceConfigDefDao.java    |  30 ++-
 .../ranger/service/RangerServiceService.java       |  88 ++++---
 .../main/resources/META-INF/jpa_named_queries.xml  |  11 +-
 .../org/apache/ranger/biz/TestServiceDBStore.java  | 292 +++++++++++++++++++++
 .../ranger/service/TestRangerServiceService.java   | 135 ++++++++++
 6 files changed, 552 insertions(+), 41 deletions(-)

diff --git a/security-admin/src/main/java/org/apache/ranger/biz/ServiceDBStore.java b/security-admin/src/main/java/org/apache/ranger/biz/ServiceDBStore.java
index 501d3e454..d5a0f1f77 100644
--- a/security-admin/src/main/java/org/apache/ranger/biz/ServiceDBStore.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/ServiceDBStore.java
@@ -371,6 +371,7 @@ public class ServiceDBStore extends AbstractServiceStore {
 
 	public static final String HIDDEN_PASSWORD_STR = "*****";
 	public static final String CONFIG_KEY_PASSWORD = "password";
+	public static final String CONFIG_TYPE_PASSWORD = "password";
 	public static final String ACCESS_TYPE_DECRYPT_EEK    = "decrypteek";
 	public static final String ACCESS_TYPE_GENERATE_EEK   = "generateeek";
 	public static final String ACCESS_TYPE_GET_METADATA   = "getmetadata";
@@ -1574,6 +1575,7 @@ public RangerService createService(RangerService service) throws Exception {
 		XXService xCreatedService = daoMgr.getXXService().getById(service.getId());
 
 		XXServiceConfigMapDao xConfMapDao = daoMgr.getXXServiceConfigMap();
+		Set<String> passwordConfigKeys = getPasswordConfigKeys(service.getType());
 		for (Entry<String, String> configMap : validConfigs.entrySet()) {
 			String configKey = configMap.getKey();
 			String configValue = configMap.getValue();
@@ -1593,7 +1595,7 @@ public RangerService createService(RangerService service) throws Exception {
 				}
 			}
 
-			if (StringUtils.equalsIgnoreCase(configKey, CONFIG_KEY_PASSWORD)) {
+			if (isPasswordConfigKey(passwordConfigKeys, configKey)) {
 				Joiner joiner = Joiner.on(",").skipNulls();
 				String iv = PasswordUtils.generateIvIfNeeded(CRYPT_ALGO);
 
@@ -1749,11 +1751,12 @@ public RangerService updateService(RangerService service, Map<String, Object> op
 
 		XXService xUpdService = daoMgr.getXXService().getById(service.getId());
 
-		String oldPassword = null;
+		Set<String>          passwordConfigKeys = getPasswordConfigKeys(service.getType());
+		Map<String, String>  oldPasswordsByKey  = new HashMap<>();
 
 		for(XXServiceConfigMap dbConfigMap : dbConfigMaps) {
-			if(StringUtils.equalsIgnoreCase(dbConfigMap.getConfigkey(), CONFIG_KEY_PASSWORD)) {
-				oldPassword = dbConfigMap.getConfigvalue();
+			if (isPasswordConfigKey(passwordConfigKeys, dbConfigMap.getConfigkey())) {
+				oldPasswordsByKey.put(StringUtils.lowerCase(dbConfigMap.getConfigkey()), dbConfigMap.getConfigvalue());
 			}
 			daoMgr.getXXServiceConfigMap().remove(dbConfigMap);
 		}
@@ -1778,7 +1781,9 @@ public RangerService updateService(RangerService service, Map<String, Object> op
 				}
 			}
 
-			if (StringUtils.equalsIgnoreCase(configKey, CONFIG_KEY_PASSWORD)) {
+			if (isPasswordConfigKey(passwordConfigKeys, configKey)) {
+				String oldPassword = oldPasswordsByKey.get(StringUtils.lowerCase(configKey));
+
 				if (StringUtils.equalsIgnoreCase(configValue, HIDDEN_PASSWORD_STR)) {
 					if (oldPassword != null && oldPassword.contains(",")) {
 						PasswordUtils util = PasswordUtils.build(oldPassword);
@@ -3694,6 +3699,28 @@ List<String> getServiceCheckUsers(RangerService createdService) {
 		return ret;
 	}
 
+	private Set<String> getPasswordConfigKeys(String serviceType) {
+		List<String> passwordConfigNames = serviceType != null ? daoMgr.getXXServiceConfigDef().findConfigNamesByServiceDefNameAndType(serviceType, CONFIG_TYPE_PASSWORD) : null;
+		return getPasswordConfigKeys(passwordConfigNames);
+	}
+
+	public static Set<String> getPasswordConfigKeys(List<String> passwordConfigNames) {
+		Set<String> passwordConfigKeys = new HashSet<>();
+		passwordConfigKeys.add(StringUtils.lowerCase(CONFIG_KEY_PASSWORD));
+		if (passwordConfigNames != null) {
+			for (String passwordConfigName : passwordConfigNames) {
+				if (passwordConfigName != null) {
+					passwordConfigKeys.add(StringUtils.lowerCase(passwordConfigName));
+				}
+			}
+		}
+		return passwordConfigKeys;
+	}
+
+	public static boolean isPasswordConfigKey(Set<String> passwordConfigKeys, String configKey) {
+		return configKey != null && passwordConfigKeys.contains(StringUtils.lowerCase(configKey));
+	}
+
 	private Map<String, String> validateRequiredConfigParams(RangerService service, Map<String, String> configs) {
 		if(LOG.isDebugEnabled()) {
 			LOG.debug("==> ServiceDBStore.validateRequiredConfigParams()");
diff --git a/security-admin/src/main/java/org/apache/ranger/db/XXServiceConfigDefDao.java b/security-admin/src/main/java/org/apache/ranger/db/XXServiceConfigDefDao.java
index 5d54876c7..fe3be4db5 100644
--- a/security-admin/src/main/java/org/apache/ranger/db/XXServiceConfigDefDao.java
+++ b/security-admin/src/main/java/org/apache/ranger/db/XXServiceConfigDefDao.java
@@ -60,5 +60,33 @@ public List<XXServiceConfigDef> findByServiceDefName(String serviceDef) {
 			return new ArrayList<XXServiceConfigDef>();
 		}
 	}
-	
+
+	public List<String> findConfigNamesByServiceDefIdAndType(Long serviceDefId, String configType) {
+		if (serviceDefId == null) {
+			return new ArrayList<>();
+		}
+		try {
+			return getEntityManager()
+					.createNamedQuery("XXServiceConfigDef.findConfigNamesByServiceDefIdAndType", String.class)
+					.setParameter("serviceDefId", serviceDefId)
+					.setParameter("configType", configType).getResultList();
+		} catch (NoResultException e) {
+			return new ArrayList<>();
+		}
+	}
+
+	public List<String> findConfigNamesByServiceDefNameAndType(String serviceDef, String configType) {
+		if (serviceDef == null) {
+			return new ArrayList<>();
+		}
+		try {
+			return getEntityManager()
+					.createNamedQuery("XXServiceConfigDef.findConfigNamesByServiceDefNameAndType", String.class)
+					.setParameter("serviceDef", serviceDef)
+					.setParameter("configType", configType).getResultList();
+		} catch (NoResultException e) {
+			return new ArrayList<>();
+		}
+	}
+
 }
diff --git a/security-admin/src/main/java/org/apache/ranger/service/RangerServiceService.java b/security-admin/src/main/java/org/apache/ranger/service/RangerServiceService.java
index 81064fd95..7f4796bb9 100644
--- a/security-admin/src/main/java/org/apache/ranger/service/RangerServiceService.java
+++ b/security-admin/src/main/java/org/apache/ranger/service/RangerServiceService.java
@@ -22,6 +22,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.hadoop.thirdparty.com.google.common.base.Joiner;
@@ -79,10 +80,11 @@ protected RangerService populateViewBean(XXService xService) {
 		HashMap<String, String> configs = new HashMap<String, String>();
 		List<XXServiceConfigMap> svcConfigMapList = daoMgr.getXXServiceConfigMap()
 				.findByServiceId(xService.getId());
+		Set<String> passwordConfigKeys = getPasswordConfigKeysByServiceDefId(xService.getType());
 		for(XXServiceConfigMap svcConfMap : svcConfigMapList) {
 			String configValue = svcConfMap.getConfigvalue();
-			
-			if(StringUtils.equalsIgnoreCase(svcConfMap.getConfigkey(), ServiceDBStore.CONFIG_KEY_PASSWORD)) {
+
+			if (ServiceDBStore.isPasswordConfigKey(passwordConfigKeys, svcConfMap.getConfigkey())) {
 				configValue = ServiceDBStore.HIDDEN_PASSWORD_STR;
 			}
 			configs.put(svcConfMap.getConfigkey(), configValue);
@@ -108,38 +110,48 @@ public List<RangerService> getAllServices() {
 
 	public Map<String, String> getConfigsWithDecryptedPassword(RangerService service) throws Exception  {
 		Map<String, String> configs = service.getConfigs();
-		
-		String pwd = configs.get(ServiceDBStore.CONFIG_KEY_PASSWORD);
-		if(!stringUtil.isEmpty(pwd) && ServiceDBStore.HIDDEN_PASSWORD_STR.equalsIgnoreCase(pwd)) {
-			XXServiceConfigMap pwdConfig = daoMgr.getXXServiceConfigMap().findByServiceAndConfigKey(service.getId(),
-					ServiceDBStore.CONFIG_KEY_PASSWORD);
-
-			if (pwdConfig != null) {
-				String encryptedPwd = pwdConfig.getConfigvalue();
-				if (encryptedPwd.contains(",")) {
-					PasswordUtils util = PasswordUtils.build(encryptedPwd);
-					String freeTextPasswordMetaData = Joiner.on(",").skipNulls().join(util.getCryptAlgo(),
-							new String(util.getEncryptKey()), new String(util.getSalt()), util.getIterationCount(),
-							PasswordUtils.needsIv(util.getCryptAlgo()) ? util.getIvAsString() : null);
-					String decryptedPwd = PasswordUtils.decryptPassword(encryptedPwd);
-					if (StringUtils
-							.equalsIgnoreCase(
-									freeTextPasswordMetaData + ","
-											+ PasswordUtils
-													.encryptPassword(freeTextPasswordMetaData + "," + decryptedPwd),
-									encryptedPwd)) {
-						configs.put(ServiceDBStore.CONFIG_KEY_PASSWORD, encryptedPwd); // XXX: method name is
-																						// getConfigsWithDecryptedPassword,
-																						// then why do we store the
-																						// encryptedPwd?
-					}
-				} else {
-					String decryptedPwd = PasswordUtils.decryptPassword(encryptedPwd);
-					if (StringUtils.equalsIgnoreCase(PasswordUtils.encryptPassword(decryptedPwd), encryptedPwd)) {
-						configs.put(ServiceDBStore.CONFIG_KEY_PASSWORD, encryptedPwd); // XXX: method name is
-																						// getConfigsWithDecryptedPassword,
-																						// then why do we store the
-																						// encryptedPwd?
+
+		if (configs == null) {
+			return configs;
+		}
+
+		Set<String> passwordConfigKeys = getPasswordConfigKeysByServiceDefName(service.getType());
+		for (String configKey : new ArrayList<>(configs.keySet())) {
+			if (!ServiceDBStore.isPasswordConfigKey(passwordConfigKeys, configKey)) {
+				continue;
+			}
+
+			String pwd = configs.get(configKey);
+			if (!stringUtil.isEmpty(pwd) && ServiceDBStore.HIDDEN_PASSWORD_STR.equalsIgnoreCase(pwd)) {
+				XXServiceConfigMap pwdConfig = daoMgr.getXXServiceConfigMap().findByServiceAndConfigKey(service.getId(), configKey);
+
+				if (pwdConfig != null) {
+					String encryptedPwd = pwdConfig.getConfigvalue();
+					if (encryptedPwd.contains(",")) {
+						PasswordUtils util = PasswordUtils.build(encryptedPwd);
+						String freeTextPasswordMetaData = Joiner.on(",").skipNulls().join(util.getCryptAlgo(),
+								new String(util.getEncryptKey()), new String(util.getSalt()), util.getIterationCount(),
+								PasswordUtils.needsIv(util.getCryptAlgo()) ? util.getIvAsString() : null);
+						String decryptedPwd = PasswordUtils.decryptPassword(encryptedPwd);
+						if (StringUtils
+								.equalsIgnoreCase(
+										freeTextPasswordMetaData + ","
+												+ PasswordUtils
+														.encryptPassword(freeTextPasswordMetaData + "," + decryptedPwd),
+										encryptedPwd)) {
+							configs.put(configKey, encryptedPwd); // XXX: method name is
+																	// getConfigsWithDecryptedPassword,
+																	// then why do we store the
+																	// encryptedPwd?
+						}
+					} else {
+						String decryptedPwd = PasswordUtils.decryptPassword(encryptedPwd);
+						if (StringUtils.equalsIgnoreCase(PasswordUtils.encryptPassword(decryptedPwd), encryptedPwd)) {
+							configs.put(configKey, encryptedPwd); // XXX: method name is
+																	// getConfigsWithDecryptedPassword,
+																	// then why do we store the
+																	// encryptedPwd?
+						}
 					}
 				}
 			}
@@ -147,6 +159,14 @@ public Map<String, String> getConfigsWithDecryptedPassword(RangerService service
 		return configs;
 	}
 
+	private Set<String> getPasswordConfigKeysByServiceDefId(Long serviceDefId) {
+		return ServiceDBStore.getPasswordConfigKeys(serviceDefId != null ? daoMgr.getXXServiceConfigDef().findConfigNamesByServiceDefIdAndType(serviceDefId, ServiceDBStore.CONFIG_TYPE_PASSWORD) : null);
+	}
+
+	private Set<String> getPasswordConfigKeysByServiceDefName(String serviceDefName) {
+		return ServiceDBStore.getPasswordConfigKeys(serviceDefName != null ? daoMgr.getXXServiceConfigDef().findConfigNamesByServiceDefNameAndType(serviceDefName, ServiceDBStore.CONFIG_TYPE_PASSWORD) : null);
+	}
+
 	@Override
 	public RangerService postCreate(XXService xObj) {
 		XXServiceVersionInfo serviceVersionInfo = new XXServiceVersionInfo();
diff --git a/security-admin/src/main/resources/META-INF/jpa_named_queries.xml b/security-admin/src/main/resources/META-INF/jpa_named_queries.xml
index 6d27d3694..db5b77eb4 100755
--- a/security-admin/src/main/resources/META-INF/jpa_named_queries.xml
+++ b/security-admin/src/main/resources/META-INF/jpa_named_queries.xml
@@ -638,10 +638,19 @@
 	</named-query>
 
 	<named-query name="XXServiceConfigDef.findByServiceDefName">
-		<query>select obj from XXServiceConfigDef obj, XXServiceDef svcDef 
+		<query>select obj from XXServiceConfigDef obj, XXServiceDef svcDef
 				where obj.defId = svcDef.id and svcDef.name = :serviceDef order by obj.order</query>
 	</named-query>
 
+	<named-query name="XXServiceConfigDef.findConfigNamesByServiceDefIdAndType">
+		<query>select obj.name from XXServiceConfigDef obj
+				where obj.defId = :serviceDefId and obj.type = :configType order by obj.order</query>
+	</named-query>
+
+	<named-query name="XXServiceConfigDef.findConfigNamesByServiceDefNameAndType">
+		<query>select obj.name from XXServiceConfigDef obj, XXServiceDef svcDef
+				where obj.defId = svcDef.id and svcDef.name = :serviceDef and obj.type = :configType order by obj.order</query>
+	</named-query>
 
 	<!-- XXAccessTypeDef -->
 	<named-query name="XXAccessTypeDef.findByServiceDefId">
diff --git a/security-admin/src/test/java/org/apache/ranger/biz/TestServiceDBStore.java b/security-admin/src/test/java/org/apache/ranger/biz/TestServiceDBStore.java
index 434a64ee7..de37c1854 100644
--- a/security-admin/src/test/java/org/apache/ranger/biz/TestServiceDBStore.java
+++ b/security-admin/src/test/java/org/apache/ranger/biz/TestServiceDBStore.java
@@ -21,6 +21,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -29,6 +30,7 @@
 import java.util.Set;
 
 import org.apache.commons.collections.ListUtils;
+import org.apache.hadoop.thirdparty.com.google.common.base.Joiner;
 import org.apache.ranger.common.ContextUtil;
 import org.apache.ranger.common.GUIDUtil;
 import org.apache.ranger.common.JSONUtil;
@@ -57,6 +59,7 @@
 import org.apache.ranger.plugin.model.RangerServiceDef.RangerServiceConfigDef;
 import org.apache.ranger.plugin.store.PList;
 import org.apache.ranger.plugin.store.ServicePredicateUtil;
+import org.apache.ranger.plugin.util.PasswordUtils;
 import org.apache.ranger.plugin.util.SearchFilter;
 import org.apache.ranger.plugin.util.ServicePolicies;
 import org.apache.ranger.security.context.RangerContextHolder;
@@ -84,6 +87,7 @@
 import org.junit.rules.ExpectedException;
 import org.junit.runner.RunWith;
 import org.junit.runners.MethodSorters;
+import org.mockito.ArgumentCaptor;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.Mockito;
@@ -1217,6 +1221,294 @@ public void test20updateService() throws Exception {
 		Mockito.verify(daoManager).getXXUser();
 	}
 
+	@Test
+	public void test20aCreateServiceEncryptsAllServiceDefTypePasswordConfigs() throws Exception {
+		final String keyKeystorePwd = "nifi.ssl.keystorePassword";
+		final String keyTruststorePwd = "nifi.ssl.truststorePassword";
+		final String keyUrl = "nifi.url";
+		final String plainKeystorePwd = "s3cr3t-keystore";
+		final String plainTruststorePwd = "s3cr3t-truststore";
+		final String plainUrl = "https://nifi.example.com:8443/nifi-api";
+
+		XXServiceDao xServiceDao = Mockito.mock(XXServiceDao.class);
+		XXServiceConfigMapDao xServiceConfigMapDao = Mockito.mock(XXServiceConfigMapDao.class);
+		XXServiceConfigDefDao xServiceConfigDefDao = Mockito.mock(XXServiceConfigDefDao.class);
+		XXService xService = Mockito.mock(XXService.class);
+
+		Map<String, String> configs = new HashMap<String, String>();
+		configs.put(keyKeystorePwd, plainKeystorePwd);
+		configs.put(keyTruststorePwd, plainTruststorePwd);
+		configs.put(keyUrl, plainUrl);
+
+		RangerService rangerService = new RangerService();
+		rangerService.setId(Id);
+		rangerService.setName("nifi-svc");
+		rangerService.setType("nifi");
+		rangerService.setConfigs(configs);
+		rangerService.setIsEnabled(true);
+
+		List<XXServiceConfigDef> nifiPasswordDefs = Arrays.asList(
+				serviceConfigDef(keyKeystorePwd, "password"),
+				serviceConfigDef(keyTruststorePwd, "password"),
+				serviceConfigDef(keyUrl, "string"));
+
+		Mockito.when(daoManager.getXXServiceConfigDef()).thenReturn(xServiceConfigDefDao);
+		Mockito.when(xServiceConfigDefDao.findByServiceDefName("nifi")).thenReturn(nifiPasswordDefs);
+		Mockito.when(xServiceConfigDefDao.findConfigNamesByServiceDefNameAndType("nifi", ServiceDBStore.CONFIG_TYPE_PASSWORD)).thenReturn(Arrays.asList(keyKeystorePwd, keyTruststorePwd));
+
+		Mockito.when(svcService.create(rangerService)).thenReturn(rangerService);
+		Mockito.when(daoManager.getXXService()).thenReturn(xServiceDao);
+		Mockito.when(xServiceDao.getById(rangerService.getId())).thenReturn(xService);
+		Mockito.when(daoManager.getXXServiceConfigMap()).thenReturn(xServiceConfigMapDao);
+
+		Mockito.when(svcService.getPopulatedViewObject(xService)).thenReturn(rangerService);
+		Mockito.when(rangerAuditFields.populateAuditFields(Mockito.any(XXServiceConfigMap.class), Mockito.any(XXService.class)))
+				.thenAnswer(invocation -> invocation.getArgument(0));
+
+		ServiceDBStore spy = Mockito.spy(serviceDBStore);
+		Mockito.doNothing().when(spy).createDefaultPolicies(rangerService);
+
+		spy.createService(rangerService);
+
+		ArgumentCaptor<XXServiceConfigMap> captor = ArgumentCaptor.forClass(XXServiceConfigMap.class);
+		Mockito.verify(xServiceConfigMapDao, Mockito.times(3)).create(captor.capture());
+
+		Map<String, String> persisted = new HashMap<String, String>();
+		for (XXServiceConfigMap m : captor.getAllValues()) {
+			persisted.put(m.getConfigkey(), m.getConfigvalue());
+		}
+
+		Assert.assertNotEquals(plainKeystorePwd, persisted.get(keyKeystorePwd));
+		Assert.assertNotEquals(plainTruststorePwd, persisted.get(keyTruststorePwd));
+		Assert.assertTrue(persisted.get(keyKeystorePwd).contains(","));
+		Assert.assertTrue(persisted.get(keyTruststorePwd).contains(","));
+		Assert.assertEquals(plainUrl, persisted.get(keyUrl));
+	}
+
+	@Test
+	public void test20aLiteralPasswordKeyStillEncryptedOnCreate() throws Exception {
+		final String keyPassword = "password";
+		final String keyUrl = "jdbc.url";
+		final String plainPassword = "s3cr3t";
+		final String plainUrl = "jdbc:hive2://localhost:10000";
+
+		XXServiceDao xServiceDao = Mockito.mock(XXServiceDao.class);
+		XXServiceConfigMapDao xServiceConfigMapDao = Mockito.mock(XXServiceConfigMapDao.class);
+		XXServiceConfigDefDao xServiceConfigDefDao = Mockito.mock(XXServiceConfigDefDao.class);
+		XXService xService = Mockito.mock(XXService.class);
+
+		Map<String, String> configs = new HashMap<String, String>();
+		configs.put(keyPassword, plainPassword);
+		configs.put(keyUrl, plainUrl);
+
+		RangerService rangerService = new RangerService();
+		rangerService.setId(Id);
+		rangerService.setName("hive-svc");
+		rangerService.setType("hive");
+		rangerService.setConfigs(configs);
+		rangerService.setIsEnabled(true);
+
+		// no type="password" declarations at all for this service-def - only the literal key
+		// "password" should still get encrypted.
+		Mockito.when(daoManager.getXXServiceConfigDef()).thenReturn(xServiceConfigDefDao);
+		Mockito.when(xServiceConfigDefDao.findByServiceDefName("hive")).thenReturn(Collections.<XXServiceConfigDef>emptyList());
+
+		Mockito.when(svcService.create(rangerService)).thenReturn(rangerService);
+		Mockito.when(daoManager.getXXService()).thenReturn(xServiceDao);
+		Mockito.when(xServiceDao.getById(rangerService.getId())).thenReturn(xService);
+		Mockito.when(daoManager.getXXServiceConfigMap()).thenReturn(xServiceConfigMapDao);
+
+		Mockito.when(svcService.getPopulatedViewObject(xService)).thenReturn(rangerService);
+		Mockito.when(rangerAuditFields.populateAuditFields(Mockito.any(XXServiceConfigMap.class), Mockito.any(XXService.class)))
+				.thenAnswer(invocation -> invocation.getArgument(0));
+
+		ServiceDBStore spy = Mockito.spy(serviceDBStore);
+		Mockito.doNothing().when(spy).createDefaultPolicies(rangerService);
+
+		spy.createService(rangerService);
+
+		ArgumentCaptor<XXServiceConfigMap> captor = ArgumentCaptor.forClass(XXServiceConfigMap.class);
+		Mockito.verify(xServiceConfigMapDao, Mockito.times(2)).create(captor.capture());
+
+		Map<String, String> persisted = new HashMap<String, String>();
+		for (XXServiceConfigMap m : captor.getAllValues()) {
+			persisted.put(m.getConfigkey(), m.getConfigvalue());
+		}
+
+		Assert.assertNotEquals(plainPassword, persisted.get(keyPassword));
+		Assert.assertTrue(persisted.get(keyPassword).contains(","));
+		Assert.assertEquals(plainUrl, persisted.get(keyUrl));
+	}
+
+	@Test
+	public void test20bUpdateServicePreservesPerKeyEncryptedValueOnHiddenSentinel() throws Exception {
+		final String keyKeystorePwd = "nifi.ssl.keystorePassword";
+		final String keyTruststorePwd = "nifi.ssl.truststorePassword";
+
+		String oldKeystoreEncrypted = encryptForStorage("old-keystore-secret");
+		String oldTruststoreEncrypted = encryptForStorage("old-truststore-secret");
+		Assert.assertNotEquals(oldKeystoreEncrypted, oldTruststoreEncrypted);
+
+		XXServiceDao xServiceDao = Mockito.mock(XXServiceDao.class);
+		XXService xService = Mockito.mock(XXService.class);
+		XXServiceConfigMapDao xServiceConfigMapDao = Mockito.mock(XXServiceConfigMapDao.class);
+		XXServiceConfigDefDao xServiceConfigDefDao = Mockito.mock(XXServiceConfigDefDao.class);
+
+		Map<String, String> configs = new HashMap<String, String>();
+		configs.put(keyKeystorePwd, ServiceDBStore.HIDDEN_PASSWORD_STR);
+		configs.put(keyTruststorePwd, ServiceDBStore.HIDDEN_PASSWORD_STR);
+
+		RangerService rangerService = new RangerService();
+		rangerService.setId(Id);
+		rangerService.setName("nifi-svc");
+		rangerService.setType("nifi");
+		rangerService.setConfigs(configs);
+		rangerService.setIsEnabled(true);
+
+		RangerService existing = new RangerService();
+		existing.setId(Id);
+		existing.setName("nifi-svc");
+		existing.setType("nifi");
+		existing.setIsEnabled(true);
+		existing.setCreateTime(new Date());
+		existing.setGuid("1427365526516_835_0");
+		existing.setVersion(1L);
+
+		List<XXServiceConfigDef> nifiPasswordDefs = Arrays.asList(
+				serviceConfigDef(keyKeystorePwd, "password"),
+				serviceConfigDef(keyTruststorePwd, "password"));
+
+		Mockito.when(daoManager.getXXServiceConfigDef()).thenReturn(xServiceConfigDefDao);
+		Mockito.when(xServiceConfigDefDao.findByServiceDefName("nifi")).thenReturn(nifiPasswordDefs);
+		Mockito.when(xServiceConfigDefDao.findConfigNamesByServiceDefNameAndType("nifi", ServiceDBStore.CONFIG_TYPE_PASSWORD)).thenReturn(Arrays.asList(keyKeystorePwd, keyTruststorePwd));
+
+		Mockito.when(daoManager.getXXService()).thenReturn(xServiceDao);
+		Mockito.when(xServiceDao.getById(Id)).thenReturn(xService);
+		Mockito.when(svcService.getPopulatedViewObject(xService)).thenReturn(existing).thenReturn(rangerService);
+
+		List<XXServiceConfigMap> dbConfigMaps = Arrays.asList(
+				storedConfigMap(keyKeystorePwd, oldKeystoreEncrypted),
+				storedConfigMap(keyTruststorePwd, oldTruststoreEncrypted));
+
+		Mockito.when(daoManager.getXXServiceConfigMap()).thenReturn(xServiceConfigMapDao);
+		Mockito.when(xServiceConfigMapDao.findByServiceId(Id)).thenReturn(dbConfigMaps);
+
+		Mockito.when(svcService.update(rangerService)).thenReturn(rangerService);
+		Mockito.when(rangerAuditFields.populateAuditFields(Mockito.any(XXServiceConfigMap.class), Mockito.any(XXService.class)))
+				.thenAnswer(invocation -> invocation.getArgument(0));
+
+		RangerService result = serviceDBStore.updateService(rangerService, null);
+		Assert.assertNotNull(result);
+
+		ArgumentCaptor<XXServiceConfigMap> captor = ArgumentCaptor.forClass(XXServiceConfigMap.class);
+		Mockito.verify(xServiceConfigMapDao, Mockito.times(2)).create(captor.capture());
+
+		Map<String, String> persisted = new HashMap<String, String>();
+		for (XXServiceConfigMap m : captor.getAllValues()) {
+			persisted.put(m.getConfigkey(), m.getConfigvalue());
+		}
+
+		Assert.assertEquals(oldKeystoreEncrypted, persisted.get(keyKeystorePwd));
+		Assert.assertEquals(oldTruststoreEncrypted, persisted.get(keyTruststorePwd));
+		Assert.assertNotEquals(oldTruststoreEncrypted, persisted.get(keyKeystorePwd));
+		Assert.assertNotEquals(oldKeystoreEncrypted, persisted.get(keyTruststorePwd));
+	}
+
+	@Test
+	public void test20cUpdateServiceReEncryptsChangedKeyAndPreservesOtherOnHiddenSentinel() throws Exception {
+		final String keyKeystorePwd = "nifi.ssl.keystorePassword";
+		final String keyTruststorePwd = "nifi.ssl.truststorePassword";
+		final String newKeystorePwd = "brand-new-keystore-secret";
+
+		String oldKeystoreEncrypted = encryptForStorage("old-keystore-secret");
+		String oldTruststoreEncrypted = encryptForStorage("old-truststore-secret");
+
+		XXServiceDao xServiceDao = Mockito.mock(XXServiceDao.class);
+		XXService xService = Mockito.mock(XXService.class);
+		XXServiceConfigMapDao xServiceConfigMapDao = Mockito.mock(XXServiceConfigMapDao.class);
+		XXServiceConfigDefDao xServiceConfigDefDao = Mockito.mock(XXServiceConfigDefDao.class);
+
+		Map<String, String> configs = new HashMap<String, String>();
+		configs.put(keyKeystorePwd, newKeystorePwd);                       // changed
+		configs.put(keyTruststorePwd, ServiceDBStore.HIDDEN_PASSWORD_STR); // untouched
+
+		RangerService rangerService = new RangerService();
+		rangerService.setId(Id);
+		rangerService.setName("nifi-svc");
+		rangerService.setType("nifi");
+		rangerService.setConfigs(configs);
+		rangerService.setIsEnabled(true);
+
+		RangerService existing = new RangerService();
+		existing.setId(Id);
+		existing.setName("nifi-svc");
+		existing.setType("nifi");
+		existing.setIsEnabled(true);
+		existing.setCreateTime(new Date());
+		existing.setGuid("1427365526516_835_0");
+		existing.setVersion(1L);
+
+		List<XXServiceConfigDef> nifiPasswordDefs = Arrays.asList(
+				serviceConfigDef(keyKeystorePwd, "password"),
+				serviceConfigDef(keyTruststorePwd, "password"));
+
+		Mockito.when(daoManager.getXXServiceConfigDef()).thenReturn(xServiceConfigDefDao);
+		Mockito.when(xServiceConfigDefDao.findByServiceDefName("nifi")).thenReturn(nifiPasswordDefs);
+		Mockito.when(xServiceConfigDefDao.findConfigNamesByServiceDefNameAndType("nifi", ServiceDBStore.CONFIG_TYPE_PASSWORD)).thenReturn(Arrays.asList(keyKeystorePwd, keyTruststorePwd));
+
+		Mockito.when(daoManager.getXXService()).thenReturn(xServiceDao);
+		Mockito.when(xServiceDao.getById(Id)).thenReturn(xService);
+		Mockito.when(svcService.getPopulatedViewObject(xService)).thenReturn(existing).thenReturn(rangerService);
+
+		List<XXServiceConfigMap> dbConfigMaps = Arrays.asList(
+				storedConfigMap(keyKeystorePwd, oldKeystoreEncrypted),
+				storedConfigMap(keyTruststorePwd, oldTruststoreEncrypted));
+
+		Mockito.when(daoManager.getXXServiceConfigMap()).thenReturn(xServiceConfigMapDao);
+		Mockito.when(xServiceConfigMapDao.findByServiceId(Id)).thenReturn(dbConfigMaps);
+
+		Mockito.when(svcService.update(rangerService)).thenReturn(rangerService);
+		Mockito.when(rangerAuditFields.populateAuditFields(Mockito.any(XXServiceConfigMap.class), Mockito.any(XXService.class)))
+				.thenAnswer(invocation -> invocation.getArgument(0));
+
+		RangerService result = serviceDBStore.updateService(rangerService, null);
+		Assert.assertNotNull(result);
+
+		ArgumentCaptor<XXServiceConfigMap> captor = ArgumentCaptor.forClass(XXServiceConfigMap.class);
+		Mockito.verify(xServiceConfigMapDao, Mockito.times(2)).create(captor.capture());
+
+		Map<String, String> persisted = new HashMap<String, String>();
+		for (XXServiceConfigMap m : captor.getAllValues()) {
+			persisted.put(m.getConfigkey(), m.getConfigvalue());
+		}
+
+		Assert.assertNotEquals(oldKeystoreEncrypted, persisted.get(keyKeystorePwd));
+		Assert.assertNotEquals(newKeystorePwd, persisted.get(keyKeystorePwd));
+		Assert.assertEquals(oldTruststoreEncrypted, persisted.get(keyTruststorePwd));
+	}
+
+	private XXServiceConfigDef serviceConfigDef(String name, String type) {
+		XXServiceConfigDef def = new XXServiceConfigDef();
+		def.setName(name);
+		def.setType(type);
+		return def;
+	}
+
+	private XXServiceConfigMap storedConfigMap(String key, String value) {
+		XXServiceConfigMap m = new XXServiceConfigMap();
+		m.setConfigkey(key);
+		m.setConfigvalue(value);
+		return m;
+	}
+
+	private String encryptForStorage(String plainValue) throws Exception {
+		String iv                = PasswordUtils.generateIvIfNeeded(ServiceDBStore.CRYPT_ALGO);
+		Joiner joiner            = Joiner.on(",").skipNulls();
+		String cryptConfigString = joiner.join(ServiceDBStore.CRYPT_ALGO, ServiceDBStore.ENCRYPT_KEY, ServiceDBStore.SALT, ServiceDBStore.ITERATION_COUNT, iv, plainValue);
+		String encryptedPwd      = PasswordUtils.encryptPassword(cryptConfigString);
+		return joiner.join(ServiceDBStore.CRYPT_ALGO, ServiceDBStore.ENCRYPT_KEY, ServiceDBStore.SALT, ServiceDBStore.ITERATION_COUNT, iv, encryptedPwd);
+	}
+
 	@Test
 	public void test21deleteService() throws Exception {
 		setup();
diff --git a/security-admin/src/test/java/org/apache/ranger/service/TestRangerServiceService.java b/security-admin/src/test/java/org/apache/ranger/service/TestRangerServiceService.java
index d7f2f1ad0..37107b0b1 100644
--- a/security-admin/src/test/java/org/apache/ranger/service/TestRangerServiceService.java
+++ b/security-admin/src/test/java/org/apache/ranger/service/TestRangerServiceService.java
@@ -17,18 +17,23 @@
 package org.apache.ranger.service;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.hadoop.thirdparty.com.google.common.base.Joiner;
 import org.apache.ranger.biz.RangerBizUtil;
+import org.apache.ranger.biz.ServiceDBStore;
 import org.apache.ranger.common.ContextUtil;
 import org.apache.ranger.common.JSONUtil;
 import org.apache.ranger.common.StringUtil;
 import org.apache.ranger.common.UserSessionBase;
 import org.apache.ranger.db.RangerDaoManager;
 import org.apache.ranger.db.XXPortalUserDao;
+import org.apache.ranger.db.XXServiceConfigDefDao;
 import org.apache.ranger.db.XXServiceConfigMapDao;
 import org.apache.ranger.db.XXServiceDao;
 import org.apache.ranger.db.XXServiceDefDao;
@@ -40,6 +45,7 @@
 import org.apache.ranger.entity.XXServiceDef;
 import org.apache.ranger.entity.XXServiceVersionInfo;
 import org.apache.ranger.plugin.model.RangerService;
+import org.apache.ranger.plugin.util.PasswordUtils;
 
 import org.apache.ranger.security.context.RangerContextHolder;
 import org.apache.ranger.security.context.RangerSecurityContext;
@@ -174,6 +180,7 @@ public void test3PopulateViewBean() {
 		XXServiceDefDao xServiceDefDao = Mockito.mock(XXServiceDefDao.class);
 		XXService xService = xService();
 		String name = "fdfdfds";
+		XXServiceConfigDefDao xServiceConfigDefDao = Mockito.mock(XXServiceConfigDefDao.class);
 
 		List<XXServiceConfigMap> svcConfigMapList = new ArrayList<XXServiceConfigMap>();
 		XXServiceConfigMap xConfMap = new XXServiceConfigMap();
@@ -227,6 +234,9 @@ public void test3PopulateViewBean() {
 		Mockito.when(xServiceConfigMapDao.findByServiceId(xService.getId()))
 				.thenReturn(svcConfigMapList);
 
+		Mockito.when(daoManager.getXXServiceConfigDef()).thenReturn(xServiceConfigDefDao);
+		Mockito.when(xServiceConfigDefDao.findConfigNamesByServiceDefIdAndType(xService.getType(), ServiceDBStore.CONFIG_TYPE_PASSWORD)).thenReturn(new ArrayList<String>());
+
 		RangerService dbService = serviceService.populateViewBean(xService);
 
 		Assert.assertNotNull(dbService);
@@ -253,6 +263,7 @@ public void test4GetPopulatedViewObject() {
 				.mock(XXServiceConfigMapDao.class);
 		XXPortalUserDao xPortalUserDao = Mockito.mock(XXPortalUserDao.class);
 		XXServiceDefDao xServiceDefDao = Mockito.mock(XXServiceDefDao.class);
+		XXServiceConfigDefDao xServiceConfigDefDao = Mockito.mock(XXServiceConfigDefDao.class);
 		XXService xService = xService();
 		String name = "fdfdfds";
 
@@ -307,6 +318,9 @@ public void test4GetPopulatedViewObject() {
 		Mockito.when(xServiceConfigMapDao.findByServiceId(xService.getId()))
 				.thenReturn(svcConfigMapList);
 
+		Mockito.when(daoManager.getXXServiceConfigDef()).thenReturn(xServiceConfigDefDao);
+		Mockito.when(xServiceConfigDefDao.findConfigNamesByServiceDefIdAndType(xService.getType(), ServiceDBStore.CONFIG_TYPE_PASSWORD)).thenReturn(new ArrayList<String>());
+
 		RangerService dbService = serviceService
 				.getPopulatedViewObject(xService);
 
@@ -328,6 +342,123 @@ public void test4GetPopulatedViewObject() {
 		Mockito.verify(daoManager).getXXServiceConfigMap();
 	}
 
+	@Test
+	public void test4aPopulateViewBeanMasksAllServiceDefTypePasswordConfigs() {
+		XXServiceConfigMapDao xServiceConfigMapDao = Mockito.mock(XXServiceConfigMapDao.class);
+		XXPortalUserDao       xPortalUserDao       = Mockito.mock(XXPortalUserDao.class);
+		XXServiceDefDao       xServiceDefDao       = Mockito.mock(XXServiceDefDao.class);
+		XXServiceConfigDefDao xServiceConfigDefDao = Mockito.mock(XXServiceConfigDefDao.class);
+		XXService             xService             = xService();
+
+		final String keyGenericPassword    = "password";
+		final String keyKeystorePassword   = "nifi.ssl.keystorePassword";
+		final String keyTruststorePassword = "nifi.ssl.truststorePassword";
+		final String keyUrl                = "nifi.url";
+
+		List<XXServiceConfigMap> svcConfigMapList = new ArrayList<XXServiceConfigMap>();
+		svcConfigMapList.add(configMap(keyGenericPassword, "s3cr3t-admin"));
+		svcConfigMapList.add(configMap(keyKeystorePassword, "s3cr3t-keystore"));
+		svcConfigMapList.add(configMap(keyTruststorePassword, "s3cr3t-truststore"));
+		svcConfigMapList.add(configMap(keyUrl, "https://nifi.example.com:8443/nifi-api"));
+
+		XXPortalUser tUser = new XXPortalUser();
+		tUser.setAddedByUserId(userId);
+		tUser.setCreateTime(new Date());
+		tUser.setEmailAddress("test@gmail.com");
+		tUser.setFirstName("nifi-prod");
+		tUser.setId(userId);
+		tUser.setLastName("nifi-prod");
+
+		XXServiceDef xServiceDef = new XXServiceDef();
+		xServiceDef.setAddedByUserId(userId);
+		xServiceDef.setCreateTime(new Date());
+		xServiceDef.setDescription("test");
+		xServiceDef.setGuid("1427365526516_835_0");
+		xServiceDef.setId(userId);
+
+		XXServiceVersionInfoDao xServiceVersionInfoDao = Mockito.mock(XXServiceVersionInfoDao.class);
+		XXServiceVersionInfo    serviceVersionInfo     = new XXServiceVersionInfo();
+		serviceVersionInfo.setServiceId(xService.getId());
+		serviceVersionInfo.setPolicyVersion(xService.getPolicyVersion());
+		serviceVersionInfo.setPolicyUpdateTime(xService.getPolicyUpdateTime());
+		serviceVersionInfo.setTagVersion(xService.getTagVersion());
+		serviceVersionInfo.setTagUpdateTime(xService.getTagUpdateTime());
+
+		Mockito.when(daoManager.getXXServiceVersionInfo()).thenReturn(xServiceVersionInfoDao);
+		Mockito.when(xServiceVersionInfoDao.findByServiceId(xService.getId())).thenReturn(serviceVersionInfo);
+
+		Mockito.when(daoManager.getXXPortalUser()).thenReturn(xPortalUserDao);
+		Mockito.when(xPortalUserDao.getById(userId)).thenReturn(tUser);
+
+		Mockito.when(daoManager.getXXServiceDef()).thenReturn(xServiceDefDao);
+		Mockito.when(xServiceDefDao.getById(xService.getType())).thenReturn(xServiceDef);
+
+		Mockito.when(daoManager.getXXServiceConfigMap()).thenReturn(xServiceConfigMapDao);
+		Mockito.when(xServiceConfigMapDao.findByServiceId(xService.getId())).thenReturn(svcConfigMapList);
+
+		/* the config item names the NiFi service-def declares with "type":"password" -
+		 * already filtered by type at the DB layer, so only those two names come back */
+		List<String> nifiPasswordTypedNames = Arrays.asList(keyKeystorePassword, keyTruststorePassword);
+
+		Mockito.when(daoManager.getXXServiceConfigDef()).thenReturn(xServiceConfigDefDao);
+		Mockito.when(xServiceConfigDefDao.findConfigNamesByServiceDefIdAndType(xService.getType(), ServiceDBStore.CONFIG_TYPE_PASSWORD)).thenReturn(nifiPasswordTypedNames);
+
+		RangerService       dbService = serviceService.getPopulatedViewObject(xService);
+		Map<String, String> configs   = dbService.getConfigs();
+
+		Assert.assertEquals("*****", configs.get(keyGenericPassword));
+		Assert.assertEquals("*****", configs.get(keyKeystorePassword));
+		Assert.assertEquals("*****", configs.get(keyTruststorePassword));
+		Assert.assertEquals("https://nifi.example.com:8443/nifi-api", configs.get(keyUrl));
+	}
+
+	@Test
+	public void test4bGetConfigsWithDecryptedPasswordResolvesMixedCaseKeys() throws Exception {
+		final String configKey  = "nifi.ssl.keystorePassword"; // mixed case, as NiFi's service-def declares it
+		final String plainValue = "s3cr3t-keystore";
+
+		String iv                = PasswordUtils.generateIvIfNeeded(ServiceDBStore.CRYPT_ALGO);
+		Joiner joiner            = Joiner.on(",").skipNulls();
+		String cryptConfigString = joiner.join(ServiceDBStore.CRYPT_ALGO, ServiceDBStore.ENCRYPT_KEY, ServiceDBStore.SALT, ServiceDBStore.ITERATION_COUNT, iv, plainValue);
+		String encryptedPwd      = PasswordUtils.encryptPassword(cryptConfigString);
+		String storedValue       = joiner.join(ServiceDBStore.CRYPT_ALGO, ServiceDBStore.ENCRYPT_KEY, ServiceDBStore.SALT, ServiceDBStore.ITERATION_COUNT, iv, encryptedPwd);
+
+		XXServiceConfigMapDao xServiceConfigMapDao = Mockito.mock(XXServiceConfigMapDao.class);
+		XXServiceConfigDefDao xServiceConfigDefDao = Mockito.mock(XXServiceConfigDefDao.class);
+
+		Map<String, String> configs = new HashMap<String, String>();
+		configs.put(configKey, ServiceDBStore.HIDDEN_PASSWORD_STR); // UI didn't change this field -> sentinel value
+
+		RangerService service = new RangerService();
+		service.setId(userId);
+		service.setType("nifi");
+		service.setConfigs(configs);
+
+		XXServiceConfigMap storedConfigMap = new XXServiceConfigMap();
+		storedConfigMap.setConfigkey(configKey);
+		storedConfigMap.setConfigvalue(storedValue);
+
+		Mockito.when(daoManager.getXXServiceConfigDef()).thenReturn(xServiceConfigDefDao);
+		Mockito.when(xServiceConfigDefDao.findConfigNamesByServiceDefNameAndType("nifi", ServiceDBStore.CONFIG_TYPE_PASSWORD)).thenReturn(Collections.singletonList(configKey));
+		Mockito.when(daoManager.getXXServiceConfigMap()).thenReturn(xServiceConfigMapDao);
+		Mockito.when(xServiceConfigMapDao.findByServiceAndConfigKey(userId, configKey)).thenReturn(storedConfigMap);
+
+		Mockito.when(stringUtil.isEmpty(Mockito.anyString())).thenReturn(false);
+
+		Map<String, String> result = serviceService.getConfigsWithDecryptedPassword(service);
+
+		Assert.assertEquals(storedValue, result.get(configKey));
+	}
+
+	private XXServiceConfigMap configMap(String key, String value) {
+		XXServiceConfigMap xConfMap = new XXServiceConfigMap();
+		xConfMap.setConfigkey(key);
+		xConfMap.setConfigvalue(value);
+		xConfMap.setCreateTime(new Date());
+		xConfMap.setUpdateTime(new Date());
+		return xConfMap;
+	}
+
 	@Test
 	public void test5GetAllServices() {
 		XXServiceDao xServiceDao = Mockito.mock(XXServiceDao.class);
@@ -335,6 +466,7 @@ public void test5GetAllServices() {
 		XXServiceConfigMapDao xServiceConfigMapDao = Mockito
 				.mock(XXServiceConfigMapDao.class);
 		XXServiceDefDao xServiceDefDao = Mockito.mock(XXServiceDefDao.class);
+		XXServiceConfigDefDao xServiceConfigDefDao = Mockito.mock(XXServiceConfigDefDao.class);
 
 		String name = "fdfdfds";
 
@@ -396,6 +528,9 @@ public void test5GetAllServices() {
 		Mockito.when(xServiceConfigMapDao.findByServiceId(xService.getId()))
 				.thenReturn(svcConfigMapList);
 
+		Mockito.when(daoManager.getXXServiceConfigDef()).thenReturn(xServiceConfigDefDao);
+		Mockito.when(xServiceConfigDefDao.findConfigNamesByServiceDefIdAndType(xService.getType(), ServiceDBStore.CONFIG_TYPE_PASSWORD)).thenReturn(new ArrayList<String>());
+
 		List<RangerService> dbServiceList = serviceService.getAllServices();
 		Assert.assertNotNull(dbServiceList);