ProjectSecurity.java

1
package edu.ucsb.cs.citelines.config;
2
3
import edu.ucsb.cs.citelines.entity.Project;
4
import edu.ucsb.cs.citelines.repository.ProjectCollaboratorRepository;
5
import edu.ucsb.cs.citelines.repository.ProjectRepository;
6
import edu.ucsb.cs.citelines.services.CurrentUserService;
7
import java.util.Collection;
8
import java.util.Optional;
9
import lombok.extern.slf4j.Slf4j;
10
import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations;
11
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
12
import org.springframework.security.access.prepost.PreAuthorize;
13
import org.springframework.security.core.GrantedAuthority;
14
import org.springframework.stereotype.Component;
15
16
/**
17
 * ProjectSecurity provides methods to check permissions for managing projects and project
18
 * collaborators. Mirrors the shape of CourseSecurity in proj-scaffold: {@code hasManagePermissions}
19
 * allows the owner, any collaborator, or an admin (read access to a project's details and its
20
 * collaborator list); {@code hasOwnerPermissions} is restricted to the owner (or an admin) for
21
 * mutating the project itself or its collaborator list.
22
 *
23
 * <p>Note that for a method with a projectId, you <em>still</em> need to verify in each method
24
 * whether the project exists or not. These annotations will <em>only</em> check whether or not the
25
 * particular user has access to a particular project.
26
 */
27
@Slf4j
28
@Component("ProjectSecurity")
29
public class ProjectSecurity {
30
  private final CurrentUserService currentUserService;
31
  private final RoleHierarchy roleHierarchy;
32
  private final ProjectRepository projectRepository;
33
  private final ProjectCollaboratorRepository projectCollaboratorRepository;
34
35
  public ProjectSecurity(
36
      CurrentUserService currentUserService,
37
      RoleHierarchy roleHierarchy,
38
      ProjectRepository projectRepository,
39
      ProjectCollaboratorRepository projectCollaboratorRepository) {
40
    this.currentUserService = currentUserService;
41
    this.roleHierarchy = roleHierarchy;
42
    this.projectRepository = projectRepository;
43
    this.projectCollaboratorRepository = projectCollaboratorRepository;
44
  }
45
46
  /**
47
   * Use this when you want to check whether the user is the owner, a collaborator, or an admin for
48
   * the project.
49
   *
50
   * @param operations
51
   * @param projectId
52
   * @return true if the user has manage permissions for the project, false otherwise.
53
   */
54
  @PreAuthorize("hasRole('ROLE_USER')")
55
  public Boolean hasManagePermissions(
56
      MethodSecurityExpressionOperations operations, Long projectId) {
57 1 1. hasManagePermissions : negated conditional → KILLED
    if (projectId == null) {
58
      // A null id can reach here from endpoints that take the projectId from a request body.
59
      // Grant access so the controller can reject the request with a proper validation error,
60
      // mirroring the project-not-found case below.
61 1 1. hasManagePermissions : replaced Boolean return with False for edu/ucsb/cs/citelines/config/ProjectSecurity::hasManagePermissions → KILLED
      return true;
62
    }
63
    Optional<Project> project = projectRepository.findById(projectId);
64 1 1. hasManagePermissions : negated conditional → KILLED
    if (project.isEmpty()) {
65 1 1. hasManagePermissions : replaced Boolean return with False for edu/ucsb/cs/citelines/config/ProjectSecurity::hasManagePermissions → KILLED
      return true;
66
    }
67 1 1. hasManagePermissions : negated conditional → KILLED
    if (isAdmin()) {
68 1 1. hasManagePermissions : replaced Boolean return with False for edu/ucsb/cs/citelines/config/ProjectSecurity::hasManagePermissions → KILLED
      return true;
69
    }
70
    String email = currentUserService.getCurrentUser().getUser().getEmail();
71 1 1. hasManagePermissions : negated conditional → KILLED
    if (email.equals(project.get().getOwner())) {
72 1 1. hasManagePermissions : replaced Boolean return with False for edu/ucsb/cs/citelines/config/ProjectSecurity::hasManagePermissions → KILLED
      return true;
73
    }
74 2 1. hasManagePermissions : replaced Boolean return with True for edu/ucsb/cs/citelines/config/ProjectSecurity::hasManagePermissions → KILLED
2. hasManagePermissions : replaced Boolean return with False for edu/ucsb/cs/citelines/config/ProjectSecurity::hasManagePermissions → KILLED
    return projectCollaboratorRepository.findAllByEmail(email).stream()
75 2 1. lambda$hasManagePermissions$0 : replaced boolean return with true for edu/ucsb/cs/citelines/config/ProjectSecurity::lambda$hasManagePermissions$0 → KILLED
2. lambda$hasManagePermissions$0 : replaced boolean return with false for edu/ucsb/cs/citelines/config/ProjectSecurity::lambda$hasManagePermissions$0 → KILLED
        .anyMatch(c -> c.getProject().getId().equals(projectId));
76
  }
77
78
  /**
79
   * Use this for operations that only the project's owner can do, such as editing or deleting the
80
   * project, or adding/removing collaborators.
81
   *
82
   * @param operations
83
   * @param projectId
84
   * @return true if the user has owner permissions for the project, false otherwise.
85
   */
86
  @PreAuthorize("hasRole('ROLE_RESEARCHER')")
87
  public Boolean hasOwnerPermissions(
88
      MethodSecurityExpressionOperations operations, Long projectId) {
89 1 1. hasOwnerPermissions : negated conditional → KILLED
    if (projectId == null) {
90 1 1. hasOwnerPermissions : replaced Boolean return with False for edu/ucsb/cs/citelines/config/ProjectSecurity::hasOwnerPermissions → KILLED
      return true;
91
    }
92
    Optional<Project> project = projectRepository.findById(projectId);
93 1 1. hasOwnerPermissions : negated conditional → KILLED
    if (project.isEmpty()) {
94 1 1. hasOwnerPermissions : replaced Boolean return with False for edu/ucsb/cs/citelines/config/ProjectSecurity::hasOwnerPermissions → KILLED
      return true;
95
    }
96 1 1. hasOwnerPermissions : negated conditional → KILLED
    if (isAdmin()) {
97 1 1. hasOwnerPermissions : replaced Boolean return with False for edu/ucsb/cs/citelines/config/ProjectSecurity::hasOwnerPermissions → KILLED
      return true;
98
    }
99
    String email = currentUserService.getCurrentUser().getUser().getEmail();
100 2 1. hasOwnerPermissions : replaced Boolean return with True for edu/ucsb/cs/citelines/config/ProjectSecurity::hasOwnerPermissions → KILLED
2. hasOwnerPermissions : replaced Boolean return with False for edu/ucsb/cs/citelines/config/ProjectSecurity::hasOwnerPermissions → KILLED
    return email.equals(project.get().getOwner());
101
  }
102
103
  private boolean isAdmin() {
104
    Collection<? extends GrantedAuthority> authorities =
105
        roleHierarchy.getReachableGrantedAuthorities(
106
            currentUserService.getCurrentUser().getRoles());
107 4 1. isAdmin : replaced boolean return with true for edu/ucsb/cs/citelines/config/ProjectSecurity::isAdmin → KILLED
2. lambda$isAdmin$1 : replaced boolean return with false for edu/ucsb/cs/citelines/config/ProjectSecurity::lambda$isAdmin$1 → KILLED
3. lambda$isAdmin$1 : replaced boolean return with true for edu/ucsb/cs/citelines/config/ProjectSecurity::lambda$isAdmin$1 → KILLED
4. isAdmin : replaced boolean return with false for edu/ucsb/cs/citelines/config/ProjectSecurity::isAdmin → KILLED
    return authorities.stream().anyMatch(role -> role.getAuthority().equals("ROLE_ADMIN"));
108
  }
109
}

Mutations

57

1.1
Location : hasManagePermissions
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasManagePermissions_false_for_unrelated_user()]
negated conditional → KILLED

61

1.1
Location : hasManagePermissions
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasManagePermissions_true_when_projectId_is_null()]
replaced Boolean return with False for edu/ucsb/cs/citelines/config/ProjectSecurity::hasManagePermissions → KILLED

64

1.1
Location : hasManagePermissions
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasManagePermissions_true_when_project_not_found()]
negated conditional → KILLED

65

1.1
Location : hasManagePermissions
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasManagePermissions_true_when_project_not_found()]
replaced Boolean return with False for edu/ucsb/cs/citelines/config/ProjectSecurity::hasManagePermissions → KILLED

67

1.1
Location : hasManagePermissions
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasManagePermissions_true_for_admin()]
negated conditional → KILLED

68

1.1
Location : hasManagePermissions
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasManagePermissions_true_for_admin()]
replaced Boolean return with False for edu/ucsb/cs/citelines/config/ProjectSecurity::hasManagePermissions → KILLED

71

1.1
Location : hasManagePermissions
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasManagePermissions_true_for_owner()]
negated conditional → KILLED

72

1.1
Location : hasManagePermissions
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasManagePermissions_true_for_owner()]
replaced Boolean return with False for edu/ucsb/cs/citelines/config/ProjectSecurity::hasManagePermissions → KILLED

74

1.1
Location : hasManagePermissions
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasManagePermissions_false_for_unrelated_user()]
replaced Boolean return with True for edu/ucsb/cs/citelines/config/ProjectSecurity::hasManagePermissions → KILLED

2.2
Location : hasManagePermissions
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasManagePermissions_true_for_collaborator()]
replaced Boolean return with False for edu/ucsb/cs/citelines/config/ProjectSecurity::hasManagePermissions → KILLED

75

1.1
Location : lambda$hasManagePermissions$0
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasManagePermissions_false_for_collaborator_of_a_different_project()]
replaced boolean return with true for edu/ucsb/cs/citelines/config/ProjectSecurity::lambda$hasManagePermissions$0 → KILLED

2.2
Location : lambda$hasManagePermissions$0
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasManagePermissions_true_for_collaborator()]
replaced boolean return with false for edu/ucsb/cs/citelines/config/ProjectSecurity::lambda$hasManagePermissions$0 → KILLED

89

1.1
Location : hasOwnerPermissions
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasOwnerPermissions_false_for_collaborator()]
negated conditional → KILLED

90

1.1
Location : hasOwnerPermissions
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasOwnerPermissions_true_when_projectId_is_null()]
replaced Boolean return with False for edu/ucsb/cs/citelines/config/ProjectSecurity::hasOwnerPermissions → KILLED

93

1.1
Location : hasOwnerPermissions
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasOwnerPermissions_true_when_project_not_found()]
negated conditional → KILLED

94

1.1
Location : hasOwnerPermissions
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasOwnerPermissions_true_when_project_not_found()]
replaced Boolean return with False for edu/ucsb/cs/citelines/config/ProjectSecurity::hasOwnerPermissions → KILLED

96

1.1
Location : hasOwnerPermissions
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasOwnerPermissions_true_for_admin()]
negated conditional → KILLED

97

1.1
Location : hasOwnerPermissions
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasOwnerPermissions_true_for_admin()]
replaced Boolean return with False for edu/ucsb/cs/citelines/config/ProjectSecurity::hasOwnerPermissions → KILLED

100

1.1
Location : hasOwnerPermissions
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasOwnerPermissions_false_for_collaborator()]
replaced Boolean return with True for edu/ucsb/cs/citelines/config/ProjectSecurity::hasOwnerPermissions → KILLED

2.2
Location : hasOwnerPermissions
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasOwnerPermissions_true_for_owner()]
replaced Boolean return with False for edu/ucsb/cs/citelines/config/ProjectSecurity::hasOwnerPermissions → KILLED

107

1.1
Location : isAdmin
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasOwnerPermissions_false_for_collaborator()]
replaced boolean return with true for edu/ucsb/cs/citelines/config/ProjectSecurity::isAdmin → KILLED

2.2
Location : lambda$isAdmin$1
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasManagePermissions_true_for_admin()]
replaced boolean return with false for edu/ucsb/cs/citelines/config/ProjectSecurity::lambda$isAdmin$1 → KILLED

3.3
Location : lambda$isAdmin$1
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasOwnerPermissions_false_for_collaborator()]
replaced boolean return with true for edu/ucsb/cs/citelines/config/ProjectSecurity::lambda$isAdmin$1 → KILLED

4.4
Location : isAdmin
Killed by : edu.ucsb.cs.citelines.config.ProjectSecurityTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.citelines.config.ProjectSecurityTests]/[method:hasManagePermissions_true_for_admin()]
replaced boolean return with false for edu/ucsb/cs/citelines/config/ProjectSecurity::isAdmin → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0