-
Notifications
You must be signed in to change notification settings - Fork 6
Implement Pulse Survey report permissions #2429
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a8cd08a
7ec6a48
bcc38bf
a85d231
406e66c
7b925d6
263c9d4
b757eb3
32affcc
3a61986
bd910de
af83686
80f6881
7d08d3b
df91b0f
c5d30e6
3bcd0bf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -150,6 +150,18 @@ public List<MemberProfile> getSupervisorsForId(UUID id) { | |
| return supervisorsForId; | ||
| } | ||
|
|
||
| @Override | ||
| @Cacheable | ||
| public List<MemberProfile> getSubordinatesForId(UUID id) { | ||
| List<MemberProfile> subordinatesForId = memberProfileRepository.findSubordinatesForId(id); | ||
| if (!currentUserServices.isAdmin()) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This bit makes me sad, but I think it's the right choice for this PR. I'll create a spike to identify any usages of the roles for security (we should be using permissions) so that we can break out stories to address those. |
||
| for (MemberProfile memberProfile : subordinatesForId) { | ||
| memberProfile.clearBirthYear(); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we want to clear the birth year of the subordinates?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was following the pattern for supervisors in the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't want to expose folks ages to everyone, so we clear them for those who don't need to know. |
||
| } | ||
| } | ||
| return subordinatesForId; | ||
| } | ||
|
|
||
| @Override | ||
| public MemberProfile updateProfile(MemberProfile memberProfile) { | ||
| return memberProfileRepository.update(memberProfile); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,81 +2,123 @@ | |
|
|
||
| import com.objectcomputing.checkins.exceptions.BadArgException; | ||
| import com.objectcomputing.checkins.services.memberprofile.MemberProfileRepository; | ||
| import com.objectcomputing.checkins.services.memberprofile.MemberProfileServices; | ||
| import com.objectcomputing.checkins.services.memberprofile.currentuser.CurrentUserServices; | ||
| import com.objectcomputing.checkins.services.permissions.Permission; | ||
| import com.objectcomputing.checkins.services.role.role_permissions.RolePermissionServices; | ||
| import jakarta.inject.Singleton; | ||
| import jakarta.validation.constraints.NotNull; | ||
|
|
||
| import java.time.LocalDate; | ||
| import java.util.HashSet; | ||
| import java.util.Set; | ||
| import java.util.UUID; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| @Singleton | ||
| public class PulseResponseServicesImpl implements PulseResponseService { | ||
|
|
||
| private final PulseResponseRepository pulseResponseRepo; | ||
| private final MemberProfileServices memberProfileServices; | ||
| private final MemberProfileRepository memberRepo; | ||
| private final CurrentUserServices currentUserServices; | ||
| private final RolePermissionServices rolePermissionServices; | ||
|
|
||
| public PulseResponseServicesImpl(PulseResponseRepository pulseResponseRepo, | ||
| MemberProfileRepository memberRepo) { | ||
| public PulseResponseServicesImpl( | ||
| PulseResponseRepository pulseResponseRepo, | ||
| MemberProfileServices memberProfileServices, | ||
| MemberProfileRepository memberRepo, | ||
| CurrentUserServices currentUserServices, | ||
| RolePermissionServices rolePermissionServices | ||
| ) { | ||
| this.pulseResponseRepo = pulseResponseRepo; | ||
| this.memberProfileServices = memberProfileServices; | ||
| this.memberRepo = memberRepo; | ||
| this.currentUserServices = currentUserServices; | ||
| this.rolePermissionServices = rolePermissionServices; | ||
| } | ||
|
|
||
| @Override | ||
| public PulseResponse save(PulseResponse pulseResponse) { | ||
| UUID currentUserId = currentUserServices.getCurrentUser().getId(); | ||
| PulseResponse pulseResponseRet = null; | ||
| if(pulseResponse!=null){ | ||
| if (pulseResponse != null) { | ||
| final UUID memberId = pulseResponse.getTeamMemberId(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given my comment about only being able to create/update your own responses. It might be worthwhile to turn the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had a look, but I am unsure as to how to get the tests working with Should I change in To more closely match your comment? We can then raise a card to investigate
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That works, yes. Sorry for the slow reply. I missed yours completely. |
||
| LocalDate pulseSubDate = pulseResponse.getSubmissionDate(); | ||
| if(pulseResponse.getId()!=null){ | ||
| throw new BadArgException(String.format("Found unexpected id for pulseresponse %s", pulseResponse.getId())); | ||
| } else if(!memberRepo.findById(memberId).isPresent()){ | ||
| throw new BadArgException(String.format("Member %s doesn't exists", memberId)); | ||
| } else if(pulseSubDate.isBefore(LocalDate.EPOCH) || pulseSubDate.isAfter(LocalDate.MAX)) { | ||
| throw new BadArgException(String.format("Invalid date for pulseresponse submission date %s",memberId)); | ||
| } | ||
| pulseResponseRet = pulseResponseRepo.save(pulseResponse); | ||
| if (pulseResponse.getId() != null) { | ||
| throw new BadArgException(String.format("Found unexpected id for pulseresponse %s", pulseResponse.getId())); | ||
| } else if (memberRepo.findById(memberId).isEmpty()) { | ||
| throw new BadArgException(String.format("Member %s doesn't exists", memberId)); | ||
| } else if (pulseSubDate.isBefore(LocalDate.EPOCH) || pulseSubDate.isAfter(LocalDate.MAX)) { | ||
| throw new BadArgException(String.format("Invalid date for pulseresponse submission date %s", memberId)); | ||
| } else if (!currentUserId.equals(memberId) && !isSubordinateTo(memberId, currentUserId)) { | ||
| throw new BadArgException(String.format("User %s does not have permission to create pulse response for user %s", currentUserId, memberId)); | ||
| } | ||
| pulseResponseRet = pulseResponseRepo.save(pulseResponse); | ||
| } | ||
| return pulseResponseRet ; | ||
| return pulseResponseRet; | ||
| } | ||
|
|
||
|
|
||
| @Override | ||
| public PulseResponse read(@NotNull UUID id) { | ||
| return pulseResponseRepo.findById(id).orElse(null); | ||
| UUID currentUserId = currentUserServices.getCurrentUser().getId(); | ||
| boolean hasPermission = rolePermissionServices.findUserPermissions(currentUserId).contains(Permission.CAN_VIEW_ALL_PULSE_RESPONSES); | ||
|
|
||
| return pulseResponseRepo.findById(id) | ||
| .filter(pulse -> hasPermission || canViewDueToReportingHierarchy(pulse, currentUserId)) | ||
| .orElse(null); | ||
| } | ||
|
|
||
| @Override | ||
| public PulseResponse update(PulseResponse pulseResponse) { | ||
| UUID currentUserId = currentUserServices.getCurrentUser().getId(); | ||
| PulseResponse pulseResponseRet = null; | ||
| if(pulseResponse!=null){ | ||
| final UUID id = pulseResponse.getId(); | ||
| final UUID memberId = pulseResponse.getTeamMemberId(); | ||
| LocalDate pulseSubDate = pulseResponse.getSubmissionDate(); | ||
| if(id==null||!pulseResponseRepo.findById(id).isPresent()){ | ||
| throw new BadArgException(String.format("Unable to find pulseresponse record with id %s", pulseResponse.getId())); | ||
| }else if(!memberRepo.findById(memberId).isPresent()){ | ||
| throw new BadArgException(String.format("Member %s doesn't exist", memberId)); | ||
| } else if(memberId==null) { | ||
| throw new BadArgException(String.format("Invalid pulseresponse %s", pulseResponse)); | ||
| } else if(pulseSubDate.isBefore(LocalDate.EPOCH) || pulseSubDate.isAfter(LocalDate.MAX)) { | ||
| throw new BadArgException(String.format("Invalid date for pulseresponse submission date %s",memberId)); | ||
| if (pulseResponse != null) { | ||
| final UUID id = pulseResponse.getId(); | ||
| final UUID memberId = pulseResponse.getTeamMemberId(); | ||
| LocalDate pulseSubDate = pulseResponse.getSubmissionDate(); | ||
| if (id == null || pulseResponseRepo.findById(id).isEmpty()) { | ||
| throw new BadArgException(String.format("Unable to find pulseresponse record with id %s", pulseResponse.getId())); | ||
| } else if (memberRepo.findById(memberId).isEmpty()) { | ||
| throw new BadArgException(String.format("Member %s doesn't exist", memberId)); | ||
| } else if (memberId == null) { | ||
| throw new BadArgException(String.format("Invalid pulseresponse %s", pulseResponse)); | ||
| } else if (pulseSubDate.isBefore(LocalDate.EPOCH) || pulseSubDate.isAfter(LocalDate.MAX)) { | ||
| throw new BadArgException(String.format("Invalid date for pulseresponse submission date %s", memberId)); | ||
| } else if (!currentUserId.equals(memberId) && !isSubordinateTo(memberId, currentUserId)) { | ||
| throw new BadArgException(String.format("User %s does not have permission to update pulse response for user %s", currentUserId, memberId)); | ||
| } | ||
| pulseResponseRet = pulseResponseRepo.update(pulseResponse); | ||
| } | ||
|
|
||
| pulseResponseRet = pulseResponseRepo.update(pulseResponse); | ||
| } | ||
| return pulseResponseRet; | ||
| } | ||
|
|
||
| @Override | ||
| public Set<PulseResponse> findByFields(UUID teamMemberId, LocalDate dateFrom, LocalDate dateTo) { | ||
| Set<PulseResponse> pulseResponse = new HashSet<>(); | ||
| pulseResponseRepo.findAll().forEach(pulseResponse::add); | ||
| if(teamMemberId!=null){ | ||
| UUID currentUserId = currentUserServices.getCurrentUser().getId(); | ||
| boolean hasPermission = rolePermissionServices.findUserPermissions(currentUserId).contains(Permission.CAN_VIEW_ALL_PULSE_RESPONSES); | ||
|
|
||
| Set<PulseResponse> pulseResponse = pulseResponseRepo.findAll() | ||
| .stream() | ||
| .filter(pulse -> hasPermission || canViewDueToReportingHierarchy(pulse, currentUserId)) | ||
| .collect(Collectors.toSet()); | ||
|
|
||
| if (teamMemberId != null) { | ||
| pulseResponse.retainAll(pulseResponseRepo.findByTeamMemberId(teamMemberId)); | ||
| } else if(dateFrom!=null && dateTo!=null) { | ||
| } else if (dateFrom != null && dateTo != null) { | ||
| pulseResponse.retainAll(pulseResponseRepo.findBySubmissionDateBetween(dateFrom, dateTo)); | ||
| } | ||
| return pulseResponse; | ||
| } | ||
| } | ||
|
|
||
| // The current user can view the pulse response if they are the team member who submitted the pulse response | ||
| // or if they are the supervisor of the team member who submitted the pulse response | ||
| private boolean canViewDueToReportingHierarchy(PulseResponse pulse, UUID currentUserId) { | ||
| return pulse.getTeamMemberId().equals(currentUserId) || | ||
| isSubordinateTo(pulse.getTeamMemberId(), currentUserId); | ||
| } | ||
|
|
||
| private boolean isSubordinateTo(UUID reportMember, UUID currentUserId) { | ||
| return memberProfileServices.getSubordinatesForId(currentUserId) | ||
| .stream().anyMatch(member -> member.getId().equals(reportMember)); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤘