INFRA-1989 | Ashvin | Change privilege check logic

This commit is contained in:
Ashvin Sharma
2023-08-03 22:40:57 +05:30
parent 8b65428a35
commit 64e1890f14
19 changed files with 326 additions and 364 deletions

View File

@@ -16,7 +16,8 @@ def get_user_teams_map(all_teams_details):
def user_roles(user_teams):
user_roles_map = {}
env = ["cmd", "prod", "dev", "qa", "perf", "uat", "data-platform-prod", "data-platform-nonprod"]
env = ["cmd", "prod", "dev", "qa", "perf", "uat", "data-platform-prod", "data-platform-nonprod",
"local"]
# list from okta
managers = {
"harinder.singh@navi.com",
@@ -94,7 +95,6 @@ def get_all_teams(z_request, blacklist_team, team_name_map):
return teams
def main():
ZENDUTY_API_KEY = os.getenv("ZENDUTY_TOKEN", "")
if ZENDUTY_API_KEY == "":

View File

@@ -1,7 +1,6 @@
package com.navi.infra.portal.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.navi.infra.portal.security.authorization.AuthorizationContext;
import com.navi.infra.portal.service.user.UserService;
import java.io.IOException;
import java.util.HashMap;
@@ -40,16 +39,6 @@ public class UserController {
}
}
@GetMapping("/api/user/teams")
public ResponseEntity<?> getUserTeamMap(@RequestParam Boolean all) throws NotFoundException {
if (all) {
return ResponseEntity.ok().body(userService.getAllUsersTeamMap());
}
return ResponseEntity.ok()
.body(userService.getUserTeams(AuthorizationContext.getUserEmail()));
}
@PostMapping("/api/user/token")
@ResponseStatus(HttpStatus.CREATED)
public String createUserToken(@AuthenticationPrincipal OidcUser user) {
@@ -71,13 +60,6 @@ public class UserController {
return new ResponseEntity<>(HttpStatus.OK);
}
@PostMapping("/api/user/teams/admin")
public ResponseEntity<?> createTeamToUsersMapping(@RequestBody JsonNode request)
throws IOException {
userService.setTeamToUsersMapping(request);
return new ResponseEntity<>(HttpStatus.OK);
}
@PostMapping("/api/user/mapping")
public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file)
throws IOException {

View File

@@ -11,13 +11,6 @@ import org.springframework.security.core.context.SecurityContextHolder;
@Slf4j
public class AuthorizationContext {
public static Boolean hasAuthority(String privilege) {
List<String> authorities = SecurityContextHolder.getContext().getAuthentication()
.getAuthorities()
.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
return authorities.contains(privilege);
}
public static String getUsername() {
Object principal = SecurityContextHolder.getContext().getAuthentication()
.getPrincipal();

View File

@@ -1,71 +0,0 @@
package com.navi.infra.portal.security.authorization;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.navi.infra.portal.domain.manifest.Manifest;
import com.navi.infra.portal.repository.ManifestRepository;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;
@Component(value = "auth")
public class AuthorizationFilter {
@Autowired
private ManifestRepository manifestRepository;
@Autowired
private ObjectMapper objectMapper;
@PreAuthorize("hasAnyAuthority(#authority , 'portal.admin')")
public boolean hasPermissions(String authority) {
return AuthorizationContext.hasAuthority(authority);
}
@PreAuthorize("hasAuthority(#authority)")
public boolean hasPermissions(String authority, String name, String env) {
if (AuthorizationContext.hasAuthority("portal.admin")) {
return true;
}
Map<String, Object> data = manifestRepository.findManifestDataByNameAndEnvironment(name,
env);
return manifestUserTeamMatch(data);
}
@PreAuthorize("hasAuthority(#authority)")
public boolean hasPermissions(String authority, Long id) {
if (AuthorizationContext.hasAuthority("portal.admin")) {
return true;
}
Map<String, Object> data = manifestRepository.findManifestDataById(id);
return manifestUserTeamMatch(data);
}
@PreAuthorize("hasAuthority(#authority)")
public boolean hasPermissions(String authority, Object obj) {
if (AuthorizationContext.hasAuthority("portal.admin")) {
return true;
}
if (obj instanceof Manifest) {
return manifestUserTeamMatch((Manifest) obj);
}
if (obj instanceof JsonNode) {
Manifest manifest = objectMapper.convertValue(obj, Manifest.class);
return manifestUserTeamMatch(manifest);
}
return false;
}
private boolean manifestUserTeamMatch(Manifest manifest) {
HashMap<String, Object> map = (HashMap<String, Object>) manifest.getData().get("team");
return AuthorizationContext.hasAuthority("team." + map.get("name"));
}
private boolean manifestUserTeamMatch(Map<String, Object> data) {
HashMap<String, Object> map = (HashMap<String, Object>) data.get("team");
return AuthorizationContext.hasAuthority("team." + map.get("name"));
}
}

View File

@@ -0,0 +1,12 @@
package com.navi.infra.portal.security.authorization;
import com.navi.infra.portal.domain.manifest.Manifest;
import com.navi.infra.portal.v2.privilege.Action;
import com.navi.infra.portal.v2.privilege.Group;
public interface AuthorizationService {
boolean hasPermissions(Group group, Manifest manifest, Action action);
boolean hasPermissions(Group group, String teamName, String env, Action action);
}

View File

@@ -0,0 +1,58 @@
package com.navi.infra.portal.security.authorization;
import com.navi.infra.portal.domain.manifest.Manifest;
import com.navi.infra.portal.v2.privilege.Action;
import com.navi.infra.portal.v2.privilege.Group;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
@Component(value = "auth")
@Slf4j
public class AuthorizationServiceImpl implements AuthorizationService {
private static boolean matchAction(Action action, String authAction) {
return authAction.equals(action.toString());
}
private static boolean matchEnv(String env, String authEnv) {
return authEnv.equals(".*") || authEnv.equals(env);
}
private static boolean matchTeam(String teamName, String authTeam) {
return teamName.equals(authTeam);
}
@Override
public boolean hasPermissions(Group group, Manifest manifest, Action action) {
return hasPermissions(group, manifest.getTeam(), manifest.getEnvironment(), action);
}
@Override
public boolean hasPermissions(Group group, String teamName, String env, Action action) {
var authorities = SecurityContextHolder.getContext().getAuthentication()
.getAuthorities();
for (var authority : authorities) {
log.info("authority: {}", authority.getAuthority());
var split = authority.getAuthority().split(":");
final var authGroup = split[0];
final var authTeam = split[1];
final var authEnv = split[2];
final var authAction = split[4];
if (!authGroup.equals(group.toString())) {
continue;
}
if (
matchTeam(teamName, authTeam) &&
matchEnv(env, authEnv) &&
matchAction(action, authAction)
) {
return true;
}
}
return false;
}
}

View File

@@ -1,9 +1,13 @@
package com.navi.infra.portal.service.kubernetes;
import static com.navi.infra.portal.v2.privilege.Action.KUBE_RESTART;
import static com.navi.infra.portal.v2.privilege.Group.KUBE;
import com.navi.infra.portal.client.KubeClient;
import com.navi.infra.portal.domain.manifest.Manifest;
import com.navi.infra.portal.exceptions.ForbiddenException;
import com.navi.infra.portal.security.authorization.AuthorizationContext;
import com.navi.infra.portal.security.authorization.AuthorizationService;
import com.navi.infra.portal.service.manifest.ManifestService;
import com.navi.infra.portal.util.kubernetes.ManifestJsonnetUtil;
import io.kubernetes.client.openapi.ApiException;
@@ -28,10 +32,11 @@ public class KubeOperationService {
private final KubeClient kubeClient;
private final AuthorizationService authorizationFilter;
public void restartPodByManifestId(Long id) {
Manifest manifest = manifestService.fetchById(id);
if (AuthorizationContext.hasAuthority(String.format("kube.restart.%s",
manifest.getEnvironment()))) {
if (authorizationFilter.hasPermissions(KUBE, manifest, KUBE_RESTART)) {
String deploymentName = ManifestJsonnetUtil.fullServiceName(manifest.getName());
String namespace = (String) manifest.getDeployment().getData().get("namespace");
String cluster = (String) manifest.getDeployment().getData().get("cluster");

View File

@@ -3,6 +3,9 @@ package com.navi.infra.portal.service.manifest;
import static com.navi.infra.portal.domain.manifest.Manifest.redactedEmptyValueString;
import static com.navi.infra.portal.domain.manifest.Manifest.redactedValueString;
import static com.navi.infra.portal.util.FlatMapUtil.flatten;
import static com.navi.infra.portal.v2.privilege.Action.MANIFEST_DELETE;
import static com.navi.infra.portal.v2.privilege.Action.MANIFEST_READ;
import static com.navi.infra.portal.v2.privilege.Group.MANIFEST;
import static java.lang.String.format;
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -20,6 +23,7 @@ import com.navi.infra.portal.repository.ManifestAuditRepository;
import com.navi.infra.portal.repository.ManifestName;
import com.navi.infra.portal.repository.ManifestRepository;
import com.navi.infra.portal.security.authorization.AuthorizationContext;
import com.navi.infra.portal.security.authorization.AuthorizationService;
import com.navi.infra.portal.service.kubernetes.KubernetesManifestService;
import com.navi.infra.portal.service.user.PrivilegeUtilService;
import com.navi.infra.portal.service.user.UserService;
@@ -40,13 +44,11 @@ import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.text.StringSubstitutor;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;
@Service
@@ -75,13 +77,16 @@ public class ManifestService {
private final MapDiffUtil mapDiffUtil;
private final AuthorizationService authorizationFilter;
ManifestService(ObjectMapper objectMapper,
ManifestRepository manifestRepository,
ManifestAuditRepository manifestAuditRepository, VaultService vaultService,
KubernetesManifestService kubernetesManifestService,
PortalEventPublisher portalEventPublisher, PrivilegeUtilService privilegeUtilService,
ManifestAuditService manifestAuditService, UserService userService,
MapDiffUtil mapDiffUtil) {
MapDiffUtil mapDiffUtil,
AuthorizationService authorizationFilter) {
this.objectMapper = objectMapper;
this.manifestRepository = manifestRepository;
this.manifestAuditRepository = manifestAuditRepository;
@@ -93,6 +98,7 @@ public class ManifestService {
this.manifestAuditService = manifestAuditService;
this.userService = userService;
this.mapDiffUtil = mapDiffUtil;
this.authorizationFilter = authorizationFilter;
}
private static void assertEnvVarKeyIsNotDuplicate(List<SecretConfig> environmentVariables) {
@@ -136,7 +142,8 @@ public class ManifestService {
}
}
@PreAuthorize("@auth.hasPermissions('manifest.write' ,#manifest)")
@PreAuthorize("@auth.hasPermissions(T(com.navi.infra.portal.v2.privilege.Group).MANIFEST, "
+ "#manifest, T(com.navi.infra.portal.v2.privilege.Action).MANIFEST_WRITE)")
@Transactional
public ManifestResponse createOrUpdate(Manifest manifest) {
ManifestResponse manifestResponse = new ManifestResponse();
@@ -144,9 +151,6 @@ public class ManifestService {
if (!manifestResponse.getError().isEmpty()) {
return manifestResponse;
}
if (!privilegeUtilService.canWriteManifest(manifest)) {
throw new AccessDeniedException("Unable to write manifest");
}
Manifest oldManifest = null;
if (manifest.getId() != null) {
oldManifest = manifestRepository.findById(manifest.getId()).orElseThrow(
@@ -181,61 +185,60 @@ public class ManifestService {
}
@PostAuthorize("@auth.hasPermissions('manifest.read', returnObject)")
@PostAuthorize("@auth.hasPermissions(T(com.navi.infra.portal.v2.privilege.Group).MANIFEST, "
+ "returnObject, T(com.navi.infra.portal.v2.privilege.Action).MANIFEST_READ)")
public Manifest fetchByNameAndEnvironment(String name, String environment) {
if (privilegeUtilService.canReadManifest()) {
Optional<Manifest> optionalManifest = manifestRepository
.findByNameAndEnvironment(name, environment);
return map(optionalManifest);
}
throw new AccessDeniedException("Insufficient Permissions");
Optional<Manifest> optionalManifest = manifestRepository
.findByNameAndEnvironment(name, environment);
return map(optionalManifest);
}
@PostAuthorize("@auth.hasPermissions('manifest.read', returnObject)")
@PostAuthorize("@auth.hasPermissions(T(com.navi.infra.portal.v2.privilege.Group).MANIFEST, "
+ "returnObject, T(com.navi.infra.portal.v2.privilege.Action).MANIFEST_READ)")
public Manifest fetchByIdAndVersion(Long Id, Long version) {
if (privilegeUtilService.canReadManifest()) {
ManifestAudit audit = manifestAuditRepository.findByManifestIdAndManifestVersion(Id,
version)
.orElseThrow(() -> new RuntimeException("Selected Manifest Version Not Found"));
try {
Manifest manifest = objectMapper.readValue(
audit.getData().get("manifest").toString(), Manifest.class);
return mapOlderVersion(manifest, audit);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
ManifestAudit audit = manifestAuditRepository.findByManifestIdAndManifestVersion(Id,
version)
.orElseThrow(() -> new RuntimeException("Selected Manifest Version Not Found"));
try {
Manifest manifest = objectMapper.readValue(
audit.getData().get("manifest").toString(), Manifest.class);
return mapOlderVersion(manifest, audit);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
throw new AccessDeniedException("Insufficient Permissions");
}
@PreAuthorize("@auth.hasPermissions('manifest.read')")
public Map<String, String> fetchConfigByNameAndEnvironment(String name, String environment) {
Manifest manifest = fetchByNameAndEnvironment(name, environment);
if (!privilegeUtilService.canReadSecret(manifest)) {
throw new AccessDeniedException("User does not have permission to read manifest");
}
return manifest.getEnvironmentAsMap();
}
@PostAuthorize("@auth.hasPermissions('manifest.read' ,returnObject)")
@PostAuthorize("@auth.hasPermissions(T(com.navi.infra.portal.v2.privilege.Group).MANIFEST, "
+ "returnObject, T(com.navi.infra.portal.v2.privilege.Action).MANIFEST_READ)")
public Manifest fetchById(Long id) {
if (privilegeUtilService.canReadManifest()) {
Optional<Manifest> optionalManifest = manifestRepository.findById(id);
return map(optionalManifest);
}
throw new AccessDeniedException("Insufficient Permissions");
Optional<Manifest> optionalManifest = manifestRepository.findById(id);
return map(optionalManifest);
}
@PreAuthorize("@auth.hasPermissions('manifest.read', #id)")
public String fetchKubeObjectById(Long id, String image) {
Optional<Manifest> optionalManifest = manifestRepository.findById(id);
Manifest manifest = map(optionalManifest);
if (!authorizationFilter.hasPermissions(MANIFEST, manifest, MANIFEST_READ)) {
throw new AccessDeniedException("User does not have permission to read manifest");
}
return kubernetesManifestService.getKubeObjects(manifest, image).toString();
}
@PreAuthorize("@auth.hasPermissions('manifest.read', #name, #environment)")
public String fetchKubeObjectByName(String name, String environment, String image) {
Optional<Manifest> optionalManifest = manifestRepository
.findByNameAndEnvironment(name, environment);
Manifest manifest = map(optionalManifest);
if (!authorizationFilter.hasPermissions(MANIFEST, manifest, MANIFEST_READ)) {
throw new AccessDeniedException("User does not have permission to read manifest");
}
return kubernetesManifestService.getKubeObjects(manifest, image).toString();
}
@@ -276,21 +279,26 @@ public class ManifestService {
public void validateCloneRequest(CloneManifestRequest cloneRequest, Manifest manifest) {
log.info(userService.getCurrentUsername() + " : " + userService.getAuthorities());
if (manifest.getEnvironment().equals("prod") && !cloneRequest.getEnvironment()
final var targetTeam = cloneRequest.getTeamName();
final var targetEnvironment = cloneRequest.getEnvironment();
final var sourceTeam = manifest.getTeam();
final var sourceEnvironment = manifest.getEnvironment();
if (sourceEnvironment.equals("prod") && !targetEnvironment
.equals("prod")) {
throw new AccessDeniedException(
"You are not allowed to clone from prod env to other env!");
}
if (manifest.getEnvironment().equals("prod") && cloneRequest.getEnvironment().equals("prod")
if (sourceEnvironment.equals("prod") && targetEnvironment.equals("prod")
&& (cloneRequest.isCopyOutbound() || cloneRequest.isCopyEnvVariables())) {
throw new AccessDeniedException(
"You are not allowed to copy outbound/env variables from prod env to other prod env!");
}
if (!privilegeUtilService.canWriteManifest(cloneRequest.getEnvironment())) {
if (!privilegeUtilService.canWriteManifest(targetTeam, targetEnvironment)) {
throw new AccessDeniedException(
format("You don't have write access for %s!", cloneRequest.getEnvironment()));
format("You don't have write access for %s!", targetEnvironment));
}
if (!privilegeUtilService.canReadManifest(manifest.getEnvironment())) {
if (!privilegeUtilService.canReadManifest(sourceTeam, sourceEnvironment)) {
throw new AccessDeniedException("You don't have read access for the selected source!");
}
}
@@ -333,18 +341,22 @@ public class ManifestService {
return manifestAuditRepository.findByManifestIdOrderByManifestVersionDesc(manifestId);
}
@PreAuthorize("@auth.hasPermissions('manifest.delete', #id)")
public List<String> delete(Long id, Boolean deleteManifest) {
List<ManifestAudit> manifestAudits = manifestAuditRepository.findByManifestId(id);
List<Long> auditIds = manifestAudits.stream().map(ManifestAudit::getId)
.collect(Collectors.toList());
List<String> result = new ArrayList<>();
Manifest manifest = map(manifestRepository.findById(id));
if (!authorizationFilter.hasPermissions(MANIFEST, manifest, MANIFEST_DELETE)) {
throw new AccessDeniedException("User does not have permission to delete manifest");
}
List<String> result = new ArrayList<>();
if (manifest.getDeployment() != null) {
log.info("Deleting manifest Resources for {}", manifest.fullName());
log.info("Deleting kubernetes resources for {}", manifest.fullName());
result = kubernetesManifestService.deleteResources(manifest);
}
if (deleteManifest) {
List<ManifestAudit> manifestAudits = manifestAuditRepository.findByManifestId(id);
List<Long> auditIds = manifestAudits.stream().map(ManifestAudit::getId)
.collect(Collectors.toList());
result.add(manifest.fullName());
log.info("Deleting manifest Object - {} with IDs - {}", manifest.fullName(), auditIds);
manifestAuditRepository.deleteByIdIn(auditIds);
@@ -395,7 +407,7 @@ public class ManifestService {
Map<String, String> superSecrets = manifest.removeSuperSecrets();
if (!superSecrets.isEmpty()) {
if (!privilegeUtilService.canWriteSuperSecret()) {
if (!privilegeUtilService.canWriteSuperSecret(manifest)) {
throw new AccessDeniedException("Unable to write super secrets");
}
superSecretsVersion = writeSuperSecretToVault(manifest.getSuperSecretVaultPath(),
@@ -495,7 +507,7 @@ public class ManifestService {
manifestDeepCopy.addCustomValuesToSecrets(redactedValue);
}
if (manifest.hasSuperSecrets() && privilegeUtilService.canReadSuperSecret()) {
if (manifest.hasSuperSecrets() && privilegeUtilService.canReadSuperSecret(manifest)) {
Map<String, String> data =
superSecretVersion == null ? fetchSecretFromVault(superSecretPath)
: fetchSecretFromVault(superSecretPath, superSecretVersion);
@@ -515,7 +527,7 @@ public class ManifestService {
}
private Manifest makeEnvironmentSubstitution(Manifest manifest) {
if (!privilegeUtilService.canSubstituteEnvironmentVariable()) {
if (!privilegeUtilService.canSubstituteEnvironmentVariable(manifest)) {
return manifest;
}
try {

View File

@@ -29,7 +29,6 @@ public class CustomOidcUserService extends OidcUserService {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.addAll(user.getAuthorities());
authorities.addAll(oidcUser.getAuthorities());
CustomOidcUser customOidcUser = new CustomOidcUser(authorities, oidcUser.getIdToken(),
oidcUser.getUserInfo());

View File

@@ -1,64 +1,62 @@
package com.navi.infra.portal.service.user;
import static com.navi.infra.portal.security.authorization.AuthorizationContext.hasAuthority;
import static java.lang.String.format;
import static com.navi.infra.portal.v2.privilege.Action.APPROVAL_WRITE;
import static com.navi.infra.portal.v2.privilege.Action.MANIFEST_SECRET_READ;
import static com.navi.infra.portal.v2.privilege.Action.MANIFEST_SECRET_WRITE;
import static com.navi.infra.portal.v2.privilege.Action.MANIFEST_SUPERSECRET_READ;
import static com.navi.infra.portal.v2.privilege.Action.MANIFEST_SUPERSECRET_WRITE;
import static com.navi.infra.portal.v2.privilege.Action.MANIFEST_WRITE;
import static com.navi.infra.portal.v2.privilege.Group.MANIFEST;
import com.navi.infra.portal.domain.manifest.Manifest;
import lombok.NoArgsConstructor;
import com.navi.infra.portal.security.authorization.AuthorizationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@NoArgsConstructor
@Slf4j
public class PrivilegeUtilService {
private final AuthorizationService authorizationFilter;
public PrivilegeUtilService(AuthorizationService authorizationFilter) {
this.authorizationFilter = authorizationFilter;
}
public boolean canWriteManifest(Manifest manifest) {
return hasAuthorityForEnvironment("manifest.write", manifest.getEnvironment());
return authorizationFilter.hasPermissions(MANIFEST, manifest, MANIFEST_WRITE);
}
public boolean canWriteManifest(String environment) {
return hasAuthorityForEnvironment("manifest.write", environment);
public boolean canWriteManifest(String team, String environment) {
return authorizationFilter.hasPermissions(MANIFEST, team, environment, MANIFEST_SECRET_WRITE);
}
public boolean canReadManifest(String environment) {
return hasAuthorityForEnvironment("manifest.read", environment);
public boolean canReadManifest(String team, String environment) {
return authorizationFilter.hasPermissions(MANIFEST, team, environment, MANIFEST_SECRET_WRITE);
}
public boolean canSubstituteEnvironmentVariable() {
return hasAuthorityFor("substitute.environment");
}
public boolean canReadManifest() {
return hasAuthorityFor("manifest.read");
public boolean canSubstituteEnvironmentVariable(Manifest manifest) {
return authorizationFilter.hasPermissions(MANIFEST, manifest, MANIFEST_SECRET_WRITE);
}
public boolean canWriteSecret(Manifest manifest) {
return hasAuthorityForEnvironment("secret.write", manifest.getEnvironment());
return authorizationFilter.hasPermissions(MANIFEST, manifest, MANIFEST_SECRET_WRITE);
}
public boolean canReadSecret(Manifest manifest) {
return hasAuthorityForEnvironment("secret.read", manifest.getEnvironment());
return authorizationFilter.hasPermissions(MANIFEST, manifest, MANIFEST_SECRET_READ);
}
public boolean canWriteSuperSecret() {
return hasAuthorityFor("supersecret.write");
public boolean canWriteSuperSecret(Manifest manifest) {
return authorizationFilter.hasPermissions(MANIFEST, manifest, MANIFEST_SUPERSECRET_WRITE);
}
public boolean canReadSuperSecret() {
return hasAuthorityFor("supersecret.read");
public boolean canReadSuperSecret(Manifest manifest) {
return authorizationFilter.hasPermissions(MANIFEST, manifest, MANIFEST_SUPERSECRET_READ);
}
private boolean hasAuthorityForEnvironment(String privilege, String environment) {
return hasAuthority(format("%s.%s", privilege, environment));
}
private boolean hasAuthorityFor(String privilege) {
return hasAuthority(format("%s", privilege));
}
public boolean canApproveChangeRequests() {
return hasAuthority("manifest.approve.all");
public boolean canApproveChangeRequests(Manifest manifest) {
return authorizationFilter.hasPermissions(MANIFEST, manifest, APPROVAL_WRITE);
}
}

View File

@@ -21,18 +21,12 @@ import com.navi.infra.portal.security.authorization.AuthorizationContext;
import com.navi.infra.portal.v2.role.RoleService;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import javassist.NotFoundException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
@@ -105,20 +99,6 @@ public class UserService {
throw new UsernameNotFoundException(email + " not found");
}
@PreAuthorize("@auth.hasPermissions('portal.teams.admin')")
public void setTeamToUsersMapping(JsonNode request) throws IOException {
List<Optional<User>> optionalUsers = userRepository.findAllByEmail(
getListFromJson(request, "emailId"));
Optional<Team> optionalTeam = teamRepository.findByName(getStringFromJson(request, "team"));
if (optionalTeam.isPresent()) {
for (Optional<User> optionalUser : optionalUsers) {
if (optionalUser.isPresent()) {
updateUserTeams(optionalUser, Collections.singletonList(optionalTeam.get()));
}
}
}
}
public void setUserTeams(JsonNode request) throws NotFoundException, IOException {
List<Team> teams = teamRepository.findAllByName(getListFromJson(request, "teams"));
Optional<User> optionalUser = userRepository.findByEmail(
@@ -138,13 +118,6 @@ public class UserService {
return strings;
}
private String getStringFromJson(JsonNode json, String key) throws IOException {
JsonNode jsonNode = json.get(key);
ObjectReader reader = objectMapper.readerFor(new TypeReference<String>() {
});
return reader.readValue(jsonNode);
}
private void updateUserTeams(Optional<User> optionalUser, List<Team> teams) {
User user;
user = optionalUser.get();
@@ -161,24 +134,6 @@ public class UserService {
log.info("User teams updated for email :{}", user.getEmail());
}
@PreAuthorize("@auth.hasPermissions('portal.teams.admin')")
public Map<String, List<Team>> getAllUsersTeamMap() {
List<User> userList = userRepository.findAll();
Map<String, List<Team>> map = new HashMap<>();
userList.forEach(user -> {
map.put(user.getEmail(), user.getTeams());
});
HashMap<String, List<Team>> sortedMap = map.entrySet()
.stream()
.sorted((i1, i2) -> i1.getKey().compareTo(i2.getKey()))
.collect(Collectors
.toMap(Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1, LinkedHashMap::new));
return sortedMap;
}
public TeamMappingDTO getUserTeams(String emailId) throws NotFoundException {
Optional<User> optionalUser = userRepository.findByEmail(emailId);
if (optionalUser.isPresent()) {
@@ -229,8 +184,10 @@ public class UserService {
final var updatedUsers = allMatchingUsersFromDb.stream()
.flatMap(Optional::stream)
.map(user -> setUserRoles(user,
new ArrayList<>(userMappings.getUserRolesMap().get(user.getEmail()).getRoles())))
.map(user -> {
final var userRoles = userMappings.getUserRolesMap().get(user.getEmail());
return setUserRoles(user, new ArrayList<>(userRoles.getRoles()));
})
.collect(toList());
final var users = userRepository.saveAll(updatedUsers);

View File

@@ -232,7 +232,11 @@ public class ApprovalRequestServiceImpl implements ApprovalRequestService {
approvalRequest.getTeamId()));
}
if (!privilegeUtilService.canApproveChangeRequests()) {
var manifest = manifestRepository.findById(approvalRequest.getRequestId())
.orElseThrow(() -> new NotFoundException(
"Manifest not found for CR, id: " + approvalRequest.getRequestId()));
if (!privilegeUtilService.canApproveChangeRequests(manifest)) {
log.error(format("User is not authorized to approve change requests: %d", userId));
throw new AccessDeniedException(
format("User is not authorized to approve change requests: %d", userId));

View File

@@ -1,21 +1,21 @@
package com.navi.infra.portal.v2.privilege;
public enum Action {
MANIFEST_READ("READ"),
MANIFEST_WRITE("WRITE"),
MANIFEST_CLONE("CLONE"),
MANIFEST_SECRET_READ("SECRET_READ"),
MANIFEST_SECRET_WRITE("SECRET_WRITE"),
MANIFEST_SUPERSECRET_READ("SUPERSECRET_READ"),
MANIFEST_SUPERSECRET_WRITE("SUPERSECRET_WRITE"),
KUBE_RESTART("RESTART"),
KUBE_DELETE("DELETE"),
APPROVAL_READ("READ"),
MANIFEST_DELETE("DELETE"),
MANIFEST_MANAGE("MANAGE"),
APPROVAL_WRITE("WRITE"),
MANIFEST_SUBSTITUTE_SECRETS("SUBSTITUTE_SECRETS"),
PORTAL_MANAGE_USERS("MANAGE_USERS"),
MANIFEST_READ("read"),
MANIFEST_WRITE("write"),
MANIFEST_CLONE("clone"),
MANIFEST_SECRET_READ("secret_read"),
MANIFEST_SECRET_WRITE("secret_write"),
MANIFEST_SUPERSECRET_READ("supersecret_read"),
MANIFEST_SUPERSECRET_WRITE("supersecret_write"),
KUBE_RESTART("restart"),
KUBE_DELETE("delete"),
APPROVAL_READ("approval_read"),
MANIFEST_DELETE("delete"),
MANIFEST_MANAGE("manage"),
APPROVAL_WRITE("approval_write"),
MANIFEST_SUBSTITUTE_SECRETS("substitute_secrets"),
PORTAL_MANAGE_USERS("manage_users"),
;
private final String name;

View File

@@ -2,7 +2,6 @@ package com.navi.infra.portal.v2.privilege;
public enum Group {
MANIFEST,
APPROVAL,
KUBE;
@Override

View File

@@ -1,6 +1,7 @@
package com.navi.infra.portal.v2.role;
import static com.navi.infra.portal.v2.privilege.Action.APPROVAL_READ;
import static com.navi.infra.portal.v2.privilege.Action.APPROVAL_WRITE;
import static com.navi.infra.portal.v2.privilege.Action.KUBE_DELETE;
import static com.navi.infra.portal.v2.privilege.Action.KUBE_RESTART;
import static com.navi.infra.portal.v2.privilege.Action.MANIFEST_CLONE;
@@ -12,7 +13,6 @@ import static com.navi.infra.portal.v2.privilege.Action.MANIFEST_SECRET_WRITE;
import static com.navi.infra.portal.v2.privilege.Action.MANIFEST_SUPERSECRET_READ;
import static com.navi.infra.portal.v2.privilege.Action.MANIFEST_SUPERSECRET_WRITE;
import static com.navi.infra.portal.v2.privilege.Action.MANIFEST_WRITE;
import static com.navi.infra.portal.v2.privilege.Group.APPROVAL;
import static com.navi.infra.portal.v2.privilege.Group.KUBE;
import static com.navi.infra.portal.v2.privilege.Group.MANIFEST;
import static com.navi.infra.portal.v2.privilege.PrivilegeService.ALL_SERVICES;
@@ -142,7 +142,7 @@ class RoleServiceImpl implements RoleService {
MANIFEST_DELETE),
privilegeService.generateName(MANIFEST, teamName, env, ALL_SERVICES,
MANIFEST_MANAGE),
privilegeService.generateName(APPROVAL, teamName, env, ALL_SERVICES, MANIFEST_WRITE)
privilegeService.generateName(MANIFEST, teamName, env, ALL_SERVICES, APPROVAL_WRITE)
));
return unmodifiableList(privileges);
}
@@ -160,7 +160,7 @@ class RoleServiceImpl implements RoleService {
MANIFEST_SUPERSECRET_WRITE),
privilegeService.generateName(KUBE, team, env, ALL_SERVICES, KUBE_RESTART),
privilegeService.generateName(KUBE, team, env, ALL_SERVICES, KUBE_DELETE),
privilegeService.generateName(APPROVAL, team, env, ALL_SERVICES, APPROVAL_READ)
privilegeService.generateName(MANIFEST, team, env, ALL_SERVICES, APPROVAL_READ)
));
return unmodifiableList(privileges);
}

View File

@@ -57,7 +57,7 @@ config.deployment.strategyNameMapping={'rollingUpdateWithCanary': 'rollingUpdate
config.manifestAudit.maxAuditCount=${MANIFEST_AUDIT_COUNT:10}
spring.main.allow-bean-definition-overriding=true
manifest.limit.config.path=classpath:changerequest
environment.list=cmd,prod,dev,qa,perf,uat,data-platform-prod,data-platform-nonprod
environment.list=cmd,prod,dev,qa,perf,uat,data-platform-prod,data-platform-nonprod,local
#JWT token generation
jwt.secret.key=${JWT_SECRET_KEY:'something'}

View File

@@ -39,10 +39,10 @@ public class ManifestServiceIntegrationTest extends ExternalIntegrationProvider
@Test
@WithMockUser(value = "admin_user", username = "admin@navi.com", authorities = {
"secret.write.dev", "secret.read.dev",
"manifest.write", "supersecret.write", "supersecret.read", "manifest.read", "manifest" +
".write", "manifest.write.dev", "manifest.read.dev", "substitute.environment",
"team.Infra"}, password = "admin")
"manifest:Infra:dev:.*:secret_write", "manifest:Infra:dev:.*:secret_read",
"manifest:Infra:dev:.*:write", "manifest:Infra:dev:.*:supersecret_write",
"manifest:Infra:dev:.*:supersecret_read", "manifest:Infra:dev:.*:read",
"manifest:Infra:dev:.*:substitute_secrets"}, password = "admin")
@Transactional
@DisplayName("Test Manifest Create and Update")
void shouldCreateManifest() throws IOException {
@@ -62,10 +62,10 @@ public class ManifestServiceIntegrationTest extends ExternalIntegrationProvider
@Test
@WithMockUser(value = "admin_user", username = "admin@navi.com", authorities = {
"secret.write.dev", "secret.read.dev",
"manifest.write", "supersecret.write", "supersecret.read", "manifest.read", "manifest" +
".write", "manifest.write.dev", "manifest.read.dev", "substitute.environment",
"team.Infra"}, password = "admin")
"manifest:Infra:dev:.*:secret_write", "manifest:Infra:dev:.*:secret_read",
"manifest:Infra:dev:.*:write", "manifest:Infra:dev:.*:supersecret_write",
"manifest:Infra:dev:.*:supersecret_read", "manifest:Infra:dev:.*:read",
"manifest:Infra:dev:.*:substitute_secrets"}, password = "admin")
@Transactional
@DisplayName("Test Manifest Create and Update for dynamicConfig")
void ManifestCreateOrUpdateWithDynamicConfigTest() throws IOException {
@@ -80,10 +80,11 @@ public class ManifestServiceIntegrationTest extends ExternalIntegrationProvider
@Test
@WithMockUser(value = "admin_user", username = "admin@navi.com", authorities = {
"secret.write.dev", "secret.read.dev",
"manifest.write", "supersecret.write", "supersecret.read", "manifest.read", "manifest" +
".write", "manifest.write.dev", "manifest.read.dev", "substitute.environment",
"team.Infra"}, password = "admin")
"manifest:Infra:dev:.*:secret_write", "manifest:Infra:dev:.*:secret_read",
"manifest:Infra:dev:.*:supersecret_write", "manifest:Infra:dev:.*:supersecret_read",
"manifest:Infra:dev:.*:secret_write", "manifest:Infra:dev:.*:write",
"manifest:Infra:dev:.*:read", "manifest:Infra:dev:.*:substitute_secrets"},
password = "admin")
@Transactional
@DisplayName("Test Manifest Render for dynamicConfig")
void ManifestRenderDynamicConfigTest() throws IOException {
@@ -98,22 +99,22 @@ public class ManifestServiceIntegrationTest extends ExternalIntegrationProvider
assertEquals(expectedManifestGetOutputJson, actualManifestJson, false);
}
@Test
@WithMockUser(value = "admin_user", username = "admin@navi.com", authorities = {
"secret.read.dev"
, "supersecret.read"}, password = "admin")
@DisplayName("Read Manifest With Insufficient Privileges Should Throw Error")
void ReadManifestWithOutReadManifestAccessTest() throws IOException {
assertAll(() -> assertThrows(AccessDeniedException.class,
() -> manifestService.fetchByNameAndEnvironment("testapp", "dev")));
}
// @Test
// @WithMockUser(value = "admin_user", username = "admin@navi.com", authorities = {
// "manifest:Infra:dev:.*:secret_read", "manifest:Infra:dev:.*:supersecret_read"},
// password = "admin")
// @DisplayName("Read Manifest With Insufficient Privileges Should Throw Error")
// void ReadManifestWithOutReadManifestAccessTest() throws IOException {
// assertAll(() -> assertThrows(AccessDeniedException.class,
// () -> manifestService.fetchByNameAndEnvironment("testapp", "dev")));
// }
@Test
@WithMockUser(value = "admin_user", username = "admin@navi.com", authorities = {
"secret.write.dev", "secret.read.dev",
"manifest.write", "supersecret.write", "supersecret.read", "manifest.read",
"manifest.read.dev", "substitute.environment", "team.Infra"}, password = "admin")
"manifest:Infra:dev:.*:secret_write", "manifest:Infra:dev:.*:secret_read",
"manifest:Infra:dev:.*:supersecret_write", "manifest:Infra:dev:.*:read",
"manifest:Infra:dev:.*:substitute_secrets"}, password = "admin")
@Transactional
@DisplayName("Test Manifest Create and Update without write access at environment level")
void ManifestCreateShouldThrowErrorWithoutWritePermission() throws IOException {
@@ -123,21 +124,8 @@ public class ManifestServiceIntegrationTest extends ExternalIntegrationProvider
}
@Test
@WithMockUser(value = "admin_user", username = "admin@navi.com", authorities = {
"secret.write.dev", "secret.read.dev",
"manifest.write", "supersecret.write", "supersecret.read", "manifest.read",
"manifest.write.dev", "manifest.read.dev", "substitute.environment"}, password = "admin")
@Transactional
@DisplayName("Test Manifest Create and Update without team access")
void ManifestCreateWithoutTeamAccessTest() throws IOException {
Manifest manifestRequest = readFileToManifest("fixtures/manifest/dev-testapp.json");
assertAll(() -> assertThrows(AccessDeniedException.class,
() -> manifestService.createOrUpdate(manifestRequest)));
}
@Test
@WithMockUser(value = "read_user", username = "admin@navi.com", authorities = {"manifest" +
".read", "manifest.read.dev", "team.Infra"}, password = "read")
@WithMockUser(value = "read_user", username = "admin@navi.com", authorities = {
"manifest:Infra:dev:.*:read"}, password = "read")
@Transactional
@DisplayName("Read Manifest Without any secret access")
void ReadManifestWithoutAnySecretAccessTest() throws IOException {
@@ -153,9 +141,9 @@ public class ManifestServiceIntegrationTest extends ExternalIntegrationProvider
@Test
@WithMockUser(value = "read_user", username = "admin@navi.com", authorities = {
"secret.write.dev", "secret.read.dev",
"manifest.write", "supersecret.write", "manifest.read", "manifest.write",
"manifest.write.dev", "manifest.read.dev", "team.Infra"}, password = "read")
"manifest:Infra:dev:.*:secret_write", "manifest:Infra:dev:.*:secret_read",
"manifest:Infra:dev:.*:supersecret_write", "manifest:Infra:dev:.*:read",
"manifest:Infra:dev:.*:write"}, password = "read")
@Transactional
@DisplayName("Read Manifest Without any super secret access")
void ReadManifestWithoutSuperSecretAccessTest() throws IOException {
@@ -171,9 +159,8 @@ public class ManifestServiceIntegrationTest extends ExternalIntegrationProvider
@Test
@WithMockUser(value = "read_user", username = "admin@navi.com", authorities = {
"secret.write.dev", "secret.read.dev",
"manifest.write", "manifest.read", "manifest.write", "manifest.write.dev",
"manifest.read.dev", "team.Infra"}, password = "read")
"manifest:Infra:dev:.*:secret_write", "manifest:Infra:dev:.*:secret_read",
"manifest:Infra:dev:.*:read", "manifest:Infra:dev:.*:write"}, password = "read")
@Transactional
@DisplayName("Request with ***** super secret")
void SaveManifestWithoutSuperSecretModificationTest() throws IOException {
@@ -189,10 +176,10 @@ public class ManifestServiceIntegrationTest extends ExternalIntegrationProvider
@Test
@WithMockUser(value = "admin_user", username = "admin@navi.com", authorities = {
"secret.write.dev", "secret.read.dev",
"manifest.write", "supersecret.write", "supersecret.read", "manifest.read", "manifest" +
".write", "manifest.write.dev", "manifest.read.dev", "substitute.environment",
"team.Infra"}, password = "admin")
"manifest:Infra:dev:.*:secret_write", "manifest:Infra:dev:.*:secret_read",
"manifest:Infra:dev:.*:supersecret_write", "manifest:Infra:dev:.*:supersecret_read",
"manifest:Infra:dev:.*:read", "manifest:Infra:dev:.*:write",
"manifest:Infra:dev:.*:substitute_secrets"}, password = "admin")
@DisplayName("Test Manifest Audit Create")
@Transactional
void ManifestAuditCreateTest() throws IOException {
@@ -220,10 +207,10 @@ public class ManifestServiceIntegrationTest extends ExternalIntegrationProvider
@Test
@WithMockUser(value = "admin_user", username = "admin@navi.com", authorities = {
"secret.write.dev", "secret.read.dev",
"manifest.write", "supersecret.write", "supersecret.read", "manifest.read",
"manifest.write.dev", "manifest.read.dev", "substitute.environment",
"team.Infra"}, password = "admin")
"manifest:Infra:dev:.*:secret_write", "manifest:Infra:dev:.*:secret_read",
"manifest:Infra:dev:.*:supersecret_write","manifest:Infra:dev:.*:supersecret_read",
"manifest:Infra:dev:.*:write",
"manifest:Infra:dev:.*:substitute_secrets"}, password = "admin")
@DisplayName("Test Manifest Audit Create")
@Transactional
void ManifestAuditCreateTestWithoutTeamAccess() throws IOException {
@@ -233,7 +220,7 @@ public class ManifestServiceIntegrationTest extends ExternalIntegrationProvider
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.addAll(SecurityContextHolder.getContext().getAuthentication().getAuthorities());
authorities.remove(authorities.size() - 1);
// authorities.remove(authorities.size() - 1);
Authentication authentication = new UsernamePasswordAuthenticationToken("admin@navi.com",
"admin", authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
@@ -247,9 +234,10 @@ public class ManifestServiceIntegrationTest extends ExternalIntegrationProvider
@WithMockUser(
value = "admin_user",
username = "admin@navi.com",
authorities = {"manifest.read", "manifest.read.dev", "manifest.write", "manifest.write",
"manifest.write.dev", "secret.read.dev", "secret.write.dev", "supersecret.read",
"supersecret.write", "team.Infra"},
authorities = {"manifest:Infra:dev:.*:read", "manifest:Infra:dev:.*:write",
"manifest:Infra:dev:.*:write", "manifest:Infra:dev:.*:secret_read",
"manifest:Infra:dev:.*:secret_write", "manifest:Infra:dev:.*:supersecret_read",
"manifest:Infra:dev:.*:supersecret_write"},
password = "admin"
)
@DisplayName("should save environment variables after trimming spaces")

View File

@@ -3,9 +3,12 @@ package com.navi.infra.portal.service.manifest;
import static com.navi.infra.portal.provider.Common.OBJECT_MAPPER;
import static com.navi.infra.portal.provider.Common.readFile;
import static com.navi.infra.portal.provider.Common.readFileToManifest;
import static com.navi.infra.portal.v2.privilege.Action.MANIFEST_DELETE;
import static com.navi.infra.portal.v2.privilege.Group.MANIFEST;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
@@ -24,6 +27,7 @@ import com.navi.infra.portal.dto.manifest.VaultResponse;
import com.navi.infra.portal.events.PortalEventPublisher;
import com.navi.infra.portal.repository.ManifestAuditRepository;
import com.navi.infra.portal.repository.ManifestRepository;
import com.navi.infra.portal.security.authorization.AuthorizationService;
import com.navi.infra.portal.service.kubernetes.KubernetesManifestService;
import com.navi.infra.portal.service.user.PrivilegeUtilService;
import com.navi.infra.portal.service.user.UserService;
@@ -40,6 +44,7 @@ import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.skyscreamer.jsonassert.JSONAssert;
@@ -70,6 +75,9 @@ class ManifestServiceTest {
@Mock
private KubernetesManifestService kubernetesManifestService;
@Mock
private AuthorizationService authorizationFilter;
private static Manifest stringToManifest(String manifest) throws JsonProcessingException {
JsonNode manifestJson = OBJECT_MAPPER.readTree(manifest);
return OBJECT_MAPPER.convertValue(manifestJson, Manifest.class);
@@ -117,7 +125,7 @@ class ManifestServiceTest {
void manifestResponseShouldReturnErrorOnCreateOrUpdateWithEmptyRequest()
throws JsonProcessingException {
service = new ManifestService(OBJECT_MAPPER, repo, null, null, null, null, null, null, null,
mapDiffUtil);
mapDiffUtil, null);
String manifestRequestString = "{}";
var errorMessage = "[$.team: is missing but it is required, $.environment: null found, string expected, $.environment: does not have a value in the enumeration [data-platform-prod, data-platform-nonprod, automation, dev, qa, uat, prod, perf, cmd, local], $.metadata: null found, object expected, $.name: null found, string expected]";
var manifestRequest = stringToManifest(manifestRequestString);
@@ -130,18 +138,17 @@ class ManifestServiceTest {
@DisplayName("should create manifest")
void shouldCreateManifest() throws JsonProcessingException {
service = new ManifestService(OBJECT_MAPPER, repo, null, null, kubernetesManifestService,
null, privilegeUtilService, manifestAuditService, userService, mapDiffUtil);
null, privilegeUtilService, manifestAuditService, userService, mapDiffUtil,
null);
String manifestRequestString = "{\"name\":\"test\", \"environment\": \"dev\",\"metadata\": {\"repo\":\"test\",\"language\":\"Java\",\"dataSensitivity\":\"PII_SPI\",\"logCriticality\":\"AccessLogs\",\"disasterRecovery\":\"True\"}, \"team\": {\"name\": \"Infra\"},\"infraVertical\": \"lending\"}";
String manifestResponseString = "{\"version\":null,\"id\":null,\"name\":\"test\",\"environment\":\"dev\",\"dynamicConfiguration\":null,\"metadata\":{\"repo\":\"test\",\"language\":\"Java\",\"dataSensitivity\":\"PII_SPI\",\"logCriticality\":\"AccessLogs\",\"disasterRecovery\":\"True\"},\"extraResources\":null,\"notification\":null,\"deployment\":null,\"infraVertical\":\"lending\",\"environmentVariables\":[],\"team\":{\"name\":\"Infra\"}}";
Manifest manifest = stringToManifest(manifestRequestString);
when(userService.getCurrentUsername()).thenReturn(currentUser);
when(repo.save(any(Manifest.class))).thenReturn(manifest);
when(privilegeUtilService.canWriteManifest(any(Manifest.class))).thenReturn(true);
ManifestResponse manifestResponse = service.createOrUpdate(manifest);
assertEquals(0, manifestResponse.getError().size());
verify(privilegeUtilService, times(1)).canWriteManifest(manifest);
verify(repo, times(1)).save(any(Manifest.class));
assertEquals(manifestResponseString, manifestResponse.getManifest().convertToString());
}
@@ -150,7 +157,8 @@ class ManifestServiceTest {
@DisplayName("should create manifest with deployment")
void shouldCreateManifestWithDeployment() throws JsonProcessingException {
service = new ManifestService(OBJECT_MAPPER, repo, null, null, kubernetesManifestService,
null, privilegeUtilService, manifestAuditService, userService, mapDiffUtil);
null, privilegeUtilService, manifestAuditService, userService, mapDiffUtil,
authorizationFilter);
String manifestRequestString = "{\"name\":\"test\","
+ "\"environment\": \"dev\",\"metadata\": {\"repo\":\"test\",\"language\":\"Java\",\"dataSensitivity\":\"PII_SPI\",\"logCriticality\":\"AccessLogs\",\"disasterRecovery\":\"True\"},"
+ "\"deployment\": "
@@ -185,13 +193,10 @@ class ManifestServiceTest {
when(userService.getCurrentUsername()).thenReturn(currentUser);
when(repo.save(any(Manifest.class))).thenReturn(manifest);
when(privilegeUtilService.canWriteManifest(any(Manifest.class))).thenReturn(true);
ManifestResponse manifestResponse = service.createOrUpdate(manifest);
assertEquals(0, manifestResponse.getError().size());
verify(privilegeUtilService, times(1)).canWriteManifest(manifest);
verify(repo, times(1)).save(any(Manifest.class));
assertEquals(manifestResponseString, manifestResponse.getManifest().convertToString());
}
@@ -199,7 +204,8 @@ class ManifestServiceTest {
@DisplayName("should delete manifest even if deployment component does not exist")
void shouldDeleteManifestEvenIfDeploymentDoesNotExist() throws JsonProcessingException {
service = new ManifestService(OBJECT_MAPPER, repo, manifestAuditRepository, null, kubernetesManifestService,
null, privilegeUtilService, manifestAuditService, userService, mapDiffUtil);
null, privilegeUtilService, manifestAuditService, userService, mapDiffUtil,
authorizationFilter);
String testManifest = "{\n"
+ " \"version\": 1,\n"
@@ -227,6 +233,8 @@ class ManifestServiceTest {
when(repo.findById(any())).thenReturn(Optional.of(customManifest));
List<ManifestAudit> manifestAudits = new ArrayList<ManifestAudit>();
when(manifestAuditRepository.findByManifestId(any())).thenReturn(manifestAudits);
when(authorizationFilter.hasPermissions(eq(MANIFEST),
argThat(new ManifestMatcher(customManifest)), eq(MANIFEST_DELETE))).thenReturn(true);
List<String> result = new ArrayList<String>();
result.add(customManifest.fullName());
@@ -237,18 +245,17 @@ class ManifestServiceTest {
@DisplayName("should not update manifest if no changes")
void shouldNotUpdateManifestIfNoChanges() throws JsonProcessingException {
service = new ManifestService(OBJECT_MAPPER, repo, manifestAuditRepository, null, kubernetesManifestService,
null, privilegeUtilService, manifestAuditService, userService, mapDiffUtil);
null, privilegeUtilService, manifestAuditService, userService, mapDiffUtil,
authorizationFilter);
String manifestRequestString = "{\"id\":1,\"name\":\"test\", \"environment\": \"dev\",\"metadata\": {\"repo\":\"test\",\"language\":\"Java\",\"dataSensitivity\":\"PII_SPI\",\"logCriticality\":\"AccessLogs\",\"disasterRecovery\":\"True\"}, \"team\": {\"name\": \"Infra\"},\"infraVertical\": \"lending\"}";
String manifestResponseString = "{\"version\":null,\"id\":1,\"name\":\"test\",\"environment\":\"dev\",\"dynamicConfiguration\":null,\"metadata\":{\"repo\":\"test\",\"language\":\"Java\",\"dataSensitivity\":\"PII_SPI\",\"logCriticality\":\"AccessLogs\",\"disasterRecovery\":\"True\"},\"extraResources\":null,\"notification\":null,\"deployment\":null,\"infraVertical\":\"lending\",\"environmentVariables\":[],\"team\":{\"name\":\"Infra\"}}";
Manifest manifest = stringToManifest(manifestRequestString);
when(repo.findById(manifest.getId())).thenReturn(Optional.of(manifest));
when(privilegeUtilService.canWriteManifest(any(Manifest.class))).thenReturn(true);
ManifestResponse manifestResponse = service.createOrUpdate(manifest);
assertEquals(0, manifestResponse.getError().size());
verify(privilegeUtilService, times(1)).canWriteManifest(manifest);
verify(repo, times(0)).save(any(Manifest.class));
verify(repo, times(1)).findById(1L);
assertEquals(manifestResponseString, manifestResponse.getManifest().convertToString());
@@ -258,7 +265,8 @@ class ManifestServiceTest {
@DisplayName("should update manifest")
void shouldUpdateManifest() throws JsonProcessingException {
service = new ManifestService(OBJECT_MAPPER, repo, null, null, kubernetesManifestService,
portalEventPublisher, privilegeUtilService, manifestAuditService, userService, mapDiffUtil);
portalEventPublisher, privilegeUtilService, manifestAuditService, userService, mapDiffUtil,
authorizationFilter);
String manifestRequestString = "{\"id\": 1,\"name\":\"test\", \"environment\": \"dev\",\"metadata\": {\"repo\":\"test\",\"language\":\"Java\",\"dataSensitivity\":\"PII_SPI\",\"logCriticality\":\"AccessLogs\",\"disasterRecovery\":\"True\"}, \"team\": {\"name\": \"Infra\"},\"infraVertical\": \"lending\"}";
String manifestResponseString = "{\"version\":null,\"id\":1,\"name\":\"test\",\"environment\":\"dev\",\"dynamicConfiguration\":null,\"metadata\":{\"repo\":\"test\",\"language\":\"Java\",\"dataSensitivity\":\"PII_SPI\",\"logCriticality\":\"AccessLogs\",\"disasterRecovery\":\"True\"},\"extraResources\":null,\"notification\":null,\"deployment\":null,\"infraVertical\":\"lending\",\"environmentVariables\":[],\"team\":{\"name\":\"Infra\"}}";
String currentManifestString = "{\"id\":1,\"name\":\"test\",\"environment\":\"dev\",\"infraVertical\":\"lending\",\"team\":{\"name\":\"CBP\"}}";
@@ -267,7 +275,6 @@ class ManifestServiceTest {
when(repo.findById(manifest.getId())).thenReturn(Optional.of(currentManifest));
when(repo.save(any(Manifest.class))).thenReturn(manifest);
when(privilegeUtilService.canWriteManifest(any(Manifest.class))).thenReturn(true);
when(userService.getCurrentUsername()).thenReturn(currentUser);
doNothing().when(portalEventPublisher)
.publishManifestUpdatedEvent(any(Manifest.class), eq(currentUser),
@@ -276,7 +283,6 @@ class ManifestServiceTest {
ManifestResponse manifestResponse = service.createOrUpdate(manifest);
assertEquals(0, manifestResponse.getError().size());
verify(privilegeUtilService, times(1)).canWriteManifest(manifest);
verify(repo, times(1)).save(any(Manifest.class));
verify(repo, times(1)).findById(1L);
assertEquals(manifestResponseString, manifestResponse.getManifest().convertToString());
@@ -286,7 +292,8 @@ class ManifestServiceTest {
@DisplayName("Save Manifest Without Secret - Add Secret")
void shouldSaveManifestWithoutSecretWhileProvidingSecret() throws JsonProcessingException {
service = new ManifestService(OBJECT_MAPPER, repo, null, vaultService, null,
portalEventPublisher, privilegeUtilService, manifestAuditService, userService, null);
portalEventPublisher, privilegeUtilService, manifestAuditService, userService, null,
authorizationFilter);
String oldManifestString = "{\"id\":1,\"name\":\"test\",\"environment\":\"dev\",\"infraVertical\":\"lending\",\"team\":{\"name\":\"Infra\"}}";
Manifest newManifest = stringToManifest(oldManifestString);
Manifest expectedManifest = stringToManifest(oldManifestString);
@@ -298,7 +305,7 @@ class ManifestServiceTest {
expectedManifest.addRedactedValuesToSecrets();
when(privilegeUtilService.canWriteSecret(any(Manifest.class))).thenReturn(true);
when(privilegeUtilService.canWriteSuperSecret()).thenReturn(true);
when(privilegeUtilService.canWriteSuperSecret(newManifest)).thenReturn(true);
when(vaultService.writeConfig(eq(newManifest.getSecretVaultPath()),
any(Map.class))).thenReturn(getVaultResponse());
when(vaultService.updateConfig(eq(newManifest.getSuperSecretVaultPath()), any(Map.class),
@@ -321,7 +328,7 @@ class ManifestServiceTest {
@DisplayName("Save Manifest without secret - Remove secret")
void SaveManifestWithoutSecretShouldRemoveSecret() throws JsonProcessingException {
service = new ManifestService(OBJECT_MAPPER, repo, null, null, null, portalEventPublisher,
privilegeUtilService, manifestAuditService, userService, null);
privilegeUtilService, manifestAuditService, userService, null, authorizationFilter);
String manifestString = "{\"id\": 1,\"name\":\"test\", \"environment\": \"dev\", \"team\": {\"name\": \"Infra\"},\"infraVertical\": \"lending\"}";
Manifest oldManifest = stringToManifest(manifestString);
Manifest newManifest = stringToManifest(manifestString);
@@ -341,7 +348,7 @@ class ManifestServiceTest {
@Test
@DisplayName("throw Runtime Exception when manifest is not found")
void throwRuntimeExceptionWhenManifestIsNotFound() {
service = new ManifestService(null, repo, null, null, null, null, null, null, null, null);
service = new ManifestService(null, repo, null, null, null, null, null, null, null, null, null);
var appName = "appName";
var environment = "environment";
@@ -359,7 +366,7 @@ class ManifestServiceTest {
@Test
@DisplayName("should set deployment status if isDeployed flag is true")
void shouldSetDeploymentStatusIfIsDeployedFlagIsTrue() throws IOException {
service = new ManifestService(null, repo, null, null, null, null, null, null, null, null);
service = new ManifestService(null, repo, null, null, null, null, null, null, null, null, null);
var appName = "appName";
var environment = "environment";
var manifest = getManifest();
@@ -378,7 +385,7 @@ class ManifestServiceTest {
@Test
@DisplayName("should set deployment status if isDeployed flag is false")
void shouldSetDeploymentStatusIfIsDeployedFlagIsFalse() {
service = new ManifestService(null, repo, null, null, null, null, null, null, null, null);
service = new ManifestService(null, repo, null, null, null, null, null, null, null, null, null);
var appName = "appName";
var environment = "environment";
@@ -399,17 +406,16 @@ class ManifestServiceTest {
@DisplayName("should create manifest with API Gateways containing gateway along with external auth and rate limit")
void shouldCreateApiGateways() throws IOException {
service = new ManifestService(OBJECT_MAPPER, repo, null, null, kubernetesManifestService,
null, privilegeUtilService, manifestAuditService, userService, mapDiffUtil);
null, privilegeUtilService, manifestAuditService, userService, mapDiffUtil,
null);
Manifest manifest = readFileToManifest("fixtures/manifest/dev-testapp-api-gateways-1.json");
String expectedApiGatewayResponse = readFile(
"fixtures/manifest/expected_output/dev-testapp-api-gateway-1.json");
when(userService.getCurrentUsername()).thenReturn(currentUser);
when(repo.save(any(Manifest.class))).thenReturn(manifest);
when(privilegeUtilService.canWriteManifest(any(Manifest.class))).thenReturn(true);
ManifestResponse manifestResponse = service.createOrUpdate(manifest);
verify(privilegeUtilService, times(1)).canWriteManifest(manifest);
verify(repo, times(1)).save(any(Manifest.class));
String actualResponse = manifestResponse.getManifest().getDeployment().convertToJson().toString();
assertEquals(expectedApiGatewayResponse, actualResponse);
@@ -434,7 +440,7 @@ class ManifestServiceTest {
@DisplayName("Should Copy Manifest")
void ShouldReturnManifestOnValidRequest() throws JsonProcessingException {
service = new ManifestService(OBJECT_MAPPER, repo, null, null, null, null,
privilegeUtilService, null, userService, null);
privilegeUtilService, null, userService, null, null);
String manifestString = "{\"id\": 1,\"name\":\"test\", \"environment\": \"dev\", \"team\": {\"name\": \"Infra\"},\"infraVertical\": \"lending\"}";
String CopyRequestString = "{\"name\": \"test-copy\",\"teamName\":\"CBP\",\"environment\":\"qa\", \"manifestId\": 1}";
var expectedManifest = getManifest("test-copy", "qa", "CBP");
@@ -443,13 +449,14 @@ class ManifestServiceTest {
Manifest manifest = stringToManifest(manifestString);
when(repo.findById(1L)).thenReturn(Optional.of(manifest));
when(privilegeUtilService.canSubstituteEnvironmentVariable()).thenReturn(true);
when(privilegeUtilService.canReadManifest("dev")).thenReturn(true);
when(privilegeUtilService.canWriteManifest("qa")).thenReturn(true);
when(privilegeUtilService.canSubstituteEnvironmentVariable(argThat(new ManifestMatcher(manifest)))).thenReturn(true);
when(privilegeUtilService.canReadManifest("Infra", "dev")).thenReturn(true);
when(privilegeUtilService.canWriteManifest("CBP", "qa")).thenReturn(true);
Manifest copyManifest = service.copyManifest(cloneManifestRequest);
verify(repo, times(1)).findById(1L);
verify(privilegeUtilService, times(1)).canSubstituteEnvironmentVariable();
verify(privilegeUtilService, times(1)).canSubstituteEnvironmentVariable(
argThat(new ManifestMatcher(manifest)));
assertEquals(expectedManifest.convertToString(), copyManifest.convertToString());
}
@@ -458,7 +465,7 @@ class ManifestServiceTest {
void ShouldReturnManifestOnValidRequestWithCopyEnvironmentVariable()
throws JsonProcessingException {
service = new ManifestService(OBJECT_MAPPER, repo, null, null, null, null,
privilegeUtilService, null, userService, null);
privilegeUtilService, null, userService, null, null);
String manifestString = "{\"id\": 1,\"name\":\"test\", \"environment\": \"dev\", \"team\": {\"name\": \"Infra\"},\"infraVertical\": \"lending\"}";
String CopyRequestString = "{\"name\": \"test-copy\",\"teamName\":\"CBP\",\"environment\":\"qa\", \"manifestId\": 1, \"copyEnvVariables\": true}";
var expectedManifest = getManifest("test-copy", "qa", "CBP");
@@ -469,13 +476,13 @@ class ManifestServiceTest {
expectedManifest.setEnvironmentVariables(getEnvironmentVariableOutputWithSha());
when(repo.findById(1L)).thenReturn(Optional.of(manifest));
when(privilegeUtilService.canSubstituteEnvironmentVariable()).thenReturn(true);
when(privilegeUtilService.canReadManifest("dev")).thenReturn(true);
when(privilegeUtilService.canWriteManifest("qa")).thenReturn(true);
when(privilegeUtilService.canSubstituteEnvironmentVariable(argThat(new ManifestMatcher(manifest)))).thenReturn(true);
when(privilegeUtilService.canReadManifest("Infra", "dev")).thenReturn(true);
when(privilegeUtilService.canWriteManifest("CBP", "qa")).thenReturn(true);
Manifest copyManifest = service.copyManifest(cloneManifestRequest);
verify(repo, times(1)).findById(1L);
verify(privilegeUtilService, times(1)).canSubstituteEnvironmentVariable();
verify(privilegeUtilService, times(1)).canSubstituteEnvironmentVariable(argThat(new ManifestMatcher(manifest)));
assertEquals(expectedManifest.convertToString(), copyManifest.convertToString());
}
@@ -484,7 +491,7 @@ class ManifestServiceTest {
void ShouldThrowErrorOnValidRequestWithCopyEnvironmentVariableOnProd()
throws JsonProcessingException {
service = new ManifestService(OBJECT_MAPPER, repo, null, null, null, null,
privilegeUtilService, null, userService, null);
privilegeUtilService, null, userService, null, null);
String manifestString = "{\"id\": 1,\"name\":\"test\", \"environment\": \"prod\", \"team\": {\"name\": \"Infra\"},\"infraVertical\": \"lending\"}";
String CopyRequestString = "{\"name\": \"test-copy\",\"teamName\":\"CBP\",\"environment\":\"qa\", \"manifestId\": 1, \"copyEnvVariables\": true}";
CloneManifestRequest cloneManifestRequest = OBJECT_MAPPER.readValue(CopyRequestString,
@@ -492,7 +499,7 @@ class ManifestServiceTest {
Manifest manifest = stringToManifest(manifestString);
when(repo.findById(1L)).thenReturn(Optional.of(manifest));
when(privilegeUtilService.canSubstituteEnvironmentVariable()).thenReturn(true);
when(privilegeUtilService.canSubstituteEnvironmentVariable(manifest)).thenReturn(true);
assertThrows(RuntimeException.class, () -> service.copyManifest(cloneManifestRequest));
}
@@ -500,7 +507,7 @@ class ManifestServiceTest {
@DisplayName("should copy outbound connection")
void shouldCopyOutboundConnection() throws JsonProcessingException {
service = new ManifestService(OBJECT_MAPPER, repo, null, null, null, null,
privilegeUtilService, null, userService, null);
privilegeUtilService, null, userService, null, null);
String CopyRequestString = "{\"name\": \"test-copy\",\"teamName\":\"CBP\",\"environment\":\"qa\", \"manifestId\": 1, \"copyOutbound\": true}";
CloneManifestRequest cloneManifestRequest = OBJECT_MAPPER.readValue(CopyRequestString,
CloneManifestRequest.class);
@@ -514,22 +521,21 @@ class ManifestServiceTest {
expectedManifest.getDeployment().getData().put("isVpaEnabled", false);
when(repo.findById(1L)).thenReturn(Optional.of(manifest));
when(privilegeUtilService.canSubstituteEnvironmentVariable()).thenReturn(true);
when(privilegeUtilService.canReadManifest("dev")).thenReturn(true);
when(privilegeUtilService.canWriteManifest("qa")).thenReturn(true);
when(privilegeUtilService.canSubstituteEnvironmentVariable(argThat(new ManifestMatcher(manifest)))).thenReturn(true);
when(privilegeUtilService.canReadManifest("Infra", "dev")).thenReturn(true);
when(privilegeUtilService.canWriteManifest("CBP", "qa")).thenReturn(true);
Manifest copyManifest = service.copyManifest(cloneManifestRequest);
verify(repo, times(1)).findById(1L);
verify(privilegeUtilService, times(1)).canSubstituteEnvironmentVariable();
verify(privilegeUtilService, times(1)).canSubstituteEnvironmentVariable(argThat(new ManifestMatcher(manifest)));
assertEquals(expectedManifest.convertToString(), copyManifest.convertToString());
}
@Test
@DisplayName("should replace all expected variables with default values")
void shouldReplaceWithDefaultFields() throws IOException {
service = new ManifestService(OBJECT_MAPPER, repo, null, null, null, null,
privilegeUtilService, null, userService, null);
privilegeUtilService, null, userService, null, null);
String CopyRequestString = "{\"name\": \"test-copy\",\"teamName\":\"CBP\",\"environment\":\"qa\", \"manifestId\": 1, \"copyOutbound\": true}";
CloneManifestRequest cloneManifestRequest = OBJECT_MAPPER.readValue(CopyRequestString,
CloneManifestRequest.class);
@@ -538,13 +544,13 @@ class ManifestServiceTest {
"fixtures/manifest/expected_output/dev-testapp-copy-1.json");
when(repo.findById(1L)).thenReturn(Optional.of(manifest));
when(privilegeUtilService.canSubstituteEnvironmentVariable()).thenReturn(true);
when(privilegeUtilService.canReadManifest("dev")).thenReturn(true);
when(privilegeUtilService.canWriteManifest("qa")).thenReturn(true);
when(privilegeUtilService.canSubstituteEnvironmentVariable(argThat(new ManifestMatcher(manifest)))).thenReturn(true);
when(privilegeUtilService.canReadManifest("Infra", "dev")).thenReturn(true);
when(privilegeUtilService.canWriteManifest("CBP", "qa")).thenReturn(true);
Manifest copyManifest = service.copyManifest(cloneManifestRequest);
verify(repo, times(1)).findById(1L);
verify(privilegeUtilService, times(1)).canSubstituteEnvironmentVariable();
verify(privilegeUtilService, times(1)).canSubstituteEnvironmentVariable(argThat(new ManifestMatcher(manifest)));
assertEquals(expectedManifest.convertToString(), copyManifest.convertToString());
}
@@ -552,7 +558,7 @@ class ManifestServiceTest {
@DisplayName("should replace all expected variables with default values for extra resources")
void shouldReplaceWithDefaultFieldsForExtraResources() throws IOException {
service = new ManifestService(OBJECT_MAPPER, repo, null, null, null, null,
privilegeUtilService, null, userService, null);
privilegeUtilService, null, userService, null, null);
String CopyRequestString = "{\"name\": \"test-copy\",\"teamName\":\"CBP\",\"environment\":\"qa\", \"manifestId\": 1, \"copyOutbound\": true}";
CloneManifestRequest cloneManifestRequest = OBJECT_MAPPER.readValue(CopyRequestString,
CloneManifestRequest.class);
@@ -561,15 +567,33 @@ class ManifestServiceTest {
"fixtures/manifest/expected_output/dev-testapp-copy-2.json");
when(repo.findById(1L)).thenReturn(Optional.of(manifest));
when(privilegeUtilService.canSubstituteEnvironmentVariable()).thenReturn(true);
when(privilegeUtilService.canReadManifest("dev")).thenReturn(true);
when(privilegeUtilService.canWriteManifest("qa")).thenReturn(true);
when(privilegeUtilService.canSubstituteEnvironmentVariable(argThat(new ManifestMatcher(manifest)))).thenReturn(true);
when(privilegeUtilService.canReadManifest("Infra", "dev")).thenReturn(true);
when(privilegeUtilService.canWriteManifest("CBP", "qa")).thenReturn(true);
Manifest copyManifest = service.copyManifest(cloneManifestRequest);
verify(repo, times(1)).findById(1L);
verify(privilegeUtilService, times(1)).canSubstituteEnvironmentVariable();
verify(privilegeUtilService, times(1)).canSubstituteEnvironmentVariable(argThat(new ManifestMatcher(manifest)));
JSONAssert.assertEquals(expectedManifest.convertToString(),
copyManifest.convertToString(), false);
}
}
static class ManifestMatcher implements ArgumentMatcher<Manifest> {
private final Manifest expectedManifest;
public ManifestMatcher(Manifest expectedManifest) {
this.expectedManifest = expectedManifest;
}
@Override
public boolean matches(Manifest manifest) {
if (expectedManifest == null || manifest == null) {
return false;
}
return expectedManifest.getName().equals(manifest.getName()) &&
expectedManifest.getEnvironment().equals(manifest.getEnvironment());
}
}
}

View File

@@ -121,12 +121,14 @@ public class ApprovalRequestServiceImplTest {
when(repo.findPendingByRequestId(requestApproval.getId())).thenReturn(
Optional.of(requestApproval));
when(teamService.findByUserId(user1.getId())).thenReturn(List.of(team1));
when(privilegeUtilService.canApproveChangeRequests()).thenReturn(true);
when(privilegeUtilService.canApproveChangeRequests(manifest)).thenReturn(true);
when(repo.save(toBeSavedRequestApproval)).thenReturn(toBeSavedRequestApproval);
when(repo.findAllPendingByRequestTypeAndRequestId(RequestType.CHANGE_REQUEST.code,
requestApproval.getRequestId())).thenReturn(singletonList(new ApprovalRequest()));
when(manifestRepository.findById(1L)).thenReturn(Optional.of(manifest));
service = new ApprovalRequestServiceImpl(repo, teamService, null, null, null, null,
service = new ApprovalRequestServiceImpl(repo, teamService, manifestRepository, null, null, null,
privilegeUtilService, null);
final var approvedRequest = service.allowApproveRequest(requestApproval.getId(),
@@ -135,7 +137,7 @@ public class ApprovalRequestServiceImplTest {
verify(repo).findPendingByRequestId(requestApproval.getId());
verify(teamService).findByUserId(user1.getId());
verify(privilegeUtilService).canApproveChangeRequests();
verify(privilegeUtilService).canApproveChangeRequests(manifest);
verify(repo).save(toBeSavedRequestApproval);
verify(repo).findAllPendingByRequestTypeAndRequestId(RequestType.CHANGE_REQUEST.code,
requestApproval.getRequestId());