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 b6f6b65bdbd..7f4e9d1ecbc 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 @@ -4493,7 +4493,7 @@ void updateDomainMembersExpiration(ResourceContext ctx, ObjectStoreConnection co List roleMembersWithUpdatedDueDates = getRoleMembersWithUpdatedDueDates(roleMembers, userExpiration, userExpiryMillis, serviceExpiration, serviceExpiryMillis, groupExpiration, groupExpiryMillis, null, 0, - null, 0, null, null, 0); + null, 0, null, 0, null); if (insertRoleMembers(ctx, con, roleMembersWithUpdatedDueDates, domain.getName(), roleName, principal, auditRef, caller)) { @@ -6972,9 +6972,8 @@ boolean updateUserAuthorityFilter(GroupMember groupMember, final String userAuth } boolean updateUserAuthorityExpiry(T member, final String userAuthorityExpiry, - Function expirationGetter, - BiConsumer expirationSetter, - Function nameGetter) { + boolean memberExpiryDaysConfigured, Function expirationGetter, + BiConsumer expirationSetter, Function nameGetter) { // if we have a service then there is no processing taking place // as the service is not managed by the user authority @@ -6986,8 +6985,9 @@ boolean updateUserAuthorityExpiry(T member, final String userAuthorityExpiry Date authorityExpiry = zmsConfig.getUserAuthority().getDateAttribute(nameGetter.apply(member), userAuthorityExpiry); - // if we don't have a date then we'll expiry the user right away + // if we don't have a date then we'll expire the user right away // otherwise we'll set the date as imposed by the user authority + // unless it's longer than the current expiry date boolean expiryDateUpdated = false; Timestamp memberExpiry = expirationGetter.apply(member); @@ -6995,7 +6995,7 @@ boolean updateUserAuthorityExpiry(T member, final String userAuthorityExpiry if (authorityExpiry == null) { // we'll update the expiration date to be the current time - // if the user doesn't have one or it's expires sometime + // if the user doesn't have one, or it's expires sometime // in the future if (memberExpiry == null || memberExpiry.millis() > System.currentTimeMillis()) { @@ -7004,10 +7004,14 @@ boolean updateUserAuthorityExpiry(T member, final String userAuthorityExpiry } } else { - // update the expiration date if it does not match to the - // value specified by the user authority value + // update the expiration date if any of these conditions are met + // 1) it's not set + // 2) does not match to the value specified by the user authority value and there are no + // role and domain level expiry days configured + // 3) the authority expiry date is earlier than the member expiry date - if (memberExpiry == null || memberExpiry.millis() != authorityExpiry.getTime()) { + if (memberExpiry == null || (!memberExpiryDaysConfigured && memberExpiry.millis() != authorityExpiry.getTime()) + || authorityExpiry.getTime() < memberExpiry.millis()) { expirationSetter.accept(member, Timestamp.fromDate(authorityExpiry)); expiryDateUpdated = true; } @@ -7015,24 +7019,20 @@ boolean updateUserAuthorityExpiry(T member, final String userAuthorityExpiry return expiryDateUpdated; } - boolean updateUserAuthorityExpiry(RoleMember roleMember, final String userAuthorityExpiry) { - return updateUserAuthorityExpiry(roleMember, - userAuthorityExpiry, - RoleMember::getExpiration, - RoleMember::setExpiration, - RoleMember::getMemberName); + boolean updateUserAuthorityExpiry(RoleMember roleMember, final String userAuthorityExpiry, + boolean memberExpiryDaysConfigured) { + return updateUserAuthorityExpiry(roleMember, userAuthorityExpiry, memberExpiryDaysConfigured, + RoleMember::getExpiration, RoleMember::setExpiration, RoleMember::getMemberName); } - boolean updateUserAuthorityExpiry(GroupMember groupMember, final String userAuthorityExpiry) { - return updateUserAuthorityExpiry(groupMember, - userAuthorityExpiry, - GroupMember::getExpiration, - GroupMember::setExpiration, - GroupMember::getMemberName); + boolean updateUserAuthorityExpiry(GroupMember groupMember, final String userAuthorityExpiry, + boolean memberExpiryDaysConfigured) { + return updateUserAuthorityExpiry(groupMember, userAuthorityExpiry, memberExpiryDaysConfigured, + GroupMember::getExpiration, GroupMember::setExpiration, GroupMember::getMemberName); } - List getRoleMembersWithUpdatedDisabledState(List roleMembers, final String roleUserAuthorityFilter, - final String domainUserAuthorityFilter) { + List getRoleMembersWithUpdatedDisabledState(List roleMembers, + final String roleUserAuthorityFilter, final String domainUserAuthorityFilter) { List roleMembersWithUpdatedDisabledStates = new ArrayList<>(); @@ -7107,41 +7107,24 @@ List getGroupMembersWithUpdatedDisabledState(List grou } List getGroupMembersWithUpdatedDueDates(List groupMembers, Timestamp userExpiration, - long userExpiryMillis, Timestamp serviceExpiration, long serviceExpiryMillis, - final String userAuthorityExpiry) { - - return getMembersWithUpdatedDueDates( - groupMembers, - userExpiration, - userExpiryMillis, - serviceExpiration, - serviceExpiryMillis, - null, - 0, - null, - 0, - null, - 0, - userAuthorityExpiry, - null, - 0, - GroupMember::getExpiration, - member -> null, - GroupMember::setExpiration, - (member, timestamp) -> { }, - GroupMember::getMemberName); + long userExpiryMillis, Timestamp serviceExpiration, long serviceExpiryMillis, + final String userAuthorityExpiry) { + + return getMembersWithUpdatedDueDates(groupMembers, userExpiration, userExpiryMillis, + serviceExpiration, serviceExpiryMillis, null, 0, null, 0, null, 0, null, 0, + userAuthorityExpiry, GroupMember::getExpiration, member -> null, + GroupMember::setExpiration, (member, timestamp) -> { }, GroupMember::getMemberName); } List getMembersWithUpdatedDueDates(List members, Timestamp userExpiration, - long userExpiryMillis, Timestamp serviceExpiration, long serviceExpiryMillis, - Timestamp groupExpiration, long groupExpiryMillis, Timestamp userReview, - long userReviewMillis, Timestamp serviceReview, long serviceReviewMillis, - final String userAuthorityExpiry, Timestamp groupReview, long groupReviewMillis, - Function expirationGetter, - Function reviewReminderGetter, - BiConsumer expirationSetter, - BiConsumer reviewReminderSetter, - Function nameGetter) { + long userExpiryMillis, Timestamp serviceExpiration, long serviceExpiryMillis, + Timestamp groupExpiration, long groupExpiryMillis, Timestamp userReview, + long userReviewMillis, Timestamp serviceReview, long serviceReviewMillis, + Timestamp groupReview, long groupReviewMillis, final String userAuthorityExpiry, + Function expirationGetter, Function reviewReminderGetter, + BiConsumer expirationSetter, BiConsumer reviewReminderSetter, + Function nameGetter) { + List membersWithUpdatedDueDates = new ArrayList<>(); for (T member : members) { Timestamp expiration = expirationGetter.apply(member); @@ -7153,6 +7136,9 @@ List getMembersWithUpdatedDueDates(List members, Timestamp userExpirat case USER: + // now let's check the role level expiry and reset it + // if it's earlier than the current value + if (isEarlierDueDate(userExpiryMillis, expiration)) { expirationSetter.accept(member, userExpiration); dueDateUpdated = true; @@ -7166,12 +7152,8 @@ List getMembersWithUpdatedDueDates(List members, Timestamp userExpirat // to make sure that the user still satisfies the filter // otherwise we'll just expire the user right away - if (userAuthorityExpiry != null && updateUserAuthorityExpiry( - member, - userAuthorityExpiry, - expirationGetter, - expirationSetter, - nameGetter)) { + if (userAuthorityExpiry != null && updateUserAuthorityExpiry(member, userAuthorityExpiry, + userExpiryMillis != 0, expirationGetter, expirationSetter, nameGetter)) { dueDateUpdated = true; } @@ -7215,28 +7197,13 @@ List getRoleMembersWithUpdatedDueDates(List roleMembers, long userExpiryMillis, Timestamp serviceExpiration, long serviceExpiryMillis, Timestamp groupExpiration, long groupExpiryMillis, Timestamp userReview, long userReviewMillis, Timestamp serviceReview, long serviceReviewMillis, - final String userAuthorityExpiry, Timestamp groupReview, long groupReviewMillis) { - - return getMembersWithUpdatedDueDates( - roleMembers, - userExpiration, - userExpiryMillis, - serviceExpiration, - serviceExpiryMillis, - groupExpiration, - groupExpiryMillis, - userReview, - userReviewMillis, - serviceReview, - serviceReviewMillis, - userAuthorityExpiry, - groupReview, - groupReviewMillis, - RoleMember::getExpiration, - RoleMember::getReviewReminder, - RoleMember::setExpiration, - RoleMember::setReviewReminder, - RoleMember::getMemberName); + Timestamp groupReview, long groupReviewMillis, final String userAuthorityExpiry) { + + return getMembersWithUpdatedDueDates(roleMembers, userExpiration, userExpiryMillis, serviceExpiration, + serviceExpiryMillis, groupExpiration, groupExpiryMillis, userReview, userReviewMillis, + serviceReview, serviceReviewMillis, groupReview, groupReviewMillis, userAuthorityExpiry, + RoleMember::getExpiration, RoleMember::getReviewReminder, RoleMember::setExpiration, + RoleMember::setReviewReminder, RoleMember::getMemberName); } private boolean insertRoleMembers(ResourceContext ctx, ObjectStoreConnection con, List roleMembers, @@ -7521,7 +7488,7 @@ String getDomainUserAuthorityFilter(ObjectStoreConnection con, final String doma } void updateGroupMembersDueDates(ResourceContext ctx, ObjectStoreConnection con, final String domainName, - final String groupName, Group originalGroup, Group updatedGroup, final String auditRef) { + final String groupName, Group originalGroup, Group updatedGroup, final String auditRef) { // if no group members, then there is nothing to do @@ -7534,7 +7501,8 @@ void updateGroupMembersDueDates(ResourceContext ctx, ObjectStoreConnection con, // changed in which case we need to verify and update members // accordingly - boolean userAuthorityExpiryChanged = isUserAuthorityExpiryChanged(originalGroup.getUserAuthorityExpiration(), updatedGroup.getUserAuthorityExpiration()); + boolean userAuthorityExpiryChanged = isUserAuthorityExpiryChanged(originalGroup.getUserAuthorityExpiration(), + updatedGroup.getUserAuthorityExpiration()); // we only need to process the group members if the new due date // is more restrictive than what we had before @@ -7610,10 +7578,10 @@ void updateRoleMembersDueDates(ResourceContext ctx, ObjectStoreConnection con, f boolean groupMemberExpiryDayReduced = isNumOfDaysReduced(originalRole.getGroupExpiryDays(), updatedRole.getGroupExpiryDays()); - boolean userMemberReviewDayReduced = isNumOfDaysReduced(originalRole.getMemberReviewDays(), - updatedRole.getMemberReviewDays()); - boolean serviceMemberReviewDayReduced = isNumOfDaysReduced(originalRole.getServiceReviewDays(), - updatedRole.getServiceReviewDays()); + boolean userMemberReviewDayReduced = isNumOfDaysReduced(originalRole.getMemberReviewDays(), + updatedRole.getMemberReviewDays()); + boolean serviceMemberReviewDayReduced = isNumOfDaysReduced(originalRole.getServiceReviewDays(), + updatedRole.getServiceReviewDays()); boolean groupMemberReviewDayReduced = isNumOfDaysReduced(originalRole.getGroupReviewDays(), updatedRole.getGroupReviewDays()); @@ -7634,10 +7602,10 @@ void updateRoleMembersDueDates(ResourceContext ctx, ObjectStoreConnection con, f long groupExpiryMillis = groupMemberExpiryDayReduced ? System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(updatedRole.getGroupExpiryDays(), TimeUnit.DAYS) : 0; - long userReviewMillis = userMemberReviewDayReduced ? System.currentTimeMillis() - + TimeUnit.MILLISECONDS.convert(updatedRole.getMemberReviewDays(), TimeUnit.DAYS) : 0; - long serviceReviewMillis = serviceMemberReviewDayReduced ? System.currentTimeMillis() - + TimeUnit.MILLISECONDS.convert(updatedRole.getServiceReviewDays(), TimeUnit.DAYS) : 0; + long userReviewMillis = userMemberReviewDayReduced ? System.currentTimeMillis() + + TimeUnit.MILLISECONDS.convert(updatedRole.getMemberReviewDays(), TimeUnit.DAYS) : 0; + long serviceReviewMillis = serviceMemberReviewDayReduced ? System.currentTimeMillis() + + TimeUnit.MILLISECONDS.convert(updatedRole.getServiceReviewDays(), TimeUnit.DAYS) : 0; long groupReviewMillis = groupMemberReviewDayReduced ? System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(updatedRole.getGroupReviewDays(), TimeUnit.DAYS) : 0; @@ -7658,7 +7626,7 @@ void updateRoleMembersDueDates(ResourceContext ctx, ObjectStoreConnection con, f List roleMembersWithUpdatedDueDates = getRoleMembersWithUpdatedDueDates(roleMembers, userExpiration, userExpiryMillis, serviceExpiration, serviceExpiryMillis, groupExpiration, groupExpiryMillis, userReview, userReviewMillis, serviceReview, serviceReviewMillis, - userAuthorityExpiry, groupReview, groupReviewMillis); + groupReview, groupReviewMillis, userAuthorityExpiry); if (insertRoleMembers(ctx, con, roleMembersWithUpdatedDueDates, domainName, roleName, principal, auditRef, caller)) { @@ -8402,7 +8370,7 @@ void processRoleUserAuthorityRestrictions() { for (PrincipalRole role : roles) { try { enforceRoleUserAuthorityRestrictions(role.getDomainName(), role.getRoleName(), - role.getDomainUserAuthorityFilter()); + role.getDomainUserAuthorityFilter(), role.getDomainMemberExpiryDays()); } catch (Exception ex) { LOG.error("Unable to process user authority restrictions for {}:role.{} - {}", role.getDomainName(), role.getRoleName(), ex.getMessage()); @@ -8470,7 +8438,7 @@ Map> applyTemplatesForListOfDomains(Map te } void enforceRoleUserAuthorityRestrictions(final String domainName, final String roleName, - final String domainUserAuthorityFilter) { + final String domainUserAuthorityFilter, int domainMemberExpiryDays) { final String caller = "enforceRoleUserAuthorityRestrictions"; try (ObjectStoreConnection con = store.getConnection(true, true)) { @@ -8493,10 +8461,12 @@ void enforceRoleUserAuthorityRestrictions(final String domainName, final String boolean expiryDBUpdated = false; final String userAuthorityExpiry = role.getUserAuthorityExpiration(); + boolean memberExpiryDaysConfigured = domainMemberExpiryDays > 0 || + (role.getMemberExpiryDays() != null && role.getMemberExpiryDays() > 0); if (userAuthorityExpiry != null) { List updatedMembers = new ArrayList<>(); for (RoleMember roleMember : roleMembers) { - if (updateUserAuthorityExpiry(roleMember, userAuthorityExpiry)) { + if (updateUserAuthorityExpiry(roleMember, userAuthorityExpiry, memberExpiryDaysConfigured)) { updatedMembers.add(roleMember); } } @@ -8535,7 +8505,7 @@ void enforceRoleUserAuthorityRestrictions(final String domainName, final String } void enforceGroupUserAuthorityRestrictions(final String domainName, final String groupName, - final String domainUserAuthorityFilter) { + final String domainUserAuthorityFilter) { final String caller = "enforceGroupUserAuthorityRestrictions"; try (ObjectStoreConnection con = store.getConnection(true, true)) { @@ -8558,10 +8528,11 @@ void enforceGroupUserAuthorityRestrictions(final String domainName, final String boolean expiryDBUpdated = false; final String userAuthorityExpiry = group.getUserAuthorityExpiration(); + boolean memberExpiryDaysConfigured = group.getMemberExpiryDays() != null && group.getMemberExpiryDays() > 0; if (userAuthorityExpiry != null) { List updatedMembers = new ArrayList<>(); for (GroupMember groupMember : groupMembers) { - if (updateUserAuthorityExpiry(groupMember, userAuthorityExpiry)) { + if (updateUserAuthorityExpiry(groupMember, userAuthorityExpiry, memberExpiryDaysConfigured)) { updatedMembers.add(groupMember); } } diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/PrincipalRole.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/PrincipalRole.java index c0c8a880076..d33e0a16eee 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/PrincipalRole.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/PrincipalRole.java @@ -20,6 +20,7 @@ public class PrincipalRole { private String domainName; private String roleName; private String domainUserAuthorityFilter; + private int domainMemberExpiryDays; public String getDomainName() { return domainName; @@ -44,4 +45,12 @@ public String getDomainUserAuthorityFilter() { public void setDomainUserAuthorityFilter(String domainUserAuthorityFilter) { this.domainUserAuthorityFilter = domainUserAuthorityFilter; } + + public int getDomainMemberExpiryDays() { + return domainMemberExpiryDays; + } + + public void setDomainMemberExpiryDays(int domainMemberExpiryDays) { + this.domainMemberExpiryDays = domainMemberExpiryDays; + } } diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSImpl.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSImpl.java index 5a5b48b3e24..e70208d9900 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSImpl.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSImpl.java @@ -4299,7 +4299,11 @@ void updateRoleMemberUserAuthorityExpiry(final Role role, final String caller) { throw ZMSUtils.requestError("Invalid member: " + roleMember.getMemberName() + ". No expiry date attribute specified in user authority", caller); } - roleMember.setExpiration(Timestamp.fromDate(expiry)); + + // otherwise only update the value is current expiry date + // is greater than the authority expiry date + + roleMember.setExpiration(ZMSUtils.smallestExpiry(roleMember.getExpiration(), Timestamp.fromDate(expiry))); } } } @@ -4834,13 +4838,16 @@ void setRoleMemberExpiration(final AthenzDomain domain, final Role role, final R case USER: - Timestamp userAuthorityExpiry = getUserAuthorityExpiry(roleMember.memberName, role.getUserAuthorityExpiration(), caller); - if (userAuthorityExpiry != null) { - roleMember.setExpiration(userAuthorityExpiry); - } else { - roleMember.setExpiration(memberDueDateTimestamp(domain.getDomain().getMemberExpiryDays(), - role.getMemberExpiryDays(), membership.getExpiration())); - } + // first check if we have a user authority expiry configured + // which will automatically reject the request if the user + // doesn't have it, and then we'll check the role/domain expiry + // and use the smallest value as the user's expiry + + Timestamp userAuthorityExpiry = getUserAuthorityExpiry(roleMember.memberName, + role.getUserAuthorityExpiration(), caller); + Timestamp memberExpiry = memberDueDateTimestamp(domain.getDomain().getMemberExpiryDays(), + role.getMemberExpiryDays(), membership.getExpiration()); + roleMember.setExpiration(ZMSUtils.smallestExpiry(memberExpiry, userAuthorityExpiry)); break; case SERVICE: @@ -4892,7 +4899,8 @@ void sendMembershipApprovalNotification(final String domain, final String org, f LOG.debug("Sending Membership Approval notification after putMembership"); } - List notifications = new PutRoleMembershipNotificationTask(domain, org, role, details, dbService, userDomainPrefix, notificationToEmailConverterCommon).getNotifications(); + List notifications = new PutRoleMembershipNotificationTask(domain, org, role, details, + dbService, userDomainPrefix, notificationToEmailConverterCommon).getNotifications(); notificationManager.sendNotifications(notifications); } @@ -4909,7 +4917,8 @@ void sendGroupMembershipApprovalNotification(final String domain, final String o LOG.debug("Sending Group Membership Approval notification after putGroupMembership"); } - List notifications = new PutGroupMembershipNotificationTask(domain, org, group, details, dbService, userDomainPrefix, notificationToEmailConverterCommon).getNotifications(); + List notifications = new PutGroupMembershipNotificationTask(domain, org, group, details, + dbService, userDomainPrefix, notificationToEmailConverterCommon).getNotifications(); notificationManager.sendNotifications(notifications); } @@ -10494,7 +10503,11 @@ void updateGroupMemberUserAuthorityExpiry(final Group group, final String caller throw ZMSUtils.requestError("Invalid member: " + groupMember.getMemberName() + ". No expiry date attribute specified in user authority", caller); } - groupMember.setExpiration(Timestamp.fromDate(expiry)); + + // only update the expiry if the current expiry is greater + // than the user authority expiry + + groupMember.setExpiration(ZMSUtils.smallestExpiry(groupMember.getExpiration(), Timestamp.fromDate(expiry))); } } } @@ -10780,13 +10793,11 @@ void setGroupMemberExpiration(final AthenzDomain domain, final Group group, fina case USER: - Timestamp userAuthorityExpiry = getUserAuthorityExpiry(groupMember.memberName, group.getUserAuthorityExpiration(), caller); - if (userAuthorityExpiry != null) { - groupMember.setExpiration(userAuthorityExpiry); - } else { - groupMember.setExpiration(memberDueDateTimestamp(domain.getDomain().getMemberExpiryDays(), - group.getMemberExpiryDays(), membership.getExpiration())); - } + Timestamp userAuthorityExpiry = getUserAuthorityExpiry(groupMember.memberName, + group.getUserAuthorityExpiration(), caller); + Timestamp memberExpiry = memberDueDateTimestamp(domain.getDomain().getMemberExpiryDays(), + group.getMemberExpiryDays(), membership.getExpiration()); + groupMember.setExpiration(ZMSUtils.smallestExpiry(memberExpiry, userAuthorityExpiry)); break; case SERVICE: diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/store/impl/jdbc/JDBCConnection.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/store/impl/jdbc/JDBCConnection.java index 85a26ffb5a7..151a4f948da 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/store/impl/jdbc/JDBCConnection.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/store/impl/jdbc/JDBCConnection.java @@ -403,8 +403,8 @@ public class JDBCConnection implements ObjectStoreConnection { + "WHERE role_member.review_last_notified_time=? AND role_member.review_server=?;"; private static final String SQL_UPDATE_ROLE_REVIEW_TIMESTAMP = "UPDATE role SET last_reviewed_time=CURRENT_TIMESTAMP(3) WHERE role_id=?;"; private static final String SQL_LIST_ROLES_WITH_RESTRICTIONS = "SELECT domain.name as domain_name, " - + "role.name as role_name, domain.user_authority_filter as domain_user_authority_filter FROM role " - + "JOIN domain ON role.domain_id=domain.domain_id WHERE role.user_authority_filter!='' " + + "role.name as role_name, domain.user_authority_filter as domain_user_authority_filter, domain.member_expiry_days " + + "FROM role JOIN domain ON role.domain_id=domain.domain_id WHERE role.user_authority_filter!='' " + "OR role.user_authority_expiration!='' OR domain.user_authority_filter!='';"; private static final String SQL_GET_GROUP = "SELECT * FROM principal_group " + "JOIN domain ON domain.domain_id=principal_group.domain_id " @@ -6000,6 +6000,7 @@ public List listRolesWithUserAuthorityRestrictions() { prRole.setDomainName(rs.getString(ZMSConsts.DB_COLUMN_AS_DOMAIN_NAME)); prRole.setRoleName(rs.getString(ZMSConsts.DB_COLUMN_AS_ROLE_NAME)); prRole.setDomainUserAuthorityFilter(rs.getString(ZMSConsts.DB_COLUMN_AS_DOMAIN_USER_AUTHORITY_FILTER)); + prRole.setDomainMemberExpiryDays(rs.getInt(ZMSConsts.DB_COLUMN_MEMBER_EXPIRY_DAYS)); roles.add(prRole); } } diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/utils/ZMSUtils.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/utils/ZMSUtils.java index 2ee4fb8277f..255fc6a6a48 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/utils/ZMSUtils.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/utils/ZMSUtils.java @@ -25,6 +25,7 @@ import com.yahoo.athenz.common.server.util.ResourceUtils; import com.yahoo.athenz.common.server.util.ServletRequestUtil; import com.yahoo.athenz.zms.*; +import com.yahoo.rdl.Timestamp; import com.yahoo.rdl.Validator; import jakarta.ws.rs.core.Response; import org.eclipse.jetty.util.StringUtil; @@ -521,4 +522,24 @@ public static void validatePolicyAssertion(Validator validator, Assertion assert } } + public static Timestamp smallestExpiry(Timestamp memberExpiry, Timestamp userAuthorityExpiry) { + + // if we have no user authority expiry then we'll use the member expiry + + if (userAuthorityExpiry == null) { + return memberExpiry; + } + + // if we have no member expiry then we'll use the user authority expiry + + if (memberExpiry == null) { + return userAuthorityExpiry; + } + + if (memberExpiry.millis() < userAuthorityExpiry.millis()) { + return memberExpiry; + } else { + return userAuthorityExpiry; + } + } } 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 a4c51cb0fb6..02bdd9b2292 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 @@ -8798,26 +8798,26 @@ public void testUpdateUserAuthorityExpiryRoleMember() { // if not a user then it's always false RoleMember roleMember = new RoleMember().setMemberName("coretech.api"); - assertFalse(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance")); + assertFalse(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance", false)); // user.joe - no expiry setting roleMember = new RoleMember().setMemberName("user.joe"); - assertTrue(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance")); + assertTrue(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance", false)); assertNotNull(roleMember.getExpiration()); // we'll change if the expiry date is in the future Timestamp expiryDate = Timestamp.fromMillis(System.currentTimeMillis() + 1000000); roleMember.setExpiration(expiryDate); - assertTrue(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance")); + assertTrue(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance", false)); assertNotEquals(roleMember.getExpiration(), expiryDate); // we will not change if the entry is already expired expiryDate = Timestamp.fromMillis(System.currentTimeMillis() - 1000000); roleMember.setExpiration(expiryDate); - assertFalse(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance")); + assertFalse(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance", false)); assertEquals(roleMember.getExpiration(), expiryDate); // now let's test a user with valid authority expiry date @@ -8825,27 +8825,94 @@ public void testUpdateUserAuthorityExpiryRoleMember() { // returned by the user authority roleMember = new RoleMember().setMemberName("user.jane"); - assertTrue(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance")); + assertTrue(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance", false)); assertNotNull(roleMember.getExpiration()); assertEquals(roleMember.getExpiration(), authorityDate); // if the value matches to our user authority value then no change roleMember.setExpiration(authorityDate); - assertFalse(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance")); + assertFalse(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance", false)); assertNotNull(roleMember.getExpiration()); assertEquals(roleMember.getExpiration(), authorityDate); // if no match then we change the value roleMember.setExpiration(Timestamp.fromMillis(System.currentTimeMillis() - 2000000)); - assertTrue(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance")); + assertTrue(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance", false)); assertNotNull(roleMember.getExpiration()); assertEquals(roleMember.getExpiration(), authorityDate); zms.dbService.zmsConfig.setUserAuthority(savedAuthority); } + @Test + public void testUpdateUserAuthorityExpiryRoleMemberWithRoleMeta() { + + Authority savedAuthority = zms.dbService.zmsConfig.getUserAuthority(); + + Authority authority = Mockito.mock(Authority.class); + + Timestamp authorityDate = ZMSTestUtils.addDays(Timestamp.fromCurrentTime(), 45); + + Mockito.when(authority.getDateAttribute("user.john", "elevated-clearance")) + .thenReturn(authorityDate.toDate()); + Mockito.when(authority.getDateAttribute("user.jane", "elevated-clearance")) + .thenReturn(authorityDate.toDate()); + Mockito.when(authority.getDateAttribute("user.joe", "elevated-clearance")) + .thenReturn(null); + + zms.dbService.zmsConfig.setUserAuthority(authority); + + // if not a user then it's always false + + RoleMember roleMember = new RoleMember().setMemberName("coretech.api"); + assertFalse(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance", true)); + + // user.joe - no expiry setting + + roleMember = new RoleMember().setMemberName("user.joe"); + assertTrue(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance", true)); + assertNotNull(roleMember.getExpiration()); + + // we'll change if the expiry date is in the future + + Timestamp expiryDate = Timestamp.fromMillis(System.currentTimeMillis() + 1000000); + roleMember.setExpiration(expiryDate); + assertTrue(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance", true)); + assertNotEquals(roleMember.getExpiration(), expiryDate); + + // we will not change if the entry is already expired + + expiryDate = Timestamp.fromMillis(System.currentTimeMillis() - 1000000); + roleMember.setExpiration(expiryDate); + assertFalse(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance", true)); + assertEquals(roleMember.getExpiration(), expiryDate); + + // now let's test a user with valid authority expiry date + // if the value matches to our user authority value then no change + + roleMember = new RoleMember().setMemberName("user.jane").setExpiration(authorityDate); + assertFalse(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance", true)); + assertNotNull(roleMember.getExpiration()); + assertEquals(roleMember.getExpiration(), authorityDate); + + // if no match then we change the value only if the authority expiry is + // smaller than the member expiry since we have a role meta set, and we + // need to honor that limit + + roleMember.setExpiration(ZMSTestUtils.addDays(Timestamp.fromCurrentTime(), 15)); + assertFalse(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance", true)); + assertNotNull(roleMember.getExpiration()); + + roleMember.setExpiration(ZMSTestUtils.addDays(Timestamp.fromCurrentTime(), 60)); + assertTrue(zms.dbService.updateUserAuthorityExpiry(roleMember, "elevated-clearance", true)); + assertNotNull(roleMember.getExpiration()); + assertEquals(roleMember.getExpiration().millis(), authorityDate.millis()); + + zms.dbService.zmsConfig.setUserAuthority(savedAuthority); + } + @Test public void testUpdateRoleMembersSystemDisabledState() { @@ -9113,8 +9180,8 @@ public void testEnforceRoleUserAuthorityRestrictionsEmptyRoles() { // calling the enforce twice - first time we should get null role // and second time role with no members - zms.dbService.enforceRoleUserAuthorityRestrictions(domainName, roleName, null); - zms.dbService.enforceRoleUserAuthorityRestrictions(domainName, roleName, null); + zms.dbService.enforceRoleUserAuthorityRestrictions(domainName, roleName, null, 0); + zms.dbService.enforceRoleUserAuthorityRestrictions(domainName, roleName, null, 0); zms.dbService.store = savedStore; } @@ -9158,7 +9225,7 @@ public void testEnforceRoleUserAuthorityExpiryRestrictionsUpdate() { // the request should complete successfully - zms.dbService.enforceRoleUserAuthorityRestrictions(domainName, roleName, null); + zms.dbService.enforceRoleUserAuthorityRestrictions(domainName, roleName, null, 0); zms.dbService.zmsConfig.setUserAuthority(savedAuthority); zms.dbService.store = savedStore; @@ -9206,7 +9273,7 @@ public void testEnforceRoleUserAuthorityFilterRestrictionsUpdate() { // the request should complete successfully - zms.dbService.enforceRoleUserAuthorityRestrictions(domainName, roleName, null); + zms.dbService.enforceRoleUserAuthorityRestrictions(domainName, roleName, null, 0); zms.dbService.zmsConfig.setUserAuthority(savedAuthority); zms.dbService.store = savedStore; @@ -9252,7 +9319,7 @@ public void testEnforceRoleUserAuthorityRestrictionsNoUpdate() { // the request should complete successfully - zms.dbService.enforceRoleUserAuthorityRestrictions(domainName, roleName, null); + zms.dbService.enforceRoleUserAuthorityRestrictions(domainName, roleName, null, 0); zms.dbService.zmsConfig.setUserAuthority(savedAuthority); zms.dbService.store = savedStore; @@ -9969,26 +10036,26 @@ public void testUpdateUserAuthorityExpiryGroupMember() { // service users are not processed GroupMember groupMember = new GroupMember().setMemberName("sports.api"); - assertFalse(zms.dbService.updateUserAuthorityExpiry(groupMember, "elevated-clearance")); + assertFalse(zms.dbService.updateUserAuthorityExpiry(groupMember, "elevated-clearance", false)); // user.joe - no expiry setting groupMember = new GroupMember().setMemberName("user.joe"); - assertTrue(zms.dbService.updateUserAuthorityExpiry(groupMember, "elevated-clearance")); + assertTrue(zms.dbService.updateUserAuthorityExpiry(groupMember, "elevated-clearance", false)); assertNotNull(groupMember.getExpiration()); // we'll change if the expiry date is in the future Timestamp expiryDate = Timestamp.fromMillis(System.currentTimeMillis() + 1000000); groupMember.setExpiration(expiryDate); - assertTrue(zms.dbService.updateUserAuthorityExpiry(groupMember, "elevated-clearance")); + assertTrue(zms.dbService.updateUserAuthorityExpiry(groupMember, "elevated-clearance", false)); assertNotEquals(groupMember.getExpiration(), expiryDate); // we will not change if the entry is already expired expiryDate = Timestamp.fromMillis(System.currentTimeMillis() - 1000000); groupMember.setExpiration(expiryDate); - assertFalse(zms.dbService.updateUserAuthorityExpiry(groupMember, "elevated-clearance")); + assertFalse(zms.dbService.updateUserAuthorityExpiry(groupMember, "elevated-clearance", false)); assertEquals(groupMember.getExpiration(), expiryDate); // now let's test a user with valid authority expiry date @@ -9996,21 +10063,21 @@ public void testUpdateUserAuthorityExpiryGroupMember() { // returned by the user authority groupMember = new GroupMember().setMemberName("user.jane"); - assertTrue(zms.dbService.updateUserAuthorityExpiry(groupMember, "elevated-clearance")); + assertTrue(zms.dbService.updateUserAuthorityExpiry(groupMember, "elevated-clearance", false)); assertNotNull(groupMember.getExpiration()); assertEquals(groupMember.getExpiration(), authorityDate); // if the value matches to our user authority value then no change groupMember.setExpiration(authorityDate); - assertFalse(zms.dbService.updateUserAuthorityExpiry(groupMember, "elevated-clearance")); + assertFalse(zms.dbService.updateUserAuthorityExpiry(groupMember, "elevated-clearance", false)); assertNotNull(groupMember.getExpiration()); assertEquals(groupMember.getExpiration(), authorityDate); // if no match then we change the value groupMember.setExpiration(Timestamp.fromMillis(System.currentTimeMillis() - 2000000)); - assertTrue(zms.dbService.updateUserAuthorityExpiry(groupMember, "elevated-clearance")); + assertTrue(zms.dbService.updateUserAuthorityExpiry(groupMember, "elevated-clearance", false)); assertNotNull(groupMember.getExpiration()); assertEquals(groupMember.getExpiration(), authorityDate); diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/PrincipalRoleTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/PrincipalRoleTest.java index 30c616c37b1..36c1cf19e18 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/PrincipalRoleTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/PrincipalRoleTest.java @@ -27,9 +27,11 @@ public void testPrincipalRole() { prRole.setRoleName("role"); prRole.setDomainName("domain"); prRole.setDomainUserAuthorityFilter("authority"); + prRole.setDomainMemberExpiryDays(10); - assertEquals("role", prRole.getRoleName()); - assertEquals("domain", prRole.getDomainName()); - assertEquals("authority", prRole.getDomainUserAuthorityFilter()); + assertEquals(prRole.getRoleName(), "role"); + assertEquals(prRole.getDomainName(), "domain"); + assertEquals(prRole.getDomainUserAuthorityFilter(), "authority"); + assertEquals(prRole.getDomainMemberExpiryDays(), 10); } } diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/ZMSExpiryTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/ZMSExpiryTest.java new file mode 100644 index 00000000000..fbacd64be1f --- /dev/null +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/ZMSExpiryTest.java @@ -0,0 +1,331 @@ +/* + * Copyright The Athenz Authors + * + * Licensed 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.yahoo.athenz.zms; + +import com.yahoo.athenz.auth.Authority; +import com.yahoo.rdl.Timestamp; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.util.*; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.testng.Assert.*; +import static org.testng.Assert.assertTrue; + +public class ZMSExpiryTest { + + private final ZMSTestInitializer zmsTestInitializer = new ZMSTestInitializer(); + + @BeforeClass + public void startMemoryMySQL() { + zmsTestInitializer.startMemoryMySQL(); + } + + @AfterClass + public void stopMemoryMySQL() { + zmsTestInitializer.stopMemoryMySQL(); + } + + @BeforeMethod + public void setUp() throws Exception { + MockitoAnnotations.openMocks(this); + zmsTestInitializer.setUp(); + } + + @Test + public void testRoleExpiryWithAuthority() { + + ZMSImpl zmsImpl = zmsTestInitializer.getZms(); + RsrcCtxWrapper ctx = zmsTestInitializer.getMockDomRsrcCtx(); + final String auditRef = zmsTestInitializer.getAuditRef(); + + // add a role with an elevated clearance option + + final String domainName = "role-expiry-with-authority"; + TopLevelDomain dom1 = zmsTestInitializer.createTopLevelDomainObject(domainName, + "Test Domain1", "testOrg", "user.user1"); + zmsImpl.postTopLevelDomain(ctx, auditRef, null, dom1); + + // create a role with 2 members with no expiry + + List roleMembers = new ArrayList<>(); + roleMembers.add(new RoleMember().setMemberName("user.john")); + roleMembers.add(new RoleMember().setMemberName("user.jane")); + roleMembers.add(new RoleMember().setMemberName("user.joe")); + + final String roleName1 = "expiry-role1"; + Role role1 = zmsTestInitializer.createRoleObject(domainName, roleName1, null, roleMembers); + zmsImpl.putRole(ctx, domainName, roleName1, auditRef, false, null, role1); + + final String roleName2 = "expiry-role2"; + Role role2 = zmsTestInitializer.createRoleObject(domainName, roleName2, null, roleMembers); + zmsImpl.putRole(ctx, domainName, roleName2, auditRef, false, null, role2); + + Authority savedAuthority = zmsImpl.userAuthority; + Authority authority = Mockito.mock(Authority.class); + Set attrs = new HashSet<>(); + attrs.add("elevated-clearance"); + when(authority.dateAttributesSupported()).thenReturn(attrs); + Timestamp days15 = ZMSTestUtils.addDays(Timestamp.fromCurrentTime(), 15); + Timestamp days45 = ZMSTestUtils.addDays(Timestamp.fromCurrentTime(), 45); + when(authority.getDateAttribute("user.john", "elevated-clearance")).thenReturn(days45.toDate()); + when(authority.getDateAttribute("user.jane", "elevated-clearance")).thenReturn(days15.toDate()); + when(authority.getDateAttribute("user.joe", "elevated-clearance")).thenReturn(null); + when(authority.getDateAttribute("user.john1", "elevated-clearance")).thenReturn(days45.toDate()); + when(authority.getDateAttribute("user.jane1", "elevated-clearance")).thenReturn(days15.toDate()); + when(authority.getDateAttribute("user.joe1", "elevated-clearance")).thenReturn(null); + zmsImpl.userAuthority = authority; + zmsImpl.dbService.zmsConfig.setUserAuthority(authority); + + // let's set the meta attributes for expiry and authority expiry + + RoleMeta rm1 = new RoleMeta().setMemberExpiryDays(30).setUserAuthorityExpiration("elevated-clearance"); + zmsImpl.putRoleMeta(ctx, domainName, roleName1, auditRef, null, rm1); + + RoleMeta rm2 = new RoleMeta().setUserAuthorityExpiration("elevated-clearance"); + zmsImpl.putRoleMeta(ctx, domainName, roleName2, auditRef, null, rm2); + + // now let's retrieve the roles and verify the expiry + + Role role1Res = zmsImpl.getRole(ctx, domainName, roleName1, false, false, false); + assertNotNull(role1Res); + + // john should have 30 days user expiry since elevated clearance is 45 + RoleMember userJohn = ZMSTestUtils.getRoleMember(role1Res, "user.john"); + assertTrue(ZMSTestUtils.validateDueDate(userJohn.getExpiration().millis(), 30L * 24 * 60 * 60 * 1000)); + + // jane should have 15 days user expiry since elevated clearance is 15 + RoleMember userJane = ZMSTestUtils.getRoleMember(role1Res, "user.jane"); + assertTrue(ZMSTestUtils.validateDueDate(userJane.getExpiration().millis(), 15L * 24 * 60 * 60 * 1000)); + + // joe has no expiry so it must be expired + RoleMember userJoe = ZMSTestUtils.getRoleMember(role1Res, "user.joe"); + assertTrue(ZMSTestUtils.validateDueDate(userJoe.getExpiration().millis(), 0)); + + // role2 is standard user authority expiry + + Role role2Res = zmsImpl.getRole(ctx, domainName, roleName2, false, false, false); + assertNotNull(role2Res); + + userJohn = ZMSTestUtils.getRoleMember(role2Res, "user.john"); + assertTrue(ZMSTestUtils.validateDueDate(userJohn.getExpiration().millis(), 45L * 24 * 60 * 60 * 1000)); + + userJane = ZMSTestUtils.getRoleMember(role2Res, "user.jane"); + assertTrue(ZMSTestUtils.validateDueDate(userJane.getExpiration().millis(), 15L * 24 * 60 * 60 * 1000)); + + userJoe = ZMSTestUtils.getRoleMember(role2Res, "user.joe"); + assertTrue(ZMSTestUtils.validateDueDate(userJoe.getExpiration().millis(), 0)); + + // add a new member john1 to both roles and verify expected outcome + + Membership mbrJohn1 = new Membership().setRoleName(roleName1).setMemberName("user.john1"); + zmsImpl.putMembership(ctx, domainName, roleName1, "user.john1", auditRef, false, null, mbrJohn1); + role1Res = zmsImpl.getRole(ctx, domainName, roleName1, false, false, false); + RoleMember userJohn1 = ZMSTestUtils.getRoleMember(role1Res, "user.john1"); + assertTrue(ZMSTestUtils.validateDueDate(userJohn1.getExpiration().millis(), 30L * 24 * 60 * 60 * 1000)); + + mbrJohn1 = new Membership().setRoleName(roleName2).setMemberName("user.john1"); + zmsImpl.putMembership(ctx, domainName, roleName2, "user.john1", auditRef, false, null, mbrJohn1); + role2Res = zmsImpl.getRole(ctx, domainName, roleName2, false, false, false); + userJohn1 = ZMSTestUtils.getRoleMember(role2Res, "user.john1"); + assertTrue(ZMSTestUtils.validateDueDate(userJohn1.getExpiration().millis(), 45L * 24 * 60 * 60 * 1000)); + + // add jane1 to both roles and verify expected outcome + + Membership mbrJane1 = new Membership().setRoleName(roleName1).setMemberName("user.jane1"); + zmsImpl.putMembership(ctx, domainName, roleName1, "user.jane1", auditRef, false, null, mbrJane1); + role1Res = zmsImpl.getRole(ctx, domainName, roleName1, false, false, false); + RoleMember userJane1 = ZMSTestUtils.getRoleMember(role1Res, "user.jane1"); + assertTrue(ZMSTestUtils.validateDueDate(userJane1.getExpiration().millis(), 15L * 24 * 60 * 60 * 1000)); + + mbrJane1 = new Membership().setRoleName(roleName2).setMemberName("user.jane1"); + zmsImpl.putMembership(ctx, domainName, roleName2, "user.jane1", auditRef, false, null, mbrJane1); + role2Res = zmsImpl.getRole(ctx, domainName, roleName2, false, false, false); + userJane1 = ZMSTestUtils.getRoleMember(role2Res, "user.jane1"); + assertTrue(ZMSTestUtils.validateDueDate(userJane1.getExpiration().millis(), 15L * 24 * 60 * 60 * 1000)); + + // add joe1 to both roles and verify expected outcome + + try { + Membership mbrJoe1 = new Membership().setRoleName(roleName1).setMemberName("user.joe1"); + zmsImpl.putMembership(ctx, domainName, roleName1, "user.joe1", auditRef, false, null, mbrJoe1); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), 400); + assertTrue(ex.getMessage().contains("User does not have required user authority expiry configured")); + } + + try { + Membership mbrJoe1 = new Membership().setRoleName(roleName2).setMemberName("user.joe1"); + zmsImpl.putMembership(ctx, domainName, roleName2, "user.joe1", auditRef, false, null, mbrJoe1); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), 400); + assertTrue(ex.getMessage().contains("User does not have required user authority expiry configured")); + } + + zmsImpl.deleteTopLevelDomain(ctx, domainName, auditRef, null); + zmsImpl.dbService.zmsConfig.setUserAuthority(savedAuthority); + zmsImpl.userAuthority = savedAuthority; + } + + @Test + public void testGroupExpiryWithAuthority() { + + ZMSImpl zmsImpl = zmsTestInitializer.getZms(); + RsrcCtxWrapper ctx = zmsTestInitializer.getMockDomRsrcCtx(); + final String auditRef = zmsTestInitializer.getAuditRef(); + + // add a role with an elevated clearance option + + final String domainName = "group-expiry-with-authority"; + TopLevelDomain dom1 = zmsTestInitializer.createTopLevelDomainObject(domainName, + "Test Domain1", "testOrg", "user.user1"); + zmsImpl.postTopLevelDomain(ctx, auditRef, null, dom1); + + // create a role with 2 members with no expiry + + List groupMembers = new ArrayList<>(); + groupMembers.add(new GroupMember().setMemberName("user.john")); + groupMembers.add(new GroupMember().setMemberName("user.jane")); + groupMembers.add(new GroupMember().setMemberName("user.joe")); + + final String groupName1 = "expiry-group1"; + Group group1 = zmsTestInitializer.createGroupObject(domainName, groupName1, groupMembers); + zmsImpl.putGroup(ctx, domainName, groupName1, auditRef, false, null, group1); + + final String groupName2 = "expiry-group2"; + Group group2 = zmsTestInitializer.createGroupObject(domainName, groupName2, groupMembers); + zmsImpl.putGroup(ctx, domainName, groupName2, auditRef, false, null, group2); + + Authority savedAuthority = zmsImpl.userAuthority; + Authority authority = Mockito.mock(Authority.class); + Set attrs = new HashSet<>(); + attrs.add("elevated-clearance"); + when(authority.dateAttributesSupported()).thenReturn(attrs); + when(authority.isValidUser(any())).thenReturn(true); + Timestamp days15 = ZMSTestUtils.addDays(Timestamp.fromCurrentTime(), 15); + Timestamp days45 = ZMSTestUtils.addDays(Timestamp.fromCurrentTime(), 45); + when(authority.getDateAttribute("user.john", "elevated-clearance")).thenReturn(days45.toDate()); + when(authority.getDateAttribute("user.jane", "elevated-clearance")).thenReturn(days15.toDate()); + when(authority.getDateAttribute("user.joe", "elevated-clearance")).thenReturn(null); + when(authority.getDateAttribute("user.john1", "elevated-clearance")).thenReturn(days45.toDate()); + when(authority.getDateAttribute("user.jane1", "elevated-clearance")).thenReturn(days15.toDate()); + when(authority.getDateAttribute("user.joe1", "elevated-clearance")).thenReturn(null); + + zmsImpl.userAuthority = authority; + zmsImpl.dbService.zmsConfig.setUserAuthority(authority); + + // let's set the meta attributes for expiry and authority expiry + + GroupMeta gm1 = new GroupMeta().setMemberExpiryDays(30).setUserAuthorityExpiration("elevated-clearance"); + zmsImpl.putGroupMeta(ctx, domainName, groupName1, auditRef, null, gm1); + + GroupMeta gm2 = new GroupMeta().setUserAuthorityExpiration("elevated-clearance"); + zmsImpl.putGroupMeta(ctx, domainName, groupName2, auditRef, null, gm2); + + // now let's retrieve the roles and verify the expiry + + Group group1Res = zmsImpl.getGroup(ctx, domainName, groupName1, false, false); + assertNotNull(group1Res); + + // john should have 30 days user expiry since elevated clearance is 45 + GroupMember userJohn = ZMSTestUtils.getGroupMember(group1Res, "user.john"); + assertTrue(ZMSTestUtils.validateDueDate(userJohn.getExpiration().millis(), 30L * 24 * 60 * 60 * 1000)); + + // jane should have 15 days user expiry since elevated clearance is 15 + GroupMember userJane = ZMSTestUtils.getGroupMember(group1Res, "user.jane"); + assertTrue(ZMSTestUtils.validateDueDate(userJane.getExpiration().millis(), 15L * 24 * 60 * 60 * 1000)); + + // joe has no expiry so it must be expired + GroupMember userJoe = ZMSTestUtils.getGroupMember(group1Res, "user.joe"); + assertTrue(ZMSTestUtils.validateDueDate(userJoe.getExpiration().millis(), 0)); + + // role2 is standard user authority expiry + + Group group2Res = zmsImpl.getGroup(ctx, domainName, groupName2, false, false); + assertNotNull(group2Res); + + userJohn = ZMSTestUtils.getGroupMember(group2Res, "user.john"); + assertTrue(ZMSTestUtils.validateDueDate(userJohn.getExpiration().millis(), 45L * 24 * 60 * 60 * 1000)); + + userJane = ZMSTestUtils.getGroupMember(group2Res, "user.jane"); + assertTrue(ZMSTestUtils.validateDueDate(userJane.getExpiration().millis(), 15L * 24 * 60 * 60 * 1000)); + + userJoe = ZMSTestUtils.getGroupMember(group2Res, "user.joe"); + assertTrue(ZMSTestUtils.validateDueDate(userJoe.getExpiration().millis(), 0)); + + // add a new member john1 to both roles and verify expected outcome + + GroupMembership mbrJohn1 = new GroupMembership().setGroupName(groupName1).setMemberName("user.john1"); + zmsImpl.putGroupMembership(ctx, domainName, groupName1, "user.john1", auditRef, false, null, mbrJohn1); + group1Res = zmsImpl.getGroup(ctx, domainName, groupName1, false, false); + GroupMember userJohn1 = ZMSTestUtils.getGroupMember(group1Res, "user.john1"); + assertTrue(ZMSTestUtils.validateDueDate(userJohn1.getExpiration().millis(), 30L * 24 * 60 * 60 * 1000)); + + mbrJohn1 = new GroupMembership().setGroupName(groupName2).setMemberName("user.john1"); + zmsImpl.putGroupMembership(ctx, domainName, groupName2, "user.john1", auditRef, false, null, mbrJohn1); + group2Res = zmsImpl.getGroup(ctx, domainName, groupName2, false, false); + userJohn1 = ZMSTestUtils.getGroupMember(group2Res, "user.john1"); + assertTrue(ZMSTestUtils.validateDueDate(userJohn1.getExpiration().millis(), 45L * 24 * 60 * 60 * 1000)); + + // add jane1 to both roles and verify expected outcome + + GroupMembership mbrJane1 = new GroupMembership().setGroupName(groupName1).setMemberName("user.jane1"); + zmsImpl.putGroupMembership(ctx, domainName, groupName1, "user.jane1", auditRef, false, null, mbrJane1); + group1Res = zmsImpl.getGroup(ctx, domainName, groupName1, false, false); + GroupMember userJane1 = ZMSTestUtils.getGroupMember(group1Res, "user.jane1"); + assertTrue(ZMSTestUtils.validateDueDate(userJane1.getExpiration().millis(), 15L * 24 * 60 * 60 * 1000)); + + mbrJane1 = new GroupMembership().setGroupName(groupName2).setMemberName("user.jane1"); + zmsImpl.putGroupMembership(ctx, domainName, groupName2, "user.jane1", auditRef, false, null, mbrJane1); + group2Res = zmsImpl.getGroup(ctx, domainName, groupName2, false, false); + userJane1 = ZMSTestUtils.getGroupMember(group2Res, "user.jane1"); + assertTrue(ZMSTestUtils.validateDueDate(userJane1.getExpiration().millis(), 15L * 24 * 60 * 60 * 1000)); + + // add joe1 to both roles and verify expected outcome + + try { + GroupMembership mbrJoe1 = new GroupMembership().setGroupName(groupName1).setMemberName("user.joe1"); + zmsImpl.putGroupMembership(ctx, domainName, groupName1, "user.joe1", auditRef, false, null, mbrJoe1); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), 400); + assertTrue(ex.getMessage().contains("User does not have required user authority expiry configured")); + } + + try { + GroupMembership mbrJoe1 = new GroupMembership().setGroupName(groupName2).setMemberName("user.joe1"); + zmsImpl.putGroupMembership(ctx, domainName, groupName2, "user.joe1", auditRef, false, null, mbrJoe1); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), 400); + assertTrue(ex.getMessage().contains("User does not have required user authority expiry configured")); + } + + zmsImpl.deleteTopLevelDomain(ctx, domainName, auditRef, null); + zmsImpl.dbService.zmsConfig.setUserAuthority(savedAuthority); + zmsImpl.userAuthority = savedAuthority; + } +} diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/ZMSTestUtils.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/ZMSTestUtils.java index 49519fcbf54..bfbba8c47f8 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/ZMSTestUtils.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/ZMSTestUtils.java @@ -211,4 +211,28 @@ public static RoleSystemMeta createRoleSystemMetaObject(Boolean auditEnabled) { } return meta; } + + public static RoleMember getRoleMember(Role role, final String memberName) { + if (role.getRoleMembers() == null) { + return null; + } + for (RoleMember roleMember : role.getRoleMembers()) { + if (roleMember.getMemberName().equals(memberName)) { + return roleMember; + } + } + return null; + } + + public static GroupMember getGroupMember(Group group, final String memberName) { + if (group.getGroupMembers() == null) { + return null; + } + for (GroupMember groupMember : group.getGroupMembers()) { + if (groupMember.getMemberName().equals(memberName)) { + return groupMember; + } + } + return null; + } } diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/utils/ZMSUtilsTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/utils/ZMSUtilsTest.java index 52382e75ac5..878243f9b06 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/utils/ZMSUtilsTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/utils/ZMSUtilsTest.java @@ -24,6 +24,7 @@ import com.yahoo.athenz.common.server.log.AuditLoggerFactory; import com.yahoo.athenz.common.server.log.impl.DefaultAuditLoggerFactory; import com.yahoo.athenz.zms.*; +import com.yahoo.rdl.Timestamp; import jakarta.servlet.http.HttpServletRequestWrapper; import org.mockito.Mockito; import org.testng.annotations.Test; @@ -408,4 +409,19 @@ public void testEmptyIfNull() { l = emptyIfNull(Collections.singletonList("s")); assertEquals(l.size(), 1); } + + @Test + public void testSmallestExpiry() { + + Timestamp currentExpiry = Timestamp.fromCurrentTime(); + assertEquals(ZMSUtils.smallestExpiry(null, null), null); + assertEquals(ZMSUtils.smallestExpiry(currentExpiry, null), currentExpiry); + assertEquals(ZMSUtils.smallestExpiry(null, currentExpiry), currentExpiry); + + Timestamp expiry1 = Timestamp.fromMillis(System.currentTimeMillis() + 1000); + Timestamp expiry2 = Timestamp.fromMillis(System.currentTimeMillis() - 1000); + + assertEquals(ZMSUtils.smallestExpiry(expiry1, expiry2), expiry2); + assertEquals(ZMSUtils.smallestExpiry(expiry2, expiry1), expiry2); + } }