From bf30cba1655afea13655ae180871bf9cb61c3baa Mon Sep 17 00:00:00 2001 From: Ramgopal Nagaboina Date: Thu, 3 Sep 2026 20:03:19 -0400 Subject: [PATCH] server: do not NPE when a VPN provider returns no per-user result applyVpnUsers sizes a Boolean[] finals to the user list but only populates it inside the if (results != null) block. A RemoteAccessVPNServiceProvider that returns null (for example when the VPN network has no router yet) leaves entries null, and the later consumption loop unboxed them with if (finals[i]), throwing NullPointerException and aborting the entire apply/add/remove-user operation. Treat a null entry as not-applied with Boolean.TRUE.equals. --- .../vpn/RemoteAccessVpnManagerImpl.java | 2 +- .../vpn/RemoteAccessVpnManagerImplTest.java | 50 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java b/server/src/main/java/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java index 29c0106dc185..1d93e73697dd 100644 --- a/server/src/main/java/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java +++ b/server/src/main/java/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java @@ -603,7 +603,7 @@ public boolean applyVpnUsers(long vpnOwnerId, String userName, boolean forRemove for (int i = 0; i < finals.length; i++) { final VpnUserVO user = users.get(i); - if (finals[i]) { + if (Boolean.TRUE.equals(finals[i])) { if (user.getState() == State.Add) { user.setState(State.Active); _vpnUsersDao.update(user.getId(), user); diff --git a/server/src/test/java/com/cloud/network/vpn/RemoteAccessVpnManagerImplTest.java b/server/src/test/java/com/cloud/network/vpn/RemoteAccessVpnManagerImplTest.java index f8b4362e76b5..ab866449e5c3 100644 --- a/server/src/test/java/com/cloud/network/vpn/RemoteAccessVpnManagerImplTest.java +++ b/server/src/test/java/com/cloud/network/vpn/RemoteAccessVpnManagerImplTest.java @@ -15,9 +15,23 @@ package com.cloud.network.vpn; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.VpnUser.State; +import com.cloud.network.VpnUserVO; +import com.cloud.network.dao.RemoteAccessVpnDao; +import com.cloud.network.dao.RemoteAccessVpnVO; +import com.cloud.network.dao.VpnUserDao; +import com.cloud.network.element.RemoteAccessVPNServiceProvider; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.AccountVO; +import com.cloud.user.dao.AccountDao; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import junit.framework.TestCase; +import org.apache.cloudstack.context.CallContext; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Collections; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -222,4 +236,40 @@ public void validateHandleExceptionOnValidateIpRangeErrorWhenInvalidParameterVal assertEquals(expectedMessage, assertThrows.getMessage()); } + + @Test + public void applyVpnUsersHandlesNullProviderResultWithoutNpe() throws Exception { + RemoteAccessVpnManagerImpl mgr = new RemoteAccessVpnManagerImpl(); + + AccountDao accountDao = Mockito.mock(AccountDao.class); + AccountManager accountMgr = Mockito.mock(AccountManager.class); + VpnUserDao vpnUsersDao = Mockito.mock(VpnUserDao.class); + RemoteAccessVpnDao remoteAccessVpnDao = Mockito.mock(RemoteAccessVpnDao.class); + RemoteAccessVPNServiceProvider provider = Mockito.mock(RemoteAccessVPNServiceProvider.class); + ReflectionTestUtils.setField(mgr, "_accountDao", accountDao); + ReflectionTestUtils.setField(mgr, "_accountMgr", accountMgr); + ReflectionTestUtils.setField(mgr, "_vpnUsersDao", vpnUsersDao); + ReflectionTestUtils.setField(mgr, "_remoteAccessVpnDao", remoteAccessVpnDao); + ReflectionTestUtils.setField(mgr, "_vpnServiceProviders", Collections.singletonList(provider)); + + Mockito.when(accountDao.findById(1L)).thenReturn(Mockito.mock(AccountVO.class)); + + RemoteAccessVpnVO vpn = Mockito.mock(RemoteAccessVpnVO.class); + Mockito.when(vpn.getNetworkId()).thenReturn(null); + Mockito.when(remoteAccessVpnDao.findByAccount(1L)).thenReturn(Collections.singletonList(vpn)); + + VpnUserVO user = Mockito.mock(VpnUserVO.class); + Mockito.when(user.getState()).thenReturn(State.Revoke); + Mockito.when(vpnUsersDao.listByAccount(1L)).thenReturn(Collections.singletonList(user)); + + Mockito.when(provider.applyVpnUsers(Mockito.eq(vpn), Mockito.anyList())).thenReturn(null); + + try (MockedStatic callContextMock = Mockito.mockStatic(CallContext.class)) { + CallContext callContext = Mockito.mock(CallContext.class); + callContextMock.when(CallContext::current).thenReturn(callContext); + Mockito.when(callContext.getCallingAccount()).thenReturn(Mockito.mock(Account.class)); + + Assert.assertTrue(mgr.applyVpnUsers(1L, "someuser", false)); + } + } }