diff --git a/servers/zms/pom.xml b/servers/zms/pom.xml index 9ada4a598ec..cefa825d107 100644 --- a/servers/zms/pom.xml +++ b/servers/zms/pom.xml @@ -27,7 +27,7 @@ war - 0.9745 + 0.9760 diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/DBService.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/DBService.java index dfcae50284e..24f37ebbd26 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/DBService.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/DBService.java @@ -528,11 +528,13 @@ boolean processPolicy(ObjectStoreConnection con, Policy originalPolicy, String d if (newAssertions != null) { for (Assertion assertion : newAssertions) { if (!con.insertAssertion(domainName, policyName, policy.getVersion(), assertion)) { + LOG.error("unable to insert assertion for policy {} in domain {}", policyName, domainName); return false; } // insert the new assertion conditions if any if (assertion.getConditions() != null) { if (!con.insertAssertionConditions(assertion.getId(), assertion.getConditions())) { + LOG.error("unable to insert assertion conditions for policy {} in domain {}", policyName, domainName); return false; } } @@ -563,6 +565,7 @@ boolean processPolicy(ObjectStoreConnection con, Policy originalPolicy, String d if (!ignoreDeletes) { for (Assertion assertion : delAssertions) { if (!con.deleteAssertion(domainName, policyName, policy.getVersion(), assertion.getId())) { + LOG.error("unable to delete assertion for policy {} in domain {}", policyName, domainName); return false; } } @@ -571,10 +574,12 @@ boolean processPolicy(ObjectStoreConnection con, Policy originalPolicy, String d for (Assertion assertion : addAssertions) { if (!con.insertAssertion(domainName, policyName, policy.getVersion(), assertion)) { + LOG.error("unable to insert assertion for policy {} in domain {}", policyName, domainName); return false; } if (assertion.getConditions() != null) { if (!con.insertAssertionConditions(assertion.getId(), assertion.getConditions())) { + LOG.error("unable to insert assertion condition for policy {} in domain {}", policyName, domainName); return false; } } @@ -588,6 +593,7 @@ boolean processPolicy(ObjectStoreConnection con, Policy originalPolicy, String d for (Map.Entry> entry : delConditions.entrySet()) { Long assertionId = entry.getKey(); if (!con.deleteAssertionConditions(assertionId)) { + LOG.error("unable to delete assertion condition for policy {} in domain {}", policyName, domainName); return false; } } @@ -596,6 +602,7 @@ boolean processPolicy(ObjectStoreConnection con, Policy originalPolicy, String d Long assertionId = entry.getKey(); List conditionsList = entry.getValue(); if (!con.insertAssertionConditions(assertionId, new AssertionConditions().setConditionsList(conditionsList))) { + LOG.error("unable to insert assertion condition for policy {} in domain {}", policyName, domainName); return false; } } @@ -819,6 +826,8 @@ boolean processRole(ObjectStoreConnection con, Role originalRole, String domainN } member.setRequestPrincipal(admin); if (!con.insertRoleMember(domainName, roleName, member, admin, auditRef)) { + LOG.error("unable to insert member {} to role {} in domain {}", + member.getMemberName(), roleName, domainName); return false; } } @@ -922,6 +931,8 @@ boolean processGroup(ObjectStoreConnection con, Group originalGroup, final Strin member.setPendingState(null); } if (!con.insertGroupMember(domainName, groupName, member, admin, auditRef)) { + LOG.error("unable to insert member {} to group {} in domain {}", + member.getMemberName(), groupName, domainName); return false; } } @@ -1155,11 +1166,15 @@ boolean processUpdateRoleMembers(ObjectStoreConnection con, Role originalRole, if (pendingState && deleteProtection == Boolean.TRUE) { member.setApproved(false).setPendingState(ZMSConsts.PENDING_REQUEST_DELETE_STATE); if (!con.insertRoleMember(domainName, roleName, member, admin, auditRef)) { + LOG.error("unable to insert member {} to role {} in domain {}", + member.getMemberName(), roleName, domainName); return false; } addMemberToNotifySet(notifyMembers, member.getMemberName()); } else { if (!con.deleteRoleMember(domainName, roleName, member.getMemberName(), admin, auditRef)) { + LOG.error("unable to delete member {} from role {} in domain {}", + member.getMemberName(), roleName, domainName); return false; } } @@ -1175,6 +1190,8 @@ boolean processUpdateRoleMembers(ObjectStoreConnection con, Role originalRole, member.setPendingState(null); } if (!con.insertRoleMember(domainName, roleName, member, admin, auditRef)) { + LOG.error("unable to insert member {} to role {} in domain {}", + member.getMemberName(), roleName, domainName); return false; } } @@ -1209,11 +1226,15 @@ private boolean processUpdateGroupMembers(ObjectStoreConnection con, Group origi if (pendingState && deleteProtection == Boolean.TRUE) { member.setApproved(false).setPendingState(ZMSConsts.PENDING_REQUEST_DELETE_STATE); if (!con.insertGroupMember(domainName, groupName, member, admin, auditRef)) { + LOG.error("unable to insert member {} to group {} in domain {}", + member.getMemberName(), groupName, domainName); return false; } addMemberToNotifySet(notifyMembers, member.getMemberName()); } else { if (!con.deleteGroupMember(domainName, groupName, member.getMemberName(), admin, auditRef)) { + LOG.error("unable to delete member {} from group {} in domain {}", + member.getMemberName(), groupName, domainName); return false; } } @@ -1229,6 +1250,8 @@ private boolean processUpdateGroupMembers(ObjectStoreConnection con, Group origi member.setPendingState(null); } if (!con.insertGroupMember(domainName, groupName, member, admin, auditRef)) { + LOG.error("unable to insert member {} to group {} in domain {}", + member.getMemberName(), groupName, domainName); return false; } } @@ -1290,6 +1313,7 @@ boolean processServiceIdentity(ResourceContext ctx, ObjectStoreConnection con, S for (PublicKeyEntry publicKey : publicKeys) { if (!con.insertPublicKeyEntry(domainName, serviceName, publicKey)) { + LOG.error("unable to insert public key to service {} in domain {}", serviceName, domainName); return false; } } @@ -1331,6 +1355,7 @@ boolean processServiceIdentity(ResourceContext ctx, ObjectStoreConnection con, S if (!ignoreDeletes) { for (String publicKey : delPublicKeysSet) { if (!con.deletePublicKeyEntry(domainName, serviceName, publicKey)) { + LOG.error("unable to delete public key from service {} in domain {}", serviceName, domainName); return false; } } @@ -1339,6 +1364,7 @@ boolean processServiceIdentity(ResourceContext ctx, ObjectStoreConnection con, S for (String publicKey : newPublicKeysSet) { if (!con.insertPublicKeyEntry(domainName, serviceName, publicKeysMap.get(publicKey))) { + LOG.error("unable to insert public key to service {} in domain {}", serviceName, domainName); return false; } } @@ -1346,6 +1372,7 @@ boolean processServiceIdentity(ResourceContext ctx, ObjectStoreConnection con, S for (String publicKey : updatePublicKeysSet) { if (!con.updatePublicKeyEntry(domainName, serviceName, updatePublicKeysMap.get(publicKey))) { + LOG.error("unable to update public key in service {} in domain {}", serviceName, domainName); return false; } } @@ -1374,6 +1401,7 @@ boolean processServiceIdentity(ResourceContext ctx, ObjectStoreConnection con, S for (String host : delHosts) { if (!con.deleteServiceHost(domainName, serviceName, host)) { + LOG.error("unable to delete host in service {} in domain {}", serviceName, domainName); return false; } } @@ -1381,6 +1409,7 @@ boolean processServiceIdentity(ResourceContext ctx, ObjectStoreConnection con, S for (String host : newHosts) { if (!con.insertServiceHost(domainName, serviceName, host)) { + LOG.error("unable to insert host to service {} in domain {}", serviceName, domainName); return false; } } @@ -1587,6 +1616,7 @@ boolean processPolicyCopyAssertions(ObjectStoreConnection con, Policy policy, fi // insert assertion (and get new assertion id) if (!con.insertAssertion(domainName, policyName, version, assertion)) { + LOG.error("unable to insert assertion to policy {} in domain {}", policyName, domainName); return false; } @@ -1594,6 +1624,7 @@ boolean processPolicyCopyAssertions(ObjectStoreConnection con, Policy policy, fi if (assertionConditions.getConditionsList() != null && !assertionConditions.getConditionsList().isEmpty()) { if (!con.insertAssertionConditions(assertion.getId(), assertionConditions)) { + LOG.error("unable to insert assertion conditions to policy {} in domain {}", policyName, domainName); return false; } } @@ -3255,8 +3286,7 @@ void executeDeleteUser(ResourceContext ctx, String userName, String domainName, // left behind will be cleaned up from this operation if (!con.deletePrincipal(userName, true)) { - throw ZMSUtils.notFoundError(caller + ": unable to delete user: " - + userName, caller); + throw ZMSUtils.notFoundError(caller + ": unable to delete user: " + userName, caller); } // automatically update any domain contact record where this user is referenced @@ -4539,6 +4569,8 @@ boolean processDomainContacts(ObjectStoreConnection con, final String domainName for (Map.Entry entry : updatedContacts.entrySet()) { if (!StringUtil.isEmpty(entry.getValue())) { if (!con.insertDomainContact(domainName, entry.getKey(), entry.getValue())) { + LOG.error("unable to insert contact {}/{} in domain {}", + entry.getKey(), entry.getValue(), domainName); return false; } } @@ -4552,6 +4584,7 @@ boolean processDomainContacts(ObjectStoreConnection con, final String domainName if (updatedContacts.isEmpty()) { for (String contact : originalContacts.keySet()) { if (!con.deleteDomainContact(domainName, contact)) { + LOG.error("unable to delete contact type {} in domain {}", contact, domainName); return false; } } @@ -4568,10 +4601,12 @@ boolean processDomainContacts(ObjectStoreConnection con, final String domainName if (!originalContacts.get(type).equals(name)) { if (StringUtil.isEmpty(name)) { if (!con.deleteDomainContact(domainName, type)) { + LOG.error("unable to delete contact {} in domain {}", type, domainName); return false; } } else { if (!con.updateDomainContact(domainName, type, name)) { + LOG.error("unable to update contact {}/{} in domain {}", type, name, domainName); return false; } } @@ -4579,6 +4614,7 @@ boolean processDomainContacts(ObjectStoreConnection con, final String domainName } else { if (!StringUtil.isEmpty(entry.getValue())) { if (!con.insertDomainContact(domainName, type, name)) { + LOG.error("unable to insert contact {}/{} in domain {}", type, name, domainName); return false; } } @@ -4591,6 +4627,7 @@ boolean processDomainContacts(ObjectStoreConnection con, final String domainName for (String type : originalContacts.keySet()) { if (!updatedContacts.containsKey(type)) { if (!con.deleteDomainContact(domainName, type)) { + LOG.error("unable to delete contact type {} in domain {}", type, domainName); return false; } } @@ -7697,11 +7734,12 @@ private boolean insertRoleMembers(ResourceContext ctx, ObjectStoreConnection con roleMember.setPendingState(roleMember.getApproved() == Boolean.FALSE ? ZMSConsts.PENDING_REQUEST_ADD_STATE : null); roleMember.setRequestPrincipal(principal); if (!con.insertRoleMember(domainName, roleName, roleMember, principal, auditRef)) { - LOG.error("unable to update member {}", roleMember.getMemberName()); + LOG.error("unable to update member {} in domain {}", roleMember.getMemberName(), domainName); continue; } } catch (Exception ex) { - LOG.error("unable to update member {} error: {}", roleMember.getMemberName(), ex.getMessage()); + LOG.error("unable to update member {} in domain {}, error: {}", roleMember.getMemberName(), + domainName, ex.getMessage()); continue; } @@ -7729,11 +7767,12 @@ boolean insertGroupMembers(ResourceContext ctx, ObjectStoreConnection con, List< for (GroupMember groupMember : groupMembers) { try { if (!con.insertGroupMember(domainName, groupName, groupMember, principal, auditRef)) { - LOG.error("unable to update group member {}", groupMember.getMemberName()); + LOG.error("unable to update group member {} in domain {}", groupMember.getMemberName(), domainName); continue; } } catch (Exception ex) { - LOG.error("unable to update member {} error: {}", groupMember.getMemberName(), ex.getMessage()); + LOG.error("unable to update member {} in domain {}, error: {}", groupMember.getMemberName(), + domainName, ex.getMessage()); continue; } @@ -7762,11 +7801,12 @@ boolean updateRoleMemberDisabledState(ResourceContext ctx, ObjectStoreConnection try { if (!con.updateRoleMemberDisabledState(domainName, roleName, roleMember.getMemberName(), principal, roleMember.getSystemDisabled(), auditRef)) { - LOG.error("unable to update member {}", roleMember.getMemberName()); + LOG.error("unable to update member {} in domain {}", roleMember.getMemberName(), domainName); continue; } } catch (Exception ex) { - LOG.error("unable to update member {} error: {}", roleMember.getMemberName(), ex.getMessage()); + LOG.error("unable to update member {} in domain {}, error: {}", roleMember.getMemberName(), + domainName, ex.getMessage()); continue; } @@ -7795,11 +7835,12 @@ boolean updateGroupMemberDisabledState(ResourceContext ctx, ObjectStoreConnectio try { if (!con.updateGroupMemberDisabledState(domainName, groupName, groupMember.getMemberName(), principal, groupMember.getSystemDisabled(), auditRef)) { - LOG.error("unable to update group member {}", groupMember.getMemberName()); + LOG.error("unable to update group member {} in domain {}", groupMember.getMemberName(), domainName); continue; } } catch (Exception ex) { - LOG.error("unable to update group member {} error: {}", groupMember.getMemberName(), ex.getMessage()); + LOG.error("unable to update group member {} in domain {}, error: {}", groupMember.getMemberName(), + domainName, ex.getMessage()); continue; } @@ -9220,7 +9261,8 @@ void executePutAssertionConditions(ResourceContext ctx, String domainName, Strin // process our insert assertion condition. if (!con.insertAssertionConditions(assertionId, assertionConditions)) { - throw ZMSUtils.requestError(String.format("%s: unable to insert assertion conditions for policy=%s assertionId=%d", caller, policyName, assertionId), caller); + throw ZMSUtils.requestError(String.format("%s: unable to insert assertion conditions for policy=%s assertionId=%d", + caller, policyName, assertionId), caller); } // update our policy and domain time-stamps, and invalidate local cache entry @@ -9573,6 +9615,7 @@ private boolean processDomainDependency(ObjectStoreConnection con, String domain // if we didn't insert a dependency then we need to return failure if (!con.insertDomainDependency(domainName, service)) { + LOG.error("unable to insert dependency {} in domain {}", service, domainName); return false; } @@ -9592,6 +9635,7 @@ private boolean processDeleteDomainDependency(ObjectStoreConnection con, String // if we didn't delete the dependency then we need to return failure if (!con.deleteDomainDependency(domainName, service)) { + LOG.error("unable to delete dependency {} in domain {}", service, domainName); return false; } diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/DBServiceTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/DBServiceTest.java index e9208236a9e..319a3eb1139 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/DBServiceTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/DBServiceTest.java @@ -6370,6 +6370,30 @@ public void testProcessRoleUpdate() throws ServerResourceException { assertFalse(role4.getSelfServe()); } + @Test + public void testProcessRoleInsertRoleMemberFailure() throws ServerResourceException { + ObjectStoreConnection conn = Mockito.mock(ObjectStoreConnection.class); + + // Create a role with role members + List roleMembers = new ArrayList<>(); + roleMembers.add(new RoleMember().setMemberName("user.member1")); + Role role = new Role().setName("newRole").setRoleMembers(roleMembers); + + // Mock insertRole to return true (so we pass the first check) + Mockito.when(conn.insertRole(anyString(), any(Role.class))).thenReturn(true); + + // Mock insertRoleMember to return false (to trigger failure) + Mockito.when(conn.insertRoleMember(anyString(), anyString(), any(RoleMember.class), + anyString(), anyString())).thenReturn(false); + + StringBuilder auditDetails = new StringBuilder("testAudit"); + boolean result = zms.dbService.processRole(conn, null, "testDomain", "newRole", + role, adminUser, null, auditRef, false, auditDetails); + + // Assert that processRole returns false when insertRoleMember fails + assertFalse(result); + } + @Test public void testUpdateRoleMetaFields() throws ServerResourceException { final String caller = "testUpdateRoleMetaFields"; @@ -10782,6 +10806,28 @@ public void testUpdateGroupMemberDisabledStateFailure() throws ServerResourceExc zms.dbService.store = saveStore; } + @Test + public void testUpdateGroupMemberDisabledStateException() throws ServerResourceException { + + final String domainName = "update-group-members-disabled-exception"; + final String groupName = "group1"; + + Mockito.when(mockJdbcConn.updateGroupMemberDisabledState(anyString(), anyString(), anyString(), anyString(), + anyInt(), anyString())).thenThrow(new ServerResourceException(ServerResourceException.INTERNAL_SERVER_ERROR, "Database error")); + + ObjectStore saveStore = zms.dbService.store; + zms.dbService.store = mockObjStore; + + List groupMembers = new ArrayList<>(); + groupMembers.add(new GroupMember().setMemberName("user.joe").setSystemDisabled(1)); + groupMembers.add(new GroupMember().setMemberName("user.jane").setSystemDisabled(2)); + + assertFalse(zms.dbService.updateGroupMemberDisabledState(mockDomRsrcCtx, mockJdbcConn, groupMembers, + domainName, groupName, adminUser, auditRef, "unit-test")); + + zms.dbService.store = saveStore; + } + @Test public void testExecutePutGroupMembershipDecisionFailure() throws ServerResourceException { @@ -11934,6 +11980,165 @@ public void testServiceSameTagKeyValues() throws ServerResourceException { } + @Test + public void testProcessServiceIdentityInsertPublicKeyEntryFailure() throws ServerResourceException { + // Test case for insertPublicKeyEntry fails for new service + ObjectStoreConnection conn = Mockito.mock(ObjectStoreConnection.class); + + PublicKeyEntry publicKey = new PublicKeyEntry().setId("key1").setKey("public-key-data"); + List publicKeys = Collections.singletonList(publicKey); + ServiceIdentity service = new ServiceIdentity() + .setName("newService") + .setPublicKeys(publicKeys); + + Mockito.when(conn.insertServiceIdentity("sys.auth", service)).thenReturn(true); + Mockito.when(conn.insertPublicKeyEntry("sys.auth", "newService", publicKey)).thenReturn(false); + + StringBuilder auditDetails = new StringBuilder("testAudit"); + boolean success = zms.dbService.processServiceIdentity(null, conn, null, "sys.auth", "newService", + service, false, auditDetails); + + assertFalse(success); + Mockito.verify(conn, times(1)).insertServiceIdentity("sys.auth", service); + Mockito.verify(conn, times(1)).insertPublicKeyEntry("sys.auth", "newService", publicKey); + } + + @Test + public void testProcessServiceIdentityDeletePublicKeyEntryFailure() throws ServerResourceException { + // Test case for deletePublicKeyEntry fails for update service + ObjectStoreConnection conn = Mockito.mock(ObjectStoreConnection.class); + + PublicKeyEntry originalPublicKey = new PublicKeyEntry().setId("key1").setKey("original-key-data"); + List originalPublicKeys = Collections.singletonList(originalPublicKey); + ServiceIdentity originalService = new ServiceIdentity() + .setName("existingService") + .setPublicKeys(originalPublicKeys); + + // New service without the public key (should trigger deletion) + ServiceIdentity service = new ServiceIdentity() + .setName("existingService") + .setPublicKeys(null); + + Mockito.when(conn.updateServiceIdentity("sys.auth", service)).thenReturn(true); + Mockito.when(conn.deletePublicKeyEntry("sys.auth", "existingService", "key1")).thenReturn(false); + + StringBuilder auditDetails = new StringBuilder("testAudit"); + boolean success = zms.dbService.processServiceIdentity(null, conn, originalService, "sys.auth", "existingService", + service, false, auditDetails); + + assertFalse(success); + Mockito.verify(conn, times(1)).updateServiceIdentity("sys.auth", service); + Mockito.verify(conn, times(1)).deletePublicKeyEntry("sys.auth", "existingService", "key1"); + } + + @Test + public void testProcessServiceIdentityUpdatePublicKeyEntryFailure() throws ServerResourceException { + // Test case for updatePublicKeyEntry fails for update service + ObjectStoreConnection conn = Mockito.mock(ObjectStoreConnection.class); + + PublicKeyEntry originalPublicKey = new PublicKeyEntry().setId("key1").setKey("original-key-data"); + List originalPublicKeys = Collections.singletonList(originalPublicKey); + ServiceIdentity originalService = new ServiceIdentity() + .setName("existingService") + .setPublicKeys(originalPublicKeys); + + // Update service with same key id but different key value (should trigger update) + PublicKeyEntry updatedPublicKey = new PublicKeyEntry().setId("key1").setKey("updated-key-data"); + List updatedPublicKeys = Collections.singletonList(updatedPublicKey); + ServiceIdentity service = new ServiceIdentity() + .setName("existingService") + .setPublicKeys(updatedPublicKeys); + + Mockito.when(conn.updateServiceIdentity("sys.auth", service)).thenReturn(true); + Mockito.when(conn.updatePublicKeyEntry("sys.auth", "existingService", updatedPublicKey)).thenReturn(false); + + StringBuilder auditDetails = new StringBuilder("testAudit"); + boolean success = zms.dbService.processServiceIdentity(null, conn, originalService, "sys.auth", "existingService", + service, false, auditDetails); + + assertFalse(success); + Mockito.verify(conn, times(1)).updateServiceIdentity("sys.auth", service); + Mockito.verify(conn, times(1)).updatePublicKeyEntry("sys.auth", "existingService", updatedPublicKey); + } + + @Test + public void testProcessServiceIdentityDeleteServiceHostFailure() throws ServerResourceException { + // Test case for deleteServiceHost fails + ObjectStoreConnection conn = Mockito.mock(ObjectStoreConnection.class); + + List originalHosts = Collections.singletonList("host1.example.com"); + ServiceIdentity originalService = new ServiceIdentity() + .setName("existingService") + .setHosts(originalHosts); + + // New service without the host (should trigger deletion) + ServiceIdentity service = new ServiceIdentity() + .setName("existingService") + .setHosts(null); + + Mockito.when(conn.updateServiceIdentity("sys.auth", service)).thenReturn(true); + Mockito.when(conn.deleteServiceHost("sys.auth", "existingService", "host1.example.com")).thenReturn(false); + + StringBuilder auditDetails = new StringBuilder("testAudit"); + boolean success = zms.dbService.processServiceIdentity(null, conn, originalService, "sys.auth", "existingService", + service, false, auditDetails); + + assertFalse(success); + Mockito.verify(conn, times(1)).updateServiceIdentity("sys.auth", service); + Mockito.verify(conn, times(1)).deleteServiceHost("sys.auth", "existingService", "host1.example.com"); + } + + @Test + public void testProcessServiceIdentityInsertServiceHostFailure() throws ServerResourceException { + // Test case for insertServiceHost fails + ObjectStoreConnection conn = Mockito.mock(ObjectStoreConnection.class); + + ServiceIdentity originalService = new ServiceIdentity() + .setName("existingService") + .setHosts(null); + + // New service with a new host (should trigger insertion) + List newHosts = Collections.singletonList("newhost.example.com"); + ServiceIdentity service = new ServiceIdentity() + .setName("existingService") + .setHosts(newHosts); + + Mockito.when(conn.updateServiceIdentity("sys.auth", service)).thenReturn(true); + Mockito.when(conn.insertServiceHost("sys.auth", "existingService", "newhost.example.com")).thenReturn(false); + + StringBuilder auditDetails = new StringBuilder("testAudit"); + boolean success = zms.dbService.processServiceIdentity(null, conn, originalService, "sys.auth", "existingService", + service, false, auditDetails); + + assertFalse(success); + Mockito.verify(conn, times(1)).updateServiceIdentity("sys.auth", service); + Mockito.verify(conn, times(1)).insertServiceHost("sys.auth", "existingService", "newhost.example.com"); + } + + @Test + public void testProcessServiceIdentityProcessTagsFailure() throws ServerResourceException { + // Test case for processServiceIdentityTags fails + ObjectStoreConnection conn = Mockito.mock(ObjectStoreConnection.class); + + Map serviceTags = Collections.singletonMap( + "tagKey", new TagValueList().setList(Collections.singletonList("tagVal")) + ); + ServiceIdentity service = new ServiceIdentity() + .setName("newService") + .setTags(serviceTags); + + Mockito.when(conn.insertServiceIdentity("sys.auth", service)).thenReturn(true); + Mockito.when(conn.insertServiceTags("newService", "sys.auth", serviceTags)).thenReturn(false); + + StringBuilder auditDetails = new StringBuilder("testAudit"); + boolean success = zms.dbService.processServiceIdentity(null, conn, null, "sys.auth", "newService", + service, false, auditDetails); + + assertFalse(success); + Mockito.verify(conn, times(1)).insertServiceIdentity("sys.auth", service); + Mockito.verify(conn, times(1)).insertServiceTags("newService", "sys.auth", serviceTags); + } + @Test public void testRoleSameTagKeyValues() throws ServerResourceException { ObjectStoreConnection conn = Mockito.mock(ObjectStoreConnection.class); @@ -13434,6 +13639,122 @@ public void testProcessPolicyAssertionConditionsConReturnFalse() throws ServerRe } + + @Test + public void testProcessPolicyDeleteAssertionReturnsFalse() throws ServerResourceException { + // Test case for deleteAssertion returns false + ObjectStoreConnection conn = Mockito.mock(ObjectStoreConnection.class); + + // Create original policy with an assertion that will be deleted + Policy originalPolicy = new Policy().setName("testPolicy"); + Assertion oldAssertion = new Assertion() + .setId(1L) + .setAction("testAction") + .setResource("sys.auth:testResource") + .setRole("sys.auth:role.testRole") + .setEffect(AssertionEffect.ALLOW); + originalPolicy.setAssertions(new LinkedList<>(Collections.singletonList(oldAssertion))); + + // Create new policy without the old assertion (so it will be deleted) + Policy newPolicy = new Policy().setName("testPolicy"); + newPolicy.setAssertions(new LinkedList<>()); + + // Mock updatePolicy to return true, but deleteAssertion to return false + Mockito.when(conn.updatePolicy("sys.auth", newPolicy)).thenReturn(true); + Mockito.when(conn.deleteAssertion("sys.auth", "testPolicy", null, 1L)).thenReturn(false); + + StringBuilder auditDetails = new StringBuilder("testAudit"); + boolean success = zms.dbService.processPolicy(conn, originalPolicy, "sys.auth", "testPolicy", + newPolicy, false, auditDetails); + + assertFalse(success); + Mockito.verify(conn, Mockito.times(1)).deleteAssertion("sys.auth", "testPolicy", null, 1L); + } + + @Test + public void testProcessPolicyInsertAssertionReturnsFalse() throws ServerResourceException { + // Test case for insertAssertion returns false + ObjectStoreConnection conn = Mockito.mock(ObjectStoreConnection.class); + + // Create original policy with an assertion + Policy originalPolicy = new Policy().setName("testPolicy"); + Assertion oldAssertion = new Assertion() + .setId(1L) + .setAction("oldAction") + .setResource("sys.auth:oldResource") + .setRole("sys.auth:role.oldRole") + .setEffect(AssertionEffect.ALLOW); + originalPolicy.setAssertions(new LinkedList<>(Collections.singletonList(oldAssertion))); + + // Create new policy with a different assertion (so old one stays, new one is added) + Policy newPolicy = new Policy().setName("testPolicy"); + Assertion newAssertion = new Assertion() + .setId(2L) + .setAction("newAction") + .setResource("sys.auth:newResource") + .setRole("sys.auth:role.newRole") + .setEffect(AssertionEffect.ALLOW); + newPolicy.setAssertions(new LinkedList<>(Collections.singletonList(newAssertion))); + + // Mock updatePolicy to return true, deleteAssertion to return true (for old assertion), + // but insertAssertion to return false + Mockito.when(conn.updatePolicy("sys.auth", newPolicy)).thenReturn(true); + Mockito.when(conn.deleteAssertion("sys.auth", "testPolicy", null, 1L)).thenReturn(true); + Mockito.when(conn.insertAssertion("sys.auth", "testPolicy", null, newAssertion)).thenReturn(false); + + StringBuilder auditDetails = new StringBuilder("testAudit"); + boolean success = zms.dbService.processPolicy(conn, originalPolicy, "sys.auth", "testPolicy", + newPolicy, false, auditDetails); + + assertFalse(success); + Mockito.verify(conn, Mockito.times(1)).insertAssertion("sys.auth", "testPolicy", null, newAssertion); + } + + @Test + public void testProcessPolicyInsertAssertionConditionsReturnsFalse() throws ServerResourceException { + // Test case for insertAssertionConditions returns false + ObjectStoreConnection conn = Mockito.mock(ObjectStoreConnection.class); + + // Create original policy with an assertion + Policy originalPolicy = new Policy().setName("testPolicy"); + Assertion oldAssertion = new Assertion() + .setId(1L) + .setAction("oldAction") + .setResource("sys.auth:oldResource") + .setRole("sys.auth:role.oldRole") + .setEffect(AssertionEffect.ALLOW); + originalPolicy.setAssertions(new LinkedList<>(Collections.singletonList(oldAssertion))); + + // Create new policy with a new assertion that has conditions + Policy newPolicy = new Policy().setName("testPolicy"); + Assertion newAssertion = new Assertion() + .setId(2L) + .setAction("newAction") + .setResource("sys.auth:newResource") + .setRole("sys.auth:role.newRole") + .setEffect(AssertionEffect.ALLOW) + .setConditions( + new AssertionConditions() + .setConditionsList(List.of(createAssertionConditionObject(1, "test1", "test1"))) + ); + newPolicy.setAssertions(new LinkedList<>(Collections.singletonList(newAssertion))); + + // Mock updatePolicy to return true, deleteAssertion to return true, + // insertAssertion to return true, but insertAssertionConditions to return false + Mockito.when(conn.updatePolicy("sys.auth", newPolicy)).thenReturn(true); + Mockito.when(conn.deleteAssertion("sys.auth", "testPolicy", null, 1L)).thenReturn(true); + Mockito.when(conn.insertAssertion("sys.auth", "testPolicy", null, newAssertion)).thenReturn(true); + Mockito.when(conn.insertAssertionConditions(2L, newAssertion.getConditions())).thenReturn(false); + + StringBuilder auditDetails = new StringBuilder("testAudit"); + boolean success = zms.dbService.processPolicy(conn, originalPolicy, "sys.auth", "testPolicy", + newPolicy, false, auditDetails); + + assertFalse(success); + Mockito.verify(conn, Mockito.times(1)).insertAssertion("sys.auth", "testPolicy", null, newAssertion); + Mockito.verify(conn, Mockito.times(1)).insertAssertionConditions(2L, newAssertion.getConditions()); + } + @Test public void testProcessPolicyWithTagsInsert() throws ServerResourceException { ObjectStoreConnection conn = Mockito.mock(ObjectStoreConnection.class); @@ -13987,6 +14308,114 @@ public void testProcessUpdateRoleMembersDeleteRoleMembers() throws ServerResourc roleName, "user.admin", false, null, null, auditRef, auditDetails)); } + @Test + public void testProcessUpdateGroupMembersInsertGroupMemberReturnsFalse() throws Exception { + + final String domainName = "process-update-group-members-insert-fails"; + final String groupName = "group1"; + + // Create original group with a member that will be deleted + List oldMembers = new ArrayList<>(); + oldMembers.add(new GroupMember().setMemberName("user.joe").setActive(true).setApproved(true)); + Group originalGroup = new Group().setName(groupName).setGroupMembers(oldMembers); + + // New group with no members (member will be deleted) + List newMembers = new ArrayList<>(); + + ObjectStoreConnection conn = Mockito.mock(ObjectStoreConnection.class); + // Mock insertGroupMember to return false + Mockito.when(conn.insertGroupMember(eq(domainName), eq(groupName), any(GroupMember.class), + eq("user.admin"), eq(auditRef))).thenReturn(false); + + StringBuilder auditDetails = new StringBuilder(); + Set notifyMembers = new HashSet<>(); + + // Use reflection to access the private method + java.lang.reflect.Method method = DBService.class.getDeclaredMethod( + "processUpdateGroupMembers", + ObjectStoreConnection.class, + Group.class, + List.class, + boolean.class, + String.class, + String.class, + String.class, + boolean.class, + Boolean.class, + Set.class, + String.class, + StringBuilder.class + ); + method.setAccessible(true); + + // Test: pendingState=true, deleteProtection=Boolean.TRUE, ignoreDeletes=false + // This will trigger the code path where insertGroupMember is called for deleted members + boolean result = (boolean) method.invoke(zms.dbService, conn, originalGroup, newMembers, false, + domainName, groupName, "user.admin", true, Boolean.TRUE, notifyMembers, auditRef, auditDetails); + + assertFalse(result, "processUpdateGroupMembers should return false when insertGroupMember returns false"); + + // Verify that insertGroupMember was called for the deleted member + Mockito.verify(conn, times(1)).insertGroupMember(eq(domainName), eq(groupName), + any(GroupMember.class), eq("user.admin"), eq(auditRef)); + } + + @Test + public void testProcessUpdateGroupMembersInsertGroupMemberReturnsTrue() throws Exception { + + final String domainName = "process-update-group-members-insert-succeeds"; + final String groupName = "group1"; + + // Create original group with a member that will be deleted + List oldMembers = new ArrayList<>(); + oldMembers.add(new GroupMember().setMemberName("user.joe").setActive(true).setApproved(true)); + Group originalGroup = new Group().setName(groupName).setGroupMembers(oldMembers); + + // New group with no members (member will be deleted) + List newMembers = new ArrayList<>(); + + ObjectStoreConnection conn = Mockito.mock(ObjectStoreConnection.class); + // Mock insertGroupMember to return true + Mockito.when(conn.insertGroupMember(eq(domainName), eq(groupName), any(GroupMember.class), + eq("user.admin"), eq(auditRef))).thenReturn(true); + + StringBuilder auditDetails = new StringBuilder(); + Set notifyMembers = new HashSet<>(); + + // Use reflection to access the private method + java.lang.reflect.Method method = DBService.class.getDeclaredMethod( + "processUpdateGroupMembers", + ObjectStoreConnection.class, + Group.class, + List.class, + boolean.class, + String.class, + String.class, + String.class, + boolean.class, + Boolean.class, + Set.class, + String.class, + StringBuilder.class + ); + method.setAccessible(true); + + // Test: pendingState=true, deleteProtection=Boolean.TRUE, ignoreDeletes=false + // This will trigger the code path where insertGroupMember is called for deleted members + boolean result = (boolean) method.invoke(zms.dbService, conn, originalGroup, newMembers, false, + domainName, groupName, "user.admin", true, Boolean.TRUE, notifyMembers, auditRef, auditDetails); + + assertTrue(result, "processUpdateGroupMembers should return true when insertGroupMember succeeds"); + + // Verify that insertGroupMember was called for the deleted member + Mockito.verify(conn, times(1)).insertGroupMember(eq(domainName), eq(groupName), + any(GroupMember.class), eq("user.admin"), eq(auditRef)); + + // Verify that the member was added to notifyMembers + assertTrue(notifyMembers.contains("user.joe"), + "Member should be added to notifyMembers when insertGroupMember succeeds"); + } + @Test public void testGetPendingRoleMember() throws ServerResourceException {