From fdc6a353d289746fe0a11a199f843eb50ec387a7 Mon Sep 17 00:00:00 2001 From: bhaavesh1567 Date: Tue, 30 Sep 2025 17:06:51 +0530 Subject: [PATCH] RANGER-5351:Test Cases for agents-cred [ranger-plugins-cred] Module --- agents-cred/pom.xml | 12 + .../CredentialsProviderUtilTest.java | 222 ++++++++++++++++++ .../KerberosCredentialsProviderTest.java | 86 +++++++ .../kerberos/KeytabJaasConfTest.java | 73 ++++++ .../utils/RangerCredentialProviderTest.java | 16 +- 5 files changed, 408 insertions(+), 1 deletion(-) create mode 100644 agents-cred/src/test/java/org/apache/ranger/authorization/credutils/CredentialsProviderUtilTest.java create mode 100644 agents-cred/src/test/java/org/apache/ranger/authorization/credutils/kerberos/KerberosCredentialsProviderTest.java create mode 100644 agents-cred/src/test/java/org/apache/ranger/authorization/credutils/kerberos/KeytabJaasConfTest.java diff --git a/agents-cred/pom.xml b/agents-cred/pom.xml index 362a4e9a9c..6c181734e1 100644 --- a/agents-cred/pom.xml +++ b/agents-cred/pom.xml @@ -118,6 +118,18 @@ ${junit.jupiter.version} test + + org.mockito + mockito-inline + ${mockito.version} + test + + + org.mockito + mockito-junit-jupiter + ${mockito.version} + test + org.slf4j log4j-over-slf4j diff --git a/agents-cred/src/test/java/org/apache/ranger/authorization/credutils/CredentialsProviderUtilTest.java b/agents-cred/src/test/java/org/apache/ranger/authorization/credutils/CredentialsProviderUtilTest.java new file mode 100644 index 0000000000..ee60b20f79 --- /dev/null +++ b/agents-cred/src/test/java/org/apache/ranger/authorization/credutils/CredentialsProviderUtilTest.java @@ -0,0 +1,222 @@ +/** + * 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.authorization.credutils; + +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.Credentials; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.ietf.jgss.GSSCredential; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.Oid; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +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.security.auth.Subject; +import javax.security.auth.kerberos.KerberosPrincipal; +import javax.security.auth.kerberos.KerberosTicket; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.Date; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +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.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.CALLS_REAL_METHODS; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockConstruction; +import static org.mockito.Mockito.mockStatic; +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 CredentialsProviderUtil + */ + +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class CredentialsProviderUtilTest { + private MockedStatic gssManagerStatic; + private MockedStatic accessControllerStatic; + + @BeforeEach + void setup() { + CredentialsProviderUtil.ticketExpireTime80 = 0L; + } + + @AfterEach + void tearDown() { + if (gssManagerStatic != null) { + gssManagerStatic.close(); + } + if (accessControllerStatic != null) { + accessControllerStatic.close(); + } + } + + @Test + void getBasicCredentials_setsUsernamePasswordOnAnyScope() { + CredentialsProvider provider = CredentialsProviderUtil.getBasicCredentials("user", "pass"); + Credentials creds = provider.getCredentials(AuthScope.ANY); + assertNotNull(creds); + assertInstanceOf(UsernamePasswordCredentials.class, creds); + UsernamePasswordCredentials upc = (UsernamePasswordCredentials) creds; + assertEquals("user", upc.getUserName()); + assertEquals("pass", upc.getPassword()); + } + + @Test + void getKerberosCredentials_wrapsGssException() { + GSSManager mockManager = mock(GSSManager.class); + gssManagerStatic = mockStatic(GSSManager.class); + gssManagerStatic.when(GSSManager::getInstance).thenReturn(mockManager); + try { + when(mockManager.createName(anyString(), any())).thenThrow(new GSSException(GSSException.FAILURE)); + } catch (GSSException e) { + // ignored + } + RuntimeException ex = assertThrows(RuntimeException.class, () -> CredentialsProviderUtil.getKerberosCredentials("user", "pw")); + assertNotNull(ex.getCause()); + } + + @Test + void getKerberosCredentials_wrapsPrivilegedActionException() throws Exception { + GSSManager mockManager = mock(GSSManager.class); + gssManagerStatic = mockStatic(GSSManager.class); + gssManagerStatic.when(GSSManager::getInstance).thenReturn(mockManager); + GSSName mockName = mock(GSSName.class); + when(mockManager.createName(eq("user@EXAMPLE.COM"), eq(GSSName.NT_USER_NAME))).thenReturn(mockName); + when(mockManager.createCredential(eq(mockName), eq(GSSCredential.DEFAULT_LIFETIME), any(Oid.class), eq(GSSCredential.INITIATE_ONLY))).thenThrow(new GSSException(GSSException.FAILURE)); + + try (MockedStatic cpu = mockStatic(CredentialsProviderUtil.class, CALLS_REAL_METHODS)) { + Subject subject = new Subject(); + cpu.when(() -> CredentialsProviderUtil.login(eq("user@EXAMPLE.COM"), eq("/tmp/keytab"))).thenReturn(subject); + + RuntimeException ex = assertThrows(RuntimeException.class, () -> CredentialsProviderUtil.getKerberosCredentials("user@EXAMPLE.COM", "/tmp/keytab")); + assertInstanceOf(PrivilegedActionException.class, ex.getCause()); + } + } + + @Test + void doAsPrivilegedWrapper_unwrapsNestedPrivilegedActionException() throws Exception { + Subject subject = new Subject(); + PrivilegedActionException inner = new PrivilegedActionException(new Exception("root")); + PrivilegedExceptionAction action = () -> { + throw inner; + }; + PrivilegedActionException thrown = assertThrows(PrivilegedActionException.class, () -> CredentialsProviderUtil.doAsPrivilegedWrapper(subject, action, mock(AccessControlContext.class))); + assertInstanceOf(PrivilegedActionException.class, thrown.getCause()); + assertInstanceOf(Exception.class, thrown.getCause().getCause()); + assertEquals("root", thrown.getCause().getCause().getMessage()); + } + + @Test + void getTGT_returnsTicketMatchingTgtPattern() { + Subject subject = new Subject(); + KerberosPrincipal client = new KerberosPrincipal("user@EXAMPLE.COM"); + KerberosPrincipal server = new KerberosPrincipal("krbtgt/EXAMPLE.COM@EXAMPLE.COM"); + Date now = new Date(); + Date end = new Date(System.currentTimeMillis() + 60_000); + Date renew = new Date(end.getTime() + 60_000); + KerberosTicket tgt = new KerberosTicket(new byte[] {1}, client, server, new byte[] {1}, 1, null, now, now, end, renew, null); + subject.getPrivateCredentials().add(tgt); + + KerberosTicket found = CredentialsProviderUtil.getTGT(subject); + assertNotNull(found); + assertEquals(server, found.getServer()); + } + + @Test + void ticketWillExpire_computes80PercentThresholdAndResets() { + KerberosPrincipal client = new KerberosPrincipal("user@EXAMPLE.COM"); + KerberosPrincipal server = new KerberosPrincipal("krbtgt/EXAMPLE.COM@EXAMPLE.COM"); + Date start = new Date(System.currentTimeMillis() - 1000); + Date end = new Date(System.currentTimeMillis() + 10_000); + Date renew = new Date(end.getTime() + 10_000); + KerberosTicket ticket = new KerberosTicket(new byte[] {1}, client, server, new byte[] {1}, 1, null, start, start, end, renew, null); + + Boolean first = CredentialsProviderUtil.ticketWillExpire(ticket); + assertFalse(first); + CredentialsProviderUtil.ticketExpireTime80 = System.currentTimeMillis() - 1; + Boolean second = CredentialsProviderUtil.ticketWillExpire(ticket); + assertTrue(second); + assertEquals(0L, CredentialsProviderUtil.ticketExpireTime80); + } + + @Test + void getTGT_returnsNullWhenNoTgt() { + Subject subject = new Subject(); + KerberosPrincipal client = new KerberosPrincipal("user@EXAMPLE.COM"); + KerberosPrincipal server = new KerberosPrincipal("HTTP/host@EXAMPLE.COM"); + Date now = new Date(); + Date end = new Date(System.currentTimeMillis() + 60_000); + Date renew = new Date(end.getTime() + 60_000); + KerberosTicket notTgt = new KerberosTicket(new byte[] {1}, client, server, new byte[] {1}, 1, null, now, now, end, renew, null); + subject.getPrivateCredentials().add(notTgt); + + assertNull(CredentialsProviderUtil.getTGT(subject)); + } + + @Test + void login_success_returnsSubjectFromLoginContext() throws Exception { + Subject subject = new Subject(); + try (MockedConstruction mocked = mockConstruction(LoginContext.class, (mock, context) -> { + when(mock.getSubject()).thenReturn(subject); + })) { + Subject out = CredentialsProviderUtil.login("user@EXAMPLE.COM", "/tmp/user.keytab"); + assertSame(subject, out); + verify(mocked.constructed().get(0), times(1)).login(); + } + } + + @Test + void login_throwsPrivilegedActionException_whenLoginContextFails() throws Exception { + try (MockedConstruction mocked = mockConstruction(LoginContext.class, (mock, context) -> { + doThrow(new LoginException("boom")).when(mock).login(); + })) { + PrivilegedActionException pae = assertThrows(PrivilegedActionException.class, () -> CredentialsProviderUtil.login("user@EXAMPLE.COM", "/tmp/user.keytab")); + assertTrue(pae.getException() instanceof LoginException); + assertEquals("boom", pae.getException().getMessage()); + } + } +} diff --git a/agents-cred/src/test/java/org/apache/ranger/authorization/credutils/kerberos/KerberosCredentialsProviderTest.java b/agents-cred/src/test/java/org/apache/ranger/authorization/credutils/kerberos/KerberosCredentialsProviderTest.java new file mode 100644 index 0000000000..eb9ad60b90 --- /dev/null +++ b/agents-cred/src/test/java/org/apache/ranger/authorization/credutils/kerberos/KerberosCredentialsProviderTest.java @@ -0,0 +1,86 @@ +/* + * 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.authorization.credutils.kerberos; + +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.Credentials; +import org.apache.http.client.config.AuthSchemes; +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.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; + +/** + * @generated by Cursor + * @description : Unit Test cases for KerberosCredentialsProvider + */ + +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class KerberosCredentialsProviderTest { + @Test + public void setCredentials_acceptsOnlySpnego() { + KerberosCredentialsProvider provider = new KerberosCredentialsProvider(); + Credentials creds = mock(Credentials.class); + + AuthScope spnego = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthSchemes.SPNEGO); + assertDoesNotThrow(() -> provider.setCredentials(spnego, creds)); + + AuthScope basic = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthSchemes.BASIC); + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> provider.setCredentials(basic, creds)); + assertTrue(ex.getMessage().contains(AuthSchemes.SPNEGO)); + } + + @Test + public void getCredentials_matchesOnScope_andReturnsNullWhenNoMatch() { + KerberosCredentialsProvider provider = new KerberosCredentialsProvider(); + Credentials creds = mock(Credentials.class); + AuthScope setScope = new AuthScope("host", 8080, AuthScope.ANY_REALM, AuthSchemes.SPNEGO); + provider.setCredentials(setScope, creds); + + // exact match + assertSame(creds, provider.getCredentials(new AuthScope("host", 8080, AuthScope.ANY_REALM, AuthSchemes.SPNEGO))); + // partial match with ANY_HOST/ANY_PORT should still match + assertSame(creds, provider.getCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthSchemes.SPNEGO))); + // different scheme should not match + assertNull(provider.getCredentials(new AuthScope("host", 8080, AuthScope.ANY_REALM, AuthSchemes.BASIC))); + // null arg returns null + assertNull(provider.getCredentials(null)); + } + + @Test + public void clear_resetsState() { + KerberosCredentialsProvider provider = new KerberosCredentialsProvider(); + Credentials creds = mock(Credentials.class); + provider.setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthSchemes.SPNEGO), creds); + + assertNotNull(provider.getCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthSchemes.SPNEGO))); + provider.clear(); + assertNull(provider.getCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthSchemes.SPNEGO))); + } +} diff --git a/agents-cred/src/test/java/org/apache/ranger/authorization/credutils/kerberos/KeytabJaasConfTest.java b/agents-cred/src/test/java/org/apache/ranger/authorization/credutils/kerberos/KeytabJaasConfTest.java new file mode 100644 index 0000000000..af061d539e --- /dev/null +++ b/agents-cred/src/test/java/org/apache/ranger/authorization/credutils/kerberos/KeytabJaasConfTest.java @@ -0,0 +1,73 @@ +/* + * 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.authorization.credutils.kerberos; + +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.security.auth.login.AppConfigurationEntry; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * @generated by Cursor + * @description : Unit Test cases for KeytabJaasConf + */ + +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class KeytabJaasConfTest { + @Test + public void getAppConfigurationEntry_includesExpectedOptions_andIsImmutable() { + KeytabJaasConf conf = new KeytabJaasConf("user@EXAMPLE.COM", "/tmp/user.keytab", true); + AppConfigurationEntry[] entries = conf.getAppConfigurationEntry("any"); + assertNotNull(entries); + assertEquals(1, entries.length); + + AppConfigurationEntry entry = entries[0]; + assertEquals("com.sun.security.auth.module.Krb5LoginModule", entry.getLoginModuleName()); + assertEquals(AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, entry.getControlFlag()); + + Map options = entry.getOptions(); + assertEquals("user@EXAMPLE.COM", options.get("principal")); + assertEquals(Boolean.TRUE.toString(), options.get("isInitiator")); + assertEquals(Boolean.TRUE.toString(), options.get("storeKey")); + assertEquals(Boolean.TRUE.toString(), options.get("debug")); + assertEquals(Boolean.TRUE.toString(), options.get("useKeyTab")); + assertEquals("/tmp/user.keytab", options.get("keyTab")); + assertEquals(Boolean.TRUE.toString(), options.get("doNotPrompt")); + + assertThrows(UnsupportedOperationException.class, () -> ((Map) options).put("x", "y")); + } + + @Test + public void getAppConfigurationEntry_respectsDebugFlag() { + KeytabJaasConf conf = new KeytabJaasConf("user@EXAMPLE.COM", "/tmp/user.keytab", false); + AppConfigurationEntry[] entries = conf.getAppConfigurationEntry("any"); + Map options = entries[0].getOptions(); + assertEquals(Boolean.FALSE.toString(), options.get("debug")); + } +} diff --git a/agents-cred/src/test/java/org/apache/ranger/authorization/hadoop/utils/RangerCredentialProviderTest.java b/agents-cred/src/test/java/org/apache/ranger/authorization/hadoop/utils/RangerCredentialProviderTest.java index dcfe5e4da6..97c2e85fc6 100644 --- a/agents-cred/src/test/java/org/apache/ranger/authorization/hadoop/utils/RangerCredentialProviderTest.java +++ b/agents-cred/src/test/java/org/apache/ranger/authorization/hadoop/utils/RangerCredentialProviderTest.java @@ -23,14 +23,21 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.util.List; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) public class RangerCredentialProviderTest { private final File ksFile; private final String[] argsCreate; @@ -128,6 +135,13 @@ public void testCredentialString() { listThreads(); } + @Test + public void testGetInstanceSingleton() { + RangerCredentialProvider first = RangerCredentialProvider.getInstance(); + RangerCredentialProvider second = RangerCredentialProvider.getInstance(); + assertSame(first, second); + } + @After public void teardown() throws Exception { System.out.println("In teardown : Number of active Threads : " + Thread.activeCount());