diff --git a/THREAT_MODEL.md b/THREAT_MODEL.md
index 52e30fc48cf..3396478f9c5 100644
--- a/THREAT_MODEL.md
+++ b/THREAT_MODEL.md
@@ -68,7 +68,7 @@ limitations under the License.
| Tag sync | `tagsync/` | Atlas/Kafka; writes to Admin | **In** |
| Audit framework | `agents-audit/`, `audit-server/` | Solr/OpenSearch/Kafka/HDFS/DB audit sinks | **In** |
| Ranger KMS | `kms/`, `plugin-kms/` | Key store (DB/HSM), HDFS NameNode/DataNode | **In** (model at its own trust level — handles key material) |
-| Authentication | `ranger-authn/`, `unixauthservice/`, `agents-cred/`, `credentialbuilder/` | Kerberos/SPNEGO/LDAP; credential stores | **In** |
+| Authentication | `ranger-authn/`, `security-admin/` (PAM/JAAS login), `agents-cred/`, `credentialbuilder/` | Kerberos/SPNEGO/LDAP/PAM; credential stores | **In** |
| Examples / sample app + tools | `ranger-examples/`, `ranger-tools/` *(documented — repo layout)* | Demo / utility | **In** *(maintainer, 2026-06-12 — §14 Q4)* |
| Build / install / migration / ugsync tooling | `distro/`, `agents-installer/`, `migration-util/`, ugsync `filesourceusersynctool`/`ldapconfigchecktool`, `dev-support/` | Build/deploy host | **Out** *(maintainer, 2026-06-12 — §14 Q4)* |
diff --git a/dev-support/ranger-docker/scripts/usersync/ranger-usersync.sh b/dev-support/ranger-docker/scripts/usersync/ranger-usersync.sh
index 18346c294dd..457b3518d5b 100755
--- a/dev-support/ranger-docker/scripts/usersync/ranger-usersync.sh
+++ b/dev-support/ranger-docker/scripts/usersync/ranger-usersync.sh
@@ -47,7 +47,7 @@ fi
cd ${RANGER_HOME}/usersync && ./start.sh
-RANGER_USERSYNC_PID=`ps -ef | grep -v grep | grep -i "org.apache.ranger.authentication.UnixAuthenticationService" | awk '{print $2}'`
+RANGER_USERSYNC_PID=`ps -ef | grep -v grep | grep -i "proc_rangerusersync" | awk '{print $2}'`
# prevent the container from exiting
if [ -z "$RANGER_USERSYNC_PID" ]
diff --git a/distro/pom.xml b/distro/pom.xml
index fa97c3fbd12..5b41c9003d7 100644
--- a/distro/pom.xml
+++ b/distro/pom.xml
@@ -464,18 +464,6 @@
${project.version}
provided
-
- org.apache.ranger
- unixauthclient
- ${project.version}
- provided
-
-
- org.apache.ranger
- unixauthservice
- ${project.version}
- provided
-
org.apache.ranger
unixusersync
@@ -589,6 +577,35 @@
+
+ ranger-usersync
+
+
+
+ maven-assembly-plugin
+ ${assembly.plugin.version}
+
+ ranger-${project.version}
+ ../target
+
+
+
+
+ single
+
+ package
+
+ false
+
+ src/main/assembly/usersync.xml
+
+
+
+
+
+
+
+
ranger-hdfs-plugin
diff --git a/distro/src/main/assembly/admin-web.xml b/distro/src/main/assembly/admin-web.xml
index b28ef08121c..46483f66d83 100644
--- a/distro/src/main/assembly/admin-web.xml
+++ b/distro/src/main/assembly/admin-web.xml
@@ -38,7 +38,6 @@
true
- org.apache.ranger:unixauthclient
ews/ranger_jaas
diff --git a/distro/src/main/assembly/usersync.xml b/distro/src/main/assembly/usersync.xml
index 0b3ed0868f4..6949a4114f7 100644
--- a/distro/src/main/assembly/usersync.xml
+++ b/distro/src/main/assembly/usersync.xml
@@ -26,7 +26,6 @@
true
- org.apache.ranger:unixauthservice
org.apache.ranger:unixusersync
@@ -126,20 +125,30 @@
+
+ ../ugsync/target
+ dist
+
+ unixusersync-${project.version}.jar
+
+ 644
+
+
+ ../ugsync/target/lib
+ lib
+ 644
+
755
644
conf.dist
- ../unixauthservice/conf.dist
-
- jaas.conf
-
+ ../ugsync/conf.dist
755
544
- ../unixauthservice/scripts
+ ../ugsync/scripts
*.properties
initd
@@ -149,7 +158,7 @@
755
700
- ../unixauthservice/scripts
+ ../ugsync/scripts
*.properties
@@ -158,34 +167,16 @@
755
644
conf.dist/cert
- ../unixauthservice/cert
-
-
- 755
- 750
- native
- ../unixauthnative/target
-
- credValidator.*
-
-
-
- 755
- 750
- native
- ../unixauthpam/target
-
- pamCredValidator.*
-
+ ../ugsync/cert
755
+ 444
${project.build.directory}
version
- 444
@@ -243,7 +234,7 @@
- ${project.parent.basedir}/unixauthservice/scripts/initd
+ ${project.parent.basedir}/ugsync/scripts/initd
ranger-usersync
755
diff --git a/migration-util/ambari2.0-hdp2.2-ranger0.40/bin/import_ranger_to_ambari.py b/migration-util/ambari2.0-hdp2.2-ranger0.40/bin/import_ranger_to_ambari.py
index 615eb358d8c..36129907452 100755
--- a/migration-util/ambari2.0-hdp2.2-ranger0.40/bin/import_ranger_to_ambari.py
+++ b/migration-util/ambari2.0-hdp2.2-ranger0.40/bin/import_ranger_to_ambari.py
@@ -176,9 +176,6 @@ def add_advanced_ranger_configurations(add_admin_or_usersync, ranger_service_pro
advanced_admin_properties['policymgr_external_url'] = ranger_service_properties_from_file['xa.webapp.url.root']
advanced_admin_properties['policymgr_http_enabled'] = ranger_service_properties_from_file['http.enabled']
advanced_admin_properties['authentication_method'] = get_authentication_method()
- advanced_admin_properties['remoteLoginEnabled'] = ranger_service_properties_from_file.get('remoteLoginEnabled','false')
- advanced_admin_properties['authServiceHostName'] = ranger_service_properties_from_file.get('authServiceHostName','localhost')
- advanced_admin_properties['authServicePort'] = ranger_service_properties_from_file.get('authServicePort','5151')
advanced_admin_properties['xa_ldap_url'] = ranger_service_properties_from_file['xa_ldap_url']
advanced_admin_properties['xa_ldap_userDNpattern'] = ranger_service_properties_from_file['xa_ldap_userDNpattern']
advanced_admin_properties['xa_ldap_groupSearchBase'] = ranger_service_properties_from_file['xa_ldap_groupSearchBase']
@@ -426,12 +423,10 @@ def get_additional_properties_for_admin(ranger_admin_properties_from_file):
ranger_webserver_properties_path = os.path.join(ranger_conf_path, 'ranger_webserver.properties')
ranger_ldap_properties_path = os.path.join(ranger_conf_path, 'xa_ldap.properties')
ranger_system_properties_path = os.path.join(ranger_conf_path, 'xa_system.properties')
- ranger_unixauth_properties_path = os.path.join(ranger_conf_path, 'ranger_jaas', 'unixauth.properties')
try:
ranger_admin_properties_from_file = import_properties_from_file(ranger_webserver_properties_path, ranger_admin_properties_from_file)
ranger_admin_properties_from_file = import_properties_from_file(ranger_ldap_properties_path, ranger_admin_properties_from_file)
ranger_admin_properties_from_file = import_properties_from_file(ranger_system_properties_path, ranger_admin_properties_from_file)
- ranger_admin_properties_from_file = import_properties_from_file(ranger_unixauth_properties_path, ranger_admin_properties_from_file)
except Exception, e:
print "Error loading property files: ", str(e)
@@ -500,12 +495,14 @@ def get_additional_properties_for_admin(ranger_admin_properties_from_file):
def get_additional_properties_for_usersync(ranger_usersync_properties_from_file):
ranger_conf_path = '/etc/ranger/usersync/conf'
- unix_auth_properties_path = os.path.join(ranger_conf_path, 'unixauthservice.properties')
- ranger_usersync_properties_from_file = import_properties_from_file(unix_auth_properties_path, ranger_usersync_properties_from_file)
- if (('unix'.lower()) in str(ranger_usersync_properties_from_file['usergroupSync.source.impl.class']).lower()):
+ ranger_ugsync_default_site_xml_properties = os.path.join(ranger_conf_path, 'ranger-ugsync-default.xml')
+ ranger_ugsync_site_xml_properties = os.path.join(ranger_conf_path, 'ranger-ugsync-site.xml')
+ ranger_usersync_properties_from_file = import_properties_from_xml(ranger_ugsync_default_site_xml_properties, ranger_usersync_properties_from_file)
+ ranger_usersync_properties_from_file = import_properties_from_xml(ranger_ugsync_site_xml_properties, ranger_usersync_properties_from_file)
+ if (('unix'.lower()) in str(ranger_usersync_properties_from_file.get('ranger.usersync.source.impl.class', '')).lower()):
print('sync_source is unix')
ranger_usersync_properties_from_file['SYNC_SOURCE'] = 'unix'
- if (('ldap'.lower()) in str(ranger_usersync_properties_from_file['usergroupSync.source.impl.class']).lower()):
+ if (('ldap'.lower()) in str(ranger_usersync_properties_from_file.get('ranger.usersync.source.impl.class', '')).lower()):
print('sync source is ldap')
ranger_usersync_properties_from_file['SYNC_SOURCE'] = 'ldap'
return ranger_usersync_properties_from_file
diff --git a/migration-util/ambari2.1-hdp2.3-ranger0.50/bin/import_ranger_to_ambari.py b/migration-util/ambari2.1-hdp2.3-ranger0.50/bin/import_ranger_to_ambari.py
index 195a116ddda..4603064b759 100755
--- a/migration-util/ambari2.1-hdp2.3-ranger0.50/bin/import_ranger_to_ambari.py
+++ b/migration-util/ambari2.1-hdp2.3-ranger0.50/bin/import_ranger_to_ambari.py
@@ -303,9 +303,6 @@ def add_advanced_ranger_configurations(add_admin_or_usersync, ranger_service_pro
advanced_ranger_admin_site_properties['ranger.jpa.audit.jdbc.user'] = ranger_service_properties_from_file.get('ranger.jpa.audit.jdbc.user','{{ranger_audit_db_user}}')
advanced_ranger_admin_site_properties['ranger.jpa.audit.jdbc.password'] = ranger_service_properties_from_file.get('ranger.jpa.audit.jdbc.password','_')
advanced_ranger_admin_site_properties['ranger.jpa.audit.jdbc.credential.alias'] = ranger_service_properties_from_file.get('ranger.jpa.audit.jdbc.credential.alias','rangeraudit')
- advanced_ranger_admin_site_properties['ranger.unixauth.remote.login.enabled'] = ranger_service_properties_from_file.get('ranger.unixauth.remote.login.enabled','true')
- advanced_ranger_admin_site_properties['ranger.unixauth.service.hostname'] = ranger_service_properties_from_file.get('ranger.unixauth.service.hostname','localhost')
- advanced_ranger_admin_site_properties['ranger.unixauth.service.port'] = ranger_service_properties_from_file.get('ranger.unixauth.service.port','5151')
advanced_ranger_admin_site_properties['ranger.jpa.jdbc.dialect'] = ranger_service_properties_from_file.get('ranger.jpa.jdbc.dialect','{{jdbc_dialect}}')
advanced_ranger_admin_site_properties['ranger.jpa.audit.jdbc.dialect'] = ranger_service_properties_from_file.get('ranger.jpa.audit.jdbc.dialect','{{jdbc_dialect}')
advanced_ranger_admin_site_properties['ranger.audit.solr.zookeepers'] = ranger_service_properties_from_file.get('ranger.audit.solr.zookeepers','NONE')
@@ -344,13 +341,10 @@ def add_advanced_ranger_configurations(add_admin_or_usersync, ranger_service_pro
sys.exit(1)
elif (add_admin_or_usersync == 2):
- advanced_user_sync_properties['ranger.usersync.port'] = ranger_service_properties_from_file['ranger.usersync.port']
- advanced_user_sync_properties['ranger.usersync.ssl'] = ranger_service_properties_from_file['ranger.usersync.ssl']
advanced_user_sync_properties['ranger.usersync.keystore.file'] = ranger_service_properties_from_file['ranger.usersync.keystore.file']
advanced_user_sync_properties['ranger.usersync.keystore.password'] = ranger_service_properties_from_file.get('ranger.usersync.keystore.password','UnIx529p')
advanced_user_sync_properties['ranger.usersync.truststore.file'] = ranger_service_properties_from_file.get('ranger.usersync.truststore.file','/usr/hdp/current/ranger-usersync/conf/mytruststore.jks')
advanced_user_sync_properties['ranger.usersync.truststore.password'] = ranger_service_properties_from_file.get('ranger.usersync.truststore.password','changeit')
- advanced_user_sync_properties['ranger.usersync.passwordvalidator.path'] = ranger_service_properties_from_file['ranger.usersync.passwordvalidator.path']
advanced_user_sync_properties['ranger.usersync.sink.impl.class'] = ranger_service_properties_from_file['ranger.usersync.sink.impl.class']
advanced_user_sync_properties['ranger.usersync.policymanager.baseURL'] = ranger_service_properties_from_file['ranger.usersync.policymanager.baseURL']
advanced_user_sync_properties['ranger.usersync.policymanager.maxrecordsperapicall'] = ranger_service_properties_from_file['ranger.usersync.policymanager.maxrecordsperapicall']
diff --git a/mkdocs/docs/project/release-process.md b/mkdocs/docs/project/release-process.md
index a539c0a6ad4..d025f1460d4 100644
--- a/mkdocs/docs/project/release-process.md
+++ b/mkdocs/docs/project/release-process.md
@@ -101,7 +101,6 @@ mvn versions:set -DnewVersion=${RANGER_VERSION} -DgenerateBackupPoms=false
# Also, manually update versions in:
# - dev-support/ranger-docker/.env
# - docs/pom.xml
-# - unixauthnative/pom.xml
# - ranger-trino-plugin-shim/pom.xml
```
diff --git a/pom.xml b/pom.xml
index b27251823f6..cb47d869a54 100755
--- a/pom.xml
+++ b/pom.xml
@@ -976,8 +976,6 @@
ugsync
ugsync-util
ugsync/ldapconfigchecktool/ldapconfigcheck
- unixauthclient
- unixauthservice
@@ -1327,22 +1325,6 @@
ugsync
ugsync-util
ugsync/ldapconfigchecktool/ldapconfigcheck
- unixauthclient
- unixauthservice
-
-
-
- linux-pam
-
-
- linux
-
-
- /usr/include/security/pam_appl.h
-
-
-
- unixauthpam
@@ -1415,8 +1397,6 @@
ugsync
ugsync-util
ugsync/ldapconfigchecktool/ldapconfigcheck
- unixauthclient
- unixauthservice
diff --git a/security-admin/pom.xml b/security-admin/pom.xml
index 4df6c6a8228..e1d09cab93c 100644
--- a/security-admin/pom.xml
+++ b/security-admin/pom.xml
@@ -545,11 +545,6 @@
-
- org.apache.ranger
- unixauthclient
- ${project.version}
-
org.apache.solr
solr-solrj
@@ -710,6 +705,11 @@
org.glassfish.jersey.media
jersey-media-multipart
+
+ org.kohsuke
+ libpam4j
+ ${libpam4j.version}
+
org.slf4j
log4j-over-slf4j
diff --git a/security-admin/scripts/install.properties b/security-admin/scripts/install.properties
index 6a6ababc0fb..52b0701bd6c 100644
--- a/security-admin/scripts/install.properties
+++ b/security-admin/scripts/install.properties
@@ -173,23 +173,11 @@ unix_group=ranger
#
#
-# UNIX authentication service for Policy Manager
-#
-# PolicyManager can authenticate using UNIX username/password
-# The UNIX server specified here as authServiceHostName needs to be installed with ranger-unix-ugsync package.
-# Once the service is installed on authServiceHostName, the UNIX username/password from the host can be used to login into policy manager
-#
-# ** The installation of ranger-unix-ugsync package can be installed after the policymanager installation is finished.
+# Authentication method for Policy Manager UI login.
+# UNIX uses PAM on the Ranger Admin host (local OS accounts).
#
#LDAP|ACTIVE_DIRECTORY|UNIX|NONE
authentication_method=NONE
-remoteLoginEnabled=true
-authServiceHostName=localhost
-authServicePort=5151
-ranger_unixauth_keystore=keystore.jks
-ranger_unixauth_keystore_password=password
-ranger_unixauth_truststore=cacerts
-ranger_unixauth_truststore_password=changeit
####LDAP settings - Required only if have selected LDAP authentication ####
#
diff --git a/security-admin/scripts/setup.sh b/security-admin/scripts/setup.sh
index b0bc262148a..35504cdd00d 100755
--- a/security-admin/scripts/setup.sh
+++ b/security-admin/scripts/setup.sh
@@ -129,13 +129,6 @@ unix_user=$(get_prop 'unix_user' $PROPFILE)
unix_user_pwd=$(get_prop 'unix_user_pwd' $PROPFILE)
unix_group=$(get_prop 'unix_group' $PROPFILE)
authentication_method=$(get_prop 'authentication_method' $PROPFILE)
-remoteLoginEnabled=$(get_prop_or_default 'remoteLoginEnabled' $PROPFILE 'false')
-authServiceHostName=$(get_prop_or_default 'authServiceHostName' $PROPFILE '')
-authServicePort=$(get_prop_or_default 'authServicePort' $PROPFILE '')
-ranger_unixauth_keystore=$(get_prop_or_default 'ranger_unixauth_keystore' $PROPFILE '')
-ranger_unixauth_keystore_password=$(get_prop_or_default 'ranger_unixauth_keystore_password' $PROPFILE '')
-ranger_unixauth_truststore=$(get_prop_or_default 'ranger_unixauth_truststore' $PROPFILE '')
-ranger_unixauth_truststore_password=$(get_prop_or_default 'ranger_unixauth_truststore_password' $PROPFILE '')
xa_ldap_url=$(get_prop 'xa_ldap_url' $PROPFILE)
xa_ldap_userDNpattern=$(get_prop 'xa_ldap_userDNpattern' $PROPFILE)
xa_ldap_groupSearchBase=$(get_prop 'xa_ldap_groupSearchBase' $PROPFILE)
@@ -1219,72 +1212,9 @@ update_properties() {
fi
fi
- if [ "${ranger_unixauth_keystore}" != "" ] && [ "${ranger_unixauth_keystore_password}" != "" ]
- then
- propertyName=ranger.unixauth.keystore
- newPropertyValue="${ranger_unixauth_keystore}"
- updatePropertyToFilePy $propertyName "${newPropertyValue}" $to_file_default
-
- ranger_unixauth_keystore_alias=unixAuthKeyStoreAlias
- propertyName=ranger.unixauth.keystore.credential.alias
- newPropertyValue="${ranger_unixauth_keystore_alias}"
- updatePropertyToFilePy $propertyName "${newPropertyValue}" $to_file_default
- if [ "${keystore}" != "" ]
- then
- propertyName=ranger.unixauth.keystore.password
- newPropertyValue="_"
- updatePropertyToFilePy $propertyName "${newPropertyValue}" $to_file_default
- $PYTHON_COMMAND_INVOKER ranger_credential_helper.py -l "cred/lib/*" -f "$keystore" -k "$ranger_unixauth_keystore_alias" -v "$ranger_unixauth_keystore_password" -c 1
-
- if test -f "${keystore}"; then
- chown -R ${unix_user}:${unix_group} ${keystore}
- else
- propertyName=ranger.unixauth.keystore.password
- newPropertyValue="${ranger_unixauth_keystore_password}"
- updatePropertyToFilePy $propertyName "${newPropertyValue}" $to_file_default
- fi
- else
- propertyName=ranger.unixauth.keystore.password
- newPropertyValue="${ranger_unixauth_keystore_password}"
- updatePropertyToFilePy $propertyName "${newPropertyValue}" $to_file_default
- fi
- fi
-
- if [ "${ranger_unixauth_truststore}" != "" ] && [ "${ranger_unixauth_truststore_password}" != "" ]
- then
- propertyName=ranger.unixauth.truststore
- newPropertyValue="${ranger_unixauth_truststore}"
- updatePropertyToFilePy $propertyName "${newPropertyValue}" $to_file_default
-
- ranger_unixauth_truststore_alias=unixAuthTrustStoreAlias
- propertyName=ranger.unixauth.truststore.credential.alias
- newPropertyValue="${ranger_unixauth_truststore_alias}"
- updatePropertyToFilePy $propertyName "${newPropertyValue}" $to_file_default
-
- if [ "${keystore}" != "" ]
- then
- propertyName=ranger.unixauth.truststore.password
- newPropertyValue="_"
- updatePropertyToFilePy $propertyName "${newPropertyValue}" $to_file_default
- $PYTHON_COMMAND_INVOKER ranger_credential_helper.py -l "cred/lib/*" -f "$keystore" -k "$ranger_unixauth_truststore_alias" -v "$ranger_unixauth_truststore_password" -c 1
-
- if test -f $keystore; then
- chown -R ${unix_user}:${unix_group} ${keystore}
- else
- propertyName=ranger.unixauth.truststore.password
- newPropertyValue="${ranger_unixauth_truststore_password}"
- updatePropertyToFilePy $propertyName "${newPropertyValue}" $to_file_default
- fi
- else
- propertyName=ranger.unixauth.truststore.password
- newPropertyValue="${ranger_unixauth_truststore_password}"
- updatePropertyToFilePy $propertyName "${newPropertyValue}" $to_file_default
- fi
- fi
-
}
-do_unixauth_setup() {
+do_unix_authentication_method_setup() {
ldap_file=$app_home/WEB-INF/classes/conf/ranger-admin-site.xml
if test -f $ldap_file; then
@@ -1293,18 +1223,6 @@ do_unixauth_setup() {
propertyName=ranger.authentication.method
newPropertyValue="${authentication_method}"
updatePropertyToFilePy $propertyName "${newPropertyValue}" $ldap_file
-
- propertyName=ranger.unixauth.remote.login.enabled
- newPropertyValue="${remoteLoginEnabled}"
- updatePropertyToFilePy $propertyName "${newPropertyValue}" $ldap_file
-
- propertyName=ranger.unixauth.service.hostname
- newPropertyValue="${authServiceHostName}"
- updatePropertyToFilePy $propertyName "${newPropertyValue}" $ldap_file
-
- propertyName=ranger.unixauth.service.port
- newPropertyValue="${authServicePort}"
- updatePropertyToFilePy $propertyName "${newPropertyValue}" $ldap_file
else
log "[E] $ldap_file does not exists" ; exit 1;
fi
@@ -1501,8 +1419,8 @@ do_authentication_setup(){
log "[E] $ldap_file does not exists" ; exit 1;
fi
fi
- if [ $authentication_method = "UNIX" ] ; then
- do_unixauth_setup
+ if [ $authentication_method = "UNIX" ] || [ $authentication_method = "PAM" ] ; then
+ do_unix_authentication_method_setup
fi
if [ $authentication_method = "NONE" ] ; then
diff --git a/security-admin/scripts/setup_authentication.sh b/security-admin/scripts/setup_authentication.sh
index 9fe892f60c5..744a39da463 100755
--- a/security-admin/scripts/setup_authentication.sh
+++ b/security-admin/scripts/setup_authentication.sh
@@ -16,7 +16,7 @@
# limitations under the License.
-USAGE="Usage: setup_authentication.sh [UNIX|LDAP|AD|NONE] "
+USAGE="Usage: setup_authentication.sh [UNIX|PAM|LDAP|AD|NONE] "
if [ $# -ne 2 ]
then
@@ -28,7 +28,7 @@ authentication_method=$1
path=$2
CONFIG_FILE=$path/WEB-INF/classes/conf/security-applicationContext.xml
-if [ $authentication_method = "UNIX" ] ; then
+if [ $authentication_method = "UNIX" ] || [ $authentication_method = "PAM" ] ; then
echo $path;
awk 'FNR==NR{ _[++d]=$0;next}
/UNIX_BEAN_SETTINGS_START/{
diff --git a/security-admin/scripts/upgrade_admin.py b/security-admin/scripts/upgrade_admin.py
index 1509a7f06db..08132c3d205 100755
--- a/security-admin/scripts/upgrade_admin.py
+++ b/security-admin/scripts/upgrade_admin.py
@@ -50,11 +50,9 @@ def showUsage():
# configDirectory: where OLD (champlain) configuration exists and NEW (dal) configuration is written to
#
configDirectory = '/etc/ranger/admin/conf'
-rangerJAASDirectoryName = join(configDirectory,'ranger_jaas')
xaSystemPropFile = 'xa_system.properties'
ldapPropFile = 'xa_ldap.properties'
-rangerJAASPropFile = 'unixauth.properties'
securityContextFile = 'security-applicationContext.xml'
webserverConfigFile = 'ranger_webserver.properties'
@@ -193,9 +191,6 @@ def main():
ldapPropFileName = join(configDirectory, ldapPropFile)
xaLdapProps = getPropertiesConfigMap (ldapPropFileName)
-
- jaasPropFileName = join(rangerJAASDirectoryName, rangerJAASPropFile)
- unixauthProps = getPropertiesConfigMap (jaasPropFileName)
webserverConfigFileName = join(configDirectory, webserverConfigFile)
webconfig = getPropertiesConfigMap(webserverConfigFileName)
@@ -206,8 +201,6 @@ def main():
xmlVal = xaSysProps[k]
elif (k in list(xaLdapProps)):
xmlVal = xaLdapProps[k]
- elif (k in list(unixauthProps)):
- xmlVal = unixauthProps[k]
elif (k in list(webconfig)):
xmlVal = webconfig[k]
else:
@@ -290,9 +283,6 @@ def main():
installProps['xa_ldap_groupRoleAttribute'] = xaLdapProps['xa_ldap_groupRoleAttribute']
elif ( len(root.findall(".//*[@id='jaasAuthProvider']",ns)) > 0 ):
installProps['authentication_method'] = 'UNIX'
- installProps['remoteLoginEnabled'] = unixauthProps['remoteLoginEnabled']
- installProps['authServiceHostName'] = unixauthProps['authServiceHostName']
- installProps['authServicePort'] = unixauthProps['authServicePort']
else:
installProps['authentication_method'] = 'NONE'
diff --git a/security-admin/src/bin/install.properties b/security-admin/src/bin/install.properties
index 6b4854b7496..0dbee410833 100644
--- a/security-admin/src/bin/install.properties
+++ b/security-admin/src/bin/install.properties
@@ -83,19 +83,11 @@ unix_group=xasecure
#
#
-# UNIX authentication service for Policy Manager
-#
-# PolicyManager can authenticate using UNIX username/password
-# The UNIX server specified here as authServiceHostName needs to be installed with xasecure-unix-ugsync package.
-# Once the service is installed on authServiceHostName, the UNIX username/password from the host can be used to login into policy manager
-#
-# ** The installation of xasecure-unix-ugsync package can be installed after the policymanager installation is finished.
+# Authentication method for Policy Manager UI login.
+# UNIX uses PAM on the Ranger Admin host (local OS accounts).
#
#LDAP|ACTIVE_DIRECTORY|UNIX|NONE
authentication_method=NONE
-remoteLoginEnabled=true
-authServiceHostName=localhost
-authServicePort=5151
####LDAP settings - Required only if have selected LDAP authentication ####
#
diff --git a/security-admin/src/bin/ranger_usersync.py b/security-admin/src/bin/ranger_usersync.py
index 4374896c768..d764f5b773b 100644
--- a/security-admin/src/bin/ranger_usersync.py
+++ b/security-admin/src/bin/ranger_usersync.py
@@ -99,7 +99,7 @@ def get_java_env():
init_variables()
jdk_options = get_jdk_options()
class_path = get_ranger_classpath()
- java_class = 'org.apache.ranger.authentication.UnixAuthenticationService'
+ java_class = 'org.apache.ranger.usergroupsync.UserSyncService'
class_arguments = ''
dom = getDOMImplementation()
diff --git a/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/ConsolePromptCallbackHandler.java b/security-admin/src/main/java/org/apache/ranger/authentication/unix/jaas/ConsolePromptCallbackHandler.java
similarity index 100%
rename from unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/ConsolePromptCallbackHandler.java
rename to security-admin/src/main/java/org/apache/ranger/authentication/unix/jaas/ConsolePromptCallbackHandler.java
diff --git a/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/PamLoginModule.java b/security-admin/src/main/java/org/apache/ranger/authentication/unix/jaas/PamLoginModule.java
similarity index 100%
rename from unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/PamLoginModule.java
rename to security-admin/src/main/java/org/apache/ranger/authentication/unix/jaas/PamLoginModule.java
diff --git a/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/PamPrincipal.java b/security-admin/src/main/java/org/apache/ranger/authentication/unix/jaas/PamPrincipal.java
similarity index 100%
rename from unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/PamPrincipal.java
rename to security-admin/src/main/java/org/apache/ranger/authentication/unix/jaas/PamPrincipal.java
diff --git a/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/UnixGroupPrincipal.java b/security-admin/src/main/java/org/apache/ranger/authentication/unix/jaas/UnixGroupPrincipal.java
similarity index 100%
rename from unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/UnixGroupPrincipal.java
rename to security-admin/src/main/java/org/apache/ranger/authentication/unix/jaas/UnixGroupPrincipal.java
diff --git a/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/UsernamePasswordCallbackHandler.java b/security-admin/src/main/java/org/apache/ranger/authentication/unix/jaas/UsernamePasswordCallbackHandler.java
similarity index 100%
rename from unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/UsernamePasswordCallbackHandler.java
rename to security-admin/src/main/java/org/apache/ranger/authentication/unix/jaas/UsernamePasswordCallbackHandler.java
diff --git a/security-admin/src/main/java/org/apache/ranger/common/PropertiesUtil.java b/security-admin/src/main/java/org/apache/ranger/common/PropertiesUtil.java
index e640048cdea..bce4b997c2a 100644
--- a/security-admin/src/main/java/org/apache/ranger/common/PropertiesUtil.java
+++ b/security-admin/src/main/java/org/apache/ranger/common/PropertiesUtil.java
@@ -266,40 +266,6 @@ protected void processProperties(ConfigurableListableBeanFactory beanFactory, Pr
System.setProperty("javax.net.ssl.keyStorePassword", propertiesMap.get("ranger.keystore.password"));
}
- //update unixauth keystore and truststore credentials
- if (propertiesMap.containsKey("ranger.credential.provider.path")) {
- String path = propertiesMap.get("ranger.credential.provider.path");
-
- if (path != null) {
- String unixAuthKeyStoreAlias = getProperty("ranger.unixauth.keystore.alias", "unixAuthKeyStoreAlias");
-
- if (unixAuthKeyStoreAlias != null) {
- String unixAuthKeyStorePass = CredentialReader.getDecryptedString(path.trim(), unixAuthKeyStoreAlias.trim(), storeType);
-
- if (unixAuthKeyStorePass != null && !unixAuthKeyStorePass.trim().isEmpty() && !unixAuthKeyStorePass.trim().equalsIgnoreCase("none")) {
- propertiesMap.put("ranger.unixauth.keystore.password", unixAuthKeyStorePass);
- props.put("ranger.unixauth.keystore.password", unixAuthKeyStorePass);
- } else {
- LOG.info("unixauth keystore password not applied; clear text password shall be applicable");
- }
- }
-
- //
- String unixAuthTrustStoreAlias = getProperty("ranger.unixauth.truststore.alias", "unixAuthTrustStoreAlias");
-
- if (unixAuthTrustStoreAlias != null) {
- String unixAuthTrustStorePass = CredentialReader.getDecryptedString(path.trim(), unixAuthTrustStoreAlias.trim(), storeType);
-
- if (unixAuthTrustStorePass != null && !unixAuthTrustStorePass.trim().isEmpty() && !unixAuthTrustStorePass.trim().equalsIgnoreCase("none")) {
- propertiesMap.put("ranger.unixauth.truststore.password", unixAuthTrustStorePass);
- props.put("ranger.unixauth.truststore.password", unixAuthTrustStorePass);
- } else {
- LOG.info("unixauth truststore password not applied; clear text password shall be applicable");
- }
- }
- }
- }
-
//update credential from keystore
if (propertiesMap.containsKey("ranger.credential.provider.path") && propertiesMap.containsKey("ranger.jpa.jdbc.credential.alias")) {
String path = propertiesMap.get("ranger.credential.provider.path");
diff --git a/security-admin/src/main/java/org/apache/ranger/security/handler/RangerAuthenticationProvider.java b/security-admin/src/main/java/org/apache/ranger/security/handler/RangerAuthenticationProvider.java
index a21d08ea0cd..837e8b8bcae 100644
--- a/security-admin/src/main/java/org/apache/ranger/security/handler/RangerAuthenticationProvider.java
+++ b/security-admin/src/main/java/org/apache/ranger/security/handler/RangerAuthenticationProvider.java
@@ -134,9 +134,7 @@ public Authentication authenticate(Authentication authentication) throws Authent
}
}
} else if ("UNIX".equalsIgnoreCase(rangerAuthenticationMethod)) {
- boolean isPAMAuthEnabled = PropertiesUtil.getBooleanProperty("ranger.pam.authentication.enabled", false);
-
- authentication = (isPAMAuthEnabled ? getPamAuthentication(authentication) : getUnixAuthentication(authentication));
+ authentication = getPamAuthentication(authentication);
if (authentication != null && authentication.isAuthenticated()) {
return authentication;
@@ -315,57 +313,6 @@ public Authentication getPamAuthentication(Authentication authentication) {
return authentication;
}
- public Authentication getUnixAuthentication(Authentication authentication) {
- try {
- String rangerLdapDefaultRole = PropertiesUtil.getProperty("ranger.ldap.default.role", "ROLE_USER");
- DefaultJaasAuthenticationProvider jaasAuthenticationProvider = new DefaultJaasAuthenticationProvider();
- String loginModuleName = "org.apache.ranger.authentication.unix.jaas.RemoteUnixLoginModule";
- LoginModuleControlFlag controlFlag = LoginModuleControlFlag.REQUIRED;
- Map options = PropertiesUtil.getPropertiesMap();
- AppConfigurationEntry appConfigurationEntry = new AppConfigurationEntry(loginModuleName, controlFlag, options);
- AppConfigurationEntry[] appConfigurationEntries = new AppConfigurationEntry[] {appConfigurationEntry};
- Map appConfigurationEntriesOptions = new HashMap<>();
-
- appConfigurationEntriesOptions.put("SPRINGSECURITY", appConfigurationEntries);
-
- Configuration configuration = new InMemoryConfiguration(appConfigurationEntriesOptions);
-
- jaasAuthenticationProvider.setConfiguration(configuration);
-
- RoleUserAuthorityGranter authorityGranter = new RoleUserAuthorityGranter();
- RoleUserAuthorityGranter[] authorityGranters = new RoleUserAuthorityGranter[] {authorityGranter};
-
- jaasAuthenticationProvider.setAuthorityGranters(authorityGranters);
- jaasAuthenticationProvider.afterPropertiesSet();
-
- String userName = authentication.getName();
- String userPassword = "";
-
- if (authentication.getCredentials() != null) {
- userPassword = authentication.getCredentials().toString();
- }
-
- // getting user authenticated
- if (userName != null && userPassword != null && !userName.trim().isEmpty() && !userPassword.trim().isEmpty()) {
- final List grantedAuths = new ArrayList<>();
-
- grantedAuths.add(new SimpleGrantedAuthority(rangerLdapDefaultRole));
-
- final UserDetails principal = new User(userName, userPassword, grantedAuths);
- final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken(principal, userPassword, grantedAuths);
-
- authentication = jaasAuthenticationProvider.authenticate(finalAuthentication);
- authentication = getAuthenticationWithGrantedAuthority(authentication);
- }
-
- return authentication;
- } catch (Exception e) {
- logger.debug("Unix Authentication Failed:", e);
- }
-
- return authentication;
- }
-
public String getRangerAuthenticationMethod() {
return rangerAuthenticationMethod;
}
diff --git a/security-admin/src/main/resources/conf.dist/ranger-admin-default-site.xml b/security-admin/src/main/resources/conf.dist/ranger-admin-default-site.xml
index 164b63d027a..b8e3ca6243c 100644
--- a/security-admin/src/main/resources/conf.dist/ranger-admin-default-site.xml
+++ b/security-admin/src/main/resources/conf.dist/ranger-admin-default-site.xml
@@ -257,58 +257,7 @@
#
-->
-
-
-
- ranger.unixauth.remote.login.enabled
- true
-
-
- ranger.unixauth.service.hostname
- localhost
-
-
- ranger.unixauth.service.port
- 5151
-
-
- ranger.unixauth.ssl.enabled
- true
-
-
- ranger.unixauth.debug
- false
-
-
- ranger.unixauth.server.cert.validation
- false
-
-
-
- ranger.unixauth.keystore
- keystore.jks
-
-
- ranger.unixauth.keystore.credential.alias
- unixAuthKeyStoreAlias
-
-
- ranger.unixauth.keystore.password
- password
-
-
- ranger.unixauth.truststore
- cacerts
-
-
- ranger.unixauth.truststore.credential.alias
- unixAuthTrustStoreAlias
-
-
- ranger.unixauth.truststore.password
- changeit
-
-
+
diff --git a/security-admin/src/main/webapp/META-INF/contextXML/unix_bean_settings.xml b/security-admin/src/main/webapp/META-INF/contextXML/unix_bean_settings.xml
index 1aab7ba5301..4fee750cbbd 100644
--- a/security-admin/src/main/webapp/META-INF/contextXML/unix_bean_settings.xml
+++ b/security-admin/src/main/webapp/META-INF/contextXML/unix_bean_settings.xml
@@ -28,7 +28,7 @@
+ value="org.apache.ranger.authentication.unix.jaas.PamLoginModule" />
diff --git a/unixauthclient/src/test/java/org/apache/ranger/authentication/unix/jaas/TestConsolePromptCallbackHandler.java b/security-admin/src/test/java/org/apache/ranger/authentication/unix/jaas/TestConsolePromptCallbackHandler.java
similarity index 100%
rename from unixauthclient/src/test/java/org/apache/ranger/authentication/unix/jaas/TestConsolePromptCallbackHandler.java
rename to security-admin/src/test/java/org/apache/ranger/authentication/unix/jaas/TestConsolePromptCallbackHandler.java
diff --git a/unixauthclient/src/test/java/org/apache/ranger/authentication/unix/jaas/TestPamLoginModule.java b/security-admin/src/test/java/org/apache/ranger/authentication/unix/jaas/TestPamLoginModule.java
similarity index 100%
rename from unixauthclient/src/test/java/org/apache/ranger/authentication/unix/jaas/TestPamLoginModule.java
rename to security-admin/src/test/java/org/apache/ranger/authentication/unix/jaas/TestPamLoginModule.java
diff --git a/unixauthclient/src/test/java/org/apache/ranger/authentication/unix/jaas/TestPamPrincipal.java b/security-admin/src/test/java/org/apache/ranger/authentication/unix/jaas/TestPamPrincipal.java
similarity index 100%
rename from unixauthclient/src/test/java/org/apache/ranger/authentication/unix/jaas/TestPamPrincipal.java
rename to security-admin/src/test/java/org/apache/ranger/authentication/unix/jaas/TestPamPrincipal.java
diff --git a/unixauthclient/src/test/java/org/apache/ranger/authentication/unix/jaas/TestUsernamePasswordCallbackHandler.java b/security-admin/src/test/java/org/apache/ranger/authentication/unix/jaas/TestUsernamePasswordCallbackHandler.java
similarity index 100%
rename from unixauthclient/src/test/java/org/apache/ranger/authentication/unix/jaas/TestUsernamePasswordCallbackHandler.java
rename to security-admin/src/test/java/org/apache/ranger/authentication/unix/jaas/TestUsernamePasswordCallbackHandler.java
diff --git a/security-admin/src/test/java/org/apache/ranger/security/handler/TestRangerAuthenticationProvider.java b/security-admin/src/test/java/org/apache/ranger/security/handler/TestRangerAuthenticationProvider.java
index 33145329f9f..a6f48500da4 100644
--- a/security-admin/src/test/java/org/apache/ranger/security/handler/TestRangerAuthenticationProvider.java
+++ b/security-admin/src/test/java/org/apache/ranger/security/handler/TestRangerAuthenticationProvider.java
@@ -181,13 +181,6 @@ public void getPamAuthentication_skipsWhenNoPassword() {
assertSame(input, result);
}
- @Test
- public void getUnixAuthentication_skipsWhenNoPassword() {
- UsernamePasswordAuthenticationToken input = new UsernamePasswordAuthenticationToken("u", "");
- Authentication result = provider.getUnixAuthentication(input);
- assertSame(input, result);
- }
-
@Test
public void getLdapAuthentication_skipsWhenNoPassword() throws Exception {
UsernamePasswordAuthenticationToken input = new UsernamePasswordAuthenticationToken("u", "");
diff --git a/ugsync/cert/.gitkeep b/ugsync/cert/.gitkeep
new file mode 100644
index 00000000000..ae1e83eeb3d
--- /dev/null
+++ b/ugsync/cert/.gitkeep
@@ -0,0 +1,14 @@
+# 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.
diff --git a/unixauthservice/conf.dist/logback.xml b/ugsync/conf.dist/logback.xml
similarity index 100%
rename from unixauthservice/conf.dist/logback.xml
rename to ugsync/conf.dist/logback.xml
diff --git a/unixauthservice/conf.dist/ranger-ugsync-default.xml b/ugsync/conf.dist/ranger-ugsync-default.xml
similarity index 65%
rename from unixauthservice/conf.dist/ranger-ugsync-default.xml
rename to ugsync/conf.dist/ranger-ugsync-default.xml
index 2e48da7e64c..879a0eaebe4 100644
--- a/unixauthservice/conf.dist/ranger-ugsync-default.xml
+++ b/ugsync/conf.dist/ranger-ugsync-default.xml
@@ -17,42 +17,6 @@
-
- ranger.usersync.port
- 5151
-
-
- ranger.usersync.ssl
- true
-
-
- ranger.usersync.https.ssl.enabled.protocols
- TLSv1.2
-
-
- ranger.usersync.unixauth.max.failed.attempts
- 5
-
-
- ranger.usersync.unixauth.attempt.window.ms
- 60000
-
-
- ranger.usersync.unixauth.lockout.duration.ms
- 30000
-
-
- ranger.usersync.unixauth.require.client.auth
- false
-
-
- ranger.usersync.unixauth.socket.timeout.ms
- 10000
-
-
- ranger.usersync.passwordvalidator.path
- ./native/credValidator.uexe
-
ranger.usersync.enabled
true
diff --git a/ugsync/pom.xml b/ugsync/pom.xml
index 1743187546b..e8561a49b53 100644
--- a/ugsync/pom.xml
+++ b/ugsync/pom.xml
@@ -326,6 +326,26 @@
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ copy-usersync-runtime-libs
+
+ copy-dependencies
+
+ package
+
+ ${project.build.directory}/lib
+ runtime
+ true
+ true
+ false
+
+
+
+
org.apache.maven.plugins
maven-surefire-plugin
diff --git a/unixauthservice/scripts/initd b/ugsync/scripts/initd
similarity index 100%
rename from unixauthservice/scripts/initd
rename to ugsync/scripts/initd
diff --git a/unixauthservice/scripts/install.properties b/ugsync/scripts/install.properties
similarity index 97%
rename from unixauthservice/scripts/install.properties
rename to ugsync/scripts/install.properties
index 7d7ed5554bd..0d9c2b59861 100644
--- a/unixauthservice/scripts/install.properties
+++ b/ugsync/scripts/install.properties
@@ -61,12 +61,11 @@ hadoop_conf=/etc/hadoop/conf
#
CRED_KEYSTORE_FILENAME=/etc/ranger/usersync/conf/rangerusersync.jceks
-# SSL Authentication
-AUTH_SSL_ENABLED=false
-AUTH_SSL_KEYSTORE_FILE=/etc/ranger/usersync/conf/cert/unixauthservice.jks
-AUTH_SSL_KEYSTORE_PASSWORD=UnIx529p
-AUTH_SSL_TRUSTSTORE_FILE=
-AUTH_SSL_TRUSTSTORE_PASSWORD=
+# Client TLS keystore for usersync-to-policymanager and LDAP connections
+USERSYNC_SSL_KEYSTORE_FILE=/etc/ranger/usersync/conf/cert/usersync.jks
+USERSYNC_SSL_KEYSTORE_PASSWORD=UnIx529p
+USERSYNC_SSL_TRUSTSTORE_FILE=
+USERSYNC_SSL_TRUSTSTORE_PASSWORD=
# ---------------------------------------------------------------
# The following properties are relevant only if SYNC_SOURCE = ldap
diff --git a/unixauthservice/scripts/ranger-usersync-services.sh b/ugsync/scripts/ranger-usersync-services.sh
similarity index 97%
rename from unixauthservice/scripts/ranger-usersync-services.sh
rename to ugsync/scripts/ranger-usersync-services.sh
index 5b1c2e4d423..c7badd3af85 100644
--- a/unixauthservice/scripts/ranger-usersync-services.sh
+++ b/ugsync/scripts/ranger-usersync-services.sh
@@ -99,6 +99,7 @@ if [ "${action}" == "START" ]; then
if [ -z "${logdir}" ]; then
logdir=${cdir}/logs
fi
+ mkdir -p "${logdir}"
if [ -z "${USERSYNC_CONF_DIR}" ]; then
USERSYNC_CONF_DIR=${cdir}/conf
fi
@@ -113,7 +114,7 @@ if [ "${action}" == "START" ]; then
fi
fi
SLEEP_TIME_AFTER_START=5
- nohup java -Dproc_rangerusersync -Djdk.tls.ephemeralDHKeySize=2048 -Dlogback.configurationFile=file:${USERSYNC_CONF_DIR}/logback.xml ${JAVA_OPTS} -Duser=${USER} -Dhostname=${HOSTNAME} -Dlogdir="${logdir}" -cp "${cp}" org.apache.ranger.authentication.UnixAuthenticationService -enableUnixAuth > ${logdir}/auth.log 2>&1 &
+ nohup java -Dproc_rangerusersync -Djdk.tls.ephemeralDHKeySize=2048 -Dlogback.configurationFile=file:${USERSYNC_CONF_DIR}/logback.xml ${JAVA_OPTS} -Duser=${USER} -Dhostname=${HOSTNAME} -Dlogdir="${logdir}" -cp "${cp}" org.apache.ranger.usergroupsync.UserSyncService > ${logdir}/usersync.log 2>&1 &
VALUE_OF_PID=$!
echo "Starting Apache Ranger Usersync Service"
sleep $SLEEP_TIME_AFTER_START
diff --git a/unixauthservice/scripts/set_globals.sh b/ugsync/scripts/set_globals.sh
similarity index 100%
rename from unixauthservice/scripts/set_globals.sh
rename to ugsync/scripts/set_globals.sh
diff --git a/unixauthservice/scripts/setup.py b/ugsync/scripts/setup.py
similarity index 94%
rename from unixauthservice/scripts/setup.py
rename to ugsync/scripts/setup.py
index e219b5e21c3..af0728e3250 100755
--- a/unixauthservice/scripts/setup.py
+++ b/ugsync/scripts/setup.py
@@ -43,7 +43,7 @@
confBaseDirName = 'conf'
confDistBaseDirName = 'conf.dist'
certBaseDirName = 'cert'
-defaultCertFileName = 'unixauthservice.jks'
+defaultCertFileName = 'usersync.jks'
outputFileName = 'ranger-ugsync-site.xml'
installPropFileName = 'install.properties'
@@ -63,14 +63,10 @@
installTemplateDirName = join(RANGER_USERSYNC_HOME, 'templates')
confDistDirName = join(RANGER_USERSYNC_HOME, confDistBaseDirName)
# ugsyncLogFolderName = join(logFolderName, 'usersync')
-nativeAuthFolderName = join(RANGER_USERSYNC_HOME, 'native')
localConfFolderName = join(RANGER_USERSYNC_HOME, confBaseDirName)
-nativeAuthProgramName = join(nativeAuthFolderName, 'credValidator.uexe')
-pamAuthProgramName = join(nativeAuthFolderName, 'pamCredValidator.uexe')
-
defaultKSPassword = 'UnIx529p'
-defaultDNAME = 'cn=unixauthservice,ou=authenticator,o=mycompany,c=US'
+defaultDNAME = 'cn=rangerusersync,ou=usersync,o=mycompany,c=US'
unixUserProp = 'unix_user'
unixGroupProp = 'unix_group'
@@ -567,26 +563,6 @@ def _safe_chown_chmod(path, uid, gid, mode):
for obj in files:
_safe_chown_chmod(join(root, obj), ownerId, groupId, 0o750)
- if isfile(nativeAuthProgramName):
- try:
- os.chown(nativeAuthProgramName, rootOwnerId, groupId)
- os.chmod(nativeAuthProgramName, 0o750)
- except PermissionError:
- print("WARNING: chmod(4550), chown(%s:%s) failed for Unix Authentication Program (%s) " % ("root", groupName, nativeAuthProgramName))
- else:
- print("WARNING: Unix Authentication Program (%s) is not available for setting chmod(4550), chown(%s:%s) " % (
- nativeAuthProgramName, "root", groupName))
-
- if isfile(pamAuthProgramName):
- try:
- os.chown(pamAuthProgramName, rootOwnerId, groupId)
- os.chmod(pamAuthProgramName, 0o750)
- except PermissionError:
- print("WARNING: chmod(0o750), chown(%s:%s) failed for Unix Authentication Program (%s) " % ("root", groupName, pamAuthProgramName))
- else:
- print("WARNING: Unix Authentication Program (%s) is not available for setting chmod(4550), chown(%s:%s) " % (
- pamAuthProgramName, "root", groupName))
-
write_env_files("logdir", logFolderName, ENV_LOGDIR_FILE);
write_env_files("RANGER_USERSYNC_HADOOP_CONF_DIR", hadoop_conf, ENV_HADOOP_CONF_FILE);
write_env_files("USERSYNC_PID_DIR_PATH", pid_dir_path, ENV_PID_FILE);
diff --git a/unixauthservice/scripts/setup.sh b/ugsync/scripts/setup.sh
similarity index 100%
rename from unixauthservice/scripts/setup.sh
rename to ugsync/scripts/setup.sh
diff --git a/unixauthservice/scripts/start.sh b/ugsync/scripts/start.sh
similarity index 100%
rename from unixauthservice/scripts/start.sh
rename to ugsync/scripts/start.sh
diff --git a/unixauthservice/scripts/stop.sh b/ugsync/scripts/stop.sh
similarity index 100%
rename from unixauthservice/scripts/stop.sh
rename to ugsync/scripts/stop.sh
diff --git a/unixauthservice/scripts/templates/installprop2xml.properties b/ugsync/scripts/templates/installprop2xml.properties
similarity index 92%
rename from unixauthservice/scripts/templates/installprop2xml.properties
rename to ugsync/scripts/templates/installprop2xml.properties
index 8b06e762c3f..5964a28268f 100644
--- a/unixauthservice/scripts/templates/installprop2xml.properties
+++ b/ugsync/scripts/templates/installprop2xml.properties
@@ -48,11 +48,10 @@ SYNC_PAGED_RESULTS_SIZE=ranger.usersync.pagedresultssize
SYNC_SOURCE = SYNC_SOURCE
unix_user = unix_user
unix_group = unix_group
-AUTH_SSL_KEYSTORE_FILE = ranger.usersync.keystore.file
-AUTH_SSL_KEYSTORE_PASSWORD = ranger.usersync.keystore.password
-AUTH_SSL_TRUSTSTORE_FILE = ranger.usersync.truststore.file
-AUTH_SSL_TRUSTSTORE_PASSWORD = ranger.usersync.truststore.password
-AUTH_SSL_ENABLED = ranger.usersync.ssl
+USERSYNC_SSL_KEYSTORE_FILE = ranger.usersync.keystore.file
+USERSYNC_SSL_KEYSTORE_PASSWORD = ranger.usersync.keystore.password
+USERSYNC_SSL_TRUSTSTORE_FILE = ranger.usersync.truststore.file
+USERSYNC_SSL_TRUSTSTORE_PASSWORD = ranger.usersync.truststore.password
SYNC_LDAP_REFERRAL = ranger.usersync.ldap.referral
usersync_principal= ranger.usersync.kerberos.principal
usersync_keytab= ranger.usersync.kerberos.keytab
diff --git a/unixauthservice/scripts/templates/ranger-ugsync-template.xml b/ugsync/scripts/templates/ranger-ugsync-template.xml
similarity index 96%
rename from unixauthservice/scripts/templates/ranger-ugsync-template.xml
rename to ugsync/scripts/templates/ranger-ugsync-template.xml
index 52f027a5c6b..de405920136 100644
--- a/unixauthservice/scripts/templates/ranger-ugsync-template.xml
+++ b/ugsync/scripts/templates/ranger-ugsync-template.xml
@@ -117,10 +117,6 @@
ranger.usersync.pagedresultssize
-
- ranger.usersync.passwordvalidator.path
-
-
ranger.usersync.policymanager.baseURL
@@ -133,10 +129,6 @@
ranger.usersync.policymanager.mockrun
-
- ranger.usersync.port
-
-
ranger.usersync.sink.impl.class
@@ -149,10 +141,6 @@
ranger.usersync.source.impl.class
-
- ranger.usersync.ssl
-
-
ranger.usersync.unix.minUserId
diff --git a/unixauthservice/scripts/update_property.py b/ugsync/scripts/update_property.py
similarity index 100%
rename from unixauthservice/scripts/update_property.py
rename to ugsync/scripts/update_property.py
diff --git a/unixauthservice/scripts/updatepolicymgrpassword.py b/ugsync/scripts/updatepolicymgrpassword.py
similarity index 100%
rename from unixauthservice/scripts/updatepolicymgrpassword.py
rename to ugsync/scripts/updatepolicymgrpassword.py
diff --git a/unixauthservice/scripts/updatepolicymgrpassword.sh b/ugsync/scripts/updatepolicymgrpassword.sh
similarity index 80%
rename from unixauthservice/scripts/updatepolicymgrpassword.sh
rename to ugsync/scripts/updatepolicymgrpassword.sh
index 82be91320aa..d4d6c70fe06 100644
--- a/unixauthservice/scripts/updatepolicymgrpassword.sh
+++ b/ugsync/scripts/updatepolicymgrpassword.sh
@@ -80,7 +80,7 @@ SYNC_LDAP_BIND_KEYSTOREPATH=`grep '^[ \t]*CRED_KEYSTORE_FILENAME[ \t]*=' ${cdir}
# END Grep configuration properties from install.properties
# Store POLICY_MGR user password in credential store
-SYNC_POLICY_MGR_ALIAS="policymgr.user.password"
+SYNC_POLICY_MGR_ALIAS="ranger.usersync.policymgr.password"
SYNC_POLICY_MGR_PASSWORD="rangerusersync"
SYNC_POLICY_MGR_USERNAME="rangerusersync"
count=0
@@ -122,26 +122,15 @@ then
$JAVA_HOME/bin/java -cp "lib/*" org.apache.ranger.credentialapi.buildks create "$SYNC_POLICY_MGR_ALIAS" -value "$SYNC_POLICY_MGR_PASSWORD" -provider jceks://file$SYNC_LDAP_BIND_KEYSTOREPATH
fi
-# Create $INSTALL_DIR/conf/unixauthservice.properties
+# Update ranger-ugsync-site.xml policymgr settings
-CFG_FILE="${cdir}/conf/unixauthservice.properties"
-NEW_CFG_FILE=${cdir}/conf/unixauthservice.properties.tmp
+CFG_FILE="${cdir}/conf/ranger-ugsync-site.xml"
-if [ -f ${CFG_FILE} ]
-then
- sed \
- -e "s|^\( *userSync.policyMgrUserName *=\).*|\1 ${SYNC_POLICY_MGR_USERNAME}|" \
- -e "s|^\( *userSync.policyMgrKeystore *=\).*|\1 ${SYNC_LDAP_BIND_KEYSTOREPATH}|" \
- -e "s|^\( *userSync.policyMgrAlias *=\).*|\1 ${SYNC_POLICY_MGR_ALIAS}|" \
- ${CFG_FILE} > ${NEW_CFG_FILE}
-
- echo "<${logdir}> ${CFG_FILE} > ${NEW_CFG_FILE}"
-else
- echo "ERROR: Required file, not found: ${CFG_FILE}, Aborting installation"
+if [ ! -f "${CFG_FILE}" ]; then
+ log "[E] Required file not found: ${CFG_FILE}, aborting"
exit 8
fi
-mv ${cdir}/conf/unixauthservice.properties ${cdir}/conf/unixauthservice.properties.${curDt}
-mv ${cdir}/conf/unixauthservice.properties.tmp ${cdir}/conf/unixauthservice.properties
-
-#END Create $INSTALL_DIR/conf/unixauthservice.properties
\ No newline at end of file
+python "${cdir}/update_property.py" "ranger.usersync.policymgr.username" "${SYNC_POLICY_MGR_USERNAME}" "${CFG_FILE}"
+python "${cdir}/update_property.py" "ranger.usersync.policymgr.keystore" "${SYNC_LDAP_BIND_KEYSTOREPATH}" "${CFG_FILE}"
+python "${cdir}/update_property.py" "ranger.usersync.policymgr.alias" "${SYNC_POLICY_MGR_ALIAS}" "${CFG_FILE}"
\ No newline at end of file
diff --git a/ugsync/src/main/java/org/apache/ranger/unixusersync/process/FileSourceUserGroupBuilder.java b/ugsync/src/main/java/org/apache/ranger/unixusersync/process/FileSourceUserGroupBuilder.java
index 381af1212e4..ed37268222e 100644
--- a/ugsync/src/main/java/org/apache/ranger/unixusersync/process/FileSourceUserGroupBuilder.java
+++ b/ugsync/src/main/java/org/apache/ranger/unixusersync/process/FileSourceUserGroupBuilder.java
@@ -231,7 +231,7 @@ public void buildUserGroupInfo() throws Throwable {
public void buildUserGroupList() throws Throwable {
if (userGroupFilename == null) {
- throw new Exception("User Group Source File is not Configured. Please maintain in unixauthservice.properties or pass it as command line argument for org.apache.ranger.unixusersync.process.FileSourceUserGroupBuilder");
+ throw new Exception("User Group Source File is not Configured. Please maintain in ranger-ugsync-site.xml or pass it as command line argument for org.apache.ranger.unixusersync.process.FileSourceUserGroupBuilder");
}
File f = new File(userGroupFilename);
diff --git a/ugsync/src/main/java/org/apache/ranger/usergroupsync/UserSyncService.java b/ugsync/src/main/java/org/apache/ranger/usergroupsync/UserSyncService.java
new file mode 100644
index 00000000000..381e9325543
--- /dev/null
+++ b/ugsync/src/main/java/org/apache/ranger/usergroupsync/UserSyncService.java
@@ -0,0 +1,83 @@
+/*
+ * 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.ranger.usergroupsync;
+
+import org.apache.ranger.unixusersync.config.UserGroupSyncConfig;
+import org.apache.ranger.unixusersync.ha.UserSyncHAInitializerImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * JVM entry point for Ranger UserSync (user/group sync, HA, metrics).
+ */
+public class UserSyncService {
+ private static final Logger LOG = LoggerFactory.getLogger(UserSyncService.class);
+
+ private UserSyncHAInitializerImpl userSyncHAInitializerImpl;
+ private Thread userGroupSyncThread;
+
+ public static void main(String[] args) {
+ UserSyncService service = new UserSyncService();
+ service.userSyncHAInitializerImpl = UserSyncHAInitializerImpl.getInstance(UserGroupSyncConfig.getInstance().getUserGroupConfig());
+ service.run();
+ }
+
+ public void run() {
+ try {
+ LOG.info("Starting User Sync Service!");
+ startUserGroupSyncProcess();
+ if (userGroupSyncThread != null) {
+ userGroupSyncThread.join();
+ }
+ } catch (Throwable t) {
+ LOG.error("ERROR: User Sync Service failed", t);
+ } finally {
+ LOG.info("User Sync Service - STOPPED");
+ if (userSyncHAInitializerImpl != null) {
+ LOG.info("Stopping curator leader latch service as main thread is closing");
+ userSyncHAInitializerImpl.stop();
+ }
+ }
+ }
+
+ private void startUserGroupSyncProcess() {
+ LOG.info("Start : startUserGroupSyncProcess");
+ UserGroupSync syncProc = new UserGroupSync();
+ Thread newSyncProcThread = new Thread(syncProc);
+ newSyncProcThread.setName("UnixUserSyncThread");
+ newSyncProcThread.setDaemon(false);
+ newSyncProcThread.start();
+ userGroupSyncThread = newSyncProcThread;
+ LOG.info("UnixUserSyncThread started");
+ LOG.info("creating UserSyncMetricsProducer thread with default metrics location : {}", System.getProperty("logdir"));
+
+ boolean isUserSyncMetricsEnabled = UserGroupSyncConfig.getInstance().isUserSyncMetricsEnabled();
+ if (isUserSyncMetricsEnabled) {
+ UserSyncMetricsProducer userSyncMetricsProducer = new UserSyncMetricsProducer();
+ Thread userSyncMetricsProducerThread = new Thread(userSyncMetricsProducer);
+ userSyncMetricsProducerThread.setName("UserSyncMetricsProducerThread");
+ userSyncMetricsProducerThread.setDaemon(false);
+ userSyncMetricsProducerThread.start();
+ LOG.info("UserSyncMetricsProducer started");
+ } else {
+ LOG.info(" Ranger userSync metrics is not enabled");
+ }
+ }
+}
diff --git a/ugsync/src/test/java/org/apache/ranger/usergroupsync/TestUserSyncService.java b/ugsync/src/test/java/org/apache/ranger/usergroupsync/TestUserSyncService.java
new file mode 100644
index 00000000000..718c01b9d01
--- /dev/null
+++ b/ugsync/src/test/java/org/apache/ranger/usergroupsync/TestUserSyncService.java
@@ -0,0 +1,108 @@
+/*
+ * 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.ranger.usergroupsync;
+
+import org.apache.ranger.unixusersync.config.UserGroupSyncConfig;
+import org.apache.ranger.unixusersync.ha.UserSyncHAInitializerImpl;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.MockedConstruction;
+import org.mockito.MockedStatic;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class TestUserSyncService {
+ @Test
+ public void userSyncServiceClassIsLoadable() {
+ assertNotNull(UserSyncService.class);
+ }
+
+ @Test
+ public void run_stopsHaWhenSyncThreadEnds() throws Exception {
+ UserSyncService svc = new UserSyncService();
+ UserSyncHAInitializerImpl ha = mock(UserSyncHAInitializerImpl.class);
+
+ setField(svc, "userSyncHAInitializerImpl", ha);
+
+ try (MockedStatic ugscStatic = mockStatic(UserGroupSyncConfig.class)) {
+ UserGroupSyncConfig ugsc = mock(UserGroupSyncConfig.class);
+ ugscStatic.when(UserGroupSyncConfig::getInstance).thenReturn(ugsc);
+ when(ugsc.isUserSyncMetricsEnabled()).thenReturn(false);
+
+ try (MockedConstruction syncConstruction = mockConstruction(UserGroupSync.class)) {
+ svc.run();
+
+ verify(ha).stop();
+ assertEquals(1, syncConstruction.constructed().size());
+ }
+ }
+ }
+
+ @Test
+ public void startUserGroupSyncProcess_metricsEnabledAndDisabled() throws Exception {
+ UserSyncService svc = new UserSyncService();
+ Method method = UserSyncService.class.getDeclaredMethod("startUserGroupSyncProcess");
+ method.setAccessible(true);
+
+ try (MockedStatic ugscStatic = mockStatic(UserGroupSyncConfig.class)) {
+ UserGroupSyncConfig ugsc = mock(UserGroupSyncConfig.class);
+ ugscStatic.when(UserGroupSyncConfig::getInstance).thenReturn(ugsc);
+
+ when(ugsc.isUserSyncMetricsEnabled()).thenReturn(true);
+ try (MockedConstruction threadConstruction = mockConstruction(Thread.class, (mockThread, ctx) -> {
+ doNothing().when(mockThread).setName(any());
+ doNothing().when(mockThread).setDaemon(false);
+ doNothing().when(mockThread).start();
+ })) {
+ method.invoke(svc);
+ assertEquals(2, threadConstruction.constructed().size());
+ }
+
+ when(ugsc.isUserSyncMetricsEnabled()).thenReturn(false);
+ try (MockedConstruction threadConstruction = mockConstruction(Thread.class, (mockThread, ctx) -> {
+ doNothing().when(mockThread).setName(any());
+ doNothing().when(mockThread).setDaemon(false);
+ doNothing().when(mockThread).start();
+ })) {
+ method.invoke(svc);
+ assertEquals(1, threadConstruction.constructed().size());
+ }
+ }
+ }
+
+ private static void setField(Object target, String fieldName, Object value) throws Exception {
+ Field field = UserSyncService.class.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ field.set(target, value);
+ }
+}
diff --git a/ugsync/src/test/resources/ranger-ugsync-site.xml b/ugsync/src/test/resources/ranger-ugsync-site.xml
index 5ed504b8694..52b80c78799 100644
--- a/ugsync/src/test/resources/ranger-ugsync-site.xml
+++ b/ugsync/src/test/resources/ranger-ugsync-site.xml
@@ -70,7 +70,7 @@
ranger.usersync.keystore.file
- /usr/hdp/current/ranger-usersync/conf/unixauthservice.jks
+ /usr/hdp/current/ranger-usersync/conf/usersync.jks
diff --git a/unixauthclient/.gitignore b/unixauthclient/.gitignore
deleted file mode 100644
index 8604ba3c425..00000000000
--- a/unixauthclient/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-/target/
-/bin/
-/target
-.settings/
diff --git a/unixauthclient/pom.xml b/unixauthclient/pom.xml
deleted file mode 100644
index d6e11536645..00000000000
--- a/unixauthclient/pom.xml
+++ /dev/null
@@ -1,107 +0,0 @@
-
-
-
- 4.0.0
-
- org.apache.ranger
- ranger
- 3.0.0-SNAPSHOT
- ..
-
- unixauthclient
- jar
- Unix Authentication Client
- Unix authentication client
-
-
- com.google.code.gson
- gson
-
-
- commons-cli
- commons-cli
- ${commons.cli.version}
-
-
- commons-collections
- commons-collections
- ${commons.collections.version}
-
-
- org.apache.commons
- commons-configuration2
- ${commons.configuration.version}
-
-
- org.apache.commons
- commons-text
-
-
-
-
- org.apache.commons
- commons-lang3
- ${commons.lang3.version}
-
-
- org.apache.commons
- commons-text
- ${commons.text.version}
-
-
- org.apache.hadoop
- hadoop-auth
- ${hadoop.version}
-
-
- log4j
- *
-
-
- net.minidev
- json-smart
-
-
- org.apache.zookeeper
- zookeeper
-
-
- org.slf4j
- *
-
-
-
-
- org.kohsuke
- libpam4j
- ${libpam4j.version}
-
-
- org.bouncycastle
- bcpkix-jdk18on
- ${org.bouncycastle.bcpkix-jdk18on}
- test
-
-
- org.mockito
- mockito-junit-jupiter
- ${mockito.version}
- test
-
-
-
diff --git a/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/RemoteUnixLoginModule.java b/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/RemoteUnixLoginModule.java
deleted file mode 100644
index 794c5644bc9..00000000000
--- a/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/RemoteUnixLoginModule.java
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
- * 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.ranger.authentication.unix.jaas;
-
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocketFactory;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509TrustManager;
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-import javax.security.auth.login.LoginException;
-import javax.security.auth.spi.LoginModule;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.net.Socket;
-import java.security.KeyStore;
-import java.security.SecureRandom;
-import java.security.cert.X509Certificate;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.Properties;
-
-public class RemoteUnixLoginModule implements LoginModule {
- private static final String DEBUG_PARAM = "ranger.unixauth.debug";
- private static final String REMOTE_LOGIN_HOST_PARAM = "ranger.unixauth.service.hostname";
- private static final String REMOTE_LOGIN_AUTH_SERVICE_PORT_PARAM = "ranger.unixauth.service.port";
- private static final String SSL_KEYSTORE_PATH_PARAM = "ranger.unixauth.keystore";
- private static final String SSL_KEYSTORE_PATH_PASSWORD_PARAM = "ranger.unixauth.keystore.password";
- private static final String SSL_TRUSTSTORE_PATH_PARAM = "ranger.unixauth.truststore";
- private static final String SSL_TRUSTSTORE_PATH_PASSWORD_PARAM = "ranger.unixauth.truststore.password";
- private static final String SSL_ENABLED_PARAM = "ranger.unixauth.ssl.enabled";
- private static final String SERVER_CERT_VALIDATION_PARAM = "ranger.unixauth.server.cert.validation";
- private static final String JAAS_ENABLED_PARAM = "ranger.unixauth.remote.login.enabled";
- private static final String SSL_ALGORITHM = "TLS";
-
- private String userName;
- private String remoteHostName;
- private String loginGroups;
- private String keyStorePath;
- private String keyStorePathPassword;
- private String trustStorePath;
- private String trustStorePathPassword;
- private char[] password;
- private int remoteHostAuthServicePort;
- private Subject subject;
- private CallbackHandler callbackHandler;
- private boolean ssEnabled;
- private boolean loginSuccessful;
- private boolean debug = true;
- private boolean serverCertValidation = true;
- private boolean remoteLoginEnabled = true;
-
- public RemoteUnixLoginModule() {
- log("Created RemoteUnixLoginModule");
- }
-
- @Override
- public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
- this.subject = subject;
- this.callbackHandler = callbackHandler;
-
- log("RemoteUnixLoginModule::initialize() has been called with callbackhandler: " + this.callbackHandler);
-
- if (this.callbackHandler == null) {
- this.callbackHandler = new ConsolePromptCallbackHandler();
- }
-
- Properties config = new Properties();
- config.putAll(options);
- initParams(config);
- }
-
- @Override
- public boolean login() throws LoginException {
- if (remoteLoginEnabled && this.callbackHandler != null) {
- NameCallback nameCallback = new NameCallback("UserName:");
- PasswordCallback passwordCallback = new PasswordCallback("Password:", false);
- try {
- callbackHandler.handle(new Callback[] {nameCallback, passwordCallback});
- } catch (IOException | UnsupportedCallbackException e) {
- throw new LoginException("Unable to get username/password due to exception: " + e);
- }
-
- userName = nameCallback.getName();
- String modifiedUserName = userName;
-
- if (userName != null) {
- int atStartsAt = userName.indexOf("@");
- if (atStartsAt > -1) {
- modifiedUserName = userName.substring(0, atStartsAt);
- }
- }
-
- password = passwordCallback.getPassword();
-
- log("userName:" + userName);
- log("modified UserName:" + modifiedUserName);
-
- char[] modifiedPasschar;
- if (password != null) {
- modifiedPasschar = Arrays.copyOf(password, password.length);
- } else {
- modifiedPasschar = new char[0];
- }
-
- doLogin(modifiedUserName, modifiedPasschar);
-
- Arrays.fill(password, ' ');
- Arrays.fill(modifiedPasschar, ' ');
- loginSuccessful = true;
- }
-
- return loginSuccessful;
- }
-
- @Override
- public boolean commit() throws LoginException {
- log("RemoteUnixLoginModule::commit() has been called. -> isLoginSuccess [" + loginSuccessful + "]");
- if (loginSuccessful) {
- if (subject != null) {
- subject.getPrincipals().add(new UnixUserPrincipal(userName.trim()));
- if (loginGroups != null) {
- loginGroups = loginGroups.trim();
- for (String group : loginGroups.split(",")) {
- subject.getPrincipals().add(new UnixGroupPrincipal(group.trim()));
- }
- }
- }
- } else {
- if (subject != null) {
- subject.getPrincipals().clear();
- }
- }
- return loginSuccessful;
- }
-
- @Override
- public boolean abort() throws LoginException {
- log("RemoteUnixLoginModule::abort() has been called.");
- loginSuccessful = false;
- return true;
- }
-
- @Override
- public boolean logout() throws LoginException {
- if (subject != null) {
- subject.getPrincipals().clear();
- }
- return true;
- }
-
- public void initParams(Properties options) {
- String val = (String) options.get(JAAS_ENABLED_PARAM);
-
- if (val != null) {
- remoteLoginEnabled = val.trim().equalsIgnoreCase("true");
- if (!remoteLoginEnabled) {
- log("Skipping RemoteLogin - [" + JAAS_ENABLED_PARAM + "] => [" + val + "]");
- return;
- }
- } else {
- remoteLoginEnabled = true;
- }
-
- val = (String) options.get(DEBUG_PARAM);
- debug = val != null && (!val.equalsIgnoreCase("false"));
-
- remoteHostName = (String) options.get(REMOTE_LOGIN_HOST_PARAM);
- log("RemoteHostName:" + remoteHostName);
-
- val = (String) options.get(REMOTE_LOGIN_AUTH_SERVICE_PORT_PARAM);
- if (val != null) {
- remoteHostAuthServicePort = Integer.parseInt(val.trim());
- }
- log("remoteHostAuthServicePort:" + remoteHostAuthServicePort);
-
- val = (String) options.get(SSL_ENABLED_PARAM);
- ssEnabled = (val != null) && val.trim().equalsIgnoreCase("true");
- log("ssEnabled: " + ssEnabled);
-
- if (ssEnabled) {
- trustStorePath = (String) options.get(SSL_TRUSTSTORE_PATH_PARAM);
- log("trustStorePath: " + trustStorePath);
-
- if (trustStorePath != null) {
- trustStorePathPassword = (String) options.get(SSL_TRUSTSTORE_PATH_PASSWORD_PARAM);
- if (trustStorePathPassword == null) {
- trustStorePathPassword = "";
- }
- log("trustStorePathPassword:*****");
- }
-
- keyStorePath = (String) options.get(SSL_KEYSTORE_PATH_PARAM);
- log("keyStorePath:" + keyStorePath);
- if (keyStorePath != null) {
- keyStorePathPassword = (String) options.get(SSL_KEYSTORE_PATH_PASSWORD_PARAM);
- if (keyStorePathPassword == null) {
- keyStorePathPassword = "";
- }
- log("keyStorePathPassword:*****");
- }
-
- String certValidationFlag = (String) options.get(SERVER_CERT_VALIDATION_PARAM);
- serverCertValidation = (!(certValidationFlag != null && ("false".equalsIgnoreCase(certValidationFlag.trim()))));
- log("Server Cert Validation : " + serverCertValidation);
- }
- }
-
- public void doLogin(String aUserName, char[] modifiedPasschar) throws LoginException {
- /* Possible values
- * null
- * OK: group1, group2, group3
- * FAILED: Invalid Password
- */
- String ret = getLoginReplyFromAuthService(aUserName, modifiedPasschar);
-
- if (ret == null) {
- throw new LoginException("FAILED: unable to authenticate to AuthenticationService: " + remoteHostName + ":" + remoteHostAuthServicePort);
- } else if (ret.startsWith("OK:")) {
- loginSuccessful = true;
- if (ret.length() > 3) {
- this.loginGroups = ret.substring(3);
- }
- } else if (ret.startsWith("FAILED:")) {
- loginSuccessful = false;
- throw new LoginException("FAILED: unable to authenticate to AuthenticationService: " + remoteHostName + ":" + remoteHostAuthServicePort);
- } else {
- throw new LoginException("FAILED: unable to authenticate to AuthenticationService: " + remoteHostName + ":" + remoteHostAuthServicePort + ", msg:" + ret);
- }
- }
-
- private String getLoginReplyFromAuthService(String aUserName, char[] modifiedPasschar) throws LoginException {
- String ret = null;
- Socket sslsocket = null;
-
- char[] prefix = ("LOGIN:" + aUserName + " ").toCharArray();
- char[] tail = "\n".toCharArray();
- char[] loginData = new char[prefix.length + modifiedPasschar.length + tail.length];
- System.arraycopy(prefix, 0, loginData, 0, prefix.length);
- System.arraycopy(modifiedPasschar, 0, loginData, prefix.length, modifiedPasschar.length);
- System.arraycopy(tail, 0, loginData, prefix.length + modifiedPasschar.length, tail.length);
- try {
- try {
- if (ssEnabled) {
- SSLContext context = SSLContext.getInstance(SSL_ALGORITHM);
-
- KeyManager[] km = null;
-
- if (keyStorePath != null) {
- KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
-
- try (InputStream in = getFileInputStream(keyStorePath)) {
- ks.load(in, keyStorePathPassword.toCharArray());
- }
-
- KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
- kmf.init(ks, keyStorePathPassword.toCharArray());
- km = kmf.getKeyManagers();
- }
-
- TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
-
- TrustManager[] tm = null;
-
- if (serverCertValidation) {
- KeyStore trustStoreKeyStore;
-
- if (trustStorePath != null) {
- trustStoreKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
-
- try (InputStream in = getFileInputStream(trustStorePath)) {
- trustStoreKeyStore.load(in, trustStorePathPassword.toCharArray());
-
- trustManagerFactory.init(trustStoreKeyStore);
-
- tm = trustManagerFactory.getTrustManagers();
- }
- }
- } else {
- TrustManager ignoreValidationTM = new X509TrustManager() {
- public void checkClientTrusted(X509Certificate[] chain, String authType) {
- // Ignore Server Certificate Validation
- }
-
- public void checkServerTrusted(X509Certificate[] chain, String authType) {
- // Ignore Server Certificate Validation
- }
-
- public X509Certificate[] getAcceptedIssuers() {
- return new X509Certificate[0];
- }
- };
-
- tm = new TrustManager[] {ignoreValidationTM};
- }
-
- SecureRandom random = new SecureRandom();
-
- context.init(km, tm, random);
-
- SSLSocketFactory sf = context.getSocketFactory();
-
- sslsocket = sf.createSocket(remoteHostName, remoteHostAuthServicePort);
- } else {
- sslsocket = new Socket(remoteHostName, remoteHostAuthServicePort);
- }
-
- OutputStreamWriter writer = new OutputStreamWriter(sslsocket.getOutputStream());
-
- writer.write(loginData);
- writer.flush();
-
- BufferedReader reader = new BufferedReader(new InputStreamReader(sslsocket.getInputStream()));
-
- ret = reader.readLine();
-
- reader.close();
- writer.close();
- } finally {
- if (sslsocket != null) {
- sslsocket.close();
- }
- }
- } catch (Throwable t) {
- throw new LoginException("FAILED: unable to authenticate to AuthenticationService: " + remoteHostName + ":" + remoteHostAuthServicePort + ", Exception: [" + t + "]");
- } finally {
- log("Login of user String: {" + aUserName + "}, return from AuthServer: {" + ret + "}");
- Arrays.fill(loginData, ' ');
- Arrays.fill(modifiedPasschar, ' ');
- }
-
- return ret;
- }
-
- private InputStream getFileInputStream(String path) throws FileNotFoundException {
- InputStream ret;
-
- File f = new File(path);
-
- if (f.exists()) {
- ret = new FileInputStream(f);
- } else {
- ret = getClass().getResourceAsStream(path);
-
- if (ret == null) {
- if (!path.startsWith("/")) {
- ret = getClass().getResourceAsStream("/" + path);
- }
- }
-
- if (ret == null) {
- ret = ClassLoader.getSystemClassLoader().getResourceAsStream(path);
- if (ret == null) {
- if (!path.startsWith("/")) {
- ret = ClassLoader.getSystemResourceAsStream("/" + path);
- }
- }
- }
- }
-
- return ret;
- }
-
- private void log(String msg) {
- if (debug) {
- System.err.println("RemoteUnixLoginModule: " + msg);
- }
- }
-}
diff --git a/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/UnixUserPrincipal.java b/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/UnixUserPrincipal.java
deleted file mode 100644
index d280e689b6e..00000000000
--- a/unixauthclient/src/main/java/org/apache/ranger/authentication/unix/jaas/UnixUserPrincipal.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.ranger.authentication.unix.jaas;
-
-import java.io.Serializable;
-import java.security.Principal;
-
-public class UnixUserPrincipal implements Principal, Serializable {
- private static final long serialVersionUID = -3568658536591178268L;
-
- private final String userName;
-
- public UnixUserPrincipal(String userName) {
- this.userName = userName;
- }
-
- @Override
- public String getName() {
- return userName;
- }
-}
diff --git a/unixauthclient/src/test/com/xasecure/test/authentication/UnixAuthenticationTester.java b/unixauthclient/src/test/com/xasecure/test/authentication/UnixAuthenticationTester.java
deleted file mode 100644
index d80dbc6ab90..00000000000
--- a/unixauthclient/src/test/com/xasecure/test/authentication/UnixAuthenticationTester.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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 com.xasecure.test.authentication;
-
-import javax.security.auth.login.LoginContext;
-
-public class UnixAuthenticationTester {
-
- public static void main(String[] args) throws Throwable {
- new UnixAuthenticationTester().run();
- }
-
- public void run() throws Throwable {
- LoginContext loginContext = new LoginContext("PolicyManager");
- System.err.println("After login ...");
- loginContext.login();
- System.err.println("Subject:" + loginContext.getSubject());
- loginContext.logout();
- }
-}
diff --git a/unixauthclient/src/test/java/org/apache/ranger/authentication/unix/jaas/TestRemoteUnixLoginModule.java b/unixauthclient/src/test/java/org/apache/ranger/authentication/unix/jaas/TestRemoteUnixLoginModule.java
deleted file mode 100644
index 9f97f057301..00000000000
--- a/unixauthclient/src/test/java/org/apache/ranger/authentication/unix/jaas/TestRemoteUnixLoginModule.java
+++ /dev/null
@@ -1,408 +0,0 @@
-/*
- * 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.ranger.authentication.unix.jaas;
-
-import org.bouncycastle.asn1.x500.X500Name;
-import org.bouncycastle.cert.X509CertificateHolder;
-import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
-import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
-import org.bouncycastle.operator.ContentSigner;
-import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
-import org.junit.jupiter.api.MethodOrderer;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestMethodOrder;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLServerSocket;
-import javax.net.ssl.SSLServerSocketFactory;
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-import javax.security.auth.login.LoginException;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.math.BigInteger;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.KeyStore;
-import java.security.Principal;
-import java.security.SecureRandom;
-import java.security.cert.Certificate;
-import java.security.cert.X509Certificate;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-/**
- * @generated by Cursor
- * @description : Unit Test cases for RemoteUnixLoginModule
- */
-
-@ExtendWith(MockitoExtension.class)
-@TestMethodOrder(MethodOrderer.MethodName.class)
-public class TestRemoteUnixLoginModule {
- @Test
- public void test01_login_success_commit_addsPrincipals_and_logout_clears() throws Exception {
- try (OneShotAuthServer server = new OneShotAuthServer("OK: g1, g2")) {
- RemoteUnixLoginModule m = new RemoteUnixLoginModule();
- Subject subject = new Subject();
- Map opts = baseOptions("127.0.0.1", server.getPort());
- m.initialize(subject, fixedCredentials("alice", "secret"), new HashMap<>(), opts);
-
- assertTrue(m.login());
- assertTrue(m.commit());
-
- Set principals = subject.getPrincipals();
- assertTrue(principals.stream().anyMatch(p -> p instanceof UnixUserPrincipal && p.getName().equals("alice")));
- assertTrue(principals.stream().anyMatch(p -> p instanceof UnixGroupPrincipal && p.getName().equals("g1")));
- assertTrue(principals.stream().anyMatch(p -> p instanceof UnixGroupPrincipal && p.getName().equals("g2")));
-
- assertTrue(m.logout());
- assertTrue(subject.getPrincipals().isEmpty());
- }
- }
-
- @Test
- public void test02_login_failed_throws_LoginException() throws Exception {
- try (OneShotAuthServer server = new OneShotAuthServer("FAILED: Invalid Password")) {
- RemoteUnixLoginModule m = new RemoteUnixLoginModule();
- Subject subject = new Subject();
- Map opts = baseOptions("127.0.0.1", server.getPort());
- m.initialize(subject, fixedCredentials("alice", "secret"), new HashMap<>(), opts);
- assertThrows(LoginException.class, m::login);
- assertFalse(m.commit());
- }
- }
-
- @Test
- public void test03_login_nullReply_throws_LoginException() throws Exception {
- try (OneShotAuthServer server = new OneShotAuthServer(null)) {
- RemoteUnixLoginModule m = new RemoteUnixLoginModule();
- Subject subject = new Subject();
- Map opts = baseOptions("127.0.0.1", server.getPort());
- m.initialize(subject, fixedCredentials("bob", "x"), new HashMap<>(), opts);
- assertThrows(LoginException.class, m::login);
- }
- }
-
- @Test
- public void test04_login_disabled_returnsFalse_and_commitClearsPrincipals() throws Exception {
- RemoteUnixLoginModule m = new RemoteUnixLoginModule();
- Subject subject = new Subject();
- Map opts = baseOptions("localhost", 65535);
- opts.put("ranger.unixauth.remote.login.enabled", "false");
- m.initialize(subject, fixedCredentials("tom", "y"), new HashMap<>(), opts);
-
- assertFalse(m.login());
- assertFalse(m.commit());
- assertTrue(subject.getPrincipals().isEmpty());
- }
-
- @Test
- public void test05_ssl_disabledValidation_connectFailure_throwsLoginException() {
- RemoteUnixLoginModule m = new RemoteUnixLoginModule();
- Subject subject = new Subject();
- Map opts = baseOptions("127.0.0.1", 65000);
- opts.put("ranger.unixauth.ssl.enabled", "true");
- opts.put("ranger.unixauth.server.cert.validation", "false");
- m.initialize(subject, fixedCredentials("alice", "secret"), new HashMap<>(), opts);
- assertThrows(LoginException.class, m::login);
- }
-
- @Test
- public void test06_ssl_withTruststore_loadFails_throwsLoginException() {
- RemoteUnixLoginModule m = new RemoteUnixLoginModule();
- Subject subject = new Subject();
- Map opts = baseOptions("127.0.0.1", 65001);
- opts.put("ranger.unixauth.ssl.enabled", "true");
- opts.put("ranger.unixauth.server.cert.validation", "true");
- opts.put("ranger.unixauth.truststore", "nonexistent.jks");
- opts.put("ranger.unixauth.truststore.password", "");
- m.initialize(subject, fixedCredentials("bob", "pw"), new HashMap<>(), opts);
- assertThrows(LoginException.class, m::login);
- }
-
- @Test
- public void test07_ssl_withKeystore_loadFails_throwsLoginException() {
- RemoteUnixLoginModule m = new RemoteUnixLoginModule();
- Subject subject = new Subject();
- Map opts = baseOptions("127.0.0.1", 65002);
- opts.put("ranger.unixauth.ssl.enabled", "true");
- opts.put("ranger.unixauth.keystore", "nonexistent-keystore.jks");
- opts.put("ranger.unixauth.keystore.password", "");
- // disable server cert validation to avoid needing truststore
- opts.put("ranger.unixauth.server.cert.validation", "false");
- m.initialize(subject, fixedCredentials("carol", "pw"), new HashMap<>(), opts);
- assertThrows(LoginException.class, m::login);
- }
-
- @Test
- public void test08_abort_resetsLoginState_and_commitReturnsFalse() throws Exception {
- try (OneShotAuthServer server = new OneShotAuthServer("OK:")) {
- RemoteUnixLoginModule m = new RemoteUnixLoginModule();
- Subject subject = new Subject();
- Map opts = baseOptions("127.0.0.1", server.getPort());
- m.initialize(subject, fixedCredentials("alice", "secret"), new HashMap<>(), opts);
-
- assertTrue(m.login());
- assertTrue(m.abort());
-
- // After abort(), loginSuccessful should be false; commit should return false and clear principals
- subject.getPrincipals().add(new UnixUserPrincipal("temp"));
- assertFalse(m.commit());
- assertTrue(subject.getPrincipals().isEmpty());
- }
- }
-
- @Test
- public void test09_ssl_ignoreValidation_successfulHandshake_executesTrustManager_andLogin() throws Exception {
- try (SslOneShotAuthServer server = new SslOneShotAuthServer("OK: gA, gB")) {
- RemoteUnixLoginModule m = new RemoteUnixLoginModule();
- Subject subject = new Subject();
- Map opts = baseOptions("127.0.0.1", server.getPort());
- opts.put("ranger.unixauth.ssl.enabled", "true");
- opts.put("ranger.unixauth.server.cert.validation", "false");
- m.initialize(subject, fixedCredentials("eve", "pw"), new HashMap<>(), opts);
-
- assertTrue(m.login());
- assertTrue(m.commit());
- assertTrue(subject.getPrincipals().stream().anyMatch(p -> p instanceof UnixUserPrincipal && p.getName().equals("eve")));
- assertTrue(subject.getPrincipals().stream().anyMatch(p -> p instanceof UnixGroupPrincipal && p.getName().equals("gA")));
- assertTrue(m.logout());
- }
- }
-
- @Test
- public void test10_login_callbackHandlerThrowsIOException_isWrappedAsLoginException() {
- RemoteUnixLoginModule m = new RemoteUnixLoginModule();
- Subject subject = new Subject();
- Map opts = baseOptions("127.0.0.1", 65010);
- CallbackHandler cb = new CallbackHandler() {
- @Override
- public void handle(Callback[] callbacks) throws IOException {
- throw new IOException("boom");
- }
- };
- m.initialize(subject, cb, new HashMap<>(), opts);
- assertThrows(LoginException.class, m::login);
- }
-
- @Test
- public void test11_login_callbackHandlerThrowsUnsupported_isWrappedAsLoginException() {
- RemoteUnixLoginModule m = new RemoteUnixLoginModule();
- Subject subject = new Subject();
- Map opts = baseOptions("127.0.0.1", 65011);
- CallbackHandler cb = new CallbackHandler() {
- @Override
- public void handle(Callback[] callbacks) throws UnsupportedCallbackException {
- throw new UnsupportedCallbackException(new NameCallback("user"));
- }
- };
- m.initialize(subject, cb, new HashMap<>(), opts);
- assertThrows(LoginException.class, m::login);
- }
-
- private static Map baseOptions(String host, int port) {
- Map opts = new HashMap<>();
- opts.put("ranger.unixauth.remote.login.enabled", "true");
- opts.put("ranger.unixauth.service.hostname", host);
- opts.put("ranger.unixauth.service.port", String.valueOf(port));
- opts.put("ranger.unixauth.ssl.enabled", "false");
- opts.put("ranger.unixauth.debug", "false");
- return opts;
- }
-
- private static CallbackHandler fixedCredentials(String user, String pass) {
- return new CallbackHandler() {
- @Override
- public void handle(Callback[] callbacks) throws IOException {
- for (Callback cb : callbacks) {
- if (cb instanceof NameCallback) {
- ((NameCallback) cb).setName(user);
- } else if (cb instanceof PasswordCallback) {
- ((PasswordCallback) cb).setPassword(pass.toCharArray());
- }
- }
- }
- };
- }
-
- private static class OneShotAuthServer implements AutoCloseable {
- private final ServerSocket server;
- private final Thread thread;
- private final CountDownLatch ready = new CountDownLatch(1);
- private final String reply;
-
- OneShotAuthServer(String reply) throws IOException {
- this.server = new ServerSocket(0);
- this.reply = reply;
- this.thread = new Thread(() -> {
- ready.countDown();
- try (Socket s = server.accept()) {
- BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
- in.readLine();
- try (OutputStreamWriter out = new OutputStreamWriter(s.getOutputStream())) {
- if (reply != null) {
- out.write(reply + "\n");
- out.flush();
- } else {
- // close without sending
- }
- }
- } catch (IOException ignored) {
- } finally {
- try {
- server.close();
- } catch (IOException ignored) {
- }
- }
- });
- this.thread.setDaemon(true);
- this.thread.start();
- try {
- ready.await(5, TimeUnit.SECONDS);
- } catch (InterruptedException ignored) {
- }
- }
-
- @Override
- public void close() {
- try {
- server.close();
- } catch (IOException ignored) {
- }
- }
-
- int getPort() {
- return server.getLocalPort();
- }
- }
-
- private static class SslOneShotAuthServer implements AutoCloseable {
- private final SSLServerSocket server;
- private final Thread thread;
- private final CountDownLatch ready = new CountDownLatch(1);
- private final String reply;
-
- SslOneShotAuthServer(String reply) throws Exception {
- this.reply = reply;
-
- KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
- kpg.initialize(2048);
- KeyPair kp = kpg.generateKeyPair();
-
- X509Certificate cert = generateSelfSigned("CN=localhost", kp);
-
- KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
- ks.load(null, null);
- char[] pass = "changeit".toCharArray();
- ks.setKeyEntry("server", kp.getPrivate(), pass, new Certificate[] {cert});
-
- KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
- kmf.init(ks, pass);
- KeyManager[] kms = kmf.getKeyManagers();
-
- SSLContext ctx = SSLContext.getInstance("TLSv1.2");
- ctx.init(kms, null, new SecureRandom());
- SSLServerSocketFactory ssf = ctx.getServerSocketFactory();
- this.server = (SSLServerSocket) ssf.createServerSocket(0);
-
- this.thread = new Thread(() -> {
- ready.countDown();
- try (Socket s = server.accept()) {
- BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
- in.readLine();
- try (OutputStreamWriter out = new OutputStreamWriter(s.getOutputStream())) {
- out.write(reply + "\n");
- out.flush();
- }
- } catch (IOException ignored) {
- } finally {
- try {
- server.close();
- } catch (IOException ignored) {
- }
- }
- });
- this.thread.setDaemon(true);
- this.thread.start();
- try {
- ready.await(5, TimeUnit.SECONDS);
- } catch (InterruptedException ignored) {
- }
- }
-
- @Override
- public void close() {
- try {
- server.close();
- } catch (IOException ignored) {
- }
- }
-
- int getPort() {
- return server.getLocalPort();
- }
-
- private static X509Certificate generateSelfSigned(String dn, KeyPair keyPair) throws Exception {
- // Generate a self-signed certificate at runtime using Bouncy Castle
- // This avoids hardcoded certificates and uses only standard APIs
-
- long now = System.currentTimeMillis();
- Date notBefore = new Date(now - 60000); // 1 minute ago
- Date notAfter = new Date(now + 86400000L); // 1 day from now
-
- BigInteger serial = new BigInteger(64, new SecureRandom());
- X500Name subject = new X500Name(dn);
-
- JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
- subject, // issuer = subject (self-signed)
- serial,
- notBefore,
- notAfter,
- subject,
- keyPair.getPublic());
-
- ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(keyPair.getPrivate());
-
- X509CertificateHolder holder = certBuilder.build(signer);
-
- return new JcaX509CertificateConverter().getCertificate(holder);
- }
- }
-}
diff --git a/unixauthclient/src/test/java/org/apache/ranger/authentication/unix/jaas/TestUnixGroupPrincipal.java b/unixauthclient/src/test/java/org/apache/ranger/authentication/unix/jaas/TestUnixGroupPrincipal.java
deleted file mode 100644
index 407c43895cd..00000000000
--- a/unixauthclient/src/test/java/org/apache/ranger/authentication/unix/jaas/TestUnixGroupPrincipal.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.ranger.authentication.unix.jaas;
-
-import org.junit.jupiter.api.MethodOrderer;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestMethodOrder;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-/**
- * @generated by Cursor
- * @description : Unit Test cases for UnixGroupPrincipal
- */
-
-@ExtendWith(MockitoExtension.class)
-@TestMethodOrder(MethodOrderer.MethodName.class)
-public class TestUnixGroupPrincipal {
- @Test
- public void test01_getName_returnsGroupName() {
- UnixGroupPrincipal principal = new UnixGroupPrincipal("dev");
- assertEquals("dev", principal.getName());
- }
-}
diff --git a/unixauthclient/src/test/java/org/apache/ranger/authentication/unix/jaas/TestUnixUserPrincipal.java b/unixauthclient/src/test/java/org/apache/ranger/authentication/unix/jaas/TestUnixUserPrincipal.java
deleted file mode 100644
index e53d0b678f6..00000000000
--- a/unixauthclient/src/test/java/org/apache/ranger/authentication/unix/jaas/TestUnixUserPrincipal.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.ranger.authentication.unix.jaas;
-
-import org.junit.jupiter.api.MethodOrderer;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestMethodOrder;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-/**
- * @generated by Cursor
- * @description : Unit Test cases for UnixUserPrincipal
- */
-
-@ExtendWith(MockitoExtension.class)
-@TestMethodOrder(MethodOrderer.MethodName.class)
-public class TestUnixUserPrincipal {
- @Test
- public void test01_getName_returnsUserName() {
- UnixUserPrincipal principal = new UnixUserPrincipal("alice");
- assertEquals("alice", principal.getName());
- }
-}
diff --git a/unixauthnative/.gitignore b/unixauthnative/.gitignore
deleted file mode 100644
index 02f68bb35da..00000000000
--- a/unixauthnative/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-/target/
-/bin/
-.settings/
diff --git a/unixauthnative/pom.xml b/unixauthnative/pom.xml
deleted file mode 100644
index f24b14e3f76..00000000000
--- a/unixauthnative/pom.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
- 4.0.0
-
- org.apache.ranger
- ranger
- 3.0.0-SNAPSHOT
- ..
-
- credValidator
- uexe
- Unix Native Authenticator
- Unix authentication service
-
-
-
- org.codehaus.mojo
- native-maven-plugin
- ${native-maven-plugin.version}
- true
-
-
- ${commonCompilerOptions}
-
-
-
- src/main/c
-
- **/*.c
-
-
-
-
- -lcrypt
-
-
-
-
-
-
diff --git a/unixauthnative/src/main/c/credValidator.c b/unixauthnative/src/main/c/credValidator.c
deleted file mode 100644
index 891b0b9e059..00000000000
--- a/unixauthnative/src/main/c/credValidator.c
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.
- */
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#define STRLEN 64
-
-int main(int ac, char **av, char **ev)
-{
- char username[STRLEN] ;
- char password[STRLEN] ;
- char line[512] ;
- char format[20];
- struct passwd *pwp;
- struct spwd *spwd ;
-
- fgets(line,512,stdin) ;
- sprintf(format, "LOGIN:%%%ds %%%ds", STRLEN-1, STRLEN-1);
- sscanf(line, format, username,password) ;
-
- pwp = getpwnam(username) ;
-
- if (pwp == (struct passwd *)NULL) {
- fprintf(stdout, "FAILED: [%s] does not exists.\n", username) ;
- exit(1) ;
- }
-
- spwd = getspnam(pwp->pw_name) ;
-
- if (spwd == (struct spwd *)NULL) {
- fprintf(stdout, "FAILED: unable to get (shadow) password for '%s', because '%s'\n", username, strerror(errno));
- exit(1) ;
- }
- else {
- char *gen = crypt(password,spwd->sp_pwdp) ;
- if (gen == (char *)NULL) {
- fprintf(stdout, "FAILED: crypt failed with: '%s'\n", strerror(errno));
- exit(1);
- }
- if (strcmp(spwd->sp_pwdp,gen) == 0) {
- fprintf(stdout, "OK:\n") ;
- exit(0);
- }
- else {
- fprintf(stdout, "FAILED: Password did not match.\n") ;
- exit(1) ;
- }
- }
- exit(0) ;
-}
diff --git a/unixauthpam/.gitignore b/unixauthpam/.gitignore
deleted file mode 100644
index 02f68bb35da..00000000000
--- a/unixauthpam/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-/target/
-/bin/
-.settings/
diff --git a/unixauthpam/pom.xml b/unixauthpam/pom.xml
deleted file mode 100644
index f8bf697f59e..00000000000
--- a/unixauthpam/pom.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
- 4.0.0
-
- org.apache.ranger
- ranger
- 3.0.0-SNAPSHOT
- ..
-
- pamCredValidator
- uexe
- PAM Authenticator
- PAM authentication service
-
-
-
- org.codehaus.mojo
- native-maven-plugin
- 1.0-alpha-11
- true
-
-
- ${commonCompilerOptions}
-
-
-
- src/main/c
-
- **/*.c
-
-
-
-
- -lpam
-
-
-
-
-
-
diff --git a/unixauthpam/src/main/c/pamCredValidator.c b/unixauthpam/src/main/c/pamCredValidator.c
deleted file mode 100644
index 8ec9f0e3e21..00000000000
--- a/unixauthpam/src/main/c/pamCredValidator.c
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * 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.
- */
-
- /*
- You need to add the following (or equivalent) to the
- /etc/pam.d/ranger-remote file:
- # check authorization
- auth required pam_unix.so
- account required pam_unix.so
- */
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#define STRLEN 64
-
-int pamconv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) {
- if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF) {
- fprintf(stderr, "ERROR: Unexpected PAM conversation '%d/%s'\n", msg[0]->msg_style, msg[0]->msg);
- return PAM_CONV_ERR;
- }
- if (!appdata_ptr) {
- fprintf(stderr, "ERROR: No password available to conversation!\n");
- return PAM_CONV_ERR;
- }
- *resp = calloc(num_msg, sizeof(struct pam_response));
- if (!*resp) {
- fprintf(stderr, "ERROR: Out of memory!\n");
- return PAM_CONV_ERR;
- }
- (*resp)[0].resp = strdup((char *) appdata_ptr);
- (*resp)[0].resp_retcode = 0;
-
- return ((*resp)[0].resp ? PAM_SUCCESS : PAM_CONV_ERR);
-}
-
-struct pam_conv conv = { pamconv, NULL };
-
-int main(int ac, char **av, char **ev)
-{
- char username[STRLEN] ;
- char password[STRLEN] ;
- char line[512] ;
- char format[20];
-
- int retval;
- pam_handle_t *pamh = NULL;
-
- sprintf(format, "LOGIN:%%%ds %%%ds", STRLEN-1, STRLEN-1);
- fgets(line,512,stdin) ;
- sscanf(line, format, username,password) ;
- conv.appdata_ptr = (char *) password;
-
- retval = pam_start("ranger-remote", username, &conv, &pamh);
- if (retval != PAM_SUCCESS) {
- /* why expose this? */
- fprintf(stdout, "FAILED: [%s] does not exists.\n", username) ;
- if (pamh) {
- pam_end(pamh, retval);
- }
- exit(1);
- }
-
- retval = pam_authenticate(pamh, 0);
- if (retval != PAM_SUCCESS) {
- fprintf(stdout, "FAILED: Password did not match(%s).\n", pam_strerror(pamh, retval)) ;
- if (pamh) {
- pam_end(pamh, retval);
- }
- exit(1);
- }
-
- /* authorize */
- retval = pam_acct_mgmt(pamh, 0);
- if (retval != PAM_SUCCESS) {
- fprintf(stdout, "FAILED: [%s] is not authorized.\n", username) ;
- if (pamh) {
- pam_end(pamh, retval);
- }
- exit(1);
- }
-
- /* establish the requested credentials */
- if ((retval = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
- fprintf(stdout, "FAILED: Error setting credentials for [%s].\n", username) ;
- if (pamh) {
- pam_end(pamh, retval);
- }
- exit(1);
- }
-
- /* not opening a session, as logout has not been implemented as a remote service */
- fprintf(stdout, "OK:\n") ;
-
- if (pamh) {
- pam_end(pamh, retval);
- }
-
- exit(0) ;
-}
diff --git a/unixauthservice/.gitignore b/unixauthservice/.gitignore
deleted file mode 100644
index 02f68bb35da..00000000000
--- a/unixauthservice/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-/target/
-/bin/
-.settings/
diff --git a/unixauthservice/conf.dist/jaas.conf b/unixauthservice/conf.dist/jaas.conf
deleted file mode 100644
index f6b30faf698..00000000000
--- a/unixauthservice/conf.dist/jaas.conf
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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.
- */
-PolicyManager {
- org.apache.ranger.authentication.unix.jaas.RemoteUnixLoginModule REQUIRED
- authServiceHostName="192.168.22.88" authServicePort="5151" debug="true"
- keyStore="keystore.jks" keyStorePassword="password"
- trustStore="unixauthclient.jks" trustStorePassword="mEmePaS3w9rD" ;
-} ;
diff --git a/unixauthservice/pom.xml b/unixauthservice/pom.xml
deleted file mode 100644
index 117f8bb0de6..00000000000
--- a/unixauthservice/pom.xml
+++ /dev/null
@@ -1,134 +0,0 @@
-
-
-
- 4.0.0
-
- org.apache.ranger
- ranger
- 3.0.0-SNAPSHOT
- ..
-
- unixauthservice
- jar
- Unix Authentication Service
- Unix authentication service
-
-
- ch.qos.logback
- logback-classic
- ${logback.version}
-
-
- com.google.code.gson
- gson
-
-
- commons-cli
- commons-cli
- ${commons.cli.version}
-
-
- commons-collections
- commons-collections
- ${commons.collections.version}
-
-
- io.netty
- netty-handler
- ${netty-all.version}
-
-
- io.netty
- netty-transport-native-epoll
- ${netty-all.version}
-
-
- jakarta.activation
- jakarta.activation-api
- ${jakarta.activation-api.version}
-
-
- jakarta.ws.rs
- jakarta.ws.rs-api
- ${jakarta.ws.rs-api.version}
-
-
- jakarta.xml.bind
- jakarta.xml.bind-api
- ${jakarta.xml.bind-api.version}
-
-
- javax.inject
- javax.inject
- ${javax.inject.version}
-
-
- org.apache.commons
- commons-configuration2
- ${commons.configuration.version}
-
-
- org.apache.commons
- commons-lang3
- ${commons.lang3.version}
-
-
- org.apache.hadoop
- hadoop-auth
- ${hadoop.version}
-
-
- com.sun.jersey
- *
-
-
- log4j
- *
-
-
- net.minidev
- json-smart
-
-
- org.apache.zookeeper
- zookeeper
-
-
- org.slf4j
- *
-
-
-
-
- org.apache.ranger
- unixusersync
- ${project.version}
-
-
- org.slf4j
- slf4j-api
- ${slf4j.version}
-
-
- org.mockito
- mockito-junit-jupiter
- ${mockito.version}
- test
-
-
-
diff --git a/unixauthservice/src/main/java/org/apache/ranger/authentication/LoginAttemptTracker.java b/unixauthservice/src/main/java/org/apache/ranger/authentication/LoginAttemptTracker.java
deleted file mode 100644
index 073bcb137c5..00000000000
--- a/unixauthservice/src/main/java/org/apache/ranger/authentication/LoginAttemptTracker.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * 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.ranger.authentication;
-
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-public class LoginAttemptTracker {
- private static final long CLEANUP_INTERVAL_MS = TimeUnit.MINUTES.toMillis(5);
-
- private final ConcurrentHashMap attemptsByIp = new ConcurrentHashMap<>();
- private final ConcurrentHashMap attemptsByAccount = new ConcurrentHashMap<>();
-
- private final int maxFailures;
- private final long windowMs;
- private final long lockoutMs;
-
- private final boolean accountFanoutEnabled;
- private final int accountDistinctIpThreshold;
- private final long accountWindowMs;
- private final long accountBaseDelayMs;
- private final long accountMaxDelayMs;
-
- private volatile long lastCleanupTime;
-
- public LoginAttemptTracker(int maxFailures, long windowMs, long lockoutMs) {
- this(maxFailures, windowMs, lockoutMs, false, 0, 0, 0, 0);
- }
-
- public LoginAttemptTracker(int maxFailures, long windowMs, long lockoutMs,
- boolean accountFanoutEnabled, int accountDistinctIpThreshold,
- long accountWindowMs, long accountBaseDelayMs, long accountMaxDelayMs) {
- this.maxFailures = maxFailures;
- this.windowMs = windowMs;
- this.lockoutMs = lockoutMs;
- this.lastCleanupTime = System.currentTimeMillis();
-
- this.accountFanoutEnabled = accountFanoutEnabled;
- this.accountDistinctIpThreshold = accountDistinctIpThreshold;
- this.accountWindowMs = accountWindowMs;
- this.accountBaseDelayMs = accountBaseDelayMs;
- this.accountMaxDelayMs = accountMaxDelayMs;
- }
-
- public boolean isBlocked(String sourceIp) {
- IpRecord record = attemptsByIp.get(sourceIp);
-
- if (record == null) {
- return false;
- }
-
- return record.lockedUntil > System.currentTimeMillis();
- }
-
- public long getAccountDelayMs(String account) {
- if (!accountFanoutEnabled || account == null || account.isEmpty()) {
- return 0;
- }
-
- AccountRecord record = attemptsByAccount.get(account);
-
- if (record == null) {
- return 0;
- }
-
- int distinctIpCount;
-
- synchronized (record) {
- if (System.currentTimeMillis() - record.windowStart > accountWindowMs) {
- return 0;
- }
-
- distinctIpCount = record.failingIps.size();
- }
-
- if (distinctIpCount <= accountDistinctIpThreshold) {
- return 0;
- }
-
- long delay = accountBaseDelayMs * (distinctIpCount - accountDistinctIpThreshold);
-
- return Math.min(delay, accountMaxDelayMs);
- }
-
- public void recordFailure(String sourceIp) {
- recordFailure(sourceIp, null);
- }
-
- public void recordFailure(String sourceIp, String account) {
- maybeCleanup();
- recordIpFailure(sourceIp);
-
- if (accountFanoutEnabled && account != null && !account.isEmpty()) {
- recordAccountFailure(account, sourceIp);
- }
- }
-
- private void recordIpFailure(String sourceIp) {
- long now = System.currentTimeMillis();
- IpRecord record = attemptsByIp.computeIfAbsent(sourceIp, k -> new IpRecord(now));
-
- synchronized (record) {
- if (now - record.windowStart > windowMs) {
- record.windowStart = now;
- record.failCount.set(0);
- }
-
- record.lastActivity = now;
-
- int count = record.failCount.incrementAndGet();
-
- if (count >= maxFailures) {
- record.lockedUntil = now + lockoutMs;
- }
- }
- }
-
- private void recordAccountFailure(String account, String sourceIp) {
- long now = System.currentTimeMillis();
- AccountRecord record = attemptsByAccount.computeIfAbsent(account, k -> new AccountRecord(now));
-
- synchronized (record) {
- if (now - record.windowStart > accountWindowMs) {
- record.windowStart = now;
- record.failingIps.clear();
- }
-
- record.lastActivity = now;
- record.failingIps.add(sourceIp);
- }
- }
-
- public void recordSuccess(String sourceIp) {
- recordSuccess(sourceIp, null);
- }
-
- public void recordSuccess(String sourceIp, String account) {
- attemptsByIp.remove(sourceIp);
-
- if (accountFanoutEnabled && account != null && !account.isEmpty()) {
- attemptsByAccount.remove(account);
- }
- }
-
- private void maybeCleanup() {
- long now = System.currentTimeMillis();
-
- if (now - lastCleanupTime < CLEANUP_INTERVAL_MS) {
- return;
- }
-
- lastCleanupTime = now;
- long ipCutoff = now - windowMs;
- Iterator> ipIt = attemptsByIp.entrySet().iterator();
- while (ipIt.hasNext()) {
- IpRecord record = ipIt.next().getValue();
-
- if (record.lastActivity < ipCutoff && record.lockedUntil <= now) {
- ipIt.remove();
- }
- }
-
- if (accountFanoutEnabled) {
- long accountCutoff = now - accountWindowMs;
- Iterator> acctIt = attemptsByAccount.entrySet().iterator();
- while (acctIt.hasNext()) {
- AccountRecord record = acctIt.next().getValue();
- if (record.lastActivity < accountCutoff) {
- acctIt.remove();
- }
- }
- }
- }
-
- private static final class IpRecord {
- final AtomicInteger failCount = new AtomicInteger(0);
- volatile long windowStart;
- volatile long lastActivity;
- volatile long lockedUntil;
-
- IpRecord(long now) {
- this.windowStart = now;
- this.lastActivity = now;
- }
- }
-
- private static final class AccountRecord {
- final Set failingIps = ConcurrentHashMap.newKeySet();
- volatile long windowStart;
- volatile long lastActivity;
-
- AccountRecord(long now) {
- this.windowStart = now;
- this.lastActivity = now;
- }
- }
-}
diff --git a/unixauthservice/src/main/java/org/apache/ranger/authentication/PasswordValidator.java b/unixauthservice/src/main/java/org/apache/ranger/authentication/PasswordValidator.java
deleted file mode 100644
index c3925dc728f..00000000000
--- a/unixauthservice/src/main/java/org/apache/ranger/authentication/PasswordValidator.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * 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.ranger.authentication;
-
-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.PrintWriter;
-import java.net.Socket;
-import java.util.List;
-
-public class PasswordValidator implements Runnable {
- private static final Logger LOG = LoggerFactory.getLogger(PasswordValidator.class);
- private static final String GENERIC_FAILURE_RESPONSE = "FAILED: Authentication failed.";
- private static final String LOCKED_OUT_RESPONSE = "FAILED: Too many attempts. Try again later.";
-
- private static List adminUserList;
- private static String adminRoleNames;
- private static String validatorProgram;
-
- private final LoginAttemptTracker loginAttemptTracker;
- private Socket client;
-
- public PasswordValidator(Socket client, LoginAttemptTracker loginAttemptTracker) {
- this.client = client;
- this.loginAttemptTracker = loginAttemptTracker;
- }
-
- public static String getValidatorProgram() {
- return validatorProgram;
- }
-
- public static void setValidatorProgram(String validatorProgram) {
- PasswordValidator.validatorProgram = validatorProgram;
- }
-
- public static List getAdminUserList() {
- return adminUserList;
- }
-
- public static void setAdminUserList(List adminUserList) {
- PasswordValidator.adminUserList = adminUserList;
- }
-
- public static String getAdminRoleNames() {
- return adminRoleNames;
- }
-
- public static void setAdminRoleNames(String adminRoleNames) {
- PasswordValidator.adminRoleNames = adminRoleNames;
- }
-
- @Override
- public void run() {
- BufferedReader reader;
- PrintWriter writer = null;
-
- String userName = null;
-
- String remoteIp = client.getInetAddress() != null ? client.getInetAddress().getHostAddress() : "unknown";
- try {
- reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
- writer = new PrintWriter(new OutputStreamWriter(client.getOutputStream()));
- String request = reader.readLine();
- userName = parseUserName(request);
- if (loginAttemptTracker.isBlocked(remoteIp)) {
- LOG.warn("Rejected UnixAuth attempt from [{}] - source IP is temporarily locked out due to repeated failures.", remoteIp);
- writer.println(LOCKED_OUT_RESPONSE);
- writer.flush();
- return;
- }
- if (request == null) {
- loginAttemptTracker.recordFailure(remoteIp, userName);
- LOG.warn("Rejected UnixAuth attempt from [{}] - connection closed before sending any data.", remoteIp);
- writer.println(GENERIC_FAILURE_RESPONSE);
- writer.flush();
- return;
- }
- long accountDelayMs = loginAttemptTracker.getAccountDelayMs(userName);
-
- if (accountDelayMs > 0) {
- LOG.warn("Applying {}ms delay for user [{}] from [{}] - failures against this account recently seen from multiple source IPs.", accountDelayMs, userName, remoteIp);
-
- try {
- Thread.sleep(accountDelayMs);
- } catch (InterruptedException ie) {
- Thread.currentThread().interrupt();
- }
- }
-
- if (validatorProgram == null) {
- loginAttemptTracker.recordFailure(remoteIp, userName);
- LOG.error("Response [{}] for user: {} from [{}] as ValidatorProgram is not defined in configuration.", GENERIC_FAILURE_RESPONSE, userName, remoteIp);
- writer.println(GENERIC_FAILURE_RESPONSE);
- writer.flush();
- } else {
- BufferedReader pReader;
- PrintWriter pWriter;
- Process p = null;
-
- try {
- p = Runtime.getRuntime().exec(validatorProgram);
-
- pReader = new BufferedReader(new InputStreamReader(p.getInputStream()));
- pWriter = new PrintWriter(new OutputStreamWriter(p.getOutputStream()));
-
- pWriter.println(request);
- pWriter.flush();
-
- String res = pReader.readLine();
-
- if (res != null && res.startsWith("OK")) {
- loginAttemptTracker.recordSuccess(remoteIp, userName);
- if (adminRoleNames != null && adminUserList != null) {
- if (adminUserList.contains(userName)) {
- res = res + " " + adminRoleNames;
- }
- }
- } else {
- loginAttemptTracker.recordFailure(remoteIp, userName);
- res = GENERIC_FAILURE_RESPONSE;
- }
-
- LOG.info("Response [{}] for user: {} from [{}]", res, userName, remoteIp);
-
- writer.println(res);
- writer.flush();
- } finally {
- if (p != null) {
- p.destroy();
- }
- }
- }
- } catch (Throwable t) {
- loginAttemptTracker.recordFailure(remoteIp, userName);
- if (userName != null && writer != null) {
- LOG.error("Response [{}] for user: {} from [{}], {}", GENERIC_FAILURE_RESPONSE, userName, remoteIp, t.getMessage());
- writer.println(GENERIC_FAILURE_RESPONSE);
- writer.flush();
- }
- } finally {
- try {
- if (client != null) {
- client.close();
- }
- } catch (IOException ioe) {
- LOG.debug("Close socket failure. Detail: ", ioe);
- } finally {
- client = null;
- }
- }
- }
-
- private static String parseUserName(String request) {
- if (request == null || !request.startsWith("LOGIN:")) {
- return null;
- }
- String line = request.substring(6).trim();
- int passwordAt = line.indexOf(' ');
- if (passwordAt == -1) {
- return null;
- }
- return line.substring(0, passwordAt).trim();
- }
-}
diff --git a/unixauthservice/src/main/java/org/apache/ranger/authentication/UnixAuthenticationService.java b/unixauthservice/src/main/java/org/apache/ranger/authentication/UnixAuthenticationService.java
deleted file mode 100644
index 8f5a461107b..00000000000
--- a/unixauthservice/src/main/java/org/apache/ranger/authentication/UnixAuthenticationService.java
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * 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.ranger.authentication;
-
-import org.apache.ranger.credentialapi.CredentialReader;
-import org.apache.ranger.plugin.util.XMLUtils;
-import org.apache.ranger.unixusersync.config.UserGroupSyncConfig;
-import org.apache.ranger.unixusersync.ha.UserSyncHAInitializerImpl;
-import org.apache.ranger.usergroupsync.UserGroupSync;
-import org.apache.ranger.usergroupsync.UserSyncMetricsProducer;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLServerSocket;
-import javax.net.ssl.SSLServerSocketFactory;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.security.KeyStore;
-import java.security.SecureRandom;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Properties;
-import java.util.Set;
-
-public class UnixAuthenticationService {
- private static final Logger LOG = LoggerFactory.getLogger(UnixAuthenticationService.class);
-
- private static final String serviceName = "UnixAuthenticationService";
- private static final String SSL_ALGORITHM = "TLS";
- private static final String REMOTE_LOGIN_AUTH_SERVICE_PORT_PARAM = "ranger.usersync.port";
- private static final String SSL_KEYSTORE_PATH_PARAM = "ranger.usersync.keystore.file";
- private static final String SSL_TRUSTSTORE_PATH_PARAM = "ranger.usersync.truststore.file";
- private static final String SSL_KEYSTORE_FILE_TYPE_PARAM = "ranger.keystore.file.type";
- private static final String SSL_TRUSTSTORE_FILE_TYPE_PARAM = "ranger.truststore.file.type";
- private static final String SSL_KEYSTORE_PATH_PASSWORD_ALIAS = "usersync.ssl.key.password";
- private static final String SSL_TRUSTSTORE_PATH_PASSWORD_ALIAS = "usersync.ssl.truststore.password";
- private static final String CRED_VALIDATOR_PROG = "ranger.usersync.passwordvalidator.path";
- private static final String ADMIN_USER_LIST_PARAM = "admin.users";
- private static final String ADMIN_ROLE_LIST_PARAM = "admin.roleNames";
- private static final String SSL_ENABLED_PARAM = "ranger.usersync.ssl";
- private static final String CREDSTORE_FILENAME_PARAM = "ranger.usersync.credstore.filename";
- private static final String[] UGSYNC_CONFIG_XML_FILES = {"ranger-ugsync-default.xml", "ranger-ugsync-site.xml"};
- private static final String UNIXAUTH_MAX_FAILED_ATTEMPTS_PARAM = "ranger.usersync.unixauth.max.failed.attempts";
- private static final String UNIXAUTH_ATTEMPT_WINDOW_MS_PARAM = "ranger.usersync.unixauth.attempt.window.ms";
- private static final String UNIXAUTH_LOCKOUT_DURATION_MS_PARAM = "ranger.usersync.unixauth.lockout.duration.ms";
- private static final String UNIXAUTH_REQUIRE_CLIENT_AUTH_PARAM = "ranger.usersync.unixauth.require.client.auth";
- private static final String UNIXAUTH_SOCKET_TIMEOUT_MS_PARAM = "ranger.usersync.unixauth.socket.timeout.ms";
- private static final String UNIXAUTH_ACCOUNT_FANOUT_ENABLED_PARAM = "ranger.usersync.unixauth.account.fanout.enabled";
- private static final String UNIXAUTH_ACCOUNT_DISTINCT_IP_THRESHOLD_PARAM = "ranger.usersync.unixauth.account.distinct.ip.threshold";
- private static final String UNIXAUTH_ACCOUNT_WINDOW_MS_PARAM = "ranger.usersync.unixauth.account.window.ms";
- private static final String UNIXAUTH_ACCOUNT_BASE_DELAY_MS_PARAM = "ranger.usersync.unixauth.account.base.delay.ms";
- private static final String UNIXAUTH_ACCOUNT_MAX_DELAY_MS_PARAM = "ranger.usersync.unixauth.account.max.delay.ms";
-
- private static boolean enableUnixAuth;
-
- private final List adminUserList = new ArrayList<>();
-
- private String adminRoleNames;
- private String keyStorePath;
- private String keyStorePathPassword;
- private String keyStoreType;
- private String trustStorePath;
- private String trustStorePathPassword;
- private String trustStoreType;
- private List enabledProtocolsList;
- private List enabledCipherSuiteList;
- private UserSyncHAInitializerImpl userSyncHAInitializerImpl;
- private int portNum;
- private boolean sslEnabled;
- private boolean requireClientAuth;
- private LoginAttemptTracker loginAttemptTracker;
- private int socketTimeoutMs;
-
- public UnixAuthenticationService() {
- }
-
- public static void main(String[] args) {
- enableUnixAuth = Arrays.stream(args).anyMatch("-enableUnixAuth"::equalsIgnoreCase);
- UnixAuthenticationService service = new UnixAuthenticationService();
- service.userSyncHAInitializerImpl = UserSyncHAInitializerImpl.getInstance(UserGroupSyncConfig.getInstance().getUserGroupConfig());
- service.run();
- }
-
- public void run() {
- try {
- LOG.info("Starting User Sync Service!");
- startUnixUserGroupSyncProcess();
- Thread.sleep(5000);
- if (enableUnixAuth) {
- LOG.info("Enabling Unix Auth Service!");
- init();
- startService();
- } else {
- LOG.info("Unix Auth Service Disabled!");
- }
- } catch (Throwable t) {
- LOG.error("ERROR: Service: {}", serviceName, t);
- } finally {
- LOG.info("Service: {} - STOPPED", serviceName);
- if (this.userSyncHAInitializerImpl != null) {
- LOG.info("Stopping curator leader latch service as main thread is closing");
- this.userSyncHAInitializerImpl.stop();
- }
- }
- }
-
- public void startService() throws Throwable {
- SSLContext context = SSLContext.getInstance(SSL_ALGORITHM);
-
- KeyManager[] km = null;
-
- if (keyStorePath != null && !keyStorePath.isEmpty()) {
- KeyStore ks = KeyStore.getInstance(keyStoreType);
-
- try (InputStream in = getFileInputStream(keyStorePath)) {
- if (keyStorePathPassword == null) {
- keyStorePathPassword = "";
- }
- ks.load(in, keyStorePathPassword.toCharArray());
- }
-
- KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
- kmf.init(ks, keyStorePathPassword.toCharArray());
- km = kmf.getKeyManagers();
- }
-
- TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
-
- KeyStore trustStoreKeyStore = null;
-
- if (trustStorePath != null && !trustStorePath.isEmpty()) {
- trustStoreKeyStore = KeyStore.getInstance(trustStoreType);
-
- try (InputStream in = getFileInputStream(trustStorePath)) {
- if (trustStorePathPassword == null) {
- trustStorePathPassword = "";
- }
- trustStoreKeyStore.load(in, trustStorePathPassword.toCharArray());
- }
- }
-
- trustManagerFactory.init(trustStoreKeyStore);
-
- TrustManager[] tm = trustManagerFactory.getTrustManagers();
-
- SecureRandom random = new SecureRandom();
-
- context.init(km, tm, random);
-
- SSLServerSocketFactory sf = context.getServerSocketFactory();
-
- try (ServerSocket socket = (sslEnabled ? sf.createServerSocket(portNum) : new ServerSocket(portNum))) {
- if (sslEnabled) {
- SSLServerSocket secureSocket = (SSLServerSocket) socket;
- String[] protocols = secureSocket.getEnabledProtocols();
- Set allowedProtocols = new HashSet<>();
- for (String ep : protocols) {
- if (enabledProtocolsList.contains(ep.toUpperCase())) {
- LOG.info("Enabling Protocol: [{}]", ep);
- allowedProtocols.add(ep);
- } else {
- LOG.info("Disabling Protocol: [{}]", ep);
- }
- }
-
- if (!allowedProtocols.isEmpty()) {
- secureSocket.setEnabledProtocols(allowedProtocols.toArray(new String[0]));
- }
- String[] enabledCipherSuites = secureSocket.getEnabledCipherSuites();
- Set allowedCipherSuites = new HashSet<>();
- for (String enabledCipherSuite : enabledCipherSuites) {
- if (enabledCipherSuiteList.contains(enabledCipherSuite)) {
- LOG.debug("Enabling CipherSuite : [{}]", enabledCipherSuite);
- allowedCipherSuites.add(enabledCipherSuite);
- } else {
- LOG.debug("Disabling CipherSuite : [{}]", enabledCipherSuite);
- }
- }
- if (requireClientAuth) {
- if (trustStoreKeyStore == null) {
- throw new RuntimeException("[" + UNIXAUTH_REQUIRE_CLIENT_AUTH_PARAM + "] is enabled but [" + SSL_TRUSTSTORE_PATH_PARAM + "] is not configured - a truststore is required to validate client certificates.");
- }
- LOG.info("Enabling TLS client certificate authentication for UnixAuth listener.");
- secureSocket.setNeedClientAuth(true);
- }
- if (!allowedCipherSuites.isEmpty()) {
- secureSocket.setEnabledCipherSuites(allowedCipherSuites.toArray(new String[0]));
- }
- }
-
- Socket client;
-
- try {
- while ((client = socket.accept()) != null) {
- try {
- client.setSoTimeout(socketTimeoutMs);
- } catch (java.net.SocketException se) {
- LOG.warn("Failed to set socket timeout for connection from [{}]: {}", client.getInetAddress(), se.getMessage());
- }
- Thread clientValidatorThread = new Thread(new PasswordValidator(client, loginAttemptTracker));
- clientValidatorThread.start();
- }
- } catch (IOException e) {
- socket.close();
- throw (e);
- }
- }
- }
-
- private void startUnixUserGroupSyncProcess() {
- // Start user group synchronization service
- LOG.info("Start : startUnixUserGroupSyncProcess ");
- UserGroupSync syncProc = new UserGroupSync();
- Thread newSyncProcThread = new Thread(syncProc);
- newSyncProcThread.setName("UnixUserSyncThread");
-
- /* If this thread is set as daemon, then the entire process will terminate if enableUnixAuth is false
- Therefore this is marked as non-daemon thread. Don't change the following line
- */
- newSyncProcThread.setDaemon(false);
- newSyncProcThread.start();
- LOG.info("UnixUserSyncThread started");
- LOG.info("creating UserSyncMetricsProducer thread with default metrics location : {}", System.getProperty("logdir"));
-
- //Start the user sync metrics
- boolean isUserSyncMetricsEnabled = UserGroupSyncConfig.getInstance().isUserSyncMetricsEnabled();
- if (isUserSyncMetricsEnabled) {
- UserSyncMetricsProducer userSyncMetricsProducer = new UserSyncMetricsProducer();
- Thread userSyncMetricsProducerThread = new Thread(userSyncMetricsProducer);
- userSyncMetricsProducerThread.setName("UserSyncMetricsProducerThread");
- userSyncMetricsProducerThread.setDaemon(false);
- userSyncMetricsProducerThread.start();
- LOG.info("UserSyncMetricsProducer started");
- } else {
- LOG.info(" Ranger userSync metrics is not enabled");
- }
- }
-
- //TODO: add more validation code
- private void init() {
- Properties prop = new Properties();
-
- for (String fn : UGSYNC_CONFIG_XML_FILES) {
- XMLUtils.loadConfig(fn, prop);
- }
-
- String credStoreFileName = prop.getProperty(CREDSTORE_FILENAME_PARAM);
- keyStorePath = prop.getProperty(SSL_KEYSTORE_PATH_PARAM);
- trustStorePath = prop.getProperty(SSL_TRUSTSTORE_PATH_PARAM);
- keyStoreType = prop.getProperty(SSL_KEYSTORE_FILE_TYPE_PARAM, KeyStore.getDefaultType());
- trustStoreType = prop.getProperty(SSL_TRUSTSTORE_FILE_TYPE_PARAM, KeyStore.getDefaultType());
-
- if (credStoreFileName == null) {
- throw new RuntimeException("Credential file is not defined. param = [" + CREDSTORE_FILENAME_PARAM + "]");
- }
-
- File credFile = new File(credStoreFileName);
-
- if (!credFile.exists()) {
- throw new RuntimeException("Credential file [" + credStoreFileName + "]: does not exists.");
- }
-
- if (!credFile.canRead()) {
- throw new RuntimeException("Credential file [" + credStoreFileName + "]: can not be read.");
- }
-
- if ("bcfks".equalsIgnoreCase(keyStoreType)) {
- String crendentialProviderPrefixBcfks = "bcfks://file";
- credStoreFileName = crendentialProviderPrefixBcfks + credStoreFileName;
- }
-
- keyStorePathPassword = CredentialReader.getDecryptedString(credStoreFileName, SSL_KEYSTORE_PATH_PASSWORD_ALIAS, keyStoreType);
- trustStorePathPassword = CredentialReader.getDecryptedString(credStoreFileName, SSL_TRUSTSTORE_PATH_PASSWORD_ALIAS, trustStoreType);
- portNum = Integer.parseInt(prop.getProperty(REMOTE_LOGIN_AUTH_SERVICE_PORT_PARAM));
- String validatorProg = prop.getProperty(CRED_VALIDATOR_PROG);
-
- if (validatorProg != null) {
- PasswordValidator.setValidatorProgram(validatorProg);
- }
-
- String adminUsers = prop.getProperty(ADMIN_USER_LIST_PARAM);
-
- if (adminUsers != null && !adminUsers.trim().isEmpty()) {
- for (String u : adminUsers.split(",")) {
- LOG.info("Adding Admin User: {}", u.trim());
- adminUserList.add(u.trim());
- }
- PasswordValidator.setAdminUserList(adminUserList);
- }
-
- adminRoleNames = prop.getProperty(ADMIN_ROLE_LIST_PARAM);
-
- if (adminRoleNames != null) {
- LOG.info("Adding Admin Group: {}", adminRoleNames);
- PasswordValidator.setAdminRoleNames(adminRoleNames);
- }
-
- String sslEnabledProp = prop.getProperty(SSL_ENABLED_PARAM);
-
- sslEnabled = (sslEnabledProp != null && (sslEnabledProp.equalsIgnoreCase("true")));
- String defaultEnabledProtocols = "TLSv1.2";
- String enabledProtocols = prop.getProperty("ranger.usersync.https.ssl.enabled.protocols", defaultEnabledProtocols);
- String enabledCipherSuites = prop.getProperty("ranger.usersync.https.ssl.enabled.cipher.suites", "");
- enabledProtocolsList = new ArrayList<>(Arrays.asList(enabledProtocols.toUpperCase().trim().split("\\s*,\\s*")));
- enabledCipherSuiteList = new ArrayList<>(Arrays.asList(enabledCipherSuites.toUpperCase().trim().split("\\s*,\\s*")));
- int maxFailedAttempts = Integer.parseInt(prop.getProperty(UNIXAUTH_MAX_FAILED_ATTEMPTS_PARAM, "5"));
- long attemptWindowMs = Long.parseLong(prop.getProperty(UNIXAUTH_ATTEMPT_WINDOW_MS_PARAM, "60000"));
- long lockoutDurationMs = Long.parseLong(prop.getProperty(UNIXAUTH_LOCKOUT_DURATION_MS_PARAM, "30000"));
- socketTimeoutMs = Integer.parseInt(prop.getProperty(UNIXAUTH_SOCKET_TIMEOUT_MS_PARAM, "10000"));
-
- boolean accountFanoutEnabled = Boolean.parseBoolean(prop.getProperty(UNIXAUTH_ACCOUNT_FANOUT_ENABLED_PARAM, "true"));
- int accountDistinctIpThreshold = Integer.parseInt(prop.getProperty(UNIXAUTH_ACCOUNT_DISTINCT_IP_THRESHOLD_PARAM, "3"));
- long accountWindowMs = Long.parseLong(prop.getProperty(UNIXAUTH_ACCOUNT_WINDOW_MS_PARAM, "60000"));
- long accountBaseDelayMs = Long.parseLong(prop.getProperty(UNIXAUTH_ACCOUNT_BASE_DELAY_MS_PARAM, "200"));
- long accountMaxDelayMs = Long.parseLong(prop.getProperty(UNIXAUTH_ACCOUNT_MAX_DELAY_MS_PARAM, "2000"));
-
- loginAttemptTracker = new LoginAttemptTracker(maxFailedAttempts, attemptWindowMs, lockoutDurationMs, accountFanoutEnabled,
- accountDistinctIpThreshold, accountWindowMs, accountBaseDelayMs, accountMaxDelayMs);
-
- String requireClientAuthProp = prop.getProperty(UNIXAUTH_REQUIRE_CLIENT_AUTH_PARAM, "false");
- requireClientAuth = requireClientAuthProp.trim().equalsIgnoreCase("true");
- }
-
- private InputStream getFileInputStream(String path) throws FileNotFoundException {
- InputStream ret;
-
- File f = new File(path);
-
- if (f.exists()) {
- ret = new FileInputStream(f);
- } else {
- ret = getClass().getResourceAsStream(path);
- if (ret == null) {
- ret = getClass().getResourceAsStream(File.separator + path);
- }
- }
-
- return ret;
- }
-}
diff --git a/unixauthservice/src/main/resources/logback.xml b/unixauthservice/src/main/resources/logback.xml
deleted file mode 100644
index e96421b9007..00000000000
--- a/unixauthservice/src/main/resources/logback.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
-
-
-
-
- ${logdir}/usersync-${hostname}-${user}.log
-
- %d{dd MMM yyyy HH:mm:ss} %5p %c{1} [%t] - %m%n
-
-
- ${logdir}/usersync-${hostname}-${user}.log.%d{'.'yyyy-MM-dd}
- 15
- true
-
-
-
-
-
-
diff --git a/unixauthservice/src/test/java/org/apache/ranger/authentication/LoginAttemptTrackerTest.java b/unixauthservice/src/test/java/org/apache/ranger/authentication/LoginAttemptTrackerTest.java
deleted file mode 100644
index 7671d0168a4..00000000000
--- a/unixauthservice/src/test/java/org/apache/ranger/authentication/LoginAttemptTrackerTest.java
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * 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.ranger.authentication;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-public class LoginAttemptTrackerTest {
- @Test
- public void allowsAttemptsBelowThreshold() {
- LoginAttemptTracker tracker = new LoginAttemptTracker(5, 60_000, 30_000);
- String ip = "10.0.0.1";
- for (int i = 0; i < 4; i++) {
- assertFalse(tracker.isBlocked(ip), "should not be blocked before threshold");
- tracker.recordFailure(ip);
- }
- assertFalse(tracker.isBlocked(ip), "still not blocked at 4 failures with threshold 5");
- }
-
- @Test
- public void locksOutAfterThresholdReached() {
- LoginAttemptTracker tracker = new LoginAttemptTracker(5, 60_000, 30_000);
- String ip = "10.0.0.2";
- for (int i = 0; i < 5; i++) {
- tracker.recordFailure(ip);
- }
- assertTrue(tracker.isBlocked(ip), "should be locked out at threshold");
- }
-
- @Test
- public void successClearsFailureHistory() {
- LoginAttemptTracker tracker = new LoginAttemptTracker(5, 60_000, 30_000);
- String ip = "10.0.0.3";
- for (int i = 0; i < 4; i++) {
- tracker.recordFailure(ip);
- }
- tracker.recordSuccess(ip);
- assertFalse(tracker.isBlocked(ip));
- // failure count should have reset - one more failure should not lock out
- tracker.recordFailure(ip);
- assertFalse(tracker.isBlocked(ip));
- }
-
- @Test
- public void windowExpiryResetsFailureCount() throws InterruptedException {
- // very short window so the test runs fast
- LoginAttemptTracker tracker = new LoginAttemptTracker(3, 50, 30_000);
- String ip = "10.0.0.4";
- tracker.recordFailure(ip);
- tracker.recordFailure(ip);
- assertFalse(tracker.isBlocked(ip));
- Thread.sleep(100); // let the window expire
- tracker.recordFailure(ip); // should start a fresh window, count = 1
- assertFalse(tracker.isBlocked(ip), "window should have reset the failure count");
- }
-
- @Test
- public void reLocksImmediatelyIfRetriedRightAfterLockoutExpires() throws InterruptedException {
- // short lockout, long window - so lockout expires but window doesn't
- LoginAttemptTracker tracker = new LoginAttemptTracker(3, 60_000, 50);
- String ip = "10.0.0.5";
- tracker.recordFailure(ip);
- tracker.recordFailure(ip);
- tracker.recordFailure(ip);
- assertTrue(tracker.isBlocked(ip));
- Thread.sleep(100); // let the lockout expire, window is still active
- assertFalse(tracker.isBlocked(ip), "lockout should have expired");
- tracker.recordFailure(ip); // one more failure within the same window
- assertTrue(tracker.isBlocked(ip), "should re-lock immediately since window's failure count carried over");
- }
-
- @Test
- public void independentIpsTrackedSeparately() {
- LoginAttemptTracker tracker = new LoginAttemptTracker(3, 60_000, 30_000);
- for (int i = 0; i < 3; i++) {
- tracker.recordFailure("10.0.0.6");
- }
- assertTrue(tracker.isBlocked("10.0.0.6"));
- assertFalse(tracker.isBlocked("10.0.0.7"), "a different source IP must not be affected");
- }
-
- // threshold=3, base delay=200ms, max delay=2000ms, account window=60s
- private static LoginAttemptTracker newFanoutTracker() {
- return new LoginAttemptTracker(100, 60_000, 30_000, true, 3, 60_000, 200, 2000);
- }
-
- @Test
- public void fanout_legitimateSingleIpNeverDelayed() {
- LoginAttemptTracker tracker = newFanoutTracker();
- // simulate a real admin mistyping their password 5 times from ONE ip
- for (int i = 0; i < 5; i++) {
- tracker.recordFailure("10.0.0.1", "alice");
- }
- assertEquals(0, tracker.getAccountDelayMs("alice"), "single-IP retries must never trigger an account delay");
- }
-
- @Test
- public void fanout_distinctIpsAcrossThresholdTriggerDelay() {
- LoginAttemptTracker tracker = newFanoutTracker();
- tracker.recordFailure("10.0.0.1", "bob");
- tracker.recordFailure("10.0.0.2", "bob");
- assertEquals(0, tracker.getAccountDelayMs("bob"), "2 distinct IPs, threshold 3 - no delay yet");
- tracker.recordFailure("10.0.0.3", "bob");
- assertEquals(0, tracker.getAccountDelayMs("bob"), "3 distinct IPs, at threshold - still no delay");
- tracker.recordFailure("10.0.0.4", "bob");
- assertTrue(tracker.getAccountDelayMs("bob") > 0, "4th distinct IP crosses the threshold - delay should apply");
- }
-
- @Test
- public void fanout_delayScalesLinearlyBeforeCap() {
- LoginAttemptTracker tracker = newFanoutTracker();
- String account = "carol";
- for (int i = 0; i < 4; i++) {
- tracker.recordFailure("10.0.1." + i, account); // 4 distinct -> 1 over threshold
- }
- assertEquals(200, tracker.getAccountDelayMs(account), "4 distinct IPs -> exactly 1x base delay");
- tracker.recordFailure("10.0.1.4", account); // 5 distinct -> 2 over threshold
- assertEquals(400, tracker.getAccountDelayMs(account), "5 distinct IPs -> exactly 2x base delay");
- tracker.recordFailure("10.0.1.5", account); // 6 distinct -> 3 over threshold
- assertEquals(600, tracker.getAccountDelayMs(account), "6 distinct IPs -> exactly 3x base delay");
- tracker.recordFailure("10.0.1.6", account); // 7 distinct -> 4 over threshold
- assertEquals(800, tracker.getAccountDelayMs(account), "7 distinct IPs -> exactly 4x base delay");
- }
-
- @Test
- public void fanout_delayCapsInsteadOfGrowingUnbounded() {
- LoginAttemptTracker tracker = newFanoutTracker();
- String account = "carol_capped";
- for (int i = 0; i < 30; i++) {
- tracker.recordFailure("10.0.1." + i, account); // well past the threshold
- }
- assertEquals(2000, tracker.getAccountDelayMs(account), "delay must cap at accountMaxDelayMs regardless of distinct-IP count");
- }
-
- @Test
- public void fanout_successClearsAccountStateImmediately() {
- LoginAttemptTracker tracker = newFanoutTracker();
- String account = "dave";
- for (int i = 0; i < 5; i++) {
- tracker.recordFailure("10.0.2." + i, account);
- }
- assertTrue(tracker.getAccountDelayMs(account) > 0, "delay should be present before success");
- tracker.recordSuccess("10.0.2.99", account);
- assertEquals(0, tracker.getAccountDelayMs(account), "delay must clear immediately after a successful login");
- }
-
- @Test
- public void fanout_windowExpiryResetsAccountState() throws InterruptedException {
- // short account window so the test runs fast
- LoginAttemptTracker tracker = new LoginAttemptTracker(100, 60_000, 30_000, true, 2, 80, 200, 2000);
- String account = "erin";
- tracker.recordFailure("10.0.3.1", account);
- tracker.recordFailure("10.0.3.2", account);
- tracker.recordFailure("10.0.3.3", account); // 3 distinct, over threshold of 2 -> delay
- assertTrue(tracker.getAccountDelayMs(account) > 0, "delay should be present within the window");
- Thread.sleep(150); // let the account window expire
- assertEquals(0, tracker.getAccountDelayMs(account), "delay must clear once the window has expired, even without a success");
- }
-
- @Test
- public void fanout_disabledMeansAlwaysZero() {
- // fan-out disabled entirely
- LoginAttemptTracker tracker = new LoginAttemptTracker(100, 60_000, 30_000, false, 1, 60_000, 200, 2000);
- String account = "frank";
- for (int i = 0; i < 10; i++) {
- tracker.recordFailure("10.0.4." + i, account);
- }
- assertEquals(0, tracker.getAccountDelayMs(account), "fan-out disabled must mean the delay is always 0, regardless of distinct IPs");
- }
-
- @Test
- public void fanout_ipLockoutStillIndependentAndUnaffected() {
- LoginAttemptTracker tracker = new LoginAttemptTracker(3, 60_000, 30_000, true, 2, 60_000, 200, 2000);
- for (int i = 0; i < 3; i++) {
- tracker.recordFailure("10.0.5.1", "grace"); // same IP, 3 failures -> IP lockout still fires
- }
- assertTrue(tracker.isBlocked("10.0.5.1"), "existing per-IP hard lockout must still work unchanged alongside account fan-out tracking");
- }
-
- @Test
- public void fanout_sameIpRetriedManyTimesCountsOnce() {
- LoginAttemptTracker tracker = newFanoutTracker();
- String account = "henry";
- for (int i = 0; i < 20; i++) {
- tracker.recordFailure("10.0.6.1", account); // SAME ip, 20 times
- }
- assertEquals(0, tracker.getAccountDelayMs(account), "20 retries from one IP must never cross the distinct-IP threshold");
- }
-
- @Test
- public void fanout_independentAccountsDoNotCrossContaminate() {
- LoginAttemptTracker tracker = newFanoutTracker();
- for (int i = 0; i < 10; i++) {
- tracker.recordFailure("10.0.8." + i, "jack"); // fan-out attack on "jack"
- }
- assertTrue(tracker.getAccountDelayMs("jack") > 0, "the attacked account should be delayed");
- assertEquals(0, tracker.getAccountDelayMs("kate"), "an unrelated account must be completely unaffected");
- }
-
- @Test
- public void fanout_nullOrEmptyAccountIsSafeNoOp() {
- LoginAttemptTracker tracker = newFanoutTracker();
- // simulates PasswordValidator's request==null / unparsable-request path
- for (int i = 0; i < 10; i++) {
- tracker.recordFailure("10.0.9." + i); // 1-arg overload, no account at all
- }
- assertEquals(0, tracker.getAccountDelayMs(null), "no-account failures must never populate any account delay");
- assertEquals(0, tracker.getAccountDelayMs(""), "empty-string account must also always be a no-op");
- tracker.recordFailure("10.0.9.1", ""); // explicit empty string account
- tracker.recordFailure("10.0.9.1", null); // explicit null account
- assertEquals(0, tracker.getAccountDelayMs(""), "explicit empty/null account args must never throw and never trigger a delay");
- }
-
- @Test
- public void fanout_concurrentFailuresFromManyIpsAreThreadSafe() throws InterruptedException {
- // Mirrors PasswordValidator's real threading model: one thread per
- // connection, all potentially hitting the same account concurrently.
- LoginAttemptTracker tracker = newFanoutTracker();
- String account = "concurrent_victim";
- int threadCount = 50;
- Thread[] threads = new Thread[threadCount];
- for (int i = 0; i < threadCount; i++) {
- final String ip = "10.1.0." + i; // 50 distinct IPs
- threads[i] = new Thread(() -> tracker.recordFailure(ip, account));
- }
- for (Thread t : threads) {
- t.start();
- }
- for (Thread t : threads) {
- t.join(5000);
- }
- // 50 distinct IPs, threshold 3 -> 47 over threshold -> would be 47*200=9400ms
- // uncapped, so this must land exactly at the configured cap.
- assertEquals(2000, tracker.getAccountDelayMs(account), "50 concurrent distinct-IP failures must land exactly at the cap, with no race corruption");
- // an unrelated account, hit concurrently at the same time, must still
- // read back as unaffected - proves no cross-account leakage under
- // concurrent access either.
- assertEquals(0, tracker.getAccountDelayMs("bystander"), "an untouched account must remain unaffected during concurrent activity");
- }
-}
diff --git a/unixauthservice/src/test/java/org/apache/ranger/authentication/TestPasswordValidator.java b/unixauthservice/src/test/java/org/apache/ranger/authentication/TestPasswordValidator.java
deleted file mode 100644
index 2e48dd958e4..00000000000
--- a/unixauthservice/src/test/java/org/apache/ranger/authentication/TestPasswordValidator.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * 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.ranger.authentication;
-
-import org.junit.jupiter.api.MethodOrderer;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestMethodOrder;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.MockedStatic;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.Collections;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.mockStatic;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-/**
- * @generated by Cursor
- * @description : Unit Test cases for PasswordValidator
- */
-
-@ExtendWith(MockitoExtension.class)
-@TestMethodOrder(MethodOrderer.MethodName.class)
-public class TestPasswordValidator {
- private static LoginAttemptTracker permissiveTracker() {
- return new LoginAttemptTracker(1000, 60_000, 30_000);
- }
-
- @Test
- public void test00_staticGettersAndSetters() {
- PasswordValidator.setValidatorProgram("/tmp/prog");
- PasswordValidator.setAdminUserList(Collections.singletonList("root"));
- PasswordValidator.setAdminRoleNames("ROLE_SUPER");
-
- assertEquals("/tmp/prog", PasswordValidator.getValidatorProgram());
- assertEquals(Collections.singletonList("root"), PasswordValidator.getAdminUserList());
- assertEquals("ROLE_SUPER", PasswordValidator.getAdminRoleNames());
- }
-
- @Test
- public void test01_nullProgramWritesFailedAndClosesSocket() throws Exception {
- PasswordValidator.setValidatorProgram(null);
- PasswordValidator.setAdminUserList(null);
- PasswordValidator.setAdminRoleNames(null);
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- Socket socket = buildSocket("LOGIN: alice secret", out);
-
- new PasswordValidator(socket, permissiveTracker()).run();
-
- String response = out.toString(StandardCharsets.UTF_8.name()).trim();
- assertEquals("FAILED: Authentication failed.", response);
- verify(socket, times(1)).close();
- }
-
- @Test
- public void test02_okResponseAppendsAdminRoleAndDestroysProcess() throws Exception {
- PasswordValidator.setValidatorProgram("/bin/validator");
- PasswordValidator.setAdminUserList(Arrays.asList("alice", "bob"));
- PasswordValidator.setAdminRoleNames("ROLE_ADMIN");
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- Socket socket = buildSocket("LOGIN: alice secret", out);
-
- Process process = mock(Process.class);
- ByteArrayInputStream processStdout = new ByteArrayInputStream("OK".getBytes(StandardCharsets.UTF_8));
- when(process.getInputStream()).thenReturn(processStdout);
- when(process.getOutputStream()).thenReturn(new ByteArrayOutputStream());
-
- try (MockedStatic runtimeStatic = mockStatic(Runtime.class)) {
- Runtime rt = mock(Runtime.class);
- runtimeStatic.when(Runtime::getRuntime).thenReturn(rt);
- when(rt.exec("/bin/validator")).thenReturn(process);
-
- new PasswordValidator(socket, permissiveTracker()).run();
- }
-
- String response = out.toString(StandardCharsets.UTF_8).trim();
- assertEquals("OK ROLE_ADMIN", response);
- verify(process, times(1)).destroy();
- verify(socket, times(1)).close();
- }
-
- @Test
- public void test03_okResponseWithoutAdminDoesNotAppendRole() throws Exception {
- PasswordValidator.setValidatorProgram("/bin/validator");
- PasswordValidator.setAdminUserList(Collections.singletonList("bob"));
- PasswordValidator.setAdminRoleNames("ROLE_ADMIN");
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- Socket socket = buildSocket("LOGIN: alice secret", out);
-
- Process process = mock(Process.class);
- ByteArrayInputStream processStdout = new ByteArrayInputStream("OK".getBytes(StandardCharsets.UTF_8));
- when(process.getInputStream()).thenReturn(processStdout);
- when(process.getOutputStream()).thenReturn(new ByteArrayOutputStream());
-
- try (MockedStatic runtimeStatic = mockStatic(Runtime.class)) {
- Runtime rt = mock(Runtime.class);
- runtimeStatic.when(Runtime::getRuntime).thenReturn(rt);
- when(rt.exec("/bin/validator")).thenReturn(process);
-
- new PasswordValidator(socket, permissiveTracker()).run();
- }
-
- String response = out.toString(StandardCharsets.UTF_8.name()).trim();
- assertEquals("OK", response);
- verify(process, times(1)).destroy();
- verify(socket, times(1)).close();
- }
-
- @Test
- public void test04_execThrowsWritesFailed() throws Exception {
- PasswordValidator.setValidatorProgram("/bin/validator");
- PasswordValidator.setAdminUserList(Collections.singletonList("alice"));
- PasswordValidator.setAdminRoleNames("ROLE_ADMIN");
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- Socket socket = buildSocket("LOGIN: alice secret", out);
-
- try (MockedStatic runtimeStatic = mockStatic(Runtime.class)) {
- Runtime rt = mock(Runtime.class);
- runtimeStatic.when(Runtime::getRuntime).thenReturn(rt);
- when(rt.exec("/bin/validator")).thenThrow(new IOException("boom"));
-
- new PasswordValidator(socket, permissiveTracker()).run();
- }
-
- String response = out.toString(StandardCharsets.UTF_8).trim();
- assertEquals("FAILED: Authentication failed.", response);
- verify(socket, times(1)).close();
- }
-
- @Test
- public void test05_closeSocketIOExceptionHandled() throws Exception {
- PasswordValidator.setValidatorProgram(null);
- PasswordValidator.setAdminUserList(null);
- PasswordValidator.setAdminRoleNames(null);
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- Socket socket = buildSocket("LOGIN: alice secret", out);
- doThrow(new IOException("close failure")).when(socket).close();
-
- new PasswordValidator(socket, permissiveTracker()).run();
-
- // No exception should be thrown even if close() fails; nothing to assert besides method completed
- verify(socket, times(1)).close();
- }
-
- @Test
- public void test06_blockedIpSkipsValidatorAndReturnsLockedOutResponse() throws Exception {
- PasswordValidator.setValidatorProgram("/bin/validator");
- PasswordValidator.setAdminUserList(null);
- PasswordValidator.setAdminRoleNames(null);
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- Socket socket = buildSocket("LOGIN: alice secret", out);
- LoginAttemptTracker tracker = mock(LoginAttemptTracker.class);
- when(tracker.isBlocked(anyString())).thenReturn(true);
- new PasswordValidator(socket, tracker).run();
- String response = out.toString(StandardCharsets.UTF_8).trim();
- assertEquals("FAILED: Too many attempts. Try again later.", response);
- verify(socket, times(1)).close();
- }
-
- @Test
- public void test07_failedValidatorResponseRecordsFailureOnTracker() throws Exception {
- PasswordValidator.setValidatorProgram("/bin/validator");
- PasswordValidator.setAdminUserList(null);
- PasswordValidator.setAdminRoleNames(null);
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- Socket socket = buildSocket("LOGIN: alice secret", out);
-
- Process process = mock(Process.class);
- ByteArrayInputStream processStdout = new ByteArrayInputStream("FAILED: Password did not match.".getBytes(StandardCharsets.UTF_8));
- when(process.getInputStream()).thenReturn(processStdout);
- when(process.getOutputStream()).thenReturn(new ByteArrayOutputStream());
-
- LoginAttemptTracker tracker = mock(LoginAttemptTracker.class);
- when(tracker.isBlocked(anyString())).thenReturn(false);
-
- try (MockedStatic runtimeStatic = mockStatic(Runtime.class)) {
- Runtime rt = mock(Runtime.class);
- runtimeStatic.when(Runtime::getRuntime).thenReturn(rt);
- when(rt.exec("/bin/validator")).thenReturn(process);
-
- new PasswordValidator(socket, tracker).run();
- }
-
- String response = out.toString(StandardCharsets.UTF_8).trim();
- assertEquals("FAILED: Authentication failed.", response);
- verify(tracker, times(1)).recordFailure(anyString(), anyString());
- verify(tracker, never()).recordSuccess(anyString(), anyString());
- }
-
- @Test
- public void test08_okValidatorResponseRecordsSuccessOnTracker() throws Exception {
- PasswordValidator.setValidatorProgram("/bin/validator");
- PasswordValidator.setAdminUserList(null);
- PasswordValidator.setAdminRoleNames(null);
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- Socket socket = buildSocket("LOGIN: alice secret", out);
-
- Process process = mock(Process.class);
- ByteArrayInputStream processStdout = new ByteArrayInputStream("OK".getBytes(StandardCharsets.UTF_8));
- when(process.getInputStream()).thenReturn(processStdout);
- when(process.getOutputStream()).thenReturn(new ByteArrayOutputStream());
-
- LoginAttemptTracker tracker = mock(LoginAttemptTracker.class);
- when(tracker.isBlocked(anyString())).thenReturn(false);
-
- try (MockedStatic runtimeStatic = mockStatic(Runtime.class)) {
- Runtime rt = mock(Runtime.class);
- runtimeStatic.when(Runtime::getRuntime).thenReturn(rt);
- when(rt.exec("/bin/validator")).thenReturn(process);
-
- new PasswordValidator(socket, tracker).run();
- }
-
- String response = out.toString(StandardCharsets.UTF_8).trim();
- assertEquals("OK", response);
- verify(tracker, times(1)).recordSuccess(anyString(), anyString());
- verify(tracker, never()).recordFailure(anyString(), anyString());
- }
-
- private Socket buildSocket(String requestLine, ByteArrayOutputStream responseOut) throws IOException {
- Socket socket = mock(Socket.class);
- InputStream in = new ByteArrayInputStream((requestLine + "\n").getBytes(StandardCharsets.UTF_8));
- OutputStream out = responseOut;
- when(socket.getInputStream()).thenReturn(in);
- when(socket.getOutputStream()).thenReturn(out);
- return socket;
- }
-}
diff --git a/unixauthservice/src/test/java/org/apache/ranger/authentication/TestUnixAuthenticationService.java b/unixauthservice/src/test/java/org/apache/ranger/authentication/TestUnixAuthenticationService.java
deleted file mode 100644
index f582e698e83..00000000000
--- a/unixauthservice/src/test/java/org/apache/ranger/authentication/TestUnixAuthenticationService.java
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- * 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.ranger.authentication;
-
-import org.apache.hadoop.conf.Configuration;
-import org.apache.ranger.credentialapi.CredentialReader;
-import org.apache.ranger.plugin.util.XMLUtils;
-import org.apache.ranger.unixusersync.config.UserGroupSyncConfig;
-import org.apache.ranger.unixusersync.ha.UserSyncHAInitializerImpl;
-import org.junit.jupiter.api.MethodOrderer;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestMethodOrder;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.MockedConstruction;
-import org.mockito.MockedStatic;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLServerSocket;
-import javax.net.ssl.SSLServerSocketFactory;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.net.Socket;
-import java.security.KeyStore;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Properties;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.mockConstruction;
-import static org.mockito.Mockito.mockStatic;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-/**
- * @generated by Cursor
- * @description : Unit Test cases for UnixAuthenticationService
- */
-
-@ExtendWith(MockitoExtension.class)
-@TestMethodOrder(MethodOrderer.MethodName.class)
-public class TestUnixAuthenticationService {
- @Test
- public void test01_initThrowsWhenCredFileMissing() throws Exception {
- UnixAuthenticationService svc = new UnixAuthenticationService();
-
- try (MockedStatic xmlMock = mockStatic(XMLUtils.class)) {
- xmlMock.when(() -> XMLUtils.loadConfig(any(String.class), any(Properties.class))).thenAnswer(inv -> null);
-
- try (MockedStatic credMock = mockStatic(CredentialReader.class)) {
- // Do not need to reach CredentialReader because missing file check will throw first
- Properties props = new Properties();
- props.setProperty("ranger.usersync.credstore.filename", "/no/such/file.jceks");
- props.setProperty("ranger.usersync.port", "6168");
-
- // Inject properties via XMLUtils sequence
- xmlMock.when(() -> XMLUtils.loadConfig(eq("ranger-ugsync-default.xml"), any(Properties.class)))
- .thenAnswer(inv -> {
- ((Properties) inv.getArgument(1)).putAll(props);
- return null;
- });
- xmlMock.when(() -> XMLUtils.loadConfig(eq("ranger-ugsync-site.xml"), any(Properties.class)))
- .thenAnswer(inv -> null);
-
- InvocationTargetException ex = assertThrows(InvocationTargetException.class, () -> invokeInit(svc));
- assertTrue(ex.getCause() instanceof RuntimeException);
- }
- }
- }
-
- @Test
- public void test02_getFileInputStreamFallsBackToResourcePath() throws Exception {
- UnixAuthenticationService svc = new UnixAuthenticationService();
- Method m = UnixAuthenticationService.class.getDeclaredMethod("getFileInputStream", String.class);
- m.setAccessible(true);
-
- // Point to a file guaranteed to not exist; should fallback to resource stream, which will be null
- InputStream is = (InputStream) m.invoke(svc, "/this/does/not/exist.txt");
- // Since no resource with that name, it can still be null; verify method returns (null allowed)
- // Next, try with a real file via temp file which should be returned
- File tmp = File.createTempFile("ranger-test", ".txt");
- try {
- InputStream fis = (InputStream) m.invoke(svc, tmp.getAbsolutePath());
- assertNotNull(fis);
- fis.close();
- } finally {
- //noinspection ResultOfMethodCallIgnored
- tmp.delete();
- }
- }
-
- @Test
- public void test03_runInterruptedStopsHA() throws Exception {
- UnixAuthenticationService svc = new UnixAuthenticationService();
- UserSyncHAInitializerImpl ha = mock(UserSyncHAInitializerImpl.class);
- setField(svc, "userSyncHAInitializerImpl", ha);
-
- try (MockedConstruction threadConstruction = mockConstruction(Thread.class, (mock, ctx) -> {
- doNothing().when(mock).setName(any());
- doNothing().when(mock).setDaemon(false);
- doNothing().when(mock).start();
- })) {
- // Interrupt current thread so Thread.sleep throws immediately
- Thread.currentThread().interrupt();
- // Call run; should catch InterruptedException and then stop HA in finally
- svc.run();
- // Clear interrupted status for subsequent tests
- Thread.interrupted();
- }
- }
-
- @Test
- public void test04_startServiceSslAndAcceptIOExceptionPath() throws Throwable {
- UnixAuthenticationService svc = new UnixAuthenticationService();
-
- // Reflectively set fields needed by startService
- setField(svc, "sslEnabled", true);
- setField(svc, "portNum", 0);
- setField(svc, "enabledProtocolsList", Collections.singletonList("TLSV1.2"));
- setField(svc, "enabledCipherSuiteList", new ArrayList<>());
- setField(svc, "keyStorePath", "");
- setField(svc, "trustStorePath", "");
- setField(svc, "keyStoreType", KeyStore.getDefaultType());
- setField(svc, "trustStoreType", KeyStore.getDefaultType());
-
- try (MockedStatic sslStatic = mockStatic(SSLContext.class)) {
- SSLContext sslContext = mock(SSLContext.class);
- SSLServerSocketFactory sf = mock(SSLServerSocketFactory.class);
- SSLServerSocket secureSocket = mock(SSLServerSocket.class);
-
- when(SSLContext.getInstance("TLS")).thenReturn(sslContext);
- when(sslContext.getServerSocketFactory()).thenReturn(sf);
- when(sf.createServerSocket(anyInt())).thenReturn(secureSocket);
- when(secureSocket.getEnabledProtocols()).thenReturn(new String[] {"TLSv1.2"});
- when(secureSocket.getEnabledCipherSuites()).thenReturn(new String[] {});
- when(secureSocket.accept()).thenThrow(new IOException("accept fail"));
-
- assertThrows(IOException.class, svc::startService);
- }
- }
-
- @Test
- public void test05_initSuccessPathWithBcfksAndAdminLists() throws Exception {
- UnixAuthenticationService svc = new UnixAuthenticationService();
-
- Properties props = new Properties();
- File credFile = File.createTempFile("ranger-cred", ".bcfks");
- try {
- props.setProperty("ranger.usersync.credstore.filename", credFile.getAbsolutePath());
- props.setProperty("ranger.usersync.keystore.file", "");
- props.setProperty("ranger.usersync.truststore.file", "");
- props.setProperty("ranger.keystore.file.type", "bcfks");
- props.setProperty("ranger.truststore.file.type", "bcfks");
- props.setProperty("ranger.usersync.port", "6168");
- props.setProperty("ranger.usersync.passwordvalidator.path", "/bin/validator");
- props.setProperty("admin.users", "alice,bob");
- props.setProperty("admin.roleNames", "ROLE_ADMIN,ROLE_AUDITOR");
- props.setProperty("ranger.usersync.ssl", "true");
- props.setProperty("ranger.usersync.https.ssl.enabled.protocols", "TLSv1.2");
- props.setProperty("ranger.usersync.https.ssl.enabled.cipher.suites", "");
-
- try (MockedStatic xmlMock = mockStatic(XMLUtils.class);
- MockedStatic credMock = mockStatic(CredentialReader.class)) {
- xmlMock.when(() -> XMLUtils.loadConfig(any(String.class), any(Properties.class))).thenAnswer(inv -> {
- ((Properties) inv.getArgument(1)).putAll(props);
- return null;
- });
- credMock.when(() -> CredentialReader.getDecryptedString(any(), any(), any())).thenReturn("pass");
-
- // invoke private init
- invokeInit(svc);
- }
- } finally {
- //noinspection ResultOfMethodCallIgnored
- credFile.delete();
- }
- }
-
- @Test
- public void test06_startServiceWithKeyAndTrustStores() throws Throwable {
- UnixAuthenticationService svc = new UnixAuthenticationService();
-
- setField(svc, "sslEnabled", true);
- setField(svc, "portNum", 0);
- setField(svc, "enabledProtocolsList", Collections.singletonList("TLSV1.2"));
- setField(svc, "enabledCipherSuiteList", new ArrayList<>());
- setField(svc, "keyStorePath", "/tmp/keystore.jks");
- setField(svc, "trustStorePath", "/tmp/truststore.jks");
- setField(svc, "keyStoreType", KeyStore.getDefaultType());
- setField(svc, "trustStoreType", KeyStore.getDefaultType());
- // leave passwords as null to exercise empty defaulting branch
-
- try (MockedStatic kmfStatic = mockStatic(KeyManagerFactory.class);
- MockedStatic tmfStatic = mockStatic(TrustManagerFactory.class);
- MockedStatic sslStatic = mockStatic(SSLContext.class)) {
- KeyManagerFactory kmf = mock(KeyManagerFactory.class);
- kmfStatic.when(() -> KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())).thenReturn(kmf);
- when(kmf.getKeyManagers()).thenReturn(new KeyManager[0]);
-
- TrustManagerFactory tmf = mock(TrustManagerFactory.class);
- tmfStatic.when(() -> TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())).thenReturn(tmf);
- when(tmf.getTrustManagers()).thenReturn(new TrustManager[0]);
-
- SSLContext sslContext = mock(SSLContext.class);
- SSLServerSocketFactory sf = mock(SSLServerSocketFactory.class);
- SSLServerSocket secureSocket = mock(SSLServerSocket.class);
-
- when(SSLContext.getInstance("TLS")).thenReturn(sslContext);
- when(sslContext.getServerSocketFactory()).thenReturn(sf);
- when(sf.createServerSocket(anyInt())).thenReturn(secureSocket);
- when(secureSocket.getEnabledProtocols()).thenReturn(new String[] {"TLSv1.2"});
- when(secureSocket.getEnabledCipherSuites()).thenReturn(new String[] {});
- when(secureSocket.accept()).thenThrow(new java.io.IOException("accept fail"));
-
- assertThrows(java.io.IOException.class, svc::startService);
- }
- }
-
- @Test
- public void test07_mainEnableUnixAuthInvokesRun() {
- try (MockedStatic ugscStatic = mockStatic(UserGroupSyncConfig.class);
- MockedStatic haStatic = mockStatic(UserSyncHAInitializerImpl.class);
- MockedConstruction serviceConstruction = mockConstruction(UnixAuthenticationService.class, (mock, ctx) -> {
- // service.run() will be called by main; make it a no-op
- })) {
- UserGroupSyncConfig ugsc = mock(UserGroupSyncConfig.class);
- ugscStatic.when(UserGroupSyncConfig::getInstance).thenReturn(ugsc);
- when(ugsc.getUserGroupConfig()).thenReturn(new Configuration());
-
- UserSyncHAInitializerImpl ha = mock(UserSyncHAInitializerImpl.class);
- haStatic.when(() -> UserSyncHAInitializerImpl.getInstance(any())).thenReturn(ha);
-
- UnixAuthenticationService.main(new String[] {"-enableUnixAuth"});
- }
- }
-
- @Test
- public void test08_startServiceEnablesCipherSuites() throws Throwable {
- UnixAuthenticationService svc = new UnixAuthenticationService();
- setField(svc, "sslEnabled", true);
- setField(svc, "portNum", 0);
- setField(svc, "enabledProtocolsList", Collections.singletonList("TLSV1.2"));
- ArrayList enabledCiphers = new ArrayList<>();
- enabledCiphers.add("CIPHER_A");
- setField(svc, "enabledCipherSuiteList", enabledCiphers);
- setField(svc, "keyStorePath", "");
- setField(svc, "trustStorePath", "");
- setField(svc, "keyStoreType", KeyStore.getDefaultType());
- setField(svc, "trustStoreType", KeyStore.getDefaultType());
-
- try (MockedStatic sslStatic = mockStatic(SSLContext.class)) {
- SSLContext sslContext = mock(SSLContext.class);
- SSLServerSocketFactory sf = mock(SSLServerSocketFactory.class);
- SSLServerSocket secureSocket = mock(SSLServerSocket.class);
-
- when(SSLContext.getInstance("TLS")).thenReturn(sslContext);
- when(sslContext.getServerSocketFactory()).thenReturn(sf);
- when(sf.createServerSocket(anyInt())).thenReturn(secureSocket);
- when(secureSocket.getEnabledProtocols()).thenReturn(new String[] {"TLSv1.2"});
- when(secureSocket.getEnabledCipherSuites()).thenReturn(new String[] {"CIPHER_A", "CIPHER_B"});
- when(secureSocket.accept()).thenThrow(new IOException("accept fail"));
-
- assertThrows(IOException.class, svc::startService);
- verify(secureSocket).setEnabledCipherSuites(new String[] {"CIPHER_A"});
- }
- }
-
- @Test
- public void test09_startServiceSpawnsValidatorThread() throws Throwable {
- UnixAuthenticationService svc = new UnixAuthenticationService();
- setField(svc, "sslEnabled", true);
- setField(svc, "portNum", 0);
- setField(svc, "enabledProtocolsList", Collections.singletonList("TLSV1.2"));
- setField(svc, "enabledCipherSuiteList", new ArrayList<>());
- setField(svc, "keyStorePath", "");
- setField(svc, "trustStorePath", "");
- setField(svc, "keyStoreType", KeyStore.getDefaultType());
- setField(svc, "trustStoreType", KeyStore.getDefaultType());
-
- final boolean[] runnableIsPasswordValidator = new boolean[] {false};
-
- try (MockedStatic sslStatic = mockStatic(SSLContext.class);
- MockedConstruction threadConstruction = mockConstruction(Thread.class, (mockThread, ctx) -> {
- Object arg0 = ctx.arguments().get(0);
- runnableIsPasswordValidator[0] = (arg0 instanceof Runnable) && arg0.getClass().getName().equals(PasswordValidator.class.getName());
- doNothing().when(mockThread).start();
- })) {
- SSLContext sslContext = mock(SSLContext.class);
- SSLServerSocketFactory sf = mock(SSLServerSocketFactory.class);
- SSLServerSocket secureSocket = mock(SSLServerSocket.class);
- Socket client = mock(Socket.class);
-
- when(SSLContext.getInstance("TLS")).thenReturn(sslContext);
- when(sslContext.getServerSocketFactory()).thenReturn(sf);
- when(sf.createServerSocket(anyInt())).thenReturn(secureSocket);
- when(secureSocket.getEnabledProtocols()).thenReturn(new String[] {"TLSV1.2"});
- when(secureSocket.getEnabledCipherSuites()).thenReturn(new String[] {});
- when(secureSocket.accept()).thenReturn(client).thenThrow(new IOException("done"));
-
- assertThrows(IOException.class, svc::startService);
- assertTrue(runnableIsPasswordValidator[0]);
- }
- }
-
- @Test
- public void test10_startUnixUserGroupSyncProcess_MetricsEnabledAndDisabled() throws Exception {
- UnixAuthenticationService svc = new UnixAuthenticationService();
- Method m = UnixAuthenticationService.class.getDeclaredMethod("startUnixUserGroupSyncProcess");
- m.setAccessible(true);
-
- try (MockedStatic ugscStatic = mockStatic(UserGroupSyncConfig.class)) {
- UserGroupSyncConfig ugsc = mock(UserGroupSyncConfig.class);
- ugscStatic.when(UserGroupSyncConfig::getInstance).thenReturn(ugsc);
-
- // Metrics enabled -> two threads created
- when(ugsc.isUserSyncMetricsEnabled()).thenReturn(true);
- try (MockedConstruction threadConstruction = mockConstruction(Thread.class, (mockThread, ctx) -> {
- doNothing().when(mockThread).setName(any());
- doNothing().when(mockThread).setDaemon(false);
- doNothing().when(mockThread).start();
- })) {
- m.invoke(svc);
- assertEquals(2, threadConstruction.constructed().size());
- }
-
- // Metrics disabled -> one thread created
- when(ugsc.isUserSyncMetricsEnabled()).thenReturn(false);
- try (MockedConstruction threadConstruction = mockConstruction(Thread.class, (mockThread, ctx) -> {
- doNothing().when(mockThread).setName(any());
- doNothing().when(mockThread).setDaemon(false);
- doNothing().when(mockThread).start();
- })) {
- m.invoke(svc);
- assertEquals(1, threadConstruction.constructed().size());
- }
- }
- }
-
- private void invokeInit(UnixAuthenticationService svc) throws Exception {
- Method m = UnixAuthenticationService.class.getDeclaredMethod("init");
- m.setAccessible(true);
- m.invoke(svc);
- }
-
- private void setField(Object target, String fieldName, Object value) throws Exception {
- Field f = UnixAuthenticationService.class.getDeclaredField(fieldName);
- f.setAccessible(true);
- f.set(target, value);
- }
-}