Skip to content

Commit

Permalink
Migrated from JSON to many-to-many
Browse files Browse the repository at this point in the history
  • Loading branch information
oharsta committed Dec 5, 2023
1 parent 759b05a commit 7a20c89
Show file tree
Hide file tree
Showing 41 changed files with 332 additions and 329 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version: 18
node-version: 20
- name: Yarn Audit client
run: yarn audit --level high --groups dependencies
working-directory: client
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ IgnoreMeTest.java
dependency.tree
NOTES.txt
private_key_pkcs8.pem
JSON.md
2 changes: 1 addition & 1 deletion client/.nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v18.14.2
v20.8.0
5 changes: 0 additions & 5 deletions client/src/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,6 @@ export function searchRoles(query) {
return fetchJson(`/api/v1/roles/search?query=${query}`);
}

export function shortNameExists(shortName, manageId, roleId) {
const body = {shortName: shortName, manageId: manageId, id: roleId};
return postPutJson("/api/v1/roles/validation/short_name", body, "POST");
}

//User roles
export function userRolesByRoleId(roleId) {
return fetchJson(`/api/v1/user_roles/roles/${roleId}`, {}, {}, false);
Expand Down
23 changes: 15 additions & 8 deletions server/src/main/java/access/api/ManageController.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
import access.config.Config;
import access.manage.EntityType;
import access.manage.Manage;
import access.manage.ManageIdentifier;
import access.model.Application;
import access.model.Authority;
import access.model.User;
import access.repository.ApplicationRepository;
import access.repository.RoleRepository;
import access.security.UserPermissions;
import io.swagger.v3.oas.annotations.Parameter;
Expand All @@ -22,7 +23,10 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static access.SwaggerOpenIdConfig.API_TOKENS_SCHEME_NAME;
Expand All @@ -41,11 +45,13 @@ public class ManageController {

private final Manage manage;
private final RoleRepository roleRepository;
private final ApplicationRepository applicationRepository;

@Autowired
public ManageController(Manage manage, RoleRepository roleRepository) {
public ManageController(Manage manage, RoleRepository roleRepository, ApplicationRepository applicationRepository) {
this.manage = manage;
this.roleRepository = roleRepository;
this.applicationRepository = applicationRepository;
}

@GetMapping("provider/{type}/{id}")
Expand Down Expand Up @@ -73,16 +79,17 @@ public ResponseEntity<List<Map<String, Object>>> providers(@Parameter(hidden = t
@GetMapping("applications")
public ResponseEntity<Map<String, List<Map<String, Object>>>> applications(@Parameter(hidden = true) User user) {
UserPermissions.assertSuperUser(user);
Set<ManageIdentifier> manageIdentifiers = roleRepository.findDistinctManageIdentifiers();
Map<EntityType, List<ManageIdentifier>> groupedByManageType = manageIdentifiers.stream().collect(Collectors.groupingBy(ManageIdentifier::manageType));
List<Application> applications = applicationRepository.findAll();
Map<EntityType, List<Application>> groupedByManageType = applications.stream().collect(Collectors.groupingBy(Application::getManageType));

List<Map<String, Object>> providers = groupedByManageType.entrySet().stream()
.map(entry -> manage.providersByIdIn(
entry.getKey(),
entry.getValue().stream().map(ManageIdentifier::manageId).collect(Collectors.toList())))
entry.getValue().stream().map(Application::getManageId).collect(Collectors.toList())))
.flatMap(Collection::stream)
.toList();
List<Map<String, Object>> provisionings = manage.provisioning(manageIdentifiers.stream()
.map(ManageIdentifier::manageId)
List<Map<String, Object>> provisionings = manage.provisioning(applications.stream()
.map(Application::getManageId)
.toList());
return ResponseEntity.ok(Map.of(
"providers", providers,
Expand Down
42 changes: 14 additions & 28 deletions server/src/main/java/access/api/RoleController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@

import access.config.Config;
import access.exception.InvalidInputException;
import access.exception.NotAllowedException;
import access.exception.NotFoundException;
import access.logging.AccessLogger;
import access.logging.Event;
import access.manage.Manage;
import access.model.*;
import access.model.Application;
import access.model.Authority;
import access.model.Role;
import access.model.User;
import access.provision.ProvisioningService;
import access.provision.scim.GroupURN;
import access.repository.ApplicationRepository;
import access.repository.RoleRepository;
import access.security.UserPermissions;
import access.validation.URLFormatValidator;
Expand Down Expand Up @@ -43,16 +46,18 @@ public class RoleController {

private final Config config;
private final RoleRepository roleRepository;
private final ApplicationRepository applicationRepository;
private final Manage manage;
private final ProvisioningService provisioningService;
private final URLFormatValidator urlFormatValidator = new URLFormatValidator();

public RoleController(Config config,
RoleRepository roleRepository,
Manage manage,
ApplicationRepository applicationRepository, Manage manage,
ProvisioningService provisioningService) {
this.config = config;
this.roleRepository = roleRepository;
this.applicationRepository = applicationRepository;
this.manage = manage;
this.provisioningService = provisioningService;
}
Expand Down Expand Up @@ -99,30 +104,10 @@ public ResponseEntity<List<Role>> search(@RequestParam(value = "query") String q
return ResponseEntity.ok(manage.addManageMetaData(roles));
}

@PostMapping("validation/short_name")
public ResponseEntity<Map<String, Boolean>> shortNameExists(@RequestBody RoleExists roleExists,
@Parameter(hidden = true) User user) {
UserPermissions.assertAuthority(user, Authority.MANAGER);
String shortName = GroupURN.sanitizeRoleShortName(roleExists.shortName());
Optional<Role> optionalRole = roleRepository.findByShortNameIgnoreCaseAndApplicationsManageId(roleExists.manageId(), shortName);
Map<String, Boolean> result = optionalRole
.map(role -> Map.of("exists", roleExists.id() == null || !role.getId().equals(roleExists.id())))
.orElse(Map.of("exists", false));
return ResponseEntity.ok(result);
}

@PostMapping("")
public ResponseEntity<Role> newRole(@Validated @RequestBody Role role, @Parameter(hidden = true) User user) {
LOG.debug("/newRole");
String shortName = GroupURN.sanitizeRoleShortName(role.getShortName());
Optional<Application> applicationOptional = role.getApplications().stream()
.filter(application -> roleRepository.findByShortNameIgnoreCaseAndApplicationsManageId(application.getManageId(), shortName).isPresent())
.findFirst();
if (applicationOptional.isPresent()) {
Application application = applicationOptional.get();
throw new NotAllowedException(
String.format("Duplicate name: '%s' for manage entity:'%s'", shortName, application.getManageId()));
}
role.setShortName(GroupURN.sanitizeRoleShortName(role.getShortName()));
role.setIdentifier(UUID.randomUUID().toString());
return saveOrUpdate(role, user);
}
Expand All @@ -145,10 +130,6 @@ public ResponseEntity<Void> deleteRole(@PathVariable("id") Long id, @Parameter(h
return Results.deleteResult();
}

private void addApplicationData(Role role) {

}

private ResponseEntity<Role> saveOrUpdate(Role role, User user) {
if (StringUtils.hasText(role.getLandingPage()) && !urlFormatValidator.isValid(role.getLandingPage())) {
throw new InvalidInputException();
Expand All @@ -165,6 +146,11 @@ private ResponseEntity<Role> saveOrUpdate(Role role, User user) {
role.setShortName(previousRole.getShortName());
previousManageIdentifiersReference.set(previousRole.applicationIdentifiers());
}
//This is the disadvantage of having to save references from Manage
role.setApplications(role.getApplications().stream()
.map(application -> applicationRepository.findByManageIdAndManageType(application.getManageId(), application.getManageType())
.orElseGet(() -> applicationRepository.save(application)))
.collect(Collectors.toSet()));
Role saved = roleRepository.save(role);
if (isNew) {
provisioningService.newGroupRequest(saved);
Expand Down
26 changes: 25 additions & 1 deletion server/src/main/java/access/model/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@


import access.manage.EntityType;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import lombok.*;

import java.io.Serializable;
Expand All @@ -13,14 +15,36 @@
import java.io.Serializable;
import java.util.Set;

@Entity(name = "applications")
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@EqualsAndHashCode
@EqualsAndHashCode(of = {"manageId", "manageType"})
public class Application implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "manage_id")
@NotNull
private String manageId;

@Column(name = "manage_type")
@Enumerated(EnumType.STRING)
@NotNull
private EntityType manageType;

@Column(name = "landing_page")
private String landingPage;

@ManyToMany(mappedBy = "applications")
@JsonIgnore
private Set<Role> roles;

public Application(String manageId, EntityType manageType) {
this.manageId = manageId;
this.manageType = manageType;
}
}
22 changes: 0 additions & 22 deletions server/src/main/java/access/model/DistinctManageIdentifiers.java

This file was deleted.

7 changes: 5 additions & 2 deletions server/src/main/java/access/model/Role.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,11 @@ public class Role implements Serializable, Provisionable {
@Formula(value = "(SELECT COUNT(*) FROM user_roles ur WHERE ur.role_id=id)")
private Long userRoleCount;

@Column
@Convert(converter = ApplicationConverter.class)
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "roles_applications",
joinColumns = @JoinColumn(name = "role_id"),
inverseJoinColumns = @JoinColumn(name = "application_id"))
private Set<Application> applications = new HashSet<>();

@OneToMany(mappedBy = "role",
Expand Down
14 changes: 14 additions & 0 deletions server/src/main/java/access/repository/ApplicationRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package access.repository;

import access.manage.EntityType;
import access.model.Application;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface ApplicationRepository extends JpaRepository<Application, Long> {

Optional<Application> findByManageIdAndManageType(String manageId, EntityType manageType);
}
27 changes: 0 additions & 27 deletions server/src/main/java/access/repository/RoleRepository.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
package access.repository;

import access.manage.ManageIdentifier;
import access.model.DistinctManageIdentifiers;
import access.model.Role;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

@Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
Expand All @@ -21,29 +15,8 @@ public interface RoleRepository extends JpaRepository<Role, Long> {
nativeQuery = true)
List<Role> search(String keyWord, int limit);


@Query(value = "SELECT *, (SELECT COUNT(*) FROM user_roles ur WHERE ur.role_id=r.id) AS userRoleCount " +
"FROM roles r WHERE JSON_CONTAINS(applications->'$[*].manageId', json_array(?1))",
nativeQuery = true)
List<Role> findByApplicationsManageId(String manageId);

@Query(value = "SELECT DISTINCT applications FROM roles", nativeQuery = true)
List<DistinctManageIdentifiers> doFindDistinctManageIdentifiers();

@Query(value = "SELECT 1", nativeQuery = true)
default Set<ManageIdentifier> findDistinctManageIdentifiers() {
return this.doFindDistinctManageIdentifiers()
.stream()
.map(DistinctManageIdentifiers::manageIdentifiers)
.flatMap(Collection::stream)
.collect(Collectors.toSet());
}

@Query(value = "SELECT *, (SELECT COUNT(*) FROM user_roles ur WHERE ur.role_id=r.id) as userRoleCount " +
"FROM roles r WHERE JSON_CONTAINS(applications->'$[*].manageId', json_array(?1)) and short_name = ?2",
nativeQuery = true)
Optional<Role> findByShortNameIgnoreCaseAndApplicationsManageId(String managerId, String name);

List<Role> findByName(String name);

}
16 changes: 12 additions & 4 deletions server/src/main/java/access/teams/TeamsController.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package access.teams;

import access.exception.NotFoundException;
import access.exception.RemoteException;
import access.manage.Manage;
import access.model.Role;
import access.model.*;
import access.provision.ProvisioningService;
import access.provision.scim.GroupURN;
import access.provision.scim.OperationType;
import access.repository.ApplicationRepository;
import access.repository.RoleRepository;
import access.repository.UserRepository;
import access.repository.UserRoleRepository;
Expand All @@ -21,7 +21,6 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.rmi.Remote;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
Expand All @@ -42,17 +41,20 @@ public class TeamsController {
private final RoleRepository roleRepository;
private final UserRepository userRepository;
private final UserRoleRepository userRoleRepository;
private final ApplicationRepository applicationRepository;
private final Manage manage;
private final ProvisioningService provisioningService;

public TeamsController(RoleRepository roleRepository,
UserRepository userRepository,
UserRoleRepository userRoleRepository,
ApplicationRepository applicationRepository,
Manage manage,
ProvisioningService provisioningService) {
this.roleRepository = roleRepository;
this.userRepository = userRepository;
this.userRoleRepository = userRoleRepository;
this.applicationRepository = applicationRepository;
this.manage = manage;
this.provisioningService = provisioningService;
}
Expand All @@ -71,11 +73,17 @@ public ResponseEntity<Void> migrateTeam(@RequestBody Team team) {
role.setIdentifier(UUID.randomUUID().toString());
role.setTeamsOrigin(true);
//Check if the applications exist in Manage
Set<Application> applications = team.getApplications().stream().filter(this::applicationExists).collect(Collectors.toSet());
Set<Application> applications = team.getApplications().stream()
.filter(this::applicationExists)
.collect(Collectors.toSet());
if (applications.isEmpty()) {
throw new NotFoundException();
}
role.setApplications(applications);
//This is the disadvantage of having to save references from Manage
role.setApplications(team.getApplications().stream()
.map(application -> applicationRepository.findByManageIdAndManageType(application.getManageId(), application.getManageType())
.orElseGet(() -> applicationRepository.save(application)))
.collect(Collectors.toSet()));
Role savedRole = roleRepository.save(role);

provisioningService.newGroupRequest(savedRole);
Expand Down
Loading

0 comments on commit 7a20c89

Please sign in to comment.