diff --git a/.dev/docker-compose.yaml b/.dev/docker-compose.yaml
index 480b416cd..aaeb0f648 100644
--- a/.dev/docker-compose.yaml
+++ b/.dev/docker-compose.yaml
@@ -1,7 +1,18 @@
version: '2'
services:
-
+ keycloak:
+ image: quay.io/keycloak/keycloak:20.0
+ command: start-dev
+ environment:
+ KEYCLOAK_ADMIN: admin
+ KEYCLOAK_ADMIN_PASSWORD: admin
+ ports:
+ - 8080:8080
+ redis:
+ image: redis
+ ports:
+ - "6379:6379"
db:
image: postgres
restart: always
@@ -10,7 +21,7 @@ services:
environment:
POSTGRES_USER: redaction
POSTGRES_PASSWORD: redaction
- POSTGRES_DB: redaction
+ POSTGRES_DB: master
rabbitmq:
image: 'rabbitmq:3.9-alpine'
mem_limit: 500m
diff --git a/persistence-service-v1/keycloak-commons/pom.xml b/persistence-service-v1/keycloak-commons/pom.xml
new file mode 100644
index 000000000..ee2c9fba7
--- /dev/null
+++ b/persistence-service-v1/keycloak-commons/pom.xml
@@ -0,0 +1,121 @@
+
+
+
+
+ persistence-service-v1
+ com.iqser.red.service
+ 1.0-SNAPSHOT
+
+
+ 4.0.0
+
+ keycloak-commons
+
+
+
+
+ com.iqser.red.commons
+ spring-commons
+
+
+ com.iqser.red.commons
+ logging-commons
+
+
+ org.apache.commons
+ commons-lang3
+ 3.9
+
+
+ org.apache.commons
+ commons-compress
+ 1.21
+
+
+ org.apache.commons
+ commons-collections4
+ 4.4
+
+
+
+ com.iqser.red.commons
+ metric-commons
+
+
+
+ org.keycloak
+ keycloak-spring-boot-adapter-core
+ ${keycloak.version}
+
+
+
+ org.keycloak
+ keycloak-spring-security-adapter
+ ${keycloak.version}
+
+
+
+ org.keycloak
+ keycloak-admin-client
+ ${keycloak.version}
+
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-xml
+ 2.13.4
+
+
+
+ com.google.guava
+ guava
+
+
+
+
+ io.github.openfeign
+ feign-core
+ provided
+
+
+
+ org.springframework
+ spring-web
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
+
+ com.iqser.red.commons
+ jackson-commons
+
+
+
+
+ com.iqser.red.commons
+ test-commons
+ test
+
+
+ org.springframework.retry
+ spring-retry
+
+
+
+
diff --git a/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/DefaultKeyCloakCommonsConfiguration.java b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/DefaultKeyCloakCommonsConfiguration.java
new file mode 100644
index 000000000..a5bb84b47
--- /dev/null
+++ b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/DefaultKeyCloakCommonsConfiguration.java
@@ -0,0 +1,24 @@
+package com.iqser.red.keycloak.commons;
+
+import static com.iqser.red.keycloak.commons.UserCacheBuilder.USERS_CACHE;
+
+import java.time.Duration;
+
+import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.cache.RedisCacheConfiguration;
+
+@Configuration
+@ComponentScan
+public class DefaultKeyCloakCommonsConfiguration {
+
+
+ @Bean
+ public RedisCacheManagerBuilderCustomizer redisUserCacheManagerBuilderCustomizer() {
+
+ return (builder) -> builder.withCacheConfiguration(USERS_CACHE, RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(10)));
+ }
+
+}
diff --git a/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/KeyCloakAdminClientService.java b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/KeyCloakAdminClientService.java
new file mode 100644
index 000000000..cbc5768c4
--- /dev/null
+++ b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/KeyCloakAdminClientService.java
@@ -0,0 +1,41 @@
+package com.iqser.red.keycloak.commons;
+
+import java.util.concurrent.TimeUnit;
+
+import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
+import org.jboss.resteasy.client.jaxrs.internal.ResteasyClientBuilderImpl;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.admin.client.Keycloak;
+import org.keycloak.admin.client.KeycloakBuilder;
+import org.springframework.stereotype.Service;
+
+@Service
+public class KeyCloakAdminClientService {
+
+ private final Keycloak adminClient;
+
+
+ public KeyCloakAdminClientService(KeyCloakSettings settings) {
+
+ adminClient = KeycloakBuilder.builder()
+ .serverUrl(settings.getServerUrl())
+ .realm(settings.getRealm())
+ .clientId(settings.getClientId())
+ .clientSecret(settings.getClientSecret())
+ .grantType(OAuth2Constants.CLIENT_CREDENTIALS)
+ .resteasyClient(new ResteasyClientBuilderImpl().connectionTTL(2, TimeUnit.SECONDS)
+ .hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY)
+ .connectionPoolSize(settings.getConnectionPoolSize())
+ .disableTrustManager()
+ .build())
+ .build();
+
+ }
+
+
+ public Keycloak getAdminClient() {
+
+ return adminClient;
+ }
+
+}
diff --git a/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/KeyCloakRoleManager.java b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/KeyCloakRoleManager.java
new file mode 100644
index 000000000..cce6591b6
--- /dev/null
+++ b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/KeyCloakRoleManager.java
@@ -0,0 +1,135 @@
+package com.iqser.red.keycloak.commons;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.GET_RSS;
+import static com.iqser.red.keycloak.commons.roles.ApplicationRoles.RED_ADMIN_ROLE;
+import static com.iqser.red.keycloak.commons.roles.ApplicationRoles.RED_MANAGER_ROLE;
+import static com.iqser.red.keycloak.commons.roles.ApplicationRoles.RED_USER_ADMIN_ROLE;
+import static com.iqser.red.keycloak.commons.roles.ApplicationRoles.RED_USER_ROLE;
+import static com.iqser.red.keycloak.commons.roles.ApplicationRoles.UNMAPPED_ACTION_ROLES;
+
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.stereotype.Component;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.iqser.red.keycloak.commons.roles.ApplicationRoles;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class KeyCloakRoleManager implements ApplicationRunner {
+
+ private final RealmService realmService;
+
+ private final KeyCloakSettings settings;
+
+
+ @Override
+ public void run(ApplicationArguments args) {
+
+ log.info("Running KeyCloak Role Manager, managing client: {} with system client {}", settings.getApplicationClientId(), settings.getClientId());
+ var existingRoles = realmService.realm().roles().list().stream().map(RoleRepresentation::getName).collect(Collectors.toList());
+
+ log.info("Existing KC roles: {}", existingRoles);
+
+ var redactionClientRepresentation = getRedactionClientRepresentation();
+ var redactionClient = realmService.realm().clients().get(redactionClientRepresentation.getId());
+ var clientRoles = redactionClient.roles().list().stream().map(RoleRepresentation::getName).collect(Collectors.toList());
+
+ var allRoles = ApplicationRoles.ROLE_DATA.values().stream().flatMap(Set::stream).collect(Collectors.toSet());
+ allRoles.addAll(UNMAPPED_ACTION_ROLES);
+ log.info("Existing KC client roles: {}", clientRoles);
+ log.info("Current Application KC client roles: {}", allRoles);
+ if (!Sets.newHashSet(clientRoles).equals(allRoles)) {
+
+ log.info("Role-Sets are different, recreating form scratch ... ");
+ // remove all roles from the redaction client
+
+ clientRoles.forEach(clientRole -> {
+ try {
+ redactionClient.roles().deleteRole(clientRole);
+ }catch (Exception e){
+ log.warn("Failed to delete client role: {}",clientRole);
+ }
+ });
+
+ // re-create all client-roles
+ allRoles.forEach(role -> {
+ var roleRepresentation = new RoleRepresentation(role, role, false);
+ redactionClient.roles().create(roleRepresentation);
+ });
+ log.info("Cleaned up KC client roles and written current ones!");
+ }
+
+ var allClientRoles = redactionClient.roles().list();
+
+ if (settings.isScmEnabled()) {
+ ApplicationRoles.ROLE_DATA.get(RED_USER_ROLE).add(GET_RSS);
+ }
+
+ // if an application-role doesn't exist, create it
+ for (String applicationRole : ApplicationRoles.ROLE_DATA.keySet()) {
+
+ log.info("Running Role Composition for role: {}", applicationRole);
+ if (!existingRoles.contains(applicationRole)) {
+
+ log.info("Application Role: {} doesn't exist, creating it now", applicationRole);
+ var role = new RoleRepresentation(applicationRole, applicationRole, false);
+ role.setComposite(true);
+ realmService.realm().roles().create(role);
+
+ }
+ var applicationRoleResource = realmService.realm().roles().get(applicationRole);
+ Set composites = realmService.realm()
+ .rolesById()
+ .getClientRoleComposites(applicationRoleResource.toRepresentation().getId(), redactionClient.toRepresentation().getId());
+
+ log.info("Deleting previous composites for application role {}", applicationRole);
+ realmService.realm().rolesById().deleteComposites(applicationRoleResource.toRepresentation().getId(), new ArrayList<>(composites));
+ var relevantClientRoles = allClientRoles.stream().filter(role -> ApplicationRoles.ROLE_DATA.get(applicationRole).contains(role.getName())).collect(Collectors.toList());
+ log.info("Writing new composites for application role {}", applicationRole);
+ realmService.realm().rolesById().addComposites(applicationRoleResource.toRepresentation().getId(), relevantClientRoles);
+ log.info("Finished application role {}", applicationRole);
+ }
+
+ // add RED_USER Realm Role to RED_MANAGER
+ var redUserRole = realmService.realm().roles().get(RED_USER_ROLE);
+ var redManagerRole = realmService.realm().roles().get(RED_MANAGER_ROLE);
+
+ realmService.realm().rolesById().addComposites(redManagerRole.toRepresentation().getId(), Lists.newArrayList(redUserRole.toRepresentation()));
+
+ // add RED_USER_ADMIN Realm Role to RED_ADMIN
+ var redAdminRole = realmService.realm().roles().get(RED_ADMIN_ROLE);
+ var redUserAdminRole = realmService.realm().roles().get(RED_USER_ADMIN_ROLE);
+
+ realmService.realm().rolesById().addComposites(redAdminRole.toRepresentation().getId(), Lists.newArrayList(redUserAdminRole.toRepresentation()));
+
+ log.info("Finished KC Role Manager");
+
+ }
+
+
+ private ClientRepresentation getRedactionClientRepresentation() {
+
+ String applicationClientId = settings.getApplicationClientId();
+ var clientRepresentationIterator = realmService.realm().clients().findByClientId(applicationClientId).iterator();
+
+ if (clientRepresentationIterator.hasNext()) {
+ return clientRepresentationIterator.next();
+ } else {
+ throw new IllegalStateException(String.format("The application client information for the id %s could not be retrieved. " + //
+ "Please check the application settings and correct the license-service/userKeycloakSettings/applicationClientId setting.", applicationClientId));
+ }
+ }
+
+}
diff --git a/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/KeyCloakSettings.java b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/KeyCloakSettings.java
new file mode 100644
index 000000000..241a9cd3c
--- /dev/null
+++ b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/KeyCloakSettings.java
@@ -0,0 +1,21 @@
+package com.iqser.red.keycloak.commons;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import lombok.Data;
+
+@Data
+@ConfigurationProperties("commons.keycloak")
+public class KeyCloakSettings {
+
+ private String serverUrl;
+ private String realm;
+ private String applicationClientId;
+ private String clientId;
+ private String clientSecret;
+ private String issuer;
+ private String rolePrefix = "RED_";
+ private int connectionPoolSize = 10;
+ private boolean scmEnabled;
+
+}
diff --git a/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/KeycloakSecurity.java b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/KeycloakSecurity.java
new file mode 100644
index 000000000..2cc8e3f55
--- /dev/null
+++ b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/KeycloakSecurity.java
@@ -0,0 +1,30 @@
+package com.iqser.red.keycloak.commons;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import lombok.experimental.UtilityClass;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Collection of helpful functions to easily access information about an authenticated user.
+ */
+@Slf4j
+@UtilityClass
+public class KeycloakSecurity {
+
+ /**
+ * Determines the unique identifier for the currently logged in user.
+ *
+ * @return The unique user identifier. Never {@code null}.
+ */
+ public String getUserId() {
+
+ Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+ if (auth == null) {
+ return "anonymousUser";
+ }
+ return auth.getName();
+ }
+
+}
diff --git a/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/KeycloakSecurityService.java b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/KeycloakSecurityService.java
new file mode 100644
index 000000000..17580dc7e
--- /dev/null
+++ b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/KeycloakSecurityService.java
@@ -0,0 +1,18 @@
+package com.iqser.red.keycloak.commons;
+
+import org.springframework.stereotype.Service;
+
+@Service
+public class KeycloakSecurityService {
+
+ /**
+ * Determines the unique identifier for the currently logged in user.
+ *
+ * @return The unique user identifier. Never {@code null}.
+ */
+ public String getUserId() {
+
+ return KeycloakSecurity.getUserId();
+ }
+
+}
diff --git a/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/RealmService.java b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/RealmService.java
new file mode 100644
index 000000000..af2cbd525
--- /dev/null
+++ b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/RealmService.java
@@ -0,0 +1,24 @@
+package com.iqser.red.keycloak.commons;
+
+import org.keycloak.admin.client.resource.RealmResource;
+import org.springframework.stereotype.Service;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class RealmService {
+
+ private final KeyCloakAdminClientService keycloak;
+
+ private final KeyCloakSettings settings;
+
+
+ public RealmResource realm() {
+
+ return keycloak.getAdminClient().realm(settings.getRealm());
+ }
+
+}
diff --git a/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/UserCacheBuilder.java b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/UserCacheBuilder.java
new file mode 100644
index 000000000..a949f589c
--- /dev/null
+++ b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/UserCacheBuilder.java
@@ -0,0 +1,26 @@
+package com.iqser.red.keycloak.commons;
+
+import javax.annotation.PostConstruct;
+
+import org.springframework.stereotype.Service;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class UserCacheBuilder {
+
+ public static final String USERS_CACHE = "users";
+
+ private final UserListingService userService;
+
+
+ @PostConstruct
+ protected void postConstruct() {
+
+ userService.getAllUsers();
+ }
+
+}
diff --git a/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/UserListingService.java b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/UserListingService.java
new file mode 100644
index 000000000..1a2fc87d0
--- /dev/null
+++ b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/UserListingService.java
@@ -0,0 +1,99 @@
+package com.iqser.red.keycloak.commons;
+
+import static com.iqser.red.keycloak.commons.UserCacheBuilder.USERS_CACHE;
+import static com.iqser.red.keycloak.commons.roles.ApplicationRoles.RED_ADMIN_ROLE;
+import static com.iqser.red.keycloak.commons.roles.ApplicationRoles.RED_MANAGER_ROLE;
+import static com.iqser.red.keycloak.commons.roles.ApplicationRoles.RED_USER_ADMIN_ROLE;
+import static com.iqser.red.keycloak.commons.roles.ApplicationRoles.RED_USER_ROLE;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.keycloak.representations.idm.UserRepresentation;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.retry.support.RetryTemplate;
+import org.springframework.stereotype.Service;
+
+import com.iqser.red.keycloak.commons.model.User;
+import com.iqser.red.keycloak.commons.roles.ApplicationRoles;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class UserListingService {
+
+ private final RealmService realmService;
+ private final RetryTemplate retryTemplate = RetryTemplate.builder().maxAttempts(3).exponentialBackoff(1000, 2, 5000).build();
+
+
+ @Cacheable(value = USERS_CACHE)
+ public List getAllUsers() {
+
+ return retryTemplate.execute(context -> {
+ List allUsers = realmService.realm().users().search(null, 0, 500);
+
+ Map> usersByRole = new HashMap<>();
+ if(!allUsers.isEmpty()) {
+ var realmRoles = realmService.realm().roles().list().stream().map(r -> r.getName().toUpperCase()).collect(Collectors.toSet());
+ for (var role : ApplicationRoles.ROLE_DATA.keySet()) {
+ if(realmRoles.contains(role)) {
+ Set users = realmService.realm().roles().get(role).getRoleUserMembers(0, 500);
+ usersByRole.put(role, users.stream().map(UserRepresentation::getId).collect(Collectors.toSet()));
+ }
+ }
+ }
+
+ return compactUsers(allUsers, usersByRole);
+
+ });
+
+ }
+
+
+ private List compactUsers(List allUsers, Map> usersByRole) {
+
+ List users = new ArrayList<>();
+
+ for (var userRepresentation : allUsers) {
+ var user = convertBasicUser(userRepresentation);
+ for (var entry : usersByRole.entrySet()) {
+ if (entry.getValue().contains(user.getUserId())) {
+ user.getRoles().add(entry.getKey());
+ }
+ }
+ users.add(user);
+ }
+
+ users.forEach(user -> {
+ if (user.getRoles().contains(RED_MANAGER_ROLE)) {
+ user.getRoles().add(RED_USER_ROLE);
+ }
+ if (user.getRoles().contains(RED_ADMIN_ROLE)) {
+ user.getRoles().add(RED_USER_ADMIN_ROLE);
+ }
+ });
+
+ return users;
+ }
+
+
+ public User convertBasicUser(UserRepresentation userRepresentation) {
+
+ return User.builder()
+ .email(userRepresentation.getEmail())
+ .username(userRepresentation.getUsername())
+ .firstName(userRepresentation.getFirstName())
+ .lastName(userRepresentation.getLastName())
+ .userId(userRepresentation.getId())
+ .isActive(userRepresentation.isEnabled())
+ .build();
+ }
+
+}
diff --git a/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/model/User.java b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/model/User.java
new file mode 100644
index 000000000..47882f690
--- /dev/null
+++ b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/model/User.java
@@ -0,0 +1,33 @@
+package com.iqser.red.keycloak.commons.model;
+
+import java.io.Serializable;
+import java.util.Set;
+import java.util.TreeSet;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class User implements Serializable {
+
+ private String userId;
+
+ private String username;
+
+ private String email;
+
+ private String firstName;
+
+ private String lastName;
+
+ private boolean isActive;
+
+ @Builder.Default
+ private Set roles = new TreeSet<>();
+
+}
diff --git a/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/roles/ActionRoles.java b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/roles/ActionRoles.java
new file mode 100644
index 000000000..ed7935b69
--- /dev/null
+++ b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/roles/ActionRoles.java
@@ -0,0 +1,179 @@
+package com.iqser.red.keycloak.commons.roles;
+
+public final class ActionRoles {
+
+ // Audit
+ public static final String SEARCH_AUDIT_LOG = "red-search-audit-log";
+
+ // Dictionary
+ public static final String ADD_DICTIONARY_ENTRY = "red-add-dictionary-entry";
+ public static final String ADD_DOSSIER_DICTIONARY_ENTRY = "red-add-dossier-dictionary-entry";
+ public static final String DELETE_DICTIONARY_ENTRY = "red-delete-dictionary-entry";
+ public static final String DELETE_DOSSIER_DICTIONARY_ENTRY = "red-delete-dossier-dictionary-entry";
+ public static final String ADD_UPDATE_DICTIONARY_TYPE = "red-add-update-dictionary-type";
+ public static final String ADD_UPDATE_DOSSIER_DICTIONARY_TYPE = "red-add-update-dossier-dictionary-type";
+ public static final String DELETE_DICTIONARY_TYPE = "red-delete-dictionary-type";
+ public static final String DELETE_DOSSIER_DICTIONARY_TYPE = "red-delete-dossier-dictionary-type";
+
+ public static final String READ_DICTIONARY_TYPES = "red-read-dictionary-types";
+
+ // Colors
+ public static final String READ_COLORS = "red-read-colors";
+ public static final String WRITE_COLORS = "red-write-colors";
+
+ // Digital Signature
+ public static final String WRITE_DIGITAL_SIGNATURE = "red-write-digital-signature";
+ public static final String READ_DIGITAL_SIGNATURE = "red-read-digital-signature";
+
+ // Download
+ public static final String PROCESS_DOWNLOAD = "red-process-download";
+ public static final String READ_DOWNLOAD_STATUS = "red-read-download-status";
+
+ // Legal Basis
+ public static final String READ_LEGAL_BASIS = "red-read-legal-basis";
+ public static final String WRITE_LEGAL_BASIS = "red-write-legal-basis";
+
+ // License Report
+ public static final String READ_LICENSE_REPORT = "red-read-license-report";
+
+ // Redaction log
+ public static final String READ_REDACTION_LOG = "red-read-redaction-log";
+
+ // DossierTemplates
+ public static final String READ_DOSSIER_TEMPLATES = "red-read-dossier-templates";
+ public static final String WRITE_DOSSIER_TEMPLATES = "red-write-dossier-templates";
+
+ // Dossier Status / States
+ public static final String READ_DOSSIER_STATUS = "red-read-dossier-status";
+ public static final String WRITE_DOSSIER_STATUS = "red-write-dossier-status";
+
+ // Rules
+ public static final String READ_RULES = "red-read-rules";
+ public static final String WRITE_RULES = "red-write-rules";
+
+ // SMTP
+ public static final String READ_SMTP_CONFIGURATION = "red-read-smtp-configuration";
+ public static final String WRITE_SMTP_CONFIGURATION = "red-write-smtp-configuration";
+
+ // General Configurations
+ public static final String READ_GENERAL_CONFIGURATION = "red-read-general-configuration";
+ public static final String WRITE_GENERAL_CONFIGURATION = "red-write-general-configuration";
+
+ // Preferences
+ public static final String MANAGE_USER_PREFERENCES = "red-manage-user-preferences";
+
+ // Users
+ public static final String READ_USERS = "red-read-users";
+ public static final String READ_ALL_USERS = "red-read-all-users";
+ public static final String WRITE_USERS = "red-write-users";
+ public static final String UPDATE_MY_PROFILE = "red-update-my-profile";
+
+ // Version
+ public static final String READ_VERSIONS = "red-read-versions";
+
+ // Viewed pages
+ public static final String MANAGE_VIEWED_PAGES = "red-manage-viewed-pages";
+
+ // Watermark
+ public static final String READ_WATERMARK = "red-read-watermark";
+ public static final String WRITE_WATERMARK = "red-write-watermark";
+
+ // Dossier
+ public static final String READ_DOSSIER = "red-read-dossier";
+ public static final String ADD_UPDATE_DOSSIER = "red-add-update-dossier";
+ public static final String DELETE_DOSSIER = "red-delete-dossier";
+ public static final String ARCHIVE_DOSSIER = "red-archived-dossier";
+ public static final String UNARCHIVE_DOSSIER = "red-unarchive-dossier";
+
+ //Re-analyze
+ public static final String REANALYZE_DOSSIER = "red-reanalyze-dossier";
+ public static final String REANALYZE_FILE = "red-reanalyze-file";
+
+ // Exclude Include File
+ public static final String EXCLUDE_INCLUDE_FILE = "red-exclude-include-file";
+ public static final String EXCLUDE_INCLUDE_PAGES = "red-exclude-include-pages";
+
+ // Rotate Page
+ public static final String ROTATE_PAGE = "red-rotate-page";
+
+ //Status
+ public static final String READ_FILE_STATUS = "red-read-file-status";
+ public static final String SET_REVIEWER = "red-set-reviewer";
+ public static final String SET_STATUS_UNDER_APPROVAL = "red-set-status-under-approval";
+ public static final String SET_STATUS_APPROVED = "red-set-status-approved";
+
+ //File Attributes
+ public static final String WRITE_FILE_ATTRIBUTES_CONFIG = "red-write-file-attributes-config";
+ public static final String READ_FILE_ATTRIBUTES_CONFIG = "red-read-file-attributes-config";
+ public static final String WRITE_FILE_ATTRIBUTES = "red-write-file-attributes";
+
+ //Files
+ public static final String UPLOAD_FILE = "red-upload-file";
+ public static final String DELETE_FILE = "red-delete-file";
+ public static final String DOWNLOAD_ORIGINAL_FILE = "red-download-original-file";
+ public static final String DOWNLOAD_REDACTED_FILE = "red-download-redacted-file";
+ public static final String DOWNLOAD_ANNOTATED_FILE = "red-download-annotated-file";
+ public static final String DOWNLOAD_REDACTION_PREVIEW_FILE = "red-download-redaction-preview-file";
+
+ //Manual Redaction
+ public static final String READ_MANUAL_REDACTIONS = "red-read-manual-redactions";
+ public static final String REQUEST_MANUAL_REDACTION = "red-request-redaction";
+ public static final String PROCESS_MANUAL_REDACTION_REQUEST = "red-process-manual-redaction-request";
+ public static final String DO_MANUAL_REDACTION = "red-add-redaction";
+ public static final String DELETE_MANUAL_REDACTION = "red-delete-manual-redaction";
+ public static final String ADD_COMMENT = "red-add-comment";
+ public static final String DELETE_COMMENT = "red-delete-comment";
+
+ //Report Template
+ public static final String UPLOAD_REPORT_TEMPLATE = "red-upload-report-template";
+ public static final String GET_REPORT_TEMPLATES = "red-get-report-templates";
+ public static final String DOWNLOAD_REPORT_TEMPLATE = "red-download-report-template";
+ public static final String DELETE_REPORT_TEMPLATE = "red-delete-report-template";
+
+ //Dossier Attributes
+ public static final String WRITE_DOSSIER_ATTRIBUTES_CONFIG = "red-write-dossier-attributes-config";
+ public static final String READ_DOSSIER_ATTRIBUTES_CONFIG = "red-read-dossier-attributes-config";
+ public static final String WRITE_DOSSIER_ATTRIBUTES = "red-write-dossier-attributes";
+ public static final String READ_DOSSIER_ATTRIBUTES = "red-read-dossier-attributes";
+
+ //Search
+ public static final String REINDEX = "red-reindex";
+ public static final String SEARCH = "red-search";
+
+ //Notifications
+ public static final String READ_NOTIFICATIONS = "red-read-notification";
+ public static final String UPDATE_NOTIFICATIONS = "red-update-notification";
+
+ // ImportedRedactions
+ public static final String PROCESS_TEXT_HIGHLIGHTS = "red-process-texthighlights";
+ public static final String DELETE_IMPORTED_REDACTIONS = "red-delete-imported-redactions";
+
+ // Highlights
+ public static final String GET_HIGHLIGHTS = "red-get-highlights";
+ public static final String CONVERT_HIGHLIGHTS = "red-convert-highlights";
+ public static final String DELETE_HIGHLIGHTS = "red-delete-highlights";
+
+ // ACL Permission Management Signature
+ public static final String MANAGE_ACL_PERMISSIONS = "red-manage-acl-permissions";
+
+ // Application Configuration
+ public static final String READ_APP_CONFIG = "red-read-app-configuration";
+ public static final String WRITE_APP_CONFIG = "red-write-app-configuration";
+
+ // License Management
+ public static final String UPDATE_LICENSE = "red-update-license";
+ public static final String READ_LICENSE = "red-read-license";
+
+ // RSS
+ public static final String GET_RSS = "red-get-rss";
+
+ // Multitenancy
+ public static final String CREATE_TENANT = "red-create-tenant";
+ public static final String GET_TENANTS = "red-get-tenants";
+
+ public static final String DEPLOYMENT_INFO = "red-deployment-info";
+
+
+ private ActionRoles() {}
+
+}
diff --git a/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/roles/ApplicationRoles.java b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/roles/ApplicationRoles.java
new file mode 100644
index 000000000..b2f0f4010
--- /dev/null
+++ b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/roles/ApplicationRoles.java
@@ -0,0 +1,257 @@
+package com.iqser.red.keycloak.commons.roles;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.ADD_COMMENT;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.ADD_DICTIONARY_ENTRY;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.ADD_DOSSIER_DICTIONARY_ENTRY;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.ADD_UPDATE_DICTIONARY_TYPE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.ADD_UPDATE_DOSSIER;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.ADD_UPDATE_DOSSIER_DICTIONARY_TYPE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.ARCHIVE_DOSSIER;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.CONVERT_HIGHLIGHTS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.CREATE_TENANT;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DELETE_COMMENT;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DELETE_DICTIONARY_ENTRY;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DELETE_DICTIONARY_TYPE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DELETE_DOSSIER;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DELETE_DOSSIER_DICTIONARY_ENTRY;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DELETE_DOSSIER_DICTIONARY_TYPE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DELETE_FILE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DELETE_HIGHLIGHTS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DELETE_IMPORTED_REDACTIONS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DELETE_MANUAL_REDACTION;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DELETE_REPORT_TEMPLATE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DEPLOYMENT_INFO;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DOWNLOAD_ANNOTATED_FILE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DOWNLOAD_ORIGINAL_FILE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DOWNLOAD_REDACTED_FILE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DOWNLOAD_REDACTION_PREVIEW_FILE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DOWNLOAD_REPORT_TEMPLATE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DO_MANUAL_REDACTION;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.EXCLUDE_INCLUDE_FILE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.EXCLUDE_INCLUDE_PAGES;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.GET_HIGHLIGHTS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.GET_REPORT_TEMPLATES;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.GET_RSS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.GET_TENANTS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.MANAGE_ACL_PERMISSIONS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.MANAGE_USER_PREFERENCES;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.MANAGE_VIEWED_PAGES;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.PROCESS_DOWNLOAD;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.PROCESS_MANUAL_REDACTION_REQUEST;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.PROCESS_TEXT_HIGHLIGHTS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_ALL_USERS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_APP_CONFIG;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_COLORS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_DICTIONARY_TYPES;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_DIGITAL_SIGNATURE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_DOSSIER;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_DOSSIER_ATTRIBUTES;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_DOSSIER_ATTRIBUTES_CONFIG;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_DOSSIER_STATUS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_DOSSIER_TEMPLATES;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_DOWNLOAD_STATUS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_FILE_ATTRIBUTES_CONFIG;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_FILE_STATUS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_GENERAL_CONFIGURATION;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_LEGAL_BASIS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_LICENSE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_LICENSE_REPORT;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_MANUAL_REDACTIONS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_NOTIFICATIONS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_REDACTION_LOG;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_RULES;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_SMTP_CONFIGURATION;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_USERS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_VERSIONS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_WATERMARK;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.REANALYZE_DOSSIER;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.REANALYZE_FILE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.REINDEX;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.REQUEST_MANUAL_REDACTION;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.ROTATE_PAGE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.SEARCH;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.SEARCH_AUDIT_LOG;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.SET_REVIEWER;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.SET_STATUS_APPROVED;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.SET_STATUS_UNDER_APPROVAL;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.UNARCHIVE_DOSSIER;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.UPDATE_LICENSE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.UPDATE_MY_PROFILE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.UPDATE_NOTIFICATIONS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.UPLOAD_FILE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.UPLOAD_REPORT_TEMPLATE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_APP_CONFIG;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_COLORS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_DIGITAL_SIGNATURE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_DOSSIER_ATTRIBUTES;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_DOSSIER_ATTRIBUTES_CONFIG;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_DOSSIER_STATUS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_DOSSIER_TEMPLATES;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_FILE_ATTRIBUTES;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_FILE_ATTRIBUTES_CONFIG;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_GENERAL_CONFIGURATION;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_LEGAL_BASIS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_RULES;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_SMTP_CONFIGURATION;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_USERS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_WATERMARK;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.common.collect.Sets;
+
+public final class ApplicationRoles {
+
+ public static final String RED_USER_ROLE = "RED_USER";
+ public static final String RED_MANAGER_ROLE = "RED_MANAGER";
+ public static final String RED_ADMIN_ROLE = "RED_ADMIN";
+ public static final String RED_USER_ADMIN_ROLE = "RED_USER_ADMIN";
+
+ public static final Set UNMAPPED_ACTION_ROLES = Sets.newHashSet(UNARCHIVE_DOSSIER, UPDATE_LICENSE, GET_RSS);
+
+ public static final Set RED_USER_ACTION_ROLES = Sets.newHashSet(ADD_COMMENT,
+ READ_LICENSE,
+ READ_APP_CONFIG,
+ READ_DOSSIER_STATUS,
+ ADD_DOSSIER_DICTIONARY_ENTRY,
+ DO_MANUAL_REDACTION,
+ ADD_UPDATE_DOSSIER_DICTIONARY_TYPE,
+ DELETE_COMMENT,
+ DELETE_DOSSIER_DICTIONARY_ENTRY,
+ DELETE_DOSSIER_DICTIONARY_TYPE,
+ DELETE_FILE,
+ DELETE_MANUAL_REDACTION,
+ DOWNLOAD_ANNOTATED_FILE,
+ DOWNLOAD_ORIGINAL_FILE,
+ DOWNLOAD_REDACTED_FILE,
+ DOWNLOAD_REDACTION_PREVIEW_FILE,
+ DOWNLOAD_REPORT_TEMPLATE,
+ EXCLUDE_INCLUDE_FILE,
+ EXCLUDE_INCLUDE_PAGES,
+ GET_REPORT_TEMPLATES,
+ MANAGE_USER_PREFERENCES,
+ MANAGE_VIEWED_PAGES,
+ PROCESS_DOWNLOAD,
+ PROCESS_MANUAL_REDACTION_REQUEST,
+ READ_COLORS,
+ READ_DICTIONARY_TYPES,
+ READ_DIGITAL_SIGNATURE,
+ READ_DOSSIER,
+ READ_DOSSIER_ATTRIBUTES,
+ READ_DOSSIER_ATTRIBUTES_CONFIG,
+ READ_DOSSIER_TEMPLATES,
+ READ_DOWNLOAD_STATUS,
+ READ_FILE_ATTRIBUTES_CONFIG,
+ READ_FILE_STATUS,
+ READ_GENERAL_CONFIGURATION,
+ READ_LEGAL_BASIS,
+ READ_MANUAL_REDACTIONS,
+ READ_NOTIFICATIONS,
+ READ_REDACTION_LOG,
+ READ_RULES,
+ READ_USERS,
+ READ_VERSIONS,
+ READ_WATERMARK,
+ REANALYZE_DOSSIER,
+ REANALYZE_FILE,
+ REQUEST_MANUAL_REDACTION,
+ ROTATE_PAGE,
+ SEARCH,
+ SEARCH_AUDIT_LOG,
+ SET_REVIEWER,
+ SET_STATUS_APPROVED,
+ SET_STATUS_UNDER_APPROVAL,
+ UPDATE_MY_PROFILE,
+ UPDATE_NOTIFICATIONS,
+ UPLOAD_FILE,
+ WRITE_FILE_ATTRIBUTES,
+ PROCESS_TEXT_HIGHLIGHTS,
+ GET_HIGHLIGHTS,
+ CONVERT_HIGHLIGHTS,
+ DELETE_HIGHLIGHTS,
+ DELETE_IMPORTED_REDACTIONS);
+
+ public static final Set RED_ADMIN_ACTION_ROLES = Sets.newHashSet(ADD_DICTIONARY_ENTRY,
+ ADD_UPDATE_DICTIONARY_TYPE,
+ WRITE_DOSSIER_STATUS,
+ READ_DOSSIER_STATUS,
+ DELETE_DICTIONARY_ENTRY,
+ DELETE_DICTIONARY_TYPE,
+ DELETE_REPORT_TEMPLATE,
+ DOWNLOAD_REPORT_TEMPLATE,
+ GET_REPORT_TEMPLATES,
+ MANAGE_USER_PREFERENCES,
+ READ_COLORS,
+ READ_DICTIONARY_TYPES,
+ READ_DIGITAL_SIGNATURE,
+ READ_DOSSIER_ATTRIBUTES,
+ READ_DOSSIER_ATTRIBUTES_CONFIG,
+ READ_DOSSIER_TEMPLATES,
+ READ_FILE_ATTRIBUTES_CONFIG,
+ READ_LEGAL_BASIS,
+ READ_LICENSE_REPORT,
+ READ_NOTIFICATIONS,
+ READ_RULES,
+ READ_SMTP_CONFIGURATION,
+ READ_VERSIONS,
+ READ_WATERMARK,
+ REINDEX,
+ SEARCH_AUDIT_LOG,
+ UPDATE_NOTIFICATIONS,
+ UPLOAD_REPORT_TEMPLATE,
+ WRITE_COLORS,
+ WRITE_DIGITAL_SIGNATURE,
+ WRITE_DOSSIER_ATTRIBUTES_CONFIG,
+ WRITE_DOSSIER_TEMPLATES,
+ WRITE_FILE_ATTRIBUTES_CONFIG,
+ WRITE_GENERAL_CONFIGURATION,
+ WRITE_LEGAL_BASIS,
+ WRITE_RULES,
+ WRITE_SMTP_CONFIGURATION,
+ WRITE_WATERMARK,
+ WRITE_APP_CONFIG,
+ MANAGE_ACL_PERMISSIONS,
+ CREATE_TENANT,
+ GET_TENANTS,
+ DEPLOYMENT_INFO);
+
+ public static final Set RED_MANAGER_ACTION_ROLES = Sets.newHashSet(ADD_UPDATE_DOSSIER, ARCHIVE_DOSSIER, DELETE_DOSSIER, WRITE_DOSSIER_ATTRIBUTES);
+
+ public static final Set RED_USER_ADMIN_ACTION_ROLES = Sets.newHashSet(MANAGE_USER_PREFERENCES,
+ READ_ALL_USERS,
+ READ_DOSSIER,
+ READ_APP_CONFIG,
+ READ_GENERAL_CONFIGURATION,
+ READ_GENERAL_CONFIGURATION,
+ READ_NOTIFICATIONS,
+ READ_USERS,
+ UPDATE_MY_PROFILE,
+ UPDATE_NOTIFICATIONS,
+ WRITE_USERS,
+ READ_LICENSE);
+
+ public static final Map> ROLE_DATA = Map.of(RED_USER_ROLE,
+ RED_USER_ACTION_ROLES,
+ RED_MANAGER_ROLE,
+ RED_MANAGER_ACTION_ROLES,
+ RED_ADMIN_ROLE,
+ RED_ADMIN_ACTION_ROLES,
+ RED_USER_ADMIN_ROLE,
+ RED_USER_ADMIN_ACTION_ROLES);
+
+
+ private ApplicationRoles() {}
+
+
+ public static void validateRoles(Collection roles) {
+
+ for (String role : roles) {
+ if (!ROLE_DATA.containsKey(role)) {
+ throw new IllegalArgumentException("Invalid Role: " + role);
+ }
+ }
+ }
+
+}
diff --git a/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/security/RedBearerTokenRequestAuthenticator.java b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/security/RedBearerTokenRequestAuthenticator.java
new file mode 100644
index 000000000..d233efc12
--- /dev/null
+++ b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/security/RedBearerTokenRequestAuthenticator.java
@@ -0,0 +1,91 @@
+package com.iqser.red.keycloak.commons.security;
+
+import javax.security.cert.X509Certificate;
+
+import org.keycloak.adapters.BearerTokenRequestAuthenticator;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.OIDCAuthenticationError;
+import org.keycloak.adapters.spi.AuthOutcome;
+import org.keycloak.adapters.spi.HttpFacade;
+import org.keycloak.common.VerificationException;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.jose.jws.JWSInputException;
+
+public class RedBearerTokenRequestAuthenticator extends BearerTokenRequestAuthenticator {
+
+ private final String issuerUrl;
+
+
+ public RedBearerTokenRequestAuthenticator(KeycloakDeployment deployment, String issuerUrl) {
+
+ super(deployment);
+ this.issuerUrl = issuerUrl;
+ }
+
+
+ // This is the exact method copied from BearerTokenRequestAuthenticator but with a custom Token Verifier.
+ @Override
+ protected AuthOutcome authenticateToken(HttpFacade exchange, String tokenString) {
+
+ this.log.debug("Verifying access_token");
+ if (this.log.isTraceEnabled()) {
+ try {
+ JWSInput jwsInput = new JWSInput(tokenString);
+ String wireString = jwsInput.getWireString();
+ this.log.tracef("\taccess_token: %s", wireString.substring(0, wireString.lastIndexOf(".")) + ".signature");
+ } catch (JWSInputException var8) {
+ this.log.debugf(var8, "Failed to parse access_token: %s", tokenString);
+ }
+ }
+
+ try {
+ this.token = RedTokenVerifier.verifyToken(tokenString, this.deployment, issuerUrl);
+ } catch (VerificationException var7) {
+ this.log.debug("Failed to verify token");
+ this.challenge = this.challengeResponse(exchange, OIDCAuthenticationError.Reason.INVALID_TOKEN, "invalid_token", var7.getMessage());
+ return AuthOutcome.FAILED;
+ }
+
+ if (this.token.getIssuedAt() < this.deployment.getNotBefore()) {
+ this.log.debug("Stale token");
+ this.challenge = this.challengeResponse(exchange, OIDCAuthenticationError.Reason.STALE_TOKEN, "invalid_token", "Stale token");
+ return AuthOutcome.FAILED;
+ } else {
+ boolean verifyCaller = false;
+ if (this.deployment.isUseResourceRoleMappings()) {
+ verifyCaller = this.token.isVerifyCaller(this.deployment.getResourceName());
+ } else {
+ verifyCaller = this.token.isVerifyCaller();
+ }
+
+ this.surrogate = null;
+ if (verifyCaller) {
+ if (this.token.getTrustedCertificates() == null || this.token.getTrustedCertificates().isEmpty()) {
+ this.log.warn("No trusted certificates in token");
+ this.challenge = this.clientCertChallenge();
+ return AuthOutcome.FAILED;
+ }
+
+ X509Certificate[] chain = new X509Certificate[0];
+
+ try {
+ chain = exchange.getCertificateChain();
+ } catch (Exception var6) {
+ log.debug(var6);
+ }
+
+ if (chain == null || chain.length == 0) {
+ this.log.warn("No certificates provided by undertow to verify the caller");
+ this.challenge = this.clientCertChallenge();
+ return AuthOutcome.FAILED;
+ }
+
+ this.surrogate = chain[0].getSubjectDN().getName();
+ }
+
+ this.log.debug("successful authorized");
+ return AuthOutcome.AUTHENTICATED;
+ }
+ }
+
+}
diff --git a/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/security/RedQueryParameterTokenRequestAuthenticator.java b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/security/RedQueryParameterTokenRequestAuthenticator.java
new file mode 100644
index 000000000..cc5c37e27
--- /dev/null
+++ b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/security/RedQueryParameterTokenRequestAuthenticator.java
@@ -0,0 +1,91 @@
+package com.iqser.red.keycloak.commons.security;
+
+import javax.security.cert.X509Certificate;
+
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.OIDCAuthenticationError;
+import org.keycloak.adapters.QueryParameterTokenRequestAuthenticator;
+import org.keycloak.adapters.spi.AuthOutcome;
+import org.keycloak.adapters.spi.HttpFacade;
+import org.keycloak.common.VerificationException;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.jose.jws.JWSInputException;
+
+public class RedQueryParameterTokenRequestAuthenticator extends QueryParameterTokenRequestAuthenticator {
+
+ private final String issuerUrl;
+
+
+ public RedQueryParameterTokenRequestAuthenticator(KeycloakDeployment deployment, String issuerUrl) {
+
+ super(deployment);
+ this.issuerUrl = issuerUrl;
+ }
+
+
+ // This is the exact method copied from BearerTokenRequestAuthenticator but with a custom Token Verifier.
+ @Override
+ protected AuthOutcome authenticateToken(HttpFacade exchange, String tokenString) {
+
+ this.log.debug("Verifying access_token");
+ if (this.log.isTraceEnabled()) {
+ try {
+ JWSInput jwsInput = new JWSInput(tokenString);
+ String wireString = jwsInput.getWireString();
+ this.log.tracef("\taccess_token: %s", wireString.substring(0, wireString.lastIndexOf(".")) + ".signature");
+ } catch (JWSInputException var8) {
+ this.log.debugf(var8, "Failed to parse access_token: %s", tokenString);
+ }
+ }
+
+ try {
+ this.token = RedTokenVerifier.verifyToken(tokenString, this.deployment, issuerUrl);
+ } catch (VerificationException var7) {
+ this.log.debug("Failed to verify token");
+ this.challenge = this.challengeResponse(exchange, OIDCAuthenticationError.Reason.INVALID_TOKEN, "invalid_token", var7.getMessage());
+ return AuthOutcome.FAILED;
+ }
+
+ if (this.token.getIssuedAt() < this.deployment.getNotBefore()) {
+ this.log.debug("Stale token");
+ this.challenge = this.challengeResponse(exchange, OIDCAuthenticationError.Reason.STALE_TOKEN, "invalid_token", "Stale token");
+ return AuthOutcome.FAILED;
+ } else {
+ boolean verifyCaller = false;
+ if (this.deployment.isUseResourceRoleMappings()) {
+ verifyCaller = this.token.isVerifyCaller(this.deployment.getResourceName());
+ } else {
+ verifyCaller = this.token.isVerifyCaller();
+ }
+
+ this.surrogate = null;
+ if (verifyCaller) {
+ if (this.token.getTrustedCertificates() == null || this.token.getTrustedCertificates().isEmpty()) {
+ this.log.warn("No trusted certificates in token");
+ this.challenge = this.clientCertChallenge();
+ return AuthOutcome.FAILED;
+ }
+
+ X509Certificate[] chain = new X509Certificate[0];
+
+ try {
+ chain = exchange.getCertificateChain();
+ } catch (Exception var6) {
+ log.debug(var6);
+ }
+
+ if (chain == null || chain.length == 0) {
+ this.log.warn("No certificates provided by undertow to verify the caller");
+ this.challenge = this.clientCertChallenge();
+ return AuthOutcome.FAILED;
+ }
+
+ this.surrogate = chain[0].getSubjectDN().getName();
+ }
+
+ this.log.debug("successful authorized");
+ return AuthOutcome.AUTHENTICATED;
+ }
+ }
+
+}
diff --git a/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/security/RedTokenVerifier.java b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/security/RedTokenVerifier.java
new file mode 100644
index 000000000..4c8697306
--- /dev/null
+++ b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/security/RedTokenVerifier.java
@@ -0,0 +1,87 @@
+package com.iqser.red.keycloak.commons.security;
+
+import static org.keycloak.TokenVerifier.IS_ACTIVE;
+import static org.keycloak.TokenVerifier.SUBJECT_EXISTS_CHECK;
+
+import java.security.PublicKey;
+
+import org.apache.commons.lang3.StringUtils;
+import org.keycloak.TokenVerifier;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.rotation.PublicKeyLocator;
+import org.keycloak.common.VerificationException;
+import org.keycloak.representations.AccessToken;
+
+import lombok.experimental.UtilityClass;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@UtilityClass
+public class RedTokenVerifier {
+
+ public AccessToken verifyToken(String tokenString, KeycloakDeployment deployment, String issuerUrl) throws VerificationException {
+
+ TokenVerifier tokenVerifier = createVerifier(tokenString, deployment, issuerUrl);
+ if (deployment.isVerifyTokenAudience()) {
+ tokenVerifier.audience(deployment.getResourceName());
+ }
+
+ return tokenVerifier.verify().getToken();
+ }
+
+
+ private TokenVerifier createVerifier(String tokenString, KeycloakDeployment deployment, String issuerUrl) throws VerificationException {
+
+ TokenVerifier tokenVerifier = TokenVerifier.create(tokenString, AccessToken.class)
+ .withChecks(SUBJECT_EXISTS_CHECK, IS_ACTIVE, new TokenVerifier.TokenTypeCheck("bearer"), new IssuerCheck(issuerUrl));
+
+ String kid = tokenVerifier.getHeader().getKeyId();
+ PublicKey publicKey = getPublicKey(kid, deployment);
+ tokenVerifier.publicKey(publicKey);
+ return tokenVerifier;
+ }
+
+
+ private PublicKey getPublicKey(String kid, KeycloakDeployment deployment) throws VerificationException {
+
+ PublicKeyLocator pkLocator = deployment.getPublicKeyLocator();
+ PublicKey publicKey = pkLocator.getPublicKey(kid, deployment);
+ if (publicKey == null) {
+ log.debug("Didn't find publicKey for kid: {}", kid);
+ throw new VerificationException("Didn't find publicKey for specified kid");
+ } else {
+ return publicKey;
+ }
+ }
+
+
+ @Slf4j
+ public static class IssuerCheck implements TokenVerifier.Predicate {
+
+ private final String issuerUrl;
+
+
+ public IssuerCheck(String issuerUrl) {
+
+ this.issuerUrl = issuerUrl;
+ }
+
+
+ public boolean test(AccessToken t) throws VerificationException {
+
+ if (StringUtils.isEmpty(this.issuerUrl)) {
+ log.debug("Issuer Not Set, skipping verification");
+ return true;
+ } else if (!this.issuerUrl.equalsIgnoreCase(t.getIssuer())) {
+ var message = "Invalid token issuer. Expected '" + this.issuerUrl + "', but was '" + t.getIssuer() + "'";
+ log.debug(message);
+ throw new VerificationException(message);
+ } else {
+ log.debug("Issuer Verification Successful");
+ return true;
+ }
+ }
+
+ }
+
+}
diff --git a/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/security/SecuredKeyCloakConfiguration.java b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/security/SecuredKeyCloakConfiguration.java
new file mode 100644
index 000000000..4ebd8ce5b
--- /dev/null
+++ b/persistence-service-v1/keycloak-commons/src/main/java/com/iqser/red/keycloak/commons/security/SecuredKeyCloakConfiguration.java
@@ -0,0 +1,156 @@
+package com.iqser.red.keycloak.commons.security;
+
+import static com.iqser.red.keycloak.commons.UserCacheBuilder.USERS_CACHE;
+
+import java.time.Duration;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.keycloak.adapters.AdapterTokenStore;
+import org.keycloak.adapters.BearerTokenRequestAuthenticator;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.QueryParameterTokenRequestAuthenticator;
+import org.keycloak.adapters.RequestAuthenticator;
+import org.keycloak.adapters.spi.HttpFacade;
+import org.keycloak.adapters.springboot.KeycloakBaseSpringBootConfiguration;
+import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
+import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
+import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
+import org.keycloak.adapters.springsecurity.authentication.SpringSecurityRequestAuthenticator;
+import org.keycloak.adapters.springsecurity.authentication.SpringSecurityRequestAuthenticatorFactory;
+import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
+import org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.Primary;
+import org.springframework.data.redis.cache.RedisCacheConfiguration;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
+import org.springframework.security.web.authentication.HttpStatusEntryPoint;
+import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
+import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
+
+import com.iqser.red.keycloak.commons.KeyCloakSettings;
+
+import lombok.RequiredArgsConstructor;
+
+@ConditionalOnProperty(value = "keycloak.enabled", havingValue = "true")
+@RequiredArgsConstructor
+@KeycloakConfiguration
+@EnableConfigurationProperties(KeyCloakSettings.class)
+@Import(KeycloakSpringBootConfigResolver.class)
+public class SecuredKeyCloakConfiguration extends KeycloakWebSecurityConfigurerAdapter {
+ private final KeyCloakSettings keyCloakSettings;
+
+ @Bean
+ public KeycloakBaseSpringBootConfiguration keycloakBaseSpringBootConfiguration() {
+
+ return new KeycloakBaseSpringBootConfiguration();
+ }
+
+
+ // Submits the KeycloakAuthenticationProvider to the AuthenticationManager
+ @Autowired
+ public void configureGlobal(AuthenticationManagerBuilder auth) {
+
+ KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
+ SimpleAuthorityMapper simpleAuthorityMapper = new SimpleAuthorityMapper();
+ simpleAuthorityMapper.setPrefix("");
+ keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(simpleAuthorityMapper);
+ auth.authenticationProvider(keycloakAuthenticationProvider);
+ }
+
+
+ @Override
+ public void configure(WebSecurity web) {
+
+ web.ignoring().antMatchers("/actuator/health/**",
+ "/redaction-gateway-v1/async/download/with-ott/**",
+ "/api/async/download/with-ott/**",
+ "/api/docs",
+ "/api/docs/**",
+ "/",
+ "/api",
+ "/internal-api/**");
+
+ web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
+ }
+
+
+ @Bean
+ @Primary
+ @Override
+ protected KeycloakAuthenticationProcessingFilter keycloakAuthenticationProcessingFilter() throws Exception {
+
+ KeycloakAuthenticationProcessingFilter filter = new KeycloakAuthenticationProcessingFilter(authenticationManagerBean());
+ filter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy());
+ filter.setRequestAuthenticatorFactory(new SpringSecurityRequestAuthenticatorFactory() {
+
+ @Override
+ public RequestAuthenticator createRequestAuthenticator(HttpFacade facade,
+ HttpServletRequest request,
+ KeycloakDeployment deployment,
+ AdapterTokenStore tokenStore,
+ int sslRedirectPort) {
+
+ return new SpringSecurityRequestAuthenticator(facade, request, deployment, tokenStore, sslRedirectPort) {
+
+ @Override
+ protected BearerTokenRequestAuthenticator createBearerTokenAuthenticator() {
+
+ return new RedBearerTokenRequestAuthenticator(deployment, keyCloakSettings.getIssuer());
+ }
+
+
+ @Override
+ protected QueryParameterTokenRequestAuthenticator createQueryParameterTokenRequestAuthenticator() {
+
+ return new RedQueryParameterTokenRequestAuthenticator(deployment, keyCloakSettings.getIssuer());
+ }
+ };
+ }
+ });
+
+ return filter;
+ }
+
+
+ // Specifies the session authentication strategy
+ @Bean
+ @Override
+ protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
+
+ return new NullAuthenticatedSessionStrategy();
+ }
+
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+
+ super.configure(http);
+
+ http.anonymous().disable();
+
+ http.authorizeRequests().anyRequest().authenticated();
+
+ http.csrf().disable();
+
+ http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
+
+ http.exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
+
+ }
+
+
+
+}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/test/java/com/iqser/red/service/persistence/service/v1/api/model/IdentityTest.java b/persistence-service-v1/keycloak-commons/src/test/java/com/iqser/red/keycloak/commons/IdentityTest.java
similarity index 78%
rename from persistence-service-v1/persistence-service-api-v1/src/test/java/com/iqser/red/service/persistence/service/v1/api/model/IdentityTest.java
rename to persistence-service-v1/keycloak-commons/src/test/java/com/iqser/red/keycloak/commons/IdentityTest.java
index fa201e412..fbb8a0d17 100644
--- a/persistence-service-v1/persistence-service-api-v1/src/test/java/com/iqser/red/service/persistence/service/v1/api/model/IdentityTest.java
+++ b/persistence-service-v1/keycloak-commons/src/test/java/com/iqser/red/keycloak/commons/IdentityTest.java
@@ -1,4 +1,4 @@
-package com.iqser.red.service.persistence.service.v1.api.model;
+package com.iqser.red.keycloak.commons;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/annotations/AnnotationStatus.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/annotations/AnnotationStatus.java
deleted file mode 100644
index 6f4d882d0..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/annotations/AnnotationStatus.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.model.annotations;
-
-public enum AnnotationStatus {
- REQUESTED,
- APPROVED,
- DECLINED
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/DossierTemplateDictionaryStats.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/DossierTemplateDictionaryStats.java
deleted file mode 100644
index 702b6fa40..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/DossierTemplateDictionaryStats.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.DictionarySummary;
-
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class DossierTemplateDictionaryStats {
-
- private String dossierTemplateId;
- private int numberOfDictionaries; // number of Types for the dossierTemplate
- private List dictionarySummaryList = new ArrayList<>();
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/DossierTemplateStatus.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/DossierTemplateStatus.java
deleted file mode 100644
index aee28ae3b..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/DossierTemplateStatus.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate;
-
-public enum DossierTemplateStatus {
- INCOMPLETE,
- INACTIVE,
- ACTIVE
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/ReportTemplateUpdateRequest.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/ReportTemplateUpdateRequest.java
deleted file mode 100644
index d4f764ecb..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/ReportTemplateUpdateRequest.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate;
-
-import lombok.AccessLevel;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.NonNull;
-
-@Data
-@Builder
-@AllArgsConstructor
-@NoArgsConstructor(access = AccessLevel.PRIVATE)
-public class ReportTemplateUpdateRequest {
-
- @NonNull
- private String fileName;
-
- private boolean multiFileReport;
- private boolean activeByDefault;
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/configuration/DigitalSignatureType.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/configuration/DigitalSignatureType.java
deleted file mode 100644
index 48e90baa8..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/configuration/DigitalSignatureType.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration;
-
-public enum DigitalSignatureType {
- CERTIFICATE,
- KMS,
- HSM;
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/configuration/Watermark.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/configuration/Watermark.java
deleted file mode 100644
index 4d7912344..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/configuration/Watermark.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration;
-
-import java.time.OffsetDateTime;
-
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class Watermark {
-
- private Long id;
- private String name;
- private boolean enabled;
- private String dossierTemplateId;
- private String text;
- private String hexColor;
- private int opacity;
- private int fontSize;
- private String fontType;
- private String createdBy;
- private OffsetDateTime dateAdded;
- private OffsetDateTime dateModified;
- private WatermarkOrientation orientation;
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/configuration/WatermarkOrientation.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/configuration/WatermarkOrientation.java
deleted file mode 100644
index b95b3de64..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/configuration/WatermarkOrientation.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration;
-
-public enum WatermarkOrientation {
- VERTICAL,
- HORIZONTAL,
- DIAGONAL
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/DossierAttributeType.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/DossierAttributeType.java
deleted file mode 100644
index bd1b877aa..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/DossierAttributeType.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier;
-
-public enum DossierAttributeType {
- TEXT,
- NUMBER,
- DATE,
- IMAGE
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/DossierInformation.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/DossierInformation.java
deleted file mode 100644
index 0fcf5a130..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/DossierInformation.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier;
-
-import lombok.Data;
-
-@Data
-public class DossierInformation {
-
- private int numberOfActiveDossiers;
- private int numberOfArchivedDossiers;
- private int numberOfSoftDeletedDossiers;
- private int numberOfHardDeletedDossiers;
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/DossierStats.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/DossierStats.java
deleted file mode 100644
index 341eea58f..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/DossierStats.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier;
-
-import java.time.OffsetDateTime;
-import java.util.Map;
-
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.ProcessingStatus;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.WorkflowStatus;
-
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.NoArgsConstructor;
-
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-@EqualsAndHashCode(of = {"dossierId"})
-public class DossierStats {
-
- private String dossierId;
- private int numberOfFiles;
- private int numberOfSoftDeletedFiles;
- private int numberOfPages; // sum of pages
- private int numberOfExcludedPages; // sum of excludedPages
- private boolean hasRedactionsFilePresent; // true if at least one file in the dossier has redactions
- private boolean hasHintsNoRedactionsFilePresent; // true if at least one file in the dossier has hints but doesn't have redactions
- private boolean hasSuggestionsFilePresent; // true if at least one file in the dossier has suggestions
- private boolean hasUpdatesFilePresent; //true if at least one file in the dossier has updates
- private boolean hasNoFlagsFilePresent; // true if at least one file in the dossier has none of the other flags
- private Map fileCountPerProcessingStatus;
- private Map fileCountPerWorkflowStatus;
- private OffsetDateTime lastFileUpdateDate;
- private OffsetDateTime fileManipulationDate;
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/DossierStatusInfo.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/DossierStatusInfo.java
deleted file mode 100644
index ee20caf97..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/DossierStatusInfo.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-@Data
-@Builder
-@AllArgsConstructor
-@NoArgsConstructor
-public class DossierStatusInfo {
-
- @JsonProperty("dossierStatusId")
- private String id;
- private String name;
- private String description;
- private String color;
- private String dossierTemplateId;
- private int rank;
- private Long dossierCount;
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/DossierVisibility.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/DossierVisibility.java
deleted file mode 100644
index 14100676f..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/DossierVisibility.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier;
-
-public enum DossierVisibility {
-
- PRIVATE,
- PUBLIC;
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/file/FileAttributeType.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/file/FileAttributeType.java
deleted file mode 100644
index c76c291e7..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/file/FileAttributeType.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file;
-
-public enum FileAttributeType {
- TEXT,
- NUMBER,
- DATE
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/file/WorkflowStatus.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/file/WorkflowStatus.java
deleted file mode 100644
index e0b6465fd..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/dossier/file/WorkflowStatus.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file;
-
-public enum WorkflowStatus {
- NEW,
- UNDER_REVIEW,
- UNDER_APPROVAL,
- APPROVED
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/type/DictionarySummary.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/type/DictionarySummary.java
deleted file mode 100644
index cbbc4fc49..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/dossiertemplate/type/DictionarySummary.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type;
-
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class DictionarySummary {
-
- private String id; // type id
- private String type; // type
- private String name; // label
- private long entriesCount; // entries size
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/ApplicationConfigurationResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/ApplicationConfigurationResource.java
deleted file mode 100644
index a046e8029..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/ApplicationConfigurationResource.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration.ApplicationConfig;
-
-@ResponseStatus(value = HttpStatus.OK)
-public interface ApplicationConfigurationResource {
-
- String APPLICATION_CONFIG_PATH = "/app-config";
-
-
- @ResponseStatus(value = HttpStatus.CREATED)
- @PostMapping(value = APPLICATION_CONFIG_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- ApplicationConfig createOrUpdateAppConfig(@RequestBody ApplicationConfig appConfig);
-
-
- @ResponseStatus(value = HttpStatus.OK)
- @GetMapping(value = APPLICATION_CONFIG_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
- ApplicationConfig getCurrentApplicationConfig();
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/AuditResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/AuditResource.java
deleted file mode 100644
index 8f2508c5e..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/AuditResource.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.util.List;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.audit.AuditModel;
-import com.iqser.red.service.persistence.service.v1.api.model.audit.AuditRequest;
-import com.iqser.red.service.persistence.service.v1.api.model.audit.AuditSearchRequest;
-import com.iqser.red.service.persistence.service.v1.api.model.audit.CategoryModel;
-import com.iqser.red.service.persistence.service.v1.api.model.common.Page;
-
-public interface AuditResource {
-
- String PATH = "/audit";
-
-
- /**
- * @param auditRequest - details to audit
- * @throws org.springframework.web.server.ResponseStatusException - 404 - Not Found in case the object doesn't exist
- */
- @ResponseBody
- @ResponseStatus(value = HttpStatus.OK)
- @PostMapping(value = PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
- void audit(@RequestBody AuditRequest auditRequest);
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.OK)
- @PostMapping(value = PATH + "/search", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
- Page search(@RequestBody AuditSearchRequest auditSearchRequest);
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.OK)
- @GetMapping(value = PATH + "/categories", produces = MediaType.APPLICATION_JSON_VALUE)
- List getCategories();
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/ComponentOverrideResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/ComponentOverrideResource.java
deleted file mode 100644
index 0fdfcc0c8..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/ComponentOverrideResource.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.component.ComponentsOverrides;
-import com.iqser.red.service.persistence.service.v1.api.model.component.RevertOverrideRequest;
-
-public interface ComponentOverrideResource {
-
- String PATH = "/component";
-
- String FILE_ID = "fileId";
- String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
-
- String DOSSIER_ID_PARAM = "dossierId";
- String DOSSIER_ID_PATH_PARAM = "/{" + DOSSIER_ID_PARAM + "}";
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.OK)
- @PostMapping(value = PATH + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
- void addOverrides(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody ComponentsOverrides componentsOverrides);
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.OK)
- @GetMapping(value = PATH + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- ComponentsOverrides getOverrides(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(FILE_ID) String fileId);
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.OK)
- @PostMapping(value = PATH + "/revert" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
- void revertOverrides(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody RevertOverrideRequest revertOverrideRequest);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DictionaryResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DictionaryResource.java
deleted file mode 100644
index 2e4fae6c9..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DictionaryResource.java
+++ /dev/null
@@ -1,113 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.util.List;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration.Colors;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.DictionaryEntry;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.DictionaryEntryType;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.type.Type;
-
-@ResponseStatus(value = HttpStatus.OK)
-public interface DictionaryResource {
-
- String DICTIONARY_PATH = "/dictionary";
- String TYPE_PATH = DICTIONARY_PATH + "/type";
-
- String TYPE_PARAMETER_NAME = "type";
- String TYPE_PATH_VARIABLE = "/{" + TYPE_PARAMETER_NAME + "}";
-
- String INCLUDE_DELETED_PARAMETER_NAME = "includeDeleted";
- String DOSSIER_TEMPLATE_PARAMETER_NAME = "dossierTemplateId";
- String DOSSIER_TEMPLATE_PATH_VARIABLE = "/{" + DOSSIER_TEMPLATE_PARAMETER_NAME + "}";
-
- String DOSSIER_ID_PARAMETER_NAME = "dossierId";
- String DOSSIER_PATH = "/dossier";
- String DOSSIER_ID_PATH_VARIABLE = "/{" + DOSSIER_ID_PARAMETER_NAME + "}";
-
- String FROM_VERSION_PARAM = "fromVersion";
-
- String COLOR_PATH = "/color";
- String VERSION_PATH = "/version";
- String ENTRIES_PATH = "/entries";
-
-
- @ResponseStatus(HttpStatus.NO_CONTENT)
- @PostMapping(value = DICTIONARY_PATH + TYPE_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
- void addEntries(@PathVariable(TYPE_PARAMETER_NAME) String typeId,
- @RequestBody List entries,
- @RequestParam(value = "removeCurrent", required = false, defaultValue = "false") boolean removeCurrent,
- @RequestParam(value = "ignoreInvalidEntries", required = false, defaultValue = "false") boolean ignoreInvalidEntries,
- @RequestParam(value = "dictionaryEntryType", required = false, defaultValue = "ENTRY") DictionaryEntryType dictionaryEntryType);
-
-
- @ResponseStatus(HttpStatus.NO_CONTENT)
- @DeleteMapping(value = DICTIONARY_PATH + TYPE_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
- void deleteEntries(@PathVariable(TYPE_PARAMETER_NAME) String typeId,
- @RequestBody List entries,
- @RequestParam(value = "dictionaryEntryType", required = false, defaultValue = "ENTRY") DictionaryEntryType dictionaryEntryType);
-
-
- @ResponseStatus(HttpStatus.NO_CONTENT)
- @PostMapping(value = TYPE_PATH + TYPE_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
- void updateTypeValue(@PathVariable(TYPE_PARAMETER_NAME) String typeId, @RequestBody Type typeValue);
-
-
- @ResponseStatus(HttpStatus.CREATED)
- @PostMapping(value = TYPE_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- Type addType(@RequestBody Type typeValue);
-
-
- @ResponseStatus(HttpStatus.NO_CONTENT)
- @DeleteMapping(value = TYPE_PATH + TYPE_PATH_VARIABLE)
- void deleteType(@PathVariable(TYPE_PARAMETER_NAME) String typeId);
-
-
- @GetMapping(value = TYPE_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- List getAllTypesForDossierTemplate(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
- @RequestParam(value = INCLUDE_DELETED_PARAMETER_NAME, required = false, defaultValue = "false") boolean includeDeleted);
-
-
- @GetMapping(value = TYPE_PATH + DOSSIER_PATH + DOSSIER_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- List getAllTypesForDossier(@PathVariable(DOSSIER_ID_PARAMETER_NAME) String dossierId,
- @RequestParam(value = INCLUDE_DELETED_PARAMETER_NAME, required = false, defaultValue = "false") boolean includeDeleted);
-
-
- @GetMapping(value = DICTIONARY_PATH + TYPE_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- Type getDictionaryForType(@PathVariable(TYPE_PARAMETER_NAME) String typeId, @RequestParam(value = FROM_VERSION_PARAM, required = false) Long fromVersion);
-
-
- @GetMapping(value = DICTIONARY_PATH + TYPE_PATH_VARIABLE + ENTRIES_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
- List getEntriesForType(@PathVariable(TYPE_PARAMETER_NAME) String typeId,
- @RequestParam(value = FROM_VERSION_PARAM, required = false) Long fromVersion,
- @RequestParam(value = "dictionaryEntryType", required = false, defaultValue = "ENTRY") DictionaryEntryType dictionaryEntryType);
-
-
- @GetMapping(value = VERSION_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE)
- long getVersion(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId);
-
-
- @GetMapping(value = VERSION_PATH + DOSSIER_PATH + DOSSIER_ID_PATH_VARIABLE)
- long getVersionForDossier(@PathVariable(DOSSIER_ID_PARAMETER_NAME) String dossierId);
-
-
- @ResponseStatus(HttpStatus.NO_CONTENT)
- @PostMapping(value = COLOR_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
- void setColors(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @RequestBody Colors colors);
-
-
- @ResponseBody
- @GetMapping(value = COLOR_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- Colors getColors(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DigitalSignatureResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DigitalSignatureResource.java
deleted file mode 100644
index 2c72d300b..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DigitalSignatureResource.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration.DigitalSignature;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration.DigitalSignatureKms;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration.DigitalSignatureType;
-
-@ResponseStatus(value = HttpStatus.OK)
-public interface DigitalSignatureResource {
-
- String DIGITAL_SIGNATURE_PATH = "/digital-signature";
- String DIGITAL_SIGNATURE_TYPE_PATH = DIGITAL_SIGNATURE_PATH + "/type";
- String DIGITAL_SIGNATURE_KMS_PATH = DIGITAL_SIGNATURE_PATH + "/kms";
-
- String DIGITAL_SIGNATURE_TYPE = "digitalSignatureType";
- String DIGITAL_SIGNATURE_TYPE_VARIABLE = "/{" + DIGITAL_SIGNATURE_TYPE + "}";
-
-
- @GetMapping(value = DIGITAL_SIGNATURE_TYPE_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
- DigitalSignatureType getActiveDigitalSignatureType();
-
-
- @ResponseStatus(HttpStatus.NO_CONTENT)
- @PostMapping(value = DIGITAL_SIGNATURE_TYPE_PATH + DIGITAL_SIGNATURE_TYPE_VARIABLE)
- void setActiveDigitalSignatureType(@PathVariable(DIGITAL_SIGNATURE_TYPE) DigitalSignatureType digitalSignatureType);
-
-
- @ResponseStatus(HttpStatus.CREATED)
- @PostMapping(value = DIGITAL_SIGNATURE_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- DigitalSignature saveDigitalSignature(@RequestBody DigitalSignature digitalSignatureModel);
-
-
- @ResponseStatus(HttpStatus.CREATED)
- @PutMapping(value = DIGITAL_SIGNATURE_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- void updateDigitalSignature(@RequestBody DigitalSignature digitalSignatureModel);
-
-
- @GetMapping(value = DIGITAL_SIGNATURE_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
- DigitalSignature getDigitalSignature();
-
-
- @ResponseStatus(HttpStatus.NO_CONTENT)
- @DeleteMapping(value = DIGITAL_SIGNATURE_PATH)
- void deleteDigitalSignature();
-
-
- @ResponseStatus(HttpStatus.CREATED)
- @PostMapping(value = DIGITAL_SIGNATURE_KMS_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- DigitalSignatureKms saveDigitalSignatureKms(@RequestBody DigitalSignatureKms digitalSignature);
-
-
- @GetMapping(value = DIGITAL_SIGNATURE_KMS_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
- DigitalSignatureKms getDigitalSignatureKms();
-
-
- @ResponseStatus(HttpStatus.NO_CONTENT)
- @DeleteMapping(value = DIGITAL_SIGNATURE_KMS_PATH)
- void deleteDigitalSignatureKms();
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DossierAttributesConfigResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DossierAttributesConfigResource.java
deleted file mode 100644
index 7a2e4a225..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DossierAttributesConfigResource.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.util.List;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.DossierAttributeConfig;
-
-public interface DossierAttributesConfigResource {
-
- String DOSSIER_ATTRIBUTE_PATH = "/dossier-attribute";
-
- String DOSSIER_TEMPLATE_ID = "dossierTemplateId";
- String DOSSIER_TEMPLATE_ID_PATH_VARIABLE = "/{" + DOSSIER_TEMPLATE_ID + "}";
-
- String DOSSIER_ATTRIBUTE_ID = "dossierAttributeId";
- String DOSSIER_ATTRIBUTE_ID_PATH_VARIABLE = "/{" + DOSSIER_ATTRIBUTE_ID + "}";
-
-
- @ResponseBody
- @ResponseStatus(HttpStatus.OK)
- @PostMapping(value = DOSSIER_ATTRIBUTE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- DossierAttributeConfig addOrUpdateDossierAttribute(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody DossierAttributeConfig dossierAttributeConfig);
-
-
- @ResponseBody
- @ResponseStatus(HttpStatus.OK)
- @PutMapping(value = DOSSIER_ATTRIBUTE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- List setDossierAttributesConfig(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId,
- @RequestBody List dossierAttributesConfig);
-
-
- @ResponseStatus(HttpStatus.NO_CONTENT)
- @DeleteMapping(value = DOSSIER_ATTRIBUTE_PATH + DOSSIER_ATTRIBUTE_ID_PATH_VARIABLE)
- void deleteDossierAttribute(@PathVariable(DOSSIER_ATTRIBUTE_ID) String dossierAttributeId);
-
-
- @ResponseStatus(HttpStatus.NO_CONTENT)
- @PostMapping(value = DOSSIER_ATTRIBUTE_PATH + "/delete")
- void deleteDossierAttributes(@RequestBody List dossierAttributeIds);
-
-
- @ResponseBody
- @ResponseStatus(HttpStatus.OK)
- @GetMapping(value = DOSSIER_ATTRIBUTE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- List getDossierAttributes(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DossierAttributesResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DossierAttributesResource.java
deleted file mode 100644
index 24b8a5b76..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DossierAttributesResource.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.util.List;
-
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.DossierAttribute;
-
-public interface DossierAttributesResource {
-
- String REST_PATH = "/dossierAttributes";
- String SET_PATH = "/set";
- String UPDATE_PATH = "/update";
-
- String DOSSIER_ID_PARAM = "dossierId";
- String DOSSIER_ID_PATH_PARAM = "/{" + DOSSIER_ID_PARAM + "}";
-
- String DOSSIER_ATTRIBUTE_ID_PARAM = "dossierAttributeId";
- String DOSSIER_ATTRIBUTE_ID_PATH_PARAM = "/{" + DOSSIER_ATTRIBUTE_ID_PARAM + "}";
-
-
- @PostMapping(value = REST_PATH + SET_PATH + DOSSIER_ID_PATH_PARAM, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- List setDossierAttributes(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @RequestBody List dossierAttributes);
-
-
- @PostMapping(value = REST_PATH + UPDATE_PATH + DOSSIER_ID_PATH_PARAM, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- DossierAttribute addOrUpdateDossierAttribute(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @RequestBody DossierAttribute dossierAttribute);
-
-
- @GetMapping(value = REST_PATH + DOSSIER_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
- List getDossierAttributes(@PathVariable(DOSSIER_ID_PARAM) String dossierId);
-
-
- @DeleteMapping(value = REST_PATH + SET_PATH + DOSSIER_ID_PATH_PARAM + DOSSIER_ATTRIBUTE_ID_PATH_PARAM)
- void deleteDossierAttribute(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(DOSSIER_ATTRIBUTE_ID_PARAM) String dossierAttributeId);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DossierResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DossierResource.java
deleted file mode 100644
index b4d053bf7..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DossierResource.java
+++ /dev/null
@@ -1,115 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.time.OffsetDateTime;
-import java.util.List;
-import java.util.Set;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.common.JSONPrimitive;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.CreateOrUpdateDossierRequest;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.Dossier;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.DossierChange;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.DossierInformation;
-
-@ResponseStatus(value = HttpStatus.OK)
-public interface DossierResource {
-
- String REST_PATH = "/dossier";
- String DOSSIER_TEMPLATE_PATH = "/dossier-template";
- String INFO_PATH = "/dossier-info";
- String DELETED_DOSSIERS_PATH = "/deletedDossiers";
- String HARD_DELETE_PATH = "/hardDelete";
- String UNDELETE_PATH = "/undelete";
-
- String ARCHIVE_DOSSIERS_PATH = "/archivedDossiers";
- String ARCHIVE_PATH = "/archive";
- String UNARCHIVE_PATH = "/unarchive";
-
- String DOSSIER_ID_PARAM = "dossierId";
- String DOSSIER_ID_PATH_PARAM = "/{" + DOSSIER_ID_PARAM + "}";
- String DOSSIER_TEMPLATE_ID_PARAM = "dossierTemplateId";
- String DOSSIER_TEMPLATE_ID_PATH_PARAM = "/{" + DOSSIER_TEMPLATE_ID_PARAM + "}";
-
- String INCLUDE_DELETED_PARAM = "includeDeleted";
- String INCLUDE_ARCHIVED_PARAM = "includeArchived";
-
- String CHANGES_PATH = "/changes";
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.OK)
- @PostMapping(value = REST_PATH + CHANGES_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
- Set changesSince(@RequestBody JSONPrimitive since);
-
-
- @PostMapping(value = REST_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- Dossier addDossier(@RequestBody CreateOrUpdateDossierRequest dossierRequest);
-
-
- @PostMapping(value = REST_PATH + DOSSIER_ID_PATH_PARAM, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- Dossier updateDossier(@RequestBody CreateOrUpdateDossierRequest dossierRequest, @PathVariable(DOSSIER_ID_PARAM) String dossierId);
-
-
- @DeleteMapping(value = REST_PATH + DOSSIER_ID_PATH_PARAM)
- void delete(@PathVariable(DOSSIER_ID_PARAM) String dossierId);
-
-
- @GetMapping(value = REST_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
- List getAllDossiers(@RequestParam(name = INCLUDE_ARCHIVED_PARAM, defaultValue = "false", required = false) boolean includeArchived,
- @RequestParam(name = INCLUDE_DELETED_PARAM, defaultValue = "false", required = false) boolean includeDeleted);
-
-
- @GetMapping(value = REST_PATH + DOSSIER_TEMPLATE_PATH + DOSSIER_TEMPLATE_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
- List getAllDossiersForDossierTemplateId(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
- @RequestParam(name = INCLUDE_ARCHIVED_PARAM, defaultValue = "false", required = false) boolean includeArchived,
- @RequestParam(name = INCLUDE_DELETED_PARAM, defaultValue = "false", required = false) boolean includeDeleted);
-
-
- @PostMapping(value = INFO_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- DossierInformation getDossierInformation(@RequestBody List filteredDossierIds);
-
-
- @GetMapping(value = REST_PATH + DOSSIER_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
- Dossier getDossierById(@PathVariable(DOSSIER_ID_PARAM) String dossierId,
- @RequestParam(name = INCLUDE_ARCHIVED_PARAM, defaultValue = "false", required = false) boolean includeArchived,
- @RequestParam(name = INCLUDE_DELETED_PARAM, defaultValue = "false", required = false) boolean includeDeleted);
-
-
- @GetMapping(value = ARCHIVE_DOSSIERS_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
- List getArchivedDossiers();
-
-
- @GetMapping(value = ARCHIVE_DOSSIERS_PATH + DOSSIER_TEMPLATE_PATH + DOSSIER_TEMPLATE_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
- List getArchivedDossiersForDossierTemplateId(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId);
-
-
- @GetMapping(value = DELETED_DOSSIERS_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
- List getSoftDeletedDossiers();
-
-
- @DeleteMapping(value = DELETED_DOSSIERS_PATH + HARD_DELETE_PATH)
- void hardDeleteDossiers(@RequestBody Set dossierIds);
-
-
- @PostMapping(value = DELETED_DOSSIERS_PATH + UNDELETE_PATH)
- void undeleteDossiers(@RequestBody Set dossierIds);
-
-
- @PostMapping(value = ARCHIVE_DOSSIERS_PATH + ARCHIVE_PATH)
- void archiveDossiers(@RequestBody Set dossierIds);
-
-
- @PostMapping(value = ARCHIVE_DOSSIERS_PATH + UNARCHIVE_PATH)
- void unarchiveDossiers(@RequestBody Set dossierIds);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DossierStatsResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DossierStatsResource.java
deleted file mode 100644
index ab4d3de93..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DossierStatsResource.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.util.List;
-import java.util.Set;
-
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.DossierStats;
-
-public interface DossierStatsResource {
-
- String REST_PATH = "/dossier-stats";
-
- String DOSSIER_ID_PARAM = "dossierId";
- String DOSSIER_ID_PATH_PARAM = "/{" + DOSSIER_ID_PARAM + "}";
-
-
- @Deprecated
- @GetMapping(value = REST_PATH + DOSSIER_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
- DossierStats getDossierStats(@PathVariable(DOSSIER_ID_PARAM) String dossierId);
-
-
- @Deprecated
- @PostMapping(value = REST_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
- List getDossierStats(@RequestBody Set dossierIds);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DossierTemplateResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DossierTemplateResource.java
deleted file mode 100644
index 0c587ef70..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DossierTemplateResource.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.util.List;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.common.JSONPrimitive;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.CloneDossierTemplateRequest;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.CreateOrUpdateDossierTemplateRequest;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.DossierTemplate;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.importexport.ExportDownloadRequest;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.importexport.ImportDossierTemplateRequest;
-
-public interface DossierTemplateResource {
-
- String DOSSIER_TEMPLATE_PATH = "/dossier-template";
-
- String DOSSIER_TEMPLATE_ID = "dossierTemplateId";
- String DOSSIER_TEMPLATE_ID_PATH_VARIABLE = "/{" + DOSSIER_TEMPLATE_ID + "}";
-
- String USER_ID_PARAM = "userId";
-
- String CLONE_PATH = "/clone";
- String EXPORT_PATH = "/export";
- String IMPORT_PATH = "/import";
-
-
- @ResponseBody
- @ResponseStatus(HttpStatus.ACCEPTED)
- @PostMapping(value = DOSSIER_TEMPLATE_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
- DossierTemplate createOrUpdateDossierTemplate(@RequestBody CreateOrUpdateDossierTemplateRequest dossierTemplate);
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.OK)
- @GetMapping(value = DOSSIER_TEMPLATE_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
- List getAllDossierTemplates();
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.OK)
- @GetMapping(value = DOSSIER_TEMPLATE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- DossierTemplate getDossierTemplate(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId);
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.NO_CONTENT)
- @DeleteMapping(value = DOSSIER_TEMPLATE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE)
- void deleteDossierTemplate(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestParam(USER_ID_PARAM) String deletingUserId);
-
-
- @ResponseBody
- @ResponseStatus(HttpStatus.OK)
- @PostMapping(value = DOSSIER_TEMPLATE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + CLONE_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
- DossierTemplate cloneDossierTemplate(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody CloneDossierTemplateRequest cloneDossierTemplateRequest);
-
-
- @PostMapping(value = DOSSIER_TEMPLATE_PATH + EXPORT_PATH + "/prepare", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- JSONPrimitive prepareExportDownload(@RequestBody ExportDownloadRequest request);
-
-
- @PostMapping(value = DOSSIER_TEMPLATE_PATH + EXPORT_PATH + "/create", consumes = MediaType.APPLICATION_JSON_VALUE)
- void createExportDownload(@RequestParam String userId, @RequestParam String storageId);
-
-
- @ResponseBody
- @PostMapping(value = DOSSIER_TEMPLATE_PATH + IMPORT_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- DossierTemplate importDossierTemplate(@RequestBody ImportDossierTemplateRequest request);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DossierTemplateStatsResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DossierTemplateStatsResource.java
deleted file mode 100644
index 760eca233..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DossierTemplateStatsResource.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.util.List;
-import java.util.Set;
-
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.DossierTemplateDictionaryStats;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.DossierTemplateStats;
-
-public interface DossierTemplateStatsResource {
-
- String REST_PATH = "/dossier-template-stats";
-
- String DICTIONARY_PATH = "/dictionary";
-
- String DOSSIER_TEMPLATE_ID = "dossierTemplateId";
- String DOSSIER_TEMPLATE_ID_PATH_VARIABLE = "/{" + DOSSIER_TEMPLATE_ID + "}";
-
-
- @PostMapping(value = REST_PATH + DICTIONARY_PATH, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
- List getDossierTemplateDictionaryStats(@RequestBody Set dossierTemplateIds);
-
-
- @PostMapping(value = REST_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- DossierTemplateStats getDossierTemplateStats(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId);
-
-
- @PostMapping(value = REST_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
- List getDossierTemplateStats();
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DownloadResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DownloadResource.java
deleted file mode 100644
index 20f5c8447..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/DownloadResource.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.util.List;
-
-import com.iqser.red.service.persistence.service.v1.api.model.download.DownloadWithOptionRequest;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.common.JSONPrimitive;
-import com.iqser.red.service.persistence.service.v1.api.model.download.DownloadRequest;
-import com.iqser.red.service.persistence.service.v1.api.model.download.DownloadStatus;
-
-@ResponseStatus(value = HttpStatus.OK)
-public interface DownloadResource {
-
- String REST_PATH = "/download";
- String USER_ID = "userId";
-
-
- @PostMapping(value = REST_PATH + "/prepare", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- JSONPrimitive prepareDownload(@RequestBody DownloadRequest request);
-
- @PostMapping(value = REST_PATH + "/prepare-option", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- JSONPrimitive prepareDownload(@RequestBody DownloadWithOptionRequest request);
-
- @GetMapping(value = REST_PATH + "/status/{" + USER_ID + "}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- List getDownloadStatus(@PathVariable(USER_ID) String userId);
-
-
- @PostMapping(value = REST_PATH + "/setDownloaded", consumes = MediaType.APPLICATION_JSON_VALUE)
- void setDownloaded(@RequestBody JSONPrimitive setDownloadedRequest);
-
-
- @PostMapping(value = REST_PATH + "/delete", consumes = MediaType.APPLICATION_JSON_VALUE)
- void deleteDownloadStatus(@RequestBody JSONPrimitive setDownloadedRequest);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/FileAttributesConfigResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/FileAttributesConfigResource.java
deleted file mode 100644
index 6c1ac4203..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/FileAttributesConfigResource.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.util.List;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.FileAttributesGeneralConfiguration;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.FileAttributeConfig;
-
-public interface FileAttributesConfigResource {
-
- String FILE_ATTRIBUTES_PATH = "/fileAttributes";
- String FILE_ATTRIBUTE_PATH = "/fileAttribute";
- String BASE_CONFIG_PATH = "/baseConfig";
-
- String DELETE_PATH = "/delete";
-
- String DOSSIER_TEMPLATE_ID = "dossierTemplateId";
- String DOSSIER_TEMPLATE_ID_PATH_VARIABLE = "/{" + DOSSIER_TEMPLATE_ID + "}";
-
- String FILE_ATTRIBUTE_ID = "fileAttributeId";
- String FILE_ATTRIBUTE_ID_PATH_VARIABLE = "/{" + FILE_ATTRIBUTE_ID + "}";
-
- String UTF_ENCODING = "UTF-8";
- String ASCII_ENCODING = "ASCII";
- String ISO_ENCODING = "ISO";
-
- @ResponseBody
- @ResponseStatus(HttpStatus.OK)
- @PutMapping(value = FILE_ATTRIBUTES_PATH + BASE_CONFIG_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- FileAttributesGeneralConfiguration setFileAttributesGeneralConfig(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId,
- @RequestBody FileAttributesGeneralConfiguration fileAttributesConfig);
-
-
- @ResponseBody
- @ResponseStatus(HttpStatus.OK)
- @GetMapping(value = FILE_ATTRIBUTES_PATH + BASE_CONFIG_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- FileAttributesGeneralConfiguration getFileAttributesGeneralConfig(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId);
-
-
- @ResponseBody
- @ResponseStatus(HttpStatus.OK)
- @PostMapping(value = FILE_ATTRIBUTES_PATH + FILE_ATTRIBUTE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- FileAttributeConfig addOrUpdateFileAttributeConfig(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody FileAttributeConfig fileAttributeConfig);
-
-
- @ResponseBody
- @ResponseStatus(HttpStatus.OK)
- @PutMapping(value = FILE_ATTRIBUTES_PATH + FILE_ATTRIBUTE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- List setFileAttributesConfig(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody List fileAttributesConfig);
-
-
- @ResponseStatus(HttpStatus.NO_CONTENT)
- @DeleteMapping(value = FILE_ATTRIBUTES_PATH + FILE_ATTRIBUTE_PATH + FILE_ATTRIBUTE_ID_PATH_VARIABLE)
- void deleteFileAttributeConfigs(@PathVariable(FILE_ATTRIBUTE_ID) String fileAttributeId);
-
-
- @ResponseStatus(HttpStatus.NO_CONTENT)
- @PostMapping(value = FILE_ATTRIBUTES_PATH + FILE_ATTRIBUTE_PATH + DELETE_PATH)
- void deleteFileAttributeConfigs(@RequestBody List fileAttributeIds);
-
-
- @ResponseBody
- @ResponseStatus(HttpStatus.OK)
- @GetMapping(value = FILE_ATTRIBUTES_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- List getFileAttributeConfigs(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/FileAttributesResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/FileAttributesResource.java
deleted file mode 100644
index d2d04ed38..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/FileAttributesResource.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.util.Map;
-
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.ImportCsvRequest;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.ImportCsvResponse;
-
-public interface FileAttributesResource {
-
- String REST_PATH = "/fileAttributes";
- String CSV_IMPORT_PATH = "/csvImport";
- String SET_PATH = "/set";
-
- String DOSSIER_ID_PARAM = "dossierId";
- String DOSSIER_ID_PATH_PARAM = "/{" + DOSSIER_ID_PARAM + "}";
-
- String FILE_ID = "fileId";
- String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
-
-
- @PostMapping(value = REST_PATH + CSV_IMPORT_PATH + DOSSIER_ID_PATH_PARAM, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- ImportCsvResponse importCsv(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @RequestBody ImportCsvRequest importCsvRequest);
-
-
- @PostMapping(value = REST_PATH + SET_PATH + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
- void setFileAttributes(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody Map fileAttributes);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/LicenseReportResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/LicenseReportResource.java
deleted file mode 100644
index c3b19a6b7..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/LicenseReportResource.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.license.LicenseReport;
-import com.iqser.red.service.persistence.service.v1.api.model.license.LicenseReportRequest;
-
-public interface LicenseReportResource {
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.OK)
- @PostMapping(value = "/report/license", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- LicenseReport getLicenseReport(@RequestBody LicenseReportRequest licenseReportRequest,
- @RequestParam(value = "offset", defaultValue = "0") int offset,
- @RequestParam(value = "limit", defaultValue = "20") int limit);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/ManualRedactionResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/ManualRedactionResource.java
deleted file mode 100644
index a25f4af13..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/ManualRedactionResource.java
+++ /dev/null
@@ -1,179 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.util.List;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.annotations.AddRedactionRequest;
-import com.iqser.red.service.persistence.service.v1.api.model.annotations.Comment;
-import com.iqser.red.service.persistence.service.v1.api.model.annotations.CommentRequest;
-import com.iqser.red.service.persistence.service.v1.api.model.annotations.ForceRedactionRequest;
-import com.iqser.red.service.persistence.service.v1.api.model.annotations.ImageRecategorizationRequest;
-import com.iqser.red.service.persistence.service.v1.api.model.annotations.LegalBasisChangeRequest;
-import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualAddResponse;
-import com.iqser.red.service.persistence.service.v1.api.model.annotations.ManualRedactions;
-import com.iqser.red.service.persistence.service.v1.api.model.annotations.RemoveRedactionRequest;
-import com.iqser.red.service.persistence.service.v1.api.model.annotations.ResizeRedactionRequest;
-import com.iqser.red.service.persistence.service.v1.api.model.annotations.UpdateRedactionRequest;
-import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.IdRemoval;
-import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.ManualForceRedaction;
-import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.ManualImageRecategorization;
-import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.ManualLegalBasisChange;
-import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.ManualRedactionEntry;
-import com.iqser.red.service.persistence.service.v1.api.model.annotations.entitymapped.ManualResizeRedaction;
-
-@ResponseStatus(value = HttpStatus.OK)
-public interface ManualRedactionResource {
-
- String MANUAL_REDACTION_REST_PATH = "/manualRedaction";
-
- String DOSSIER_ID = "dossierId";
- String DOSSIER_ID_PATH_PARAM = "/{" + DOSSIER_ID + "}";
-
- String FILE_ID = "fileId";
- String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
-
- String ANNOTATION_ID = "annotationId";
- String ANNOTATION_ID_PATH_VARIABLE = "/{" + ANNOTATION_ID + "}";
-
- String COMMENT_ID = "commentId";
- String COMMENT_ID_PATH_VARIABLE = "/{" + COMMENT_ID + "}";
-
- String DELETE_PATH = "/delete";
-
-
- @PostMapping(value = MANUAL_REDACTION_REST_PATH + "/add" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- List addAddRedaction(@PathVariable(DOSSIER_ID) String dossierId,
- @PathVariable(FILE_ID) String fileId,
- @RequestBody List addRedactionRequests);
-
-
- @PostMapping(value = MANUAL_REDACTION_REST_PATH + "/remove" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- List addRemoveRedaction(@PathVariable(DOSSIER_ID) String dossierId,
- @PathVariable(FILE_ID) String fileId,
- @RequestBody List removeRedactionRequest);
-
-
- @PostMapping(value = MANUAL_REDACTION_REST_PATH + "/force" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- List addForceRedaction(@PathVariable(DOSSIER_ID) String dossierId,
- @PathVariable(FILE_ID) String fileId,
- @RequestBody List forceRedactionRequests);
-
-
- @PostMapping(value = MANUAL_REDACTION_REST_PATH + "/legalBasis" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- List addLegalBasisChange(@PathVariable(DOSSIER_ID) String dossierId,
- @PathVariable(FILE_ID) String fileId,
- @RequestBody List legalBasisChangeRequests);
-
-
- @PostMapping(value = MANUAL_REDACTION_REST_PATH + "/recategorize" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- List addImageRecategorization(@PathVariable(DOSSIER_ID) String dossierId,
- @PathVariable(FILE_ID) String fileId,
- @RequestBody List imageRecategorizationRequests);
-
-
- @PostMapping(value = MANUAL_REDACTION_REST_PATH + "/comment" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE + ANNOTATION_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- Comment addComment(@PathVariable(DOSSIER_ID) String dossierId,
- @PathVariable(FILE_ID) String fileId,
- @PathVariable(ANNOTATION_ID) String annotationId,
- @RequestBody CommentRequest comment);
-
-
- @PostMapping(value = MANUAL_REDACTION_REST_PATH + "/resize" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- List addResizeRedaction(@PathVariable(DOSSIER_ID) String dossierId,
- @PathVariable(FILE_ID) String fileId,
- @RequestBody List resizeRedactionRequests);
-
-
- @GetMapping(value = MANUAL_REDACTION_REST_PATH + "/add" + FILE_ID_PATH_VARIABLE + ANNOTATION_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- ManualRedactionEntry getAddRedaction(@PathVariable(FILE_ID) String fileId, @PathVariable(ANNOTATION_ID) String annotationId);
-
-
- @GetMapping(value = MANUAL_REDACTION_REST_PATH + "/remove" + FILE_ID_PATH_VARIABLE + ANNOTATION_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- IdRemoval getRemoveRedaction(@PathVariable(FILE_ID) String fileId, @PathVariable(ANNOTATION_ID) String annotationId);
-
-
- @GetMapping(value = MANUAL_REDACTION_REST_PATH + "/force" + FILE_ID_PATH_VARIABLE + ANNOTATION_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- ManualForceRedaction getForceRedaction(@PathVariable(FILE_ID) String fileId, @PathVariable(ANNOTATION_ID) String annotationId);
-
-
- @GetMapping(value = MANUAL_REDACTION_REST_PATH + "/legalBasis" + FILE_ID_PATH_VARIABLE + ANNOTATION_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- ManualLegalBasisChange getLegalBasisChange(@PathVariable(FILE_ID) String fileId, @PathVariable(ANNOTATION_ID) String annotationId);
-
-
- @GetMapping(value = MANUAL_REDACTION_REST_PATH + "/recategorize" + FILE_ID_PATH_VARIABLE + ANNOTATION_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- ManualImageRecategorization getImageRecategorization(@PathVariable(FILE_ID) String fileId, @PathVariable(ANNOTATION_ID) String annotationId);
-
-
- @GetMapping(value = MANUAL_REDACTION_REST_PATH + "/comment" + COMMENT_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- Comment getComment(@PathVariable(COMMENT_ID) long commentId);
-
-
- @GetMapping(value = MANUAL_REDACTION_REST_PATH + "/resize" + FILE_ID_PATH_VARIABLE + ANNOTATION_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- ManualResizeRedaction getResizeRedaction(@PathVariable(FILE_ID) String fileId, @PathVariable(ANNOTATION_ID) String annotationId);
-
-
- @PostMapping(MANUAL_REDACTION_REST_PATH + DELETE_PATH + "/add" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE)
- void deleteAddRedaction(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody List annotationIds);
-
-
- @PostMapping(MANUAL_REDACTION_REST_PATH + DELETE_PATH + "/remove" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE)
- void deleteRemoveRedaction(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody List annotationIds);
-
-
- @PostMapping(MANUAL_REDACTION_REST_PATH + DELETE_PATH + "/force" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE)
- void deleteForceRedaction(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody List annotationIds);
-
-
- @PostMapping(MANUAL_REDACTION_REST_PATH + DELETE_PATH + "/legalBasis" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE)
- void deleteLegalBasisChange(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody List annotationIds);
-
-
- @PostMapping(MANUAL_REDACTION_REST_PATH + DELETE_PATH + "/recategorize" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE)
- void deleteImageRecategorization(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody List annotationIds);
-
-
- @PostMapping(MANUAL_REDACTION_REST_PATH + DELETE_PATH + "/comment" + FILE_ID_PATH_VARIABLE)
- void deleteComment(@PathVariable(FILE_ID) String fileId, @RequestBody List commentIds);
-
-
- @PostMapping(MANUAL_REDACTION_REST_PATH + DELETE_PATH + "/resize" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE)
- void deleteResizeRedaction(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody List annotationIds);
-
-
- @PostMapping(value = MANUAL_REDACTION_REST_PATH + "/status/add" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
- void updateAddRedactionStatus(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody UpdateRedactionRequest updateStatusRequest);
-
-
- @PostMapping(value = MANUAL_REDACTION_REST_PATH + "/status/remove" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
- void updateRemoveRedactionStatus(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody UpdateRedactionRequest updateStatusRequest);
-
-
- @PostMapping(value = MANUAL_REDACTION_REST_PATH + "/status/force" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
- void updateForceRedactionStatus(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody UpdateRedactionRequest updateStatusRequest);
-
-
- @PostMapping(value = MANUAL_REDACTION_REST_PATH + "/status/legalBasis" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
- void updateLegalBasisChangeStatus(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody UpdateRedactionRequest updateStatusRequest);
-
-
- @PostMapping(value = MANUAL_REDACTION_REST_PATH + "/status/recategorize" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
- void updateImageRecategorizationStatus(@PathVariable(DOSSIER_ID) String dossierId,
- @PathVariable(FILE_ID) String fileId,
- @RequestBody UpdateRedactionRequest updateStatusRequest);
-
-
- @PostMapping(value = MANUAL_REDACTION_REST_PATH + "/status/resize" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
- void updateResizeRedactionStatus(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody UpdateRedactionRequest updateStatusRequest);
-
-
- @GetMapping(value = MANUAL_REDACTION_REST_PATH + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- ManualRedactions getManualRedactions(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/NotificationPreferencesResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/NotificationPreferencesResource.java
deleted file mode 100644
index 06491fcf1..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/NotificationPreferencesResource.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.notification.NotificationPreferences;
-
-public interface NotificationPreferencesResource {
-
- String REST_PATH = "/notification-preferences";
-
- String USER_ID_PARAM = "userId";
- String USER_ID_PATH_PARAM = "/{" + USER_ID_PARAM + "}";
-
-
- @PostMapping(value = REST_PATH + USER_ID_PATH_PARAM, consumes = MediaType.APPLICATION_JSON_VALUE)
- void setNotificationPreferences(@PathVariable(USER_ID_PARAM) String userId, @RequestBody NotificationPreferences notificationRequest);
-
-
- @ResponseStatus(value = HttpStatus.OK)
- @GetMapping(value = REST_PATH + USER_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
- NotificationPreferences getNotificationPreferences(@PathVariable(USER_ID_PARAM) String userId);
-
-
- @DeleteMapping(value = REST_PATH + USER_ID_PATH_PARAM, consumes = MediaType.APPLICATION_JSON_VALUE)
- void deleteNotificationPreferences(@PathVariable(USER_ID_PARAM) String userId);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/NotificationResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/NotificationResource.java
deleted file mode 100644
index 8908cf013..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/NotificationResource.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.time.OffsetDateTime;
-import java.util.List;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.audit.AddNotificationRequest;
-import com.iqser.red.service.persistence.service.v1.api.model.common.JSONPrimitive;
-import com.iqser.red.service.persistence.service.v1.api.model.notification.Notification;
-
-@ResponseStatus(value = HttpStatus.OK)
-public interface NotificationResource {
-
- String NOTIFICATION_PATH = "/notification";
- String TOGGLE_SEEN_PATH = "/toggle-seen";
- String TOGGLE_READ_PATH = "/toggle-read";
- String CHANGES_PATH = "/has-changes";
-
- String USER_ID_PARAM = "userId";
- String USER_ID_PATH_PARAM = "/{" + USER_ID_PARAM + "}";
-
- String INCLUDE_SEEN_PARAM = "includeSeen";
- String SET_SEEN_PARAM = "setSeen";
- String SET_READ_PARAM = "setRead";
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.OK)
- @PostMapping(value = NOTIFICATION_PATH + CHANGES_PATH + USER_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
- JSONPrimitive hasNewNotificationsSince(@PathVariable(USER_ID_PARAM) String userId, @RequestBody JSONPrimitive since);
-
-
- @PostMapping(value = NOTIFICATION_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
- void addNotification(@RequestBody AddNotificationRequest addNotificationRequest);
-
-
- @PostMapping(value = NOTIFICATION_PATH + TOGGLE_SEEN_PATH + USER_ID_PATH_PARAM, consumes = MediaType.APPLICATION_JSON_VALUE)
- void toggleSeen(@PathVariable(USER_ID_PARAM) String userId, @RequestBody List notificationIds, @RequestParam(SET_SEEN_PARAM) boolean setSeen);
-
-
- @PostMapping(value = NOTIFICATION_PATH + TOGGLE_READ_PATH + USER_ID_PATH_PARAM, consumes = MediaType.APPLICATION_JSON_VALUE)
- void toggleRead(@PathVariable(USER_ID_PARAM) String userId, @RequestBody List notificationIds, @RequestParam(SET_READ_PARAM) boolean setRead);
-
-
- @DeleteMapping(value = NOTIFICATION_PATH + USER_ID_PATH_PARAM, consumes = MediaType.APPLICATION_JSON_VALUE)
- void softDelete(@PathVariable(USER_ID_PARAM) String userId, @RequestBody List notificationIds);
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.OK)
- @GetMapping(value = NOTIFICATION_PATH + USER_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
- List getNotifications(@PathVariable(USER_ID_PARAM) String userId, @RequestParam(INCLUDE_SEEN_PARAM) boolean includeSeen);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/ReanalysisResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/ReanalysisResource.java
deleted file mode 100644
index 8e8c3a188..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/ReanalysisResource.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.util.Set;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.pdftron.redaction.v1.api.model.ByteContentDocument;
-import com.iqser.red.service.pdftron.redaction.v1.api.model.highlights.TextHighlightConversionRequest;
-import com.iqser.red.service.persistence.service.v1.api.model.annotations.DeleteImportedRedactionsRequest;
-
-@ResponseStatus(value = HttpStatus.NO_CONTENT)
-public interface ReanalysisResource {
-
- String REANALYZE_PATH = "/reanalyze";
- String IMPORT_REDACTIONS_PATH = "/import-redactions";
- String CONVERT_TEXT_HIGHLIGHTS_PATH = "/convert-texthighlights";
- String OCR_REANALYZE_PATH = "/ocr/reanalyze";
- String REINDEX_PATH = "/reindex";
- String DOSSIER_ID_PARAM = "dossierId";
- String DOSSIER_ID_PATH_PARAM = "/{" + DOSSIER_ID_PARAM + "}";
- String FILE_ID_PARAM = "fileId";
- String FILE_ID_PATH_PARAM = "/{" + FILE_ID_PARAM + "}";
- String BULK_REST_PATH = "/bulk";
- String FALSE = "false";
-
-
- @PostMapping(value = REANALYZE_PATH + DOSSIER_ID_PATH_PARAM)
- void reanalyzeDossier(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @RequestParam(value = "force", required = false, defaultValue = FALSE) boolean force);
-
-
- @PostMapping(value = REANALYZE_PATH + DOSSIER_ID_PATH_PARAM + BULK_REST_PATH)
- void reanalyzeFiles(@PathVariable(DOSSIER_ID_PARAM) String dossierId,
- @RequestBody Set fileIds,
- @RequestParam(value = "force", required = false, defaultValue = FALSE) boolean force);
-
-
- @PostMapping(value = OCR_REANALYZE_PATH + DOSSIER_ID_PATH_PARAM)
- void ocrDossier(@PathVariable(DOSSIER_ID_PARAM) String dossierId);
-
-
- @PostMapping(value = OCR_REANALYZE_PATH + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_PARAM)
- void ocrFile(@PathVariable(DOSSIER_ID_PARAM) String dossierId,
- @PathVariable(FILE_ID_PARAM) String fileId,
- @RequestParam(value = "force", required = false, defaultValue = FALSE) boolean force);
-
-
- @PostMapping(value = OCR_REANALYZE_PATH + DOSSIER_ID_PATH_PARAM + BULK_REST_PATH)
- void ocrFiles(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @RequestBody Set fileIds);
-
-
- @PostMapping(value = REINDEX_PATH)
- void reindex(@RequestParam(value = DOSSIER_ID_PARAM, required = false) String dossierId,
- @RequestParam(value = "dropIndex", required = false, defaultValue = FALSE) boolean dropIndex,
- @RequestBody Set fileIds);
-
-
- @PostMapping(value = IMPORT_REDACTIONS_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
- void importRedactions(@RequestBody ByteContentDocument documentRequest);
-
-
- @PostMapping(value = IMPORT_REDACTIONS_PATH + "/delete", consumes = MediaType.APPLICATION_JSON_VALUE)
- void deleteImportedRedactions(@RequestBody DeleteImportedRedactionsRequest deleteImportedRedactionsRequest);
-
-
- @PostMapping(value = CONVERT_TEXT_HIGHLIGHTS_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
- void convertTextHighlights(@RequestBody TextHighlightConversionRequest textHighlightRequest);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/RedactionLogResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/RedactionLogResource.java
deleted file mode 100644
index 14b2b350a..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/RedactionLogResource.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.util.List;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.redactionlog.FilteredRedactionLogRequest;
-import com.iqser.red.service.redaction.v1.model.RedactionLog;
-import com.iqser.red.service.redaction.v1.model.SectionGrid;
-
-@ResponseStatus(value = HttpStatus.OK)
-public interface RedactionLogResource {
-
- String REDACTION_LOG_PATH = "/redactionLog";
- String SECTION_GRID_PATH = "/sectionGrid";
-
- String FILE_ID = "fileId";
- String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
-
- String DOSSIER_ID_PARAM = "dossierId";
- String DOSSIER_ID_PATH_PARAM = "/{" + DOSSIER_ID_PARAM + "}";
-
-
- @GetMapping(value = REDACTION_LOG_PATH + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- RedactionLog getRedactionLog(@PathVariable(DOSSIER_ID_PARAM) String dossierId,
- @PathVariable(FILE_ID) String fileId,
- @RequestParam(value = "excludedType", required = false) List excludedTypes,
- @RequestParam(value = "withManualRedactions", required = false, defaultValue = "true") boolean withManualRedactions,
- @RequestParam(value = "includeFalsePositives", required = false, defaultValue = "false") boolean includeFalsePositives);
-
-
- @GetMapping(value = SECTION_GRID_PATH + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- SectionGrid getSectionGrid(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(FILE_ID) String fileId);
-
-
- @PostMapping(value = REDACTION_LOG_PATH + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE + "/filtered", produces = MediaType.APPLICATION_JSON_VALUE)
- RedactionLog getFilteredRedactionLog(@PathVariable(DOSSIER_ID_PARAM) String dossierId,
- @PathVariable(FILE_ID) String fileId,
- @RequestBody FilteredRedactionLogRequest filteredRedactionLogRequest);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/ReportTemplateResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/ReportTemplateResource.java
deleted file mode 100644
index 8a0401e47..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/ReportTemplateResource.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.util.List;
-
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.ReportTemplate;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.ReportTemplateDownload;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.ReportTemplateUpdateRequest;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.ReportTemplateUploadRequest;
-
-public interface ReportTemplateResource {
-
- String REPORT_TEMPLATE_UPLOAD_PATH = "/templateUpload";
-
- String DOSSIER_TEMPLATE_ID = "dossierTemplateId";
- String DOSSIER_TEMPLATE_ID_PATH_VARIABLE = "/{" + DOSSIER_TEMPLATE_ID + "}";
-
- String TEMPLATE_ID = "templateId";
- String TEMPLATE_ID_PATH_VARIABLE = "/{" + TEMPLATE_ID + "}";
-
- String DOWNLOAD_PATH = "download";
- String UPDATE_PATH = "update";
-
-
- @PostMapping(value = REPORT_TEMPLATE_UPLOAD_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- ReportTemplate uploadTemplate(@RequestBody ReportTemplateUploadRequest reportTemplateUploadRequest);
-
-
- @GetMapping(value = REPORT_TEMPLATE_UPLOAD_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- List getAvailableReportTemplates(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId);
-
-
- @GetMapping(value = REPORT_TEMPLATE_UPLOAD_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + TEMPLATE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- ReportTemplate getReportTemplate(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @PathVariable(TEMPLATE_ID) String templateId);
-
-
- @GetMapping(value = REPORT_TEMPLATE_UPLOAD_PATH + DOWNLOAD_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + TEMPLATE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- ReportTemplateDownload downloadReportTemplate(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @PathVariable(TEMPLATE_ID) String templateId);
-
-
- @DeleteMapping(value = REPORT_TEMPLATE_UPLOAD_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + TEMPLATE_ID_PATH_VARIABLE)
- void deleteTemplate(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @PathVariable(TEMPLATE_ID) String templateId);
-
-
- @PutMapping(value = REPORT_TEMPLATE_UPLOAD_PATH + UPDATE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + TEMPLATE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
- void updateTemplate(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId,
- @PathVariable(TEMPLATE_ID) String templateId,
- @RequestBody ReportTemplateUpdateRequest reportTemplateUpdateRequest);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/SMTPConfigurationResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/SMTPConfigurationResource.java
deleted file mode 100644
index 842602eb9..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/SMTPConfigurationResource.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration.SMTPConfiguration;
-
-public interface SMTPConfigurationResource {
-
- String SMTP_PATH = "/smtp";
-
- String TEST_PATH = "/test";
-
- String TEST_EMAIL = "testEmail";
-
- String MASK_PASSWORD = "maskPassword";
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.OK)
- @GetMapping(value = SMTP_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
- SMTPConfiguration getCurrentSMTPConfiguration(@RequestParam(value = MASK_PASSWORD, required = false, defaultValue = "true") boolean maskPassword);
-
-
- @ResponseStatus(value = HttpStatus.NO_CONTENT)
- @PostMapping(value = SMTP_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
- void updateSMTPConfiguration(@RequestBody SMTPConfiguration smtpConfigurationModel);
-
-
- @ResponseStatus(value = HttpStatus.OK)
- @PostMapping(value = SMTP_PATH + TEST_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
- void testSMTPConfiguration(@RequestParam(value = TEST_EMAIL, required = false) String testEmail, @RequestBody SMTPConfiguration smtpConfigurationModel);
-
-
- @ResponseStatus(value = HttpStatus.NO_CONTENT)
- @DeleteMapping(value = SMTP_PATH)
- void clearSMTPConfiguration();
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/StatusResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/StatusResource.java
deleted file mode 100644
index 96a55e2de..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/StatusResource.java
+++ /dev/null
@@ -1,129 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.time.OffsetDateTime;
-import java.util.List;
-import java.util.Set;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.common.JSONPrimitive;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.FileModel;
-
-public interface StatusResource {
-
- String STATUS_PATH = "/status";
- String DELETED_PATH = "/softdeleted";
- String ALL_PATH = "/all";
- String DOSSIER_ID_PARAM = "dossierId";
- String DOSSIER_ID_PATH_PARAM = "/{" + DOSSIER_ID_PARAM + "}";
-
- String FILE_ID = "fileId";
- String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
-
- String CHANGES_PATH = "/has-changes";
-
- String EXCLUDED_STATUS_PARAM = "excluded";
-
- String EXCLUDED_FROM_AUTOMATIC_ANALYSIS_PARAM = "excludedFromAutomaticAnalysis";
-
- String APPROVER_ID_REQUEST_PARAM = "approverId";
- String ASSIGNEE_ID_REQUEST_PARAM = "assigneeId";
- String USER_ID_REQUEST_PARAM = "userId";
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.OK)
- @PostMapping(value = STATUS_PATH + DOSSIER_ID_PATH_PARAM + CHANGES_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
- JSONPrimitive hasChangesSince(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @RequestBody JSONPrimitive since);
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.OK)
- @GetMapping(value = STATUS_PATH + DOSSIER_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
- List getDossierStatus(@PathVariable(DOSSIER_ID_PARAM) String dossierId);
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.OK)
- @GetMapping(value = STATUS_PATH + DOSSIER_ID_PATH_PARAM + ALL_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
- List getAllDossierStatus(@PathVariable(DOSSIER_ID_PARAM) String dossierId);
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.OK)
- @GetMapping(value = STATUS_PATH + DELETED_PATH + DOSSIER_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
- List getSoftDeletedDossierStatus(@PathVariable(DOSSIER_ID_PARAM) String dossierId);
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.OK)
- @GetMapping(value = STATUS_PATH + DELETED_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
- List getSoftDeletedForDossierList(@RequestBody List dossierIds);
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.OK)
- @GetMapping(value = STATUS_PATH + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- FileModel getFileStatus(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(FILE_ID) String fileId);
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.ACCEPTED)
- @PostMapping(value = STATUS_PATH + "/update-modification-date" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE)
- void updateFileModificationDate(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(FILE_ID) String fileId);
-
-
- @PostMapping(value = STATUS_PATH + "/assignee" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
- void setCurrentFileAssignee(@PathVariable(DOSSIER_ID_PARAM) String dossierId,
- @PathVariable(FILE_ID) String fileId,
- @RequestParam(value = ASSIGNEE_ID_REQUEST_PARAM, required = false) String assigneeId);
-
-
- @PostMapping(value = STATUS_PATH + "/underreview" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE)
- void setStatusUnderReview(@PathVariable(DOSSIER_ID_PARAM) String dossierId,
- @PathVariable(FILE_ID) String fileId,
- @RequestParam(value = USER_ID_REQUEST_PARAM, required = false) String userId);
-
-
- @PostMapping(value = STATUS_PATH + "/underapproval" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE)
- void setStatusUnderApproval(@PathVariable(DOSSIER_ID_PARAM) String dossierId,
- @PathVariable(FILE_ID) String fileId,
- @RequestParam(value = APPROVER_ID_REQUEST_PARAM, required = false) String approverId);
-
-
- @PostMapping(value = STATUS_PATH + "/approved" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE)
- void setStatusApproved(@PathVariable(DOSSIER_ID_PARAM) String dossierId,
- @PathVariable(FILE_ID) String fileId,
- @RequestParam(value = APPROVER_ID_REQUEST_PARAM, required = false) String approverId);
-
-
- @PostMapping(value = STATUS_PATH + "/new" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE)
- void setStatusNew(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(FILE_ID) String fileId);
-
-
- @PostMapping(value = STATUS_PATH + "/toggle-analysis" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE)
- void toggleExclusion(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestParam(EXCLUDED_STATUS_PARAM) boolean excluded);
-
-
- @PostMapping(value = STATUS_PATH + "/toggle-automatic-analysis" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE)
- void toggleAutomaticAnalysis(@PathVariable(DOSSIER_ID_PARAM) String dossierId,
- @PathVariable(FILE_ID) String fileId,
- @RequestParam(EXCLUDED_STATUS_PARAM) boolean excludedFromAutomaticAnalysis);
-
-
- @PostMapping(value = STATUS_PATH + "/exclude-pages" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE)
- void excludePages(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody Set pages);
-
-
- @PostMapping(value = STATUS_PATH + "/include-pages" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE)
- void includePages(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody Set pages);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/TenantsResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/TenantsResource.java
deleted file mode 100644
index b66581c84..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/TenantsResource.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.util.List;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.common.JSONPrimitive;
-import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.TenantRequest;
-import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.TenantResponse;
-
-@ResponseStatus(value = HttpStatus.OK)
-public interface TenantsResource {
-
- String TENANT_ID_PARAM = "tenantId";
- String TENANT_ID_PATH_PARAM = "/{" + TENANT_ID_PARAM + "}";
-
-
- @PostMapping(value = "/tenants", consumes = MediaType.APPLICATION_JSON_VALUE)
- void createTenant(@RequestBody TenantRequest tenantRequest);
-
-
- @GetMapping(value = "/tenants", produces = MediaType.APPLICATION_JSON_VALUE)
- List getTenants();
-
-
- @GetMapping(value = "/tenants" + TENANT_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
- TenantResponse getTenant(@PathVariable(TENANT_ID_PARAM) String tenantId);
-
-
- @GetMapping(value = "/deploymentKey" + TENANT_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
- JSONPrimitive getDeploymentKey(@PathVariable(TENANT_ID_PARAM) String tenantId);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/UploadResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/UploadResource.java
deleted file mode 100644
index 9cb12d85f..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/UploadResource.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.util.Set;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.common.JSONPrimitive;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file.AddFileRequest;
-
-@ResponseStatus(value = HttpStatus.OK)
-public interface UploadResource {
-
- String REST_PATH = "/file";
-
- String DELETE_PATH = REST_PATH + "/delete";
- String UPLOAD_PATH = REST_PATH + "/upload";
- String HARD_DELETE_PATH = REST_PATH + "/hardDelete";
- String UNDELETE_PATH = REST_PATH + "/undelete";
-
- String FILE_ID = "fileId";
- String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
-
- String DOSSIER_ID_PARAM = "dossierId";
- String DOSSIER_ID_PATH_PARAM = "/{" + DOSSIER_ID_PARAM + "}";
-
-
- @PostMapping(value = UPLOAD_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- JSONPrimitive upload(@RequestBody AddFileRequest request,
- @RequestParam(value = "keepManualRedactions", required = false, defaultValue = "false") boolean keepManualRedactions);
-
-
- @DeleteMapping(value = DELETE_PATH + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE)
- void deleteFile(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(FILE_ID) String fileId);
-
-
- @DeleteMapping(value = HARD_DELETE_PATH + DOSSIER_ID_PATH_PARAM)
- void hardDeleteFiles(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @RequestBody Set fileIds);
-
-
- @PostMapping(value = UNDELETE_PATH + DOSSIER_ID_PATH_PARAM)
- void undeleteFiles(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @RequestBody Set fileIds);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/UserPreferenceResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/UserPreferenceResource.java
deleted file mode 100644
index 153a8332f..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/UserPreferenceResource.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.fasterxml.jackson.databind.JsonNode;
-
-public interface UserPreferenceResource {
-
- String PREFERENCES_PATH = "/preferences";
-
- String KEY_PARAMETER_NAME = "key";
- String KEY_PATH_VARIABLE = "/{" + KEY_PARAMETER_NAME + "}";
-
- String USER_ID_PARAMETER_NAME = "userId";
- String USER_ID_PATH_VARIABLE = "/{" + USER_ID_PARAMETER_NAME + "}";
-
-
- @ResponseStatus(HttpStatus.NO_CONTENT)
- @PutMapping(value = PREFERENCES_PATH + USER_ID_PATH_VARIABLE + KEY_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
- void savePreferences(@PathVariable(USER_ID_PARAMETER_NAME) String userId, @PathVariable(KEY_PARAMETER_NAME) String key, @RequestBody JsonNode jsonNode);
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.OK)
- @GetMapping(value = PREFERENCES_PATH + USER_ID_PATH_VARIABLE + KEY_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- JsonNode getPreferences(@PathVariable(USER_ID_PARAMETER_NAME) String userId, @PathVariable(KEY_PARAMETER_NAME) String key);
-
-
- @ResponseBody
- @ResponseStatus(value = HttpStatus.NO_CONTENT)
- @DeleteMapping(value = PREFERENCES_PATH + USER_ID_PATH_VARIABLE + KEY_PATH_VARIABLE)
- void deletePreferences(@PathVariable(USER_ID_PARAMETER_NAME) String userId, @PathVariable(KEY_PARAMETER_NAME) String key);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/ViewedPagesResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/ViewedPagesResource.java
deleted file mode 100644
index 14aa70e50..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/ViewedPagesResource.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.util.List;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.annotations.ViewedPage;
-
-@ResponseStatus(value = HttpStatus.OK)
-public interface ViewedPagesResource {
-
- String REST_PATH = "/viewedPages";
-
- String FILE_ID = "fileId";
- String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
-
- String ROLE = "role";
- String ROLE_PATH_VARIABLE = "/{" + ROLE + "}";
-
-
- @PostMapping(value = REST_PATH + FILE_ID_PATH_VARIABLE + ROLE_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
- void addPage(@PathVariable(FILE_ID) String fileId, @PathVariable(ROLE) String role, @RequestBody Integer page);
-
-
- @DeleteMapping(value = REST_PATH + FILE_ID_PATH_VARIABLE + ROLE_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
- void removePage(@PathVariable(FILE_ID) String fileId, @PathVariable(ROLE) String role, @RequestBody Integer page);
-
-
- @GetMapping(value = REST_PATH + FILE_ID_PATH_VARIABLE + ROLE_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- List getViewedPages(@PathVariable(FILE_ID) String fileId, @PathVariable(ROLE) String role);
-
-}
diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/WatermarkResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/WatermarkResource.java
deleted file mode 100644
index 378bd0791..000000000
--- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/WatermarkResource.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.iqser.red.service.persistence.service.v1.api.resources;
-
-import java.util.List;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseStatus;
-
-import com.iqser.red.service.persistence.service.v1.api.model.common.JSONPrimitive;
-import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration.Watermark;
-
-@ResponseStatus(value = HttpStatus.OK)
-public interface WatermarkResource {
-
- String WATERMARK_PATH = "/watermark";
- String CHECK_USED_REST_PATH = "/used";
- String WATERMARK_ID_PARAMETER_NAME = "watermarkId";
- String WATERMARK_ID_PATH_VARIABLE = "/{" + WATERMARK_ID_PARAMETER_NAME + "}";
-
- String DOSSIER_TEMPLATE_ID_PARAMETER_NAME = "dossierTemplateId";
-
-
- @ResponseStatus(HttpStatus.OK)
- @PostMapping(value = WATERMARK_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- Watermark createOrUpdateWatermark(@RequestBody Watermark watermark);
-
-
- @GetMapping(value = WATERMARK_PATH + WATERMARK_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
- Watermark getWatermark(@PathVariable(WATERMARK_ID_PARAMETER_NAME) long watermarkId);
-
-
- @GetMapping(value = WATERMARK_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
- List getWatermarksForDossierTemplateId(@RequestParam(DOSSIER_TEMPLATE_ID_PARAMETER_NAME) String dossierTemplateId);
-
-
- @ResponseStatus(HttpStatus.NO_CONTENT)
- @DeleteMapping(value = WATERMARK_PATH + WATERMARK_ID_PATH_VARIABLE)
- void deleteWatermark(@PathVariable(WATERMARK_ID_PARAMETER_NAME) long watermarkId);
-
-
- @ResponseStatus(value = HttpStatus.OK)
- @GetMapping(value = WATERMARK_PATH + CHECK_USED_REST_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
- JSONPrimitive isWatermarkUsed(@RequestParam(WATERMARK_ID_PARAMETER_NAME) long watermarkId);
-
-}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/pom.xml b/persistence-service-v1/persistence-service-external-api-impl-v1/pom.xml
new file mode 100644
index 000000000..bee778fff
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/pom.xml
@@ -0,0 +1,33 @@
+
+
+
+
+ persistence-service-v1
+ com.iqser.red.service
+ 1.0-SNAPSHOT
+
+
+ 4.0.0
+
+ persistence-service-external-api-impl-v1
+
+ 1.7.30
+ UTF-8
+
+
+
+
+ com.iqser.red.service
+ persistence-service-processor-v1
+ ${project.version}
+
+
+
+ org.springdoc
+ springdoc-openapi-ui
+
+
+
+
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/PersistenceServiceExternalApiConfiguration.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/PersistenceServiceExternalApiConfiguration.java
new file mode 100644
index 000000000..b766c71fb
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/PersistenceServiceExternalApiConfiguration.java
@@ -0,0 +1,11 @@
+package com.iqser.red.persistence.service.v1.external.api.impl;
+
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+
+@Configuration
+@ComponentScan
+public class PersistenceServiceExternalApiConfiguration {
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/cache/PersistenceServiceExternalApiCacheConfiguration.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/cache/PersistenceServiceExternalApiCacheConfiguration.java
new file mode 100644
index 000000000..c738ae7bf
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/cache/PersistenceServiceExternalApiCacheConfiguration.java
@@ -0,0 +1,42 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.cache;
+
+import static com.iqser.red.persistence.service.v1.external.api.impl.service.OneTimeTokenCacheService.OTT_CACHE;
+
+import java.time.Duration;
+
+import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.cache.RedisCacheConfiguration;
+import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
+import org.springframework.data.redis.serializer.RedisSerializationContext;
+
+@Configuration
+public class PersistenceServiceExternalApiCacheConfiguration {
+
+ public static final String RATE_LIMITER_CACHE = "buckets";
+ public static final String ACL_CACHE = "acl";
+
+
+ @Bean
+ public RedisCacheConfiguration cacheConfiguration() {
+
+ return RedisCacheConfiguration.defaultCacheConfig()
+ .entryTtl(Duration.ofMinutes(1))
+ .disableCachingNullValues()
+ .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
+ }
+
+
+ @Bean
+ public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
+
+ return (builder) -> builder.withCacheConfiguration(RATE_LIMITER_CACHE, RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(60)))
+ .withCacheConfiguration(ACL_CACHE, RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(1)).serializeValuesWith(
+ RedisSerializationContext.SerializationPair.fromSerializer(new JdkSerializationRedisSerializer()))
+ )
+ .withCacheConfiguration(OTT_CACHE, RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(10)));
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/ApplicationConfigurationController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/ApplicationConfigurationController.java
new file mode 100644
index 000000000..5fa62e395
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/ApplicationConfigurationController.java
@@ -0,0 +1,45 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_APP_CONFIG;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_APP_CONFIG;
+
+import javax.validation.Valid;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.ApplicationConfigurationEntity;
+import com.iqser.red.service.persistence.management.v1.processor.service.ApplicationConfigService;
+import com.iqser.red.service.persistence.management.v1.processor.utils.MagicConverter;
+import com.iqser.red.service.persistence.service.v1.api.external.resource.ApplicationConfigurationResource;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.ApplicationConfig;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class ApplicationConfigurationController implements ApplicationConfigurationResource {
+
+ private final ApplicationConfigService applicationConfigService;
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_APP_CONFIG + "')")
+ public ApplicationConfig createOrUpdateAppConfig(@Valid @RequestBody ApplicationConfig appConfig) {
+
+ return MagicConverter.convert(applicationConfigService.saveApplicationConfiguration(MagicConverter.convert(appConfig, ApplicationConfigurationEntity.class)),
+ ApplicationConfig.class);
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_APP_CONFIG + "')")
+ public ApplicationConfig getCurrentApplicationConfig() {
+
+ return MagicConverter.convert(applicationConfigService.getApplicationConfig(), ApplicationConfig.class);
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/AuditController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/AuditController.java
new file mode 100644
index 000000000..b1674e124
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/AuditController.java
@@ -0,0 +1,56 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.SEARCH_AUDIT_LOG;
+import static com.iqser.red.service.persistence.management.v1.processor.utils.MagicConverter.convert;
+
+import java.util.List;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.iqser.red.keycloak.commons.KeycloakSecurity;
+import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
+import com.iqser.red.service.persistence.service.v1.api.external.resource.AuditResource;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditResponse;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditModel;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditSearchRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.CategoryModel;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class AuditController implements AuditResource {
+
+ private final AuditPersistenceService auditPersistenceService;
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + SEARCH_AUDIT_LOG + "')")
+ public AuditResponse searchAuditLog(@RequestBody AuditSearchRequest auditSearchRequest) {
+
+ var auditModels = convert(auditPersistenceService.search(auditSearchRequest), AuditModel.class);
+ auditPersistenceService.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(auditSearchRequest.getObjectId())
+ .category(AuditCategory.AUDIT.name())
+ .message("Audit Log has been viewed.")
+ .build());
+
+ return new AuditResponse(auditModels.getElements(), auditModels.getTotalHits(), auditModels.getPage(), auditModels.getPageSize());
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + SEARCH_AUDIT_LOG + "')")
+ public List getAuditCategories() {
+
+ return auditPersistenceService.getCategories();
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/CustomPermissionMappingController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/CustomPermissionMappingController.java
new file mode 100644
index 000000000..e136ef769
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/CustomPermissionMappingController.java
@@ -0,0 +1,65 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.MANAGE_ACL_PERMISSIONS;
+
+import java.util.List;
+import java.util.Set;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.iqser.red.service.persistence.management.v1.processor.acl.custom.service.CustomPermissionService;
+import com.iqser.red.service.persistence.service.v1.api.external.resource.CustomPermissionMappingResource;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.permission.CustomPermissionMappingModel;
+
+import lombok.RequiredArgsConstructor;
+
+@RestController
+@RequiredArgsConstructor
+public class CustomPermissionMappingController implements CustomPermissionMappingResource {
+
+ private final CustomPermissionService customPermissionService;
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + MANAGE_ACL_PERMISSIONS + "')")
+ public List getCustomPermissionMappings(@PathVariable(TARGET_OBJECT_NAME) String targetObject) {
+
+ return customPermissionService.getCustomPermissionMappings(targetObject);
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + MANAGE_ACL_PERMISSIONS + "')")
+ public void saveCustomPermissionMappings(@PathVariable(TARGET_OBJECT_NAME) String targetObject, @RequestBody List customPermissionMappingModels) {
+
+ customPermissionService.saveCustomPermissionMappings(targetObject, customPermissionMappingModels);
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + MANAGE_ACL_PERMISSIONS + "')")
+ public List getValidMapping(@PathVariable(TARGET_OBJECT_NAME) String targetObject) {
+
+ return customPermissionService.getExistingPermissions(targetObject);
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + MANAGE_ACL_PERMISSIONS + "')")
+ public Set getAllSupportedTargetObjects() {
+
+ return customPermissionService.getAllSupportedTargetObjects();
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + MANAGE_ACL_PERMISSIONS + "')")
+ public void syncAllCustomPermissions() {
+
+ customPermissionService.syncAllCustomPermissions();
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DictionaryController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DictionaryController.java
new file mode 100644
index 000000000..cfd4f5974
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DictionaryController.java
@@ -0,0 +1,330 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.validation.Valid;
+
+import org.springframework.core.io.InputStreamResource;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.iqser.red.keycloak.commons.KeycloakSecurity;
+import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
+import com.iqser.red.service.persistence.management.v1.processor.service.DictionaryService;
+import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
+import com.iqser.red.service.persistence.management.v1.processor.utils.MagicConverter;
+import com.iqser.red.service.persistence.management.v1.processor.utils.StringEncodingUtils;
+import com.iqser.red.service.persistence.management.v1.processor.utils.TypeValueMapper;
+import com.iqser.red.service.persistence.service.v1.api.external.resource.DictionaryResource;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.CreateTypeValue;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.Dictionary;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.TypeResponse;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.TypeValue;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.UpdateTypeValue;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.Colors;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.DictionaryEntryType;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.Type;
+
+import feign.FeignException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class DictionaryController implements DictionaryResource {
+
+ private final DictionaryService dictionaryService;
+ private final AuditPersistenceService auditClient;
+
+
+ @Override
+ public void addEntry(@PathVariable(TYPE_PARAMETER_NAME) String type,
+ @PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
+ @RequestBody List entries,
+ @RequestParam(name = REMOVE_CURRENT_REQUEST_PARAM, defaultValue = "false", required = false) boolean removeCurrent,
+ @RequestParam(value = DOSSIER_ID_PARAMETER_NAME, required = false) String dossierId,
+ @RequestParam(value = DICTIONARY_ENTRY_TYPE_PARAM, required = false, defaultValue = DEFAULT_DICTIONARY_ENTRY_TYPE) DictionaryEntryType dictionaryEntryType) {
+
+ addEntries(type, dossierTemplateId, entries, removeCurrent, dossierId, dictionaryEntryType);
+ auditClient.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DICTIONARY.name())
+ .message("Dictionary entries were added.")
+ .details(Map.of("Type", type, "Number", entries.size()))
+ .build());
+ }
+
+
+ private void addEntries(String type, String dossierTemplateId, List entries, boolean removeCurrent, String dossierId, DictionaryEntryType dictionaryEntryType) {
+
+ if (dossierId == null) {
+ dictionaryService.addGlobalEntries(type, dossierTemplateId, entries, removeCurrent, dictionaryEntryType);
+ } else {
+ dictionaryService.addDossierEntries(type, dossierTemplateId, entries, removeCurrent, dossierId, dictionaryEntryType);
+ }
+ }
+
+
+ @Override
+ public void deleteEntry(@PathVariable(TYPE_PARAMETER_NAME) String type,
+ @PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
+ @PathVariable(ENTRY_PARAMETER_NAME) String entry,
+ @RequestParam(value = DOSSIER_ID_PARAMETER_NAME, required = false) String dossierId,
+ @RequestParam(value = DICTIONARY_ENTRY_TYPE_PARAM, required = false, defaultValue = DEFAULT_DICTIONARY_ENTRY_TYPE) DictionaryEntryType dictionaryEntryType) {
+
+ deleteEntries(type, dossierTemplateId, Arrays.asList(entry), dossierId, dictionaryEntryType);
+ auditClient.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DICTIONARY.name())
+ .message("Dictionary entry was deleted.")
+ .details(Map.of("Type", type, "Value", entry))
+ .build());
+ }
+
+
+ @Override
+ public void deleteEntries(@PathVariable(TYPE_PARAMETER_NAME) String type,
+ @PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
+ @RequestBody List entries,
+ @RequestParam(value = DOSSIER_ID_PARAMETER_NAME, required = false) String dossierId,
+ @RequestParam(value = DICTIONARY_ENTRY_TYPE_PARAM, required = false, defaultValue = DEFAULT_DICTIONARY_ENTRY_TYPE) DictionaryEntryType dictionaryEntryType) {
+
+ if (dossierId == null) {
+ dictionaryService.deleteGlobalEntries(type, dossierTemplateId, entries, dictionaryEntryType);
+ } else {
+ dictionaryService.deleteDossierEntries(type, dossierTemplateId, entries, dossierId, dictionaryEntryType);
+ }
+
+ auditClient.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DICTIONARY.name())
+ .message("Dictionary entries were deleted.")
+ .details(Map.of("Type", type, "Number", entries.size()))
+ .build());
+ }
+
+
+ @Override
+ public void updateType(@PathVariable(TYPE_PARAMETER_NAME) String type,
+ @PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
+ @RequestBody UpdateTypeValue typeValue,
+ @RequestParam(value = DOSSIER_ID_PARAMETER_NAME, required = false) String dossierId) {
+
+ if (dossierId == null) {
+ dictionaryService.updateGlobalType(type, dossierTemplateId, typeValue);
+ } else {
+ dictionaryService.updateDossierType(type, dossierTemplateId, typeValue, dossierId);
+ }
+
+ auditClient.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DICTIONARY.name())
+ .message("Dictionary type was updated.")
+ .details(Map.of("Type", type))
+ .build());
+ }
+
+
+ @Override
+ public TypeValue addType(@Valid @RequestBody CreateTypeValue typeValue, @RequestParam(value = DOSSIER_ID_PARAMETER_NAME, required = false) String dossierId) {
+
+ Type result;
+
+ if (dossierId == null) {
+ result = dictionaryService.addGlobalType(typeValue);
+ } else {
+ result = dictionaryService.addDossierType(typeValue, dossierId);
+ }
+
+ auditClient.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(typeValue.getDossierTemplateId())
+ .category(AuditCategory.DICTIONARY.name())
+ .message("Dictionary type was added.")
+ .details(Map.of("Type", typeValue.getType()))
+ .build());
+
+ var converted = MagicConverter.convert(result, TypeValue.class, new TypeValueMapper());
+ return converted;
+ }
+
+
+ @Override
+ public void deleteType(@PathVariable(TYPE_PARAMETER_NAME) String type,
+ @PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
+ @RequestParam(value = DOSSIER_ID_PARAMETER_NAME, required = false) String dossierId) {
+
+ if (dossierId == null) {
+ dictionaryService.deleteGlobalType(type, dossierTemplateId);
+ } else {
+ dictionaryService.deleteDossierType(type, dossierTemplateId, dossierId);
+ }
+
+ auditClient.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DICTIONARY.name())
+ .message("Dictionary type was deleted.")
+ .details(Map.of("Type", type))
+ .build());
+ }
+
+
+ @Override
+ public void deleteTypes(@RequestBody List types,
+ @PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
+ @RequestParam(value = DOSSIER_ID_PARAMETER_NAME, required = false) String dossierId) {
+
+ List errorIds = new ArrayList<>();
+
+ for (var type : types) {
+ try {
+ if (dossierId == null) {
+ dictionaryService.deleteGlobalType(type, dossierTemplateId);
+ } else {
+ dictionaryService.deleteDossierType(type, dossierTemplateId, dossierId);
+ }
+
+ auditClient.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DICTIONARY.name())
+ .message("Dictionary type was deleted.")
+ .details(Map.of("Type", type))
+ .build());
+ } catch (FeignException e) {
+ errorIds.add(type);
+ }
+ }
+
+ if (errorIds.size() > 0) {
+ throw new BadRequestException("Failed to delete dictionary types: " + errorIds);
+ }
+ }
+
+
+ @Override
+ public TypeResponse getAllTypes(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
+ @RequestParam(value = DOSSIER_ID_PARAMETER_NAME, required = false) String dossierId,
+ @RequestParam(value = INCLUDE_DELETED_PARAMETER_NAME, required = false, defaultValue = "false") boolean includeDeleted) {
+
+ return dictionaryService.getAllTypes(dossierTemplateId, dossierId, includeDeleted);
+ }
+
+
+ @Override
+ public Colors getColors(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId) {
+
+ return dictionaryService.getColors(dossierTemplateId);
+ }
+
+
+ @Override
+ public void uploadDictionary(@RequestPart(name = "file", required = false) MultipartFile file,
+ @PathVariable(TYPE_PARAMETER_NAME) String type,
+ @PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
+ @RequestParam(value = DOSSIER_ID_PARAMETER_NAME, required = false) String dossierId,
+ @RequestParam(value = DICTIONARY_ENTRY_TYPE_PARAM, required = false, defaultValue = DEFAULT_DICTIONARY_ENTRY_TYPE) DictionaryEntryType dictionaryEntryType) {
+
+ validateFile(file);
+
+ try {
+ addEntries(type, dossierTemplateId, new String(file.getBytes(), StandardCharsets.UTF_8).lines().collect(Collectors.toList()), true, dossierId, dictionaryEntryType);
+ } catch (IOException e) {
+ log.debug(e.getMessage(), e);
+ throw new BadRequestException("Could not upload file.", e);
+ }
+
+ auditClient.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DICTIONARY.name())
+ .message("Dictionary has been uploaded.")
+ .details(Map.of("Type", type))
+ .build());
+ }
+
+
+ private void validateFile(@RequestPart(name = "file", required = false) MultipartFile file) {
+
+ if (file == null || file.isEmpty()) {
+ throw new BadRequestException("File cannot be null or empty");
+ }
+ }
+
+
+ @Override
+ public ResponseEntity> downloadDictionary(@PathVariable(TYPE_PARAMETER_NAME) String type,
+ @PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
+ @RequestParam(value = DOSSIER_ID_PARAMETER_NAME, required = false) String dossierId,
+ @RequestParam(value = DICTIONARY_ENTRY_TYPE_PARAM, required = false, defaultValue = DEFAULT_DICTIONARY_ENTRY_TYPE) DictionaryEntryType dictionaryEntryType) {
+
+ byte[] data = null;
+ switch (dictionaryEntryType) {
+ case ENTRY:
+ data = String.join("\n", getDictionaryForType(type, dossierTemplateId, dossierId).getEntries()).getBytes();
+ break;
+ case FALSE_POSITIVE:
+ data = String.join("\n", getDictionaryForType(type, dossierTemplateId, dossierId).getFalsePositiveEntries()).getBytes();
+ break;
+ case FALSE_RECOMMENDATION:
+ data = String.join("\n", getDictionaryForType(type, dossierTemplateId, dossierId).getFalseRecommendationEntries()).getBytes();
+ break;
+ }
+
+ HttpHeaders httpHeaders = new HttpHeaders();
+ httpHeaders.setContentType(MediaType.TEXT_PLAIN);
+
+ httpHeaders.add("Content-Disposition", "attachment; filename*=utf-8''" + StringEncodingUtils.urlEncode(type) + ".txt");
+ InputStream is = new ByteArrayInputStream(data);
+
+ return new ResponseEntity<>(new InputStreamResource(is), httpHeaders, HttpStatus.OK);
+ }
+
+
+ @Override
+ public Dictionary getDictionaryForType(@PathVariable(TYPE_PARAMETER_NAME) String type,
+ @PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
+ @RequestParam(value = DOSSIER_ID_PARAMETER_NAME, required = false) String dossierId) {
+
+ return dictionaryService.getDictionaryForType(type, dossierTemplateId, dossierId);
+ }
+
+
+ @Override
+ public void setColors(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @RequestBody Colors colors) {
+
+ dictionaryService.setColors(dossierTemplateId, colors);
+
+ auditClient.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DOSSIER_TEMPLATE.name())
+ .message("Colors have been changed.")
+ .build());
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DigitalSignatureController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DigitalSignatureController.java
new file mode 100644
index 000000000..1a4ebbedd
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DigitalSignatureController.java
@@ -0,0 +1,213 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_DIGITAL_SIGNATURE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_DIGITAL_SIGNATURE;
+
+import java.nio.charset.StandardCharsets;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.util.Base64Utils;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.iqser.red.keycloak.commons.KeycloakSecurity;
+import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.DigitalSignatureEntity;
+import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.DigitalSignatureKmsEntity;
+import com.iqser.red.service.persistence.management.v1.processor.service.DigitalSignatureKmsService;
+import com.iqser.red.service.persistence.management.v1.processor.service.DigitalSignatureService;
+import com.iqser.red.service.persistence.management.v1.processor.service.DigitalSignatureTypeService;
+import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
+import com.iqser.red.service.persistence.service.v1.api.external.resource.DigitalSignatureResource;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.DigitalSignatureKms;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.DigitalSignatureKmsViewModel;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.DigitalSignatureViewModel;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.DigitalSignature;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.DigitalSignatureType;
+
+import lombok.RequiredArgsConstructor;
+
+@RestController
+@RequiredArgsConstructor
+public class DigitalSignatureController implements DigitalSignatureResource {
+
+ private static final String DIGITAL_SIGNATURE_AUDIT_ID = "DigitalSignature";
+ private final DigitalSignatureTypeService digitalSignatureTypeService;
+ private final DigitalSignatureService digitalSignatureService;
+ private final DigitalSignatureKmsService digitalSignatureKmsService;
+ private final AuditPersistenceService auditPersistenceService;
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_DIGITAL_SIGNATURE + "')")
+ public DigitalSignatureType getActiveDigitalSignatureType() {
+
+ return digitalSignatureTypeService.getActiveDigitalSignatureType();
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_DIGITAL_SIGNATURE + "')")
+ public void setActiveDigitalSignatureType(DigitalSignatureType digitalSignatureType) {
+
+ digitalSignatureTypeService.setActiveDigitalSignatureType(digitalSignatureType);
+ auditPersistenceService.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(DIGITAL_SIGNATURE_AUDIT_ID)
+ .category(AuditCategory.SETTINGS.name())
+ .message("Digital signature type has been updated.")
+ .build());
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_DIGITAL_SIGNATURE + "')")
+ public DigitalSignatureViewModel saveDigitalSignature(@RequestBody DigitalSignature digitalSignatureModel) {
+
+ DigitalSignatureViewModel digitalSignatureViewModel = convertToView(digitalSignatureService.saveDigitalSignature(convert(digitalSignatureModel)));
+ auditPersistenceService.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(DIGITAL_SIGNATURE_AUDIT_ID)
+ .category(AuditCategory.SETTINGS.name())
+ .message("Digital signature has been saved.")
+ .build());
+ return digitalSignatureViewModel;
+
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_DIGITAL_SIGNATURE + "')")
+ public void updateDigitalSignature(@RequestBody DigitalSignatureViewModel digitalSignatureModel) {
+
+ digitalSignatureService.updateDigitalSignature(convert(digitalSignatureModel));
+ auditPersistenceService.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(DIGITAL_SIGNATURE_AUDIT_ID)
+ .category(AuditCategory.SETTINGS.name())
+ .message("Digital signature has been updated.")
+ .build());
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_DIGITAL_SIGNATURE + "')")
+ public DigitalSignatureViewModel getDigitalSignature() {
+
+ return convertToView(digitalSignatureService.getDigitalSignature());
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_DIGITAL_SIGNATURE + "')")
+ public void deleteDigitalSignature() {
+
+ digitalSignatureService.deleteDigitalSignature();
+ auditPersistenceService.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(DIGITAL_SIGNATURE_AUDIT_ID)
+ .category(AuditCategory.SETTINGS.name())
+ .message("Digital signature has been deleted.")
+ .build());
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_DIGITAL_SIGNATURE + "')")
+ public DigitalSignatureKmsViewModel saveDigitalSignatureKms(@RequestBody DigitalSignatureKms digitalSignature) {
+
+ DigitalSignatureKmsViewModel result = convert(digitalSignatureKmsService.saveDigitalSignature(convert(digitalSignature)));
+ auditPersistenceService.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(DIGITAL_SIGNATURE_AUDIT_ID)
+ .category(AuditCategory.SETTINGS.name())
+ .message("Digital KMS signature has been saved.")
+ .build());
+ return result;
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_DIGITAL_SIGNATURE + "')")
+ public DigitalSignatureKmsViewModel getDigitalSignatureKms() {
+
+ return convert(digitalSignatureKmsService.getDigitalSignature());
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_DIGITAL_SIGNATURE + "')")
+ public void deleteDigitalSignatureKms() {
+
+ digitalSignatureKmsService.deleteDigitalSignature();
+ auditPersistenceService.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(DIGITAL_SIGNATURE_AUDIT_ID)
+ .category(AuditCategory.SETTINGS.name())
+ .message("Digital KMS signature has been deleted.")
+ .build());
+ }
+
+
+ private DigitalSignatureKmsViewModel convert(DigitalSignatureKmsEntity digitalSignature) {
+
+ return DigitalSignatureKmsViewModel.builder()
+ .kmsAccessKey(digitalSignature.getKmsAccessKey())
+ .kmsKeyId(digitalSignature.getKmsKeyId())
+ .kmsRegion(digitalSignature.getKmsRegion())
+ .kmsServiceEndpoint(digitalSignature.getKmsServiceEndpoint())
+ .certificateName(digitalSignature.getCertificateName())
+ .build();
+ }
+
+
+ private DigitalSignatureKmsEntity convert(DigitalSignatureKms digitalSignatureKms) {
+
+ return DigitalSignatureKmsEntity.builder()
+ .certificate(digitalSignatureKms.getCertificate().getBytes(StandardCharsets.UTF_8))
+ .kmsAccessKey(digitalSignatureKms.getKmsAccessKey())
+ .kmsKeyId(digitalSignatureKms.getKmsKeyId())
+ .kmsRegion(digitalSignatureKms.getKmsRegion())
+ .kmsSecretKey(digitalSignatureKms.getKmsSecretKey())
+ .kmsServiceEndpoint(digitalSignatureKms.getKmsServiceEndpoint())
+ .certificateName(digitalSignatureKms.getCertificateName())
+ .build();
+ }
+
+
+ private DigitalSignatureEntity convert(DigitalSignatureViewModel digitalSignature) {
+
+ return DigitalSignatureEntity.builder()
+ .certificateName(digitalSignature.getCertificateName())
+ .contactInfo(digitalSignature.getContactInfo())
+ .location(digitalSignature.getLocation())
+ .reason(digitalSignature.getReason())
+ .build();
+ }
+
+
+ public DigitalSignatureViewModel convertToView(DigitalSignatureEntity model) {
+
+ return DigitalSignatureViewModel.builder()
+ .certificateName(model.getCertificateName())
+ .contactInfo(model.getContactInfo())
+ .location(model.getLocation())
+ .reason(model.getReason())
+ .build();
+ }
+
+
+ public DigitalSignatureEntity convert(DigitalSignature digitalSignature) {
+
+ return DigitalSignatureEntity.builder()
+ .certificateName(digitalSignature.getCertificateName())
+ .contactInfo(digitalSignature.getContactInfo())
+ .location(digitalSignature.getLocation())
+ .password(digitalSignature.getPassword())
+ .reason(digitalSignature.getReason())
+ .privateKey(Base64Utils.decodeFromString(digitalSignature.getPrivateKey()))
+ .build();
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierAttributesController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierAttributesController.java
new file mode 100644
index 000000000..db854f671
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierAttributesController.java
@@ -0,0 +1,178 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_DOSSIER_ATTRIBUTES;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_DOSSIER_ATTRIBUTES_CONFIG;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_DOSSIER_ATTRIBUTES;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_DOSSIER_ATTRIBUTES_CONFIG;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_FILE_ATTRIBUTES;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.iqser.red.keycloak.commons.KeycloakSecurity;
+import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierAttributeConfigEntity;
+import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
+import com.iqser.red.service.persistence.management.v1.processor.service.DossierAttributesManagementService;
+import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
+import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierAttributeConfigPersistenceService;
+import com.iqser.red.service.persistence.management.v1.processor.utils.MagicConverter;
+import com.iqser.red.service.persistence.service.v1.api.external.resource.DossierAttributesResource;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierAttributes;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierAttributesConfig;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierAttributeConfig;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierAttribute;
+
+import lombok.RequiredArgsConstructor;
+
+@RestController
+@RequiredArgsConstructor
+public class DossierAttributesController implements DossierAttributesResource {
+
+ private final DossierAttributeConfigPersistenceService dossierAttributeConfigPersistenceService;
+ private final AuditPersistenceService auditPersistenceService;
+ private final DossierAttributesManagementService dossierAttributesManagementService;
+ private final AccessControlService accessControlService;
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_DOSSIER_ATTRIBUTES_CONFIG + "')")
+ public DossierAttributesConfig setDossierAttributesConfig(String dossierTemplateId, DossierAttributesConfig dossierAttributesConfig) {
+
+ var result = MagicConverter.convert(dossierAttributeConfigPersistenceService.setDossierAttributesConfig(dossierTemplateId,
+ MagicConverter.convert(dossierAttributesConfig.getDossierAttributeConfigs(), DossierAttributeConfigEntity.class)), DossierAttributeConfig.class);
+ auditPersistenceService.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DOSSIER_TEMPLATE.name())
+ .message("Changed dossier attributes base configuration.")
+ .build());
+ return new DossierAttributesConfig(result);
+
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_DOSSIER_ATTRIBUTES_CONFIG + "')")
+ public DossierAttributeConfig addOrUpdateDossierAttributeConfig(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId,
+ @RequestBody DossierAttributeConfig dossierAttribute) {
+
+ var result = MagicConverter.convert(dossierAttributeConfigPersistenceService.addOrUpdateDossierAttribute(dossierTemplateId,
+ MagicConverter.convert(dossierAttribute, DossierAttributeConfigEntity.class)), DossierAttributeConfig.class);
+ auditPersistenceService.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DOSSIER_TEMPLATE.name())
+ .message("Dossier attributes added/updated")
+ .build());
+
+ return result;
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_DOSSIER_ATTRIBUTES_CONFIG + "')")
+ public void deleteDossierAttributeConfig(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @PathVariable(DOSSIER_ATTRIBUTE_ID) String dossierAttributeId) {
+
+ dossierAttributeConfigPersistenceService.deleteDossierAttribute(dossierAttributeId);
+ auditPersistenceService.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DOSSIER_TEMPLATE.name())
+ .message("Dossier attributes removed")
+ .details(Map.of("DossierAttributeId", dossierAttributeId))
+ .build());
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_DOSSIER_ATTRIBUTES_CONFIG + "')")
+ public void deleteDossierAttributesConfig(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestParam(DOSSIER_ATTRIBUTE_IDS) List dossierAttributeIds) {
+
+ dossierAttributeConfigPersistenceService.deleteDossierAttributes(dossierAttributeIds);
+ auditPersistenceService.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DOSSIER_TEMPLATE.name())
+ .message("Dossier attributes removed")
+ .details(Map.of("DossierAttributeId", dossierAttributeIds))
+ .build());
+ }
+
+
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER_ATTRIBUTES_CONFIG + "')")
+ public DossierAttributesConfig getDossierAttributesConfig(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId) {
+
+ var result = dossierAttributeConfigPersistenceService.getDossierAttributes(dossierTemplateId);
+ return new DossierAttributesConfig(MagicConverter.convert(result, DossierAttributeConfig.class));
+ }
+
+
+ @PreAuthorize("hasAuthority('" + WRITE_FILE_ATTRIBUTES + "')")
+ public DossierAttributes setDossierAttributes(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody DossierAttributes dossierAttributes) {
+
+ accessControlService.verifyUserIsDossierOwner(dossierId);
+ var result = dossierAttributesManagementService.setDossierAttributes(dossierId, dossierAttributes.getDossierAttributeList());
+ auditPersistenceService.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierId)
+ .category(AuditCategory.DOSSIER.name())
+ .message("Changed dossier attributes.")
+ .build());
+ return new DossierAttributes(result);
+
+ }
+
+
+ @PreAuthorize("hasAuthority('" + WRITE_DOSSIER_ATTRIBUTES + "')")
+ public DossierAttributes addOrUpdateDossierAttribute(String dossierId, DossierAttribute dossierAttribute) {
+
+ accessControlService.verifyUserIsDossierOwner(dossierId);
+ DossierAttribute result = dossierAttributesManagementService.addOrUpdateDossierAttribute(dossierId, dossierAttribute);
+ auditPersistenceService.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierId)
+ .category(AuditCategory.DOSSIER.name())
+ .message("Added or updated dossier attributes.")
+ .build());
+ return new DossierAttributes(List.of(result)); // TODO should be single Object???
+ }
+
+
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER_ATTRIBUTES + "')")
+ public DossierAttributes getDossierAttributes(String dossierId) {
+
+ var result = dossierAttributesManagementService.getDossierAttributes(dossierId);
+ auditPersistenceService.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierId)
+ .category(AuditCategory.DOSSIER.name())
+ .message("Got dossier attributes.")
+ .build());
+ return new DossierAttributes(result);
+
+ }
+
+
+ @PreAuthorize("hasAuthority('" + WRITE_DOSSIER_ATTRIBUTES + "')")
+ public void deleteDossierAttribute(String dossierId, String dossierAttributeId) {
+
+ accessControlService.verifyUserIsDossierOwner(dossierId);
+ dossierAttributesManagementService.deleteDossierAttribute(dossierId, dossierAttributeId);
+ auditPersistenceService.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierId)
+ .category(AuditCategory.DOSSIER.name())
+ .message("Changed dossier attributes.")
+ .build());
+
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierController.java
new file mode 100644
index 000000000..1769a0cab
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierController.java
@@ -0,0 +1,507 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.ADD_UPDATE_DOSSIER;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.ARCHIVE_DOSSIER;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DELETE_DOSSIER;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_DOSSIER;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.UNARCHIVE_DOSSIER;
+
+import java.time.OffsetDateTime;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PostAuthorize;
+import org.springframework.security.access.prepost.PostFilter;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.access.prepost.PreFilter;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.google.common.collect.Lists;
+import com.iqser.red.keycloak.commons.KeycloakSecurity;
+import com.iqser.red.keycloak.commons.model.User;
+import com.iqser.red.keycloak.commons.roles.ApplicationRoles;
+import com.iqser.red.service.persistence.management.v1.processor.acl.custom.dossier.DossierACLService;
+import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
+import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
+import com.iqser.red.service.persistence.management.v1.processor.service.DossierManagementService;
+import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusManagementService;
+import com.iqser.red.service.persistence.management.v1.processor.service.UserService;
+import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
+import com.iqser.red.service.persistence.management.v1.processor.service.persistence.NotificationPersistenceService;
+import com.iqser.red.service.persistence.service.v1.api.external.resource.DossierResource;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierChangeEntry;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierInformation;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AddNotificationRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSONPrimitive;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.CreateOrUpdateDossierRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.notification.NotificationType;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class DossierController implements DossierResource {
+
+ private static final Set VALID_MEMBER_ROLES = Set.of(ApplicationRoles.RED_USER_ROLE, ApplicationRoles.RED_MANAGER_ROLE);
+
+ private final DossierManagementService dossierManagementService;
+ private final UserService userService;
+ private final FileStatusManagementService fileStatusManagementService;
+ private final AuditPersistenceService auditPersistenceService;
+ private final NotificationPersistenceService notificationPersistenceService;
+ private final AccessControlService accessControlService;
+ private final DossierACLService dossierACLService;
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
+ public DossierInformation getDossierInformation() {
+
+ return dossierManagementService.getDossierInformation(dossierACLService.getDossierIdsWithViewPermission());
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
+ @PostFilter("hasPermission(filterObject.dossierId, 'Dossier', 'VIEW_OBJECT')")
+ public List changesSince(@RequestBody JSONPrimitive since) {
+
+ return dossierManagementService.changesSince(since)
+ .stream()
+ .map(d -> new DossierChangeEntry(d.getDossierId(), d.isDossierChanges(), d.isFileChanges()))
+ .collect(Collectors.toList());
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + ADD_UPDATE_DOSSIER + "') && (#dossierRequest.dossierId == null || hasPermission(#dossierRequest.dossierId, 'Dossier', 'ACCESS_OBJECT') )")
+ public ResponseEntity createDossierOrUpdateDossier(@RequestBody DossierRequest dossierRequest) {
+
+ if (dossierRequest.getDossierId() != null && dossierRequest.getOwnerId() == null) {
+ throw new BadRequestException("Owner must be set for any update");
+ }
+
+ // this code always executes - members, approvers and owner must be valid
+ String ownerId = getAndValidateOwnerId(dossierRequest.getOwnerId());
+ Set members = getAndValidateMembers(ownerId, dossierRequest.getMemberIds());
+ Set approvers = getAndValidateMembers(ownerId, dossierRequest.getApproverIds());
+ members.addAll(approvers);
+
+ if ((dossierRequest.getDownloadFileTypes() == null || dossierRequest.getDownloadFileTypes()
+ .isEmpty()) && (dossierRequest.getReportTemplateIds() == null || dossierRequest.getReportTemplateIds().isEmpty())) {
+ throw new BadRequestException("Download and report types cannot both be empty");
+ }
+
+ // if dossierId is set - load dossier
+ if (StringUtils.isNotEmpty(dossierRequest.getDossierId())) {
+
+ Dossier existingDossier = dossierACLService.enhanceDossierWithACLData(dossierManagementService.getDossierById(dossierRequest.getDossierId(), true, false));
+
+ if (existingDossier.getArchivedTime() != null) {
+ checkValidityForArchivedDossierUpdateRequest(dossierRequest, existingDossier);
+ }
+
+ // update using data from request and computed owner/members/approvers
+ Dossier updatedDossier = dossierManagementService.updateDossier(CreateOrUpdateDossierRequest.builder()
+ .dossierName(dossierRequest.getDossierName())
+ .description(dossierRequest.getDescription())
+ .dossierTemplateId(dossierRequest.getDossierTemplateId())
+ .downloadFileTypes(dossierRequest.getDownloadFileTypes())
+ .dueDate(dossierRequest.getDueDate())
+ .reportTemplateIds(new ArrayList<>(dossierRequest.getReportTemplateIds()))
+ .watermarkId(dossierRequest.getWatermarkId())
+ .previewWatermarkId(dossierRequest.getPreviewWatermarkId())
+ .dossierStatusId(dossierRequest.getDossierStatusId())
+ .build(), existingDossier.getId());
+
+ dossierACLService.updateDossierACL(members, approvers, ownerId, updatedDossier.getId());
+ dossierACLService.enhanceDossierWithACLData(updatedDossier);
+
+ updateFileStatusForDossierFiles(updatedDossier.getId(), members);
+
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(updatedDossier.getId())
+ .category(AuditCategory.DOSSIER.name())
+ .message("Dossier has been updated.")
+ .build());
+
+ if (existingDossier.getOwnerId() == null || !existingDossier.getOwnerId().equals(ownerId)) {
+ if (ownerId != null && !ownerId.equals(KeycloakSecurity.getUserId())) {
+ notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
+ .userId(ownerId)
+ .issuerId(KeycloakSecurity.getUserId())
+ .notificationType(NotificationType.DOSSIER_OWNER_SET.name())
+ .target(Map.of("dossierId", dossierRequest.getDossierId()))
+ .build());
+ }
+ if (existingDossier.getOwnerId() != null && !existingDossier.getOwnerId().equals(KeycloakSecurity.getUserId())) {
+ notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
+ .userId(existingDossier.getOwnerId())
+ .issuerId(KeycloakSecurity.getUserId())
+ .notificationType(NotificationType.DOSSIER_OWNER_REMOVED.name())
+ .target(Map.of("dossierId", dossierRequest.getDossierId()))
+ .build());
+ }
+ }
+
+ Stream.concat(members.stream(), approvers.stream())
+ .filter(member -> !member.equals(ownerId) && !member.equals(KeycloakSecurity.getUserId()) && (existingDossier.getMemberIds() == null || !existingDossier.getMemberIds()
+ .contains(member)))
+ .forEach(member -> notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
+ .userId(member)
+ .issuerId(KeycloakSecurity.getUserId())
+ .notificationType(NotificationType.USER_BECOMES_DOSSIER_MEMBER.name())
+ .target(Map.of("dossierId", dossierRequest.getDossierId()))
+ .build()));
+
+ if (existingDossier.getMemberIds() != null) {
+ existingDossier.getMemberIds()
+ .stream()
+ .filter(member -> !members.contains(member) && !approvers.contains(member) && !member.equals(KeycloakSecurity.getUserId()))
+ .forEach(member -> notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
+ .userId(member)
+ .issuerId(KeycloakSecurity.getUserId())
+ .notificationType(NotificationType.USER_REMOVED_AS_DOSSIER_MEMBER.name())
+ .target(Map.of("dossierId", dossierRequest.getDossierId()))
+ .build()));
+ }
+
+ approvers.stream()
+ .filter(approver -> !KeycloakSecurity.getUserId().equals(approver) && existingDossier.getMemberIds() != null && existingDossier.getMemberIds()
+ .contains(approver) && (existingDossier.getApproverIds() == null || !existingDossier.getApproverIds().contains(approver)))
+ .forEach(approver -> notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
+ .userId(approver)
+ .issuerId(KeycloakSecurity.getUserId())
+ .notificationType(NotificationType.USER_PROMOTED_TO_APPROVER.name())
+ .target(Map.of("dossierId", dossierRequest.getDossierId()))
+ .build()));
+
+ members.stream()
+ .filter(member -> !member.equals(KeycloakSecurity.getUserId()) && existingDossier.getApproverIds() != null && existingDossier.getApproverIds()
+ .contains(member) && !approvers.contains(member))
+ .forEach(member -> notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
+ .userId(member)
+ .issuerId(KeycloakSecurity.getUserId())
+ .notificationType(NotificationType.USER_DEGRADED_TO_REVIEWER.name())
+ .target(Map.of("dossierId", dossierRequest.getDossierId()))
+ .build()));
+
+ HttpHeaders httpHeaders = new HttpHeaders();
+ httpHeaders.setContentType(MediaType.APPLICATION_JSON);
+ return new ResponseEntity<>(updatedDossier, httpHeaders, HttpStatus.OK);
+
+ } else {
+ Dossier created = createNewDossier(dossierRequest, ownerId, members, approvers);
+
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(created.getId())
+ .category(AuditCategory.DOSSIER.name())
+ .message("Dossier has been created.")
+ .build());
+
+ HttpHeaders httpHeaders = new HttpHeaders();
+ httpHeaders.setContentType(MediaType.APPLICATION_JSON);
+ return new ResponseEntity<>(created, httpHeaders, HttpStatus.CREATED);
+ }
+
+ }
+
+
+ private String getAndValidateOwnerId(String ownerId) {
+
+ String currentUserId = KeycloakSecurity.getUserId();
+ // quick check if self, just proceed
+ if (currentUserId.equals(ownerId)) {
+ return currentUserId;
+ }
+
+ String actualOwnerId = ownerId == null ? currentUserId : ownerId;
+
+ var user = userService.getUserById(actualOwnerId);
+ if (user.isEmpty()) {
+ userService.removeDeletedUsers(Collections.singleton(ownerId));
+ actualOwnerId = null;
+ }
+
+ // check he has a manager role, thus he can be the owner
+ if (user.isPresent() && user.get().getRoles().stream().noneMatch(ApplicationRoles.RED_MANAGER_ROLE::equals)) {
+ throw new BadRequestException("Make sure provided user id has the manager role.");
+ }
+
+ return actualOwnerId;
+
+ }
+
+
+ private Set getAndValidateMembers(String ownerId, Set memberIds) {
+
+ Set actualMemberIds = memberIds == null ? new TreeSet<>() : memberIds;
+ if (actualMemberIds.stream().anyMatch(Objects::isNull)) {
+ throw new BadRequestException("Member IDs cannot be null");
+ }
+
+ // add owner automatically
+ if (ownerId != null) {
+ actualMemberIds.add(ownerId);
+ }
+
+ List users = userService.getUsersByIds(actualMemberIds);
+ if (users.size() != actualMemberIds.size()) {
+ Set deletedUserIds = userService.removeDeletedUsers(actualMemberIds);
+ actualMemberIds.removeAll(deletedUserIds);
+ }
+ if (users.stream().anyMatch(u -> u.getRoles().stream().noneMatch(VALID_MEMBER_ROLES::contains))) {
+ throw new BadRequestException("Make sure each provided member id has the permission to be a member of a dossier.");
+ }
+ return actualMemberIds;
+ }
+
+
+ private void checkValidityForArchivedDossierUpdateRequest(DossierRequest updatedDossier, Dossier existingDossier) {
+ //these must not be editted for archived dossiers
+ checkEquality(updatedDossier.getDossierTemplateId(), existingDossier.getDossierTemplateId(), "dossier template id");
+ checkEquality(updatedDossier.getDossierName(), existingDossier.getDossierName(), "dossier name");
+ checkEquality(updatedDossier.getDescription(), existingDossier.getDescription(), "the description");
+ checkEquality(updatedDossier.getDueDate(), existingDossier.getDueDate(), "the due date");
+ checkEquality(updatedDossier.getDossierStatusId(), existingDossier.getDossierStatusId(), "dossier status id");
+ }
+
+
+ private void updateFileStatusForDossierFiles(String dossierId, Collection members) {
+
+ fileStatusManagementService.getDossierStatus(dossierId).stream().filter(fileStatus -> !fileStatus.isSoftOrHardDeleted()).forEach(f -> {
+
+ if (f.getAssignee() != null && !members.contains(f.getAssignee())) {
+ fileStatusManagementService.setCurrentFileAssignee(dossierId, f.getId(), null);
+ }
+ });
+
+ }
+
+
+ private Dossier createNewDossier(DossierRequest dossier, String ownerId, Set members, Set approvers) {
+
+ Dossier newDossier = dossierManagementService.addDossier(CreateOrUpdateDossierRequest.builder()
+ .dossierName(dossier.getDossierName())
+ .description(dossier.getDescription())
+ .dossierTemplateId(dossier.getDossierTemplateId())
+ .downloadFileTypes(dossier.getDownloadFileTypes())
+ .dueDate(dossier.getDueDate())
+ .reportTemplateIds(dossier.getReportTemplateIds() != null ? new ArrayList<>(dossier.getReportTemplateIds()) : Lists.newArrayList())
+ .watermarkId(dossier.getWatermarkId())
+ .previewWatermarkId(dossier.getPreviewWatermarkId())
+ .dossierStatusId(dossier.getDossierStatusId())
+ .build());
+
+ dossierACLService.updateDossierACL(members, approvers, ownerId, newDossier.getId());
+ dossierACLService.enhanceDossierWithACLData(newDossier);
+
+ return newDossier;
+
+ }
+
+
+ private void checkEquality(T updatedValue, T existingValue, String message) {
+
+ if (updatedValue == null && existingValue != null || updatedValue != null && existingValue == null) {
+ throw new BadRequestException(String.format("You cannot edit %s in an archived dossier.", message));
+ }
+ if (updatedValue == null && existingValue == null) {
+ return;
+ }
+
+ if (!updatedValue.equals(existingValue)) {
+ throw new BadRequestException(String.format("You cannot edit %s in an archived dossier.", message));
+ }
+ }
+
+
+
+
+ @PreAuthorize("hasAuthority('" + DELETE_DOSSIER + "') && hasPermission(#dossierId, 'Dossier', 'ACCESS_OBJECT')")
+ public void deleteDossier(@PathVariable(DOSSIER_ID_PARAM) String dossierId) {
+
+ var dossierToBeDeleted = dossierACLService.enhanceDossierWithACLData(dossierManagementService.getDossierById(dossierId, true, false));
+
+ dossierManagementService.delete(dossierId);
+
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierId)
+ .category(AuditCategory.DOSSIER.name())
+ .message("Dossier moved to trash.")
+ .build());
+
+ dossierToBeDeleted.getMemberIds()
+ .stream()
+ .filter(m -> !KeycloakSecurity.getUserId().equals(m))
+ .forEach(member -> notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
+ .userId(member)
+ .issuerId(KeycloakSecurity.getUserId())
+ .notificationType(NotificationType.DOSSIER_DELETED.name())
+ .target(Map.of("dossierId", dossierId, "dossierName", dossierToBeDeleted.getDossierName()))
+ .build()));
+
+ }
+
+
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
+ @PostAuthorize("hasPermission(#dossierId, 'Dossier', 'VIEW_OBJECT')")
+ public Dossier getDossier(@PathVariable(DOSSIER_ID_PARAM) String dossierId,
+ @RequestParam(name = INCLUDE_ARCHIVED_PARAM, defaultValue = "false", required = false) boolean includeArchived,
+ @RequestParam(name = INCLUDE_DELETED_PARAM, defaultValue = "false", required = false) boolean includeDeleted) {
+
+ return dossierACLService.enhanceDossierWithACLData(dossierManagementService.getDossierById(dossierId, includeArchived, includeDeleted));
+ }
+
+
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
+ @PostFilter("hasPermission(filterObject.id, 'Dossier', 'VIEW_OBJECT')")
+ public List getDossiers(@RequestParam(name = INCLUDE_ARCHIVED_PARAM, defaultValue = "false", required = false) boolean includeArchived,
+ @RequestParam(name = INCLUDE_DELETED_PARAM, defaultValue = "false", required = false) boolean includeDeleted) {
+
+ return dossierManagementService.getAllDossiers(includeArchived, includeDeleted).stream().map(dossierACLService::enhanceDossierWithACLData).collect(Collectors.toList());
+ }
+
+
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
+ @PostFilter("hasPermission(filterObject.id, 'Dossier', 'VIEW_OBJECT')")
+ public List getDossiersForDossierTemplate(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
+ @RequestParam(name = INCLUDE_ARCHIVED_PARAM, defaultValue = "false", required = false) boolean includeArchived,
+ @RequestParam(name = INCLUDE_DELETED_PARAM, defaultValue = "false", required = false) boolean includeDeleted) {
+
+ return dossierManagementService.getAllDossiersForDossierTemplateId(dossierTemplateId, includeArchived, includeDeleted)
+ .stream()
+ .map(dossierACLService::enhanceDossierWithACLData)
+ .collect(Collectors.toList());
+ }
+
+
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
+ @PostFilter("hasPermission(filterObject.id, 'Dossier', 'VIEW_OBJECT')")
+ public List getSoftDeletedDossiers() {
+
+ return dossierManagementService.getSoftDeletedDossiers().stream().map(dossierACLService::enhanceDossierWithACLData).collect(Collectors.toList());
+
+ }
+
+
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
+ @PostFilter("hasPermission(filterObject.id, 'Dossier', 'VIEW_OBJECT')")
+ public List getArchivedDossiers() {
+
+ return dossierManagementService.getArchivedDossiers().stream().map(dossierACLService::enhanceDossierWithACLData).collect(Collectors.toList());
+ }
+
+
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
+ @PostFilter("hasPermission(filterObject.id, 'Dossier', 'VIEW_OBJECT')")
+ public List getArchivedDossiersForDossierTemplate(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) {
+
+ return dossierManagementService.getArchivedDossiersForDossierTemplateId(dossierTemplateId)
+ .stream()
+ .map(dossierACLService::enhanceDossierWithACLData)
+ .collect(Collectors.toList());
+ }
+
+
+ @PreAuthorize("hasAuthority('" + ARCHIVE_DOSSIER + "')")
+ @PreFilter("hasPermission(filterObject, 'Dossier', 'ACCESS_OBJECT')")
+ public void archiveDossiers(@RequestBody Set dossierIds) {
+
+ for (String dossierId : dossierIds) {
+ accessControlService.verifyUserIsDossierOwner(dossierId);
+ }
+ dossierManagementService.archiveDossiers(dossierIds);
+
+ for (String dossierId : dossierIds) {
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierId)
+ .category(AuditCategory.DOSSIER.name())
+ .message("Dossier archived.")
+ .build());
+ }
+ }
+
+
+ @PreAuthorize("hasAuthority('" + UNARCHIVE_DOSSIER + "')")
+ public void unarchiveDossiers(@RequestBody Set dossierIds) {
+
+ dossierManagementService.unarchiveDossiers(dossierIds);
+
+ for (String dossierId : dossierIds) {
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierId)
+ .category(AuditCategory.DOSSIER.name())
+ .message("Dossier restored from archive.")
+ .build());
+ }
+ }
+
+
+ @PreAuthorize("hasAuthority('" + DELETE_DOSSIER + "')")
+ @PreFilter("hasPermission(filterObject, 'Dossier', 'ACCESS_OBJECT')")
+ public void hardDeleteDossiers(@RequestParam(DOSSIER_ID_PARAM) Set dossierIds) {
+
+ for (String dossierId : dossierIds) {
+ accessControlService.verifyUserIsDossierOwner(dossierId);
+ }
+ dossierManagementService.hardDeleteDossiers(dossierIds);
+
+ for (String dossierId : dossierIds) {
+
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierId)
+ .category(AuditCategory.DOSSIER.name())
+ .message("Dossier permanently deleted.")
+ .build());
+
+ }
+ }
+
+
+ @PreAuthorize("hasAuthority('" + DELETE_DOSSIER + "')")
+ @PreFilter("hasPermission(filterObject, 'Dossier', 'ACCESS_OBJECT')")
+ public void undeleteDossiers(@RequestBody Set dossierIds) {
+
+ dossierManagementService.undeleteDossiers(dossierIds);
+ for (String dossierId : dossierIds) {
+
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierId)
+ .category(AuditCategory.DOSSIER.name())
+ .message("Dossier restored from trash.")
+ .build());
+
+ }
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierStatsController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierStatsController.java
new file mode 100644
index 000000000..f880671e2
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierStatsController.java
@@ -0,0 +1,47 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_DOSSIER;
+
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.access.prepost.PreFilter;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.iqser.red.service.persistence.management.v1.processor.service.DossierStatsService;
+import com.iqser.red.service.persistence.service.v1.api.external.resource.DossierStatsResource;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierStats;
+
+import feign.FeignException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class DossierStatsController implements DossierStatsResource {
+
+ private final DossierStatsService dossierStatsService;
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER + "') && hasPermission(#dossierId, 'Dossier', 'VIEW_OBJECT')")
+ public DossierStats getDossierStats(@PathVariable(DOSSIER_ID_PARAM) String dossierId) {
+
+ return dossierStatsService.getDossierStats(dossierId);
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
+ @PreFilter("hasPermission(filterObject, 'Dossier', 'ACCESS_OBJECT')")
+ public List getDossierStats(@RequestBody Set dossierIds) {
+
+ return dossierIds.stream().map(dossierStatsService::getDossierStats).collect(Collectors.toList());
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierStatusController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierStatusController.java
new file mode 100644
index 000000000..412904122
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierStatusController.java
@@ -0,0 +1,105 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_DOSSIER_STATUS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_DOSSIER_STATUS;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.transaction.Transactional;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
+import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierStatusPersistenceService;
+import com.iqser.red.service.persistence.management.v1.processor.utils.ColorUtils;
+import com.iqser.red.service.persistence.management.v1.processor.utils.DossierStatusMapper;
+import com.iqser.red.service.persistence.management.v1.processor.utils.MagicConverter;
+import com.iqser.red.service.persistence.service.v1.api.external.resource.DossierStatusResource;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierStatusRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.CreateOrUpdateDossierStatusRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierStatusInfo;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class DossierStatusController implements DossierStatusResource {
+
+ private final DossierStatusPersistenceService dossierStatusPersistenceService;
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_DOSSIER_STATUS + "')")
+ public DossierStatusInfo createOrUpdateDossierStatus(@RequestBody DossierStatusRequest dossierStatusRequest) {
+
+ if (dossierStatusRequest.getDossierTemplateId() == null) {
+ throw new BadRequestException("Dossier Template must be set for creation");
+ }
+ if (dossierStatusRequest.getName() == null || dossierStatusRequest.getName().isEmpty()) {
+ throw new BadRequestException("Dossier status name must be set");
+ }
+ //validate color
+ ColorUtils.validateColor(dossierStatusRequest.getColor());
+ //validate rank
+ if (dossierStatusRequest.getRank() < 0) {
+ throw new BadRequestException("The rank must not be negative");
+ }
+ var response = dossierStatusPersistenceService.createOrUpdateDossierStatus(CreateOrUpdateDossierStatusRequest.builder()
+ .dossierStatusId(dossierStatusRequest.getDossierStatusId())
+ .name(dossierStatusRequest.getName())
+ .description(dossierStatusRequest.getDescription())
+ .dossierTemplateId(dossierStatusRequest.getDossierTemplateId())
+ .rank(dossierStatusRequest.getRank())
+ .color(dossierStatusRequest.getColor())
+ .build());
+ return MagicConverter.convert(response, DossierStatusInfo.class);
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER_STATUS + "')")
+ public List getAllDossierStatusForTemplate(@PathVariable("dossierTemplateId") String dossierTemplateId) {
+
+ return dossierStatusPersistenceService.getAllDossierStatusForTemplate(dossierTemplateId)
+ .stream()
+ .map(d -> MagicConverter.convert(d, DossierStatusInfo.class))
+ .collect(Collectors.toList());
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER_STATUS + "')")
+ public List getAllDossierStatuses(@RequestBody List dossierTemplateIds) {
+
+ return dossierStatusPersistenceService.getAllDossierStatuses(dossierTemplateIds)
+ .stream()
+ .map(d -> MagicConverter.convert(d, DossierStatusInfo.class))
+ .collect(Collectors.toList());
+ }
+
+
+ @Override
+ @Transactional
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER_STATUS + "')")
+ public DossierStatusInfo getDossierStatus(@PathVariable("dossierStatusId") String dossierStatusId) {
+
+ return MagicConverter.convert(dossierStatusPersistenceService.getDossierStatus(dossierStatusId), DossierStatusInfo.class, new DossierStatusMapper());
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_DOSSIER_STATUS + "')")
+ public void deleteDossierStatus(@PathVariable("dossierStatusId") String dossierStatusId,
+ @RequestParam(value = DOSSIER_STATUS_REPLACE_ID, required = false) String replaceDossierStatusId) {
+
+ dossierStatusPersistenceService.deleteDossierStatus(dossierStatusId, replaceDossierStatusId);
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierTemplateController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierTemplateController.java
new file mode 100644
index 000000000..f217de344
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierTemplateController.java
@@ -0,0 +1,307 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_DOSSIER_TEMPLATES;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_DOSSIER_TEMPLATES;
+import static com.iqser.red.service.persistence.management.v1.processor.service.FeignExceptionHandler.processFeignException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.iqser.red.keycloak.commons.KeycloakSecurity;
+import com.iqser.red.service.persistence.management.v1.processor.acl.custom.dossier.DossierACLService;
+import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
+import com.iqser.red.service.persistence.management.v1.processor.exception.ConflictException;
+import com.iqser.red.service.persistence.management.v1.processor.service.DossierManagementService;
+import com.iqser.red.service.persistence.management.v1.processor.service.DossierTemplateManagementService;
+import com.iqser.red.service.persistence.management.v1.processor.service.DossierTemplateStatsService;
+import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
+import com.iqser.red.service.persistence.service.v1.api.external.resource.DossierTemplateResource;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemplateModel;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.DownloadResponse;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.CloneDossierTemplateRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.CreateOrUpdateDossierTemplateRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplate;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplateStats;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplateStatus;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.importexport.ExportDownloadRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.importexport.ImportDossierTemplateRequest;
+
+import feign.FeignException;
+import io.micrometer.core.annotation.Timed;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class DossierTemplateController implements DossierTemplateResource {
+
+ private final DossierTemplateManagementService dossierTemplateManagementService;
+ private final DossierTemplateStatsService dossierTemplateStatsService;
+ private final AuditPersistenceService auditPersistenceService;
+ private final DossierManagementService dossierManagementService;
+ private final DossierACLService dossierACLService;
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_DOSSIER_TEMPLATES + "')")
+ public DossierTemplateModel createOrUpdateDossierTemplate(@RequestBody DossierTemplateModel dossierTemplateModel) {
+
+ String userId = KeycloakSecurity.getUserId();
+ dossierTemplateModel.setCreatedBy(userId);
+ dossierTemplateModel.setModifiedBy(userId);
+ var dossierTemplate = new CreateOrUpdateDossierTemplateRequest();
+ BeanUtils.copyProperties(dossierTemplateModel, dossierTemplate);
+ try {
+ DossierTemplateModel response = convert(dossierTemplateManagementService.createOrUpdateDossierTemplate(dossierTemplate));
+
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(response.getDossierTemplateId())
+ .category(AuditCategory.DOSSIER_TEMPLATE.name())
+ .message("Dossier Template has been added or updated")
+ .build());
+ return response;
+ } catch (FeignException e) {
+ throw processFeignException(e);
+ }
+
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER_TEMPLATES + "')")
+ public List getAllDossierTemplates() {
+
+ return dossierTemplateManagementService.getAllDossierTemplates().stream().map(this::convert).collect(Collectors.toList());
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER_TEMPLATES + "')")
+ public DossierTemplateModel getDossierTemplate(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId) {
+
+ try {
+ return convert(dossierTemplateManagementService.getDossierTemplate(dossierTemplateId));
+ } catch (FeignException e) {
+ throw processFeignException(e);
+ }
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER_TEMPLATES + "')")
+ public void deleteDossierTemplate(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId) {
+
+ String userId = KeycloakSecurity.getUserId();
+
+ List dossiers = dossierManagementService.getAllDossiers(true, false);
+ if (dossiers != null && dossiers.stream().anyMatch(dossier -> dossier.getDossierTemplateId().equals(dossierTemplateId))) {
+ throw new ConflictException("Can not delete dossier template because there are dossiers based on it");
+ }
+
+ dossierTemplateManagementService.deleteDossierTemplate(dossierTemplateId, userId);
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DOSSIER_TEMPLATE.name())
+ .message("Dossier Template has been deleted")
+ .build());
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER_TEMPLATES + "')")
+ public void deleteDossierTemplates(@RequestBody List dossierTemplateIds) {
+
+ String userId = KeycloakSecurity.getUserId();
+ List errorIds = new ArrayList<>();
+
+ for (String dossierTemplateId : dossierTemplateIds) {
+ try {
+ List dossiers = dossierManagementService.getAllDossiers(true, false);
+ if (dossiers != null && dossiers.stream().anyMatch(dossier -> dossier.getDossierTemplateId().equals(dossierTemplateId))) {
+ throw new ConflictException("Can not delete dossier template because there are dossiers based on it");
+ }
+
+ dossierTemplateManagementService.deleteDossierTemplate(dossierTemplateId, userId);
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DOSSIER_TEMPLATE.name())
+ .message("Dossier template has been deleted")
+ .build());
+ } catch (FeignException e) {
+ errorIds.add(dossierTemplateId);
+ }
+ }
+
+ if (!errorIds.isEmpty()) {
+ throw new BadRequestException("Failed to delete dossier templates with ids: " + errorIds);
+ }
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_DOSSIER_TEMPLATES + "')")
+ public DossierTemplateModel cloneDossierTemplate(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId,
+ @RequestBody CloneDossierTemplateRequest cloneDossierTemplateRequest) {
+
+ String userId = KeycloakSecurity.getUserId();
+
+ try {
+
+ DossierTemplateModel response = convert(dossierTemplateManagementService.cloneDossierTemplate(dossierTemplateId, cloneDossierTemplateRequest));
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(userId)
+ .objectId(response.getDossierTemplateId())
+ .category(AuditCategory.DOSSIER_TEMPLATE.name())
+ .message("Dossier Template has been cloned")
+ .build());
+ return response;
+ } catch (FeignException e) {
+ throw processFeignException(e);
+ }
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER_TEMPLATES + "')")
+ public DossierTemplateStats getDossierTemplateStats(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId) {
+
+ try {
+ var stats = dossierTemplateStatsService.getDossierTemplateStats(dossierTemplateId);
+ enhanceDossierTemplateStatsWithACLMemberDetails(stats);
+ return stats;
+ } catch (FeignException e) {
+ throw processFeignException(e);
+ }
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER_TEMPLATES + "')")
+ public List getDossierTemplateStats() {
+
+ try {
+ var statsList = dossierTemplateStatsService.getDossierTemplateStats();
+ statsList.forEach(this::enhanceDossierTemplateStatsWithACLMemberDetails);
+ return statsList;
+ } catch (FeignException e) {
+ throw processFeignException(e);
+ }
+ }
+
+
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER_TEMPLATES + "')")
+ public DownloadResponse prepareExportDownload(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId) {
+
+ try {
+ ExportDownloadRequest request = ExportDownloadRequest.builder().dossierTemplateId(dossierTemplateId).userId(KeycloakSecurity.getUserId()).build();
+ var response = dossierTemplateManagementService.prepareExportDownload(request);
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(response.getValue())
+ .category(AuditCategory.DOWNLOAD.name())
+ .message("Export Download was prepared")
+ .details(Map.of("dossierTemplateId", request.getDossierTemplateId()))
+ .build());
+ return new DownloadResponse(response.getValue());
+ } catch (FeignException e) {
+ throw processFeignException(e);
+ }
+ }
+
+
+ @Timed
+ @PreAuthorize("hasAuthority('" + WRITE_DOSSIER_TEMPLATES + "')")
+ public DossierTemplateModel importDossierTemplate(@RequestPart(name = "file") MultipartFile file,
+ @RequestParam(value = DOSSIER_TEMPLATE_ID, required = false) String dossierTemplateId,
+ @RequestParam(value = "updateExistingDossierTemplate", required = false, defaultValue = "false") boolean updateExistingDossierTemplate) {
+
+ String originalFileName = file.getOriginalFilename();
+ if (originalFileName == null || originalFileName.isEmpty()) {
+ throw new BadRequestException("Could not upload file, no filename provided.");
+ }
+ var extension = originalFileName.substring(originalFileName.lastIndexOf(".") + 1).toLowerCase();
+ if ("zip".equalsIgnoreCase(extension)) {
+ if (StringUtils.isEmpty(dossierTemplateId) && updateExistingDossierTemplate) {
+ throw new BadRequestException("Could not update with dossier template empty");
+ }
+ try {
+ if (dossierTemplateId != null && updateExistingDossierTemplate) {
+ dossierTemplateManagementService.getDossierTemplate(dossierTemplateId); //check if the dossierTemplate to update exists
+ }
+
+ ImportDossierTemplateRequest request = ImportDossierTemplateRequest.builder()
+ .dossierTemplateId(dossierTemplateId)
+ .updateExistingDossierTemplate(updateExistingDossierTemplate)
+ .userId(KeycloakSecurity.getUserId())
+ .archive(file.getBytes())
+ .build();
+ DossierTemplate loadedDossierTemplate = dossierTemplateManagementService.importDossierTemplate(request);
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(loadedDossierTemplate.getId())
+ .category(AuditCategory.DOSSIER_TEMPLATE.name())
+ .message("Dossier template was imported")
+ .details(Map.of("dossierTemplateId", loadedDossierTemplate.getId()))
+ .build());
+ return convert(loadedDossierTemplate);
+ } catch (IOException e) {
+ throw new BadRequestException(e.getMessage(), e);
+ } catch (FeignException e) {
+ throw processFeignException(e);
+ }
+ } else {
+ throw new BadRequestException("Invalid extension");
+ }
+ }
+
+
+ private void enhanceDossierTemplateStatsWithACLMemberDetails(DossierTemplateStats stats) {
+
+ Set members = new HashSet<>();
+ stats.getDossiersInTemplate().forEach(d -> members.addAll(dossierACLService.getMembers(d)));
+ stats.setNumberOfPeople(members.size());
+ }
+
+
+ private DossierTemplateModel convert(DossierTemplate dossierTemplate) {
+
+ return DossierTemplateModel.builder()
+ .dossierTemplateId(dossierTemplate.getId())
+ .name(dossierTemplate.getName())
+ .description(dossierTemplate.getDescription())
+ .dateAdded(dossierTemplate.getDateAdded())
+ .dateModified(dossierTemplate.getDateModified())
+ .createdBy(dossierTemplate.getCreatedBy())
+ .modifiedBy(dossierTemplate.getModifiedBy())
+ .validFrom(dossierTemplate.getValidFrom())
+ .validTo(dossierTemplate.getValidTo())
+ .downloadFileTypes(dossierTemplate.getDownloadFileTypes())
+ .dossierTemplateStatus(DossierTemplateStatus.valueOf(dossierTemplate.getDossierTemplateStatus().name()))
+ .build();
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierTemplateStatsController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierTemplateStatsController.java
new file mode 100644
index 000000000..c7f9c886f
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DossierTemplateStatsController.java
@@ -0,0 +1,33 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_DOSSIER_TEMPLATES;
+
+import java.util.List;
+import java.util.Set;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.iqser.red.service.persistence.management.v1.processor.service.DossierTemplateStatsService;
+import com.iqser.red.service.persistence.service.v1.api.external.resource.DossierTemplateStatsResource;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplateDictionaryStats;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class DossierTemplateStatsController implements DossierTemplateStatsResource {
+
+ private final DossierTemplateStatsService dossierTemplateStatsService;
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_DOSSIER_TEMPLATES + "')")
+ public List getDossierTemplateStats(Set dossierTemplateIds) {
+
+ return dossierTemplateStatsService.getDossierTemplateStats(dossierTemplateIds);
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DownloadController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DownloadController.java
new file mode 100644
index 000000000..07e8be157
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DownloadController.java
@@ -0,0 +1,283 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.PROCESS_DOWNLOAD;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_DOWNLOAD_STATUS;
+import static com.iqser.red.service.persistence.management.v1.processor.utils.DownloadBufferUtils.fileProxyStreamForDownload;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.InputStreamResource;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.iqser.red.keycloak.commons.KeycloakSecurity;
+import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
+import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
+import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
+import com.iqser.red.service.persistence.management.v1.processor.service.DossierManagementService;
+import com.iqser.red.service.persistence.management.v1.processor.service.DownloadService;
+import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusService;
+import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
+import com.iqser.red.service.persistence.management.v1.processor.utils.StringEncodingUtils;
+import com.iqser.red.service.persistence.service.v1.api.external.resource.DownloadResource;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.DownloadResponse;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.DownloadStatusResponse;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.PrepareDownloadRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.PrepareDownloadWithOptionRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.RemoveDownloadRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSONPrimitive;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DownloadFileType;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.WorkflowStatus;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.download.DownloadRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.download.DownloadStatus;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.download.DownloadWithOptionRequest;
+import com.iqser.red.storage.commons.service.StorageService;
+
+import com.iqser.red.persistence.service.v1.external.api.impl.service.OneTimeTokenService;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class DownloadController implements DownloadResource {
+
+ private static final Pattern COLOR_PATTERN = Pattern.compile("^#[\\da-f]{6,6}$");
+
+ private final DossierManagementService dossierService;
+ private final FileStatusService fileStatusService;
+ private final DownloadService downloadService;
+ private final StorageService storageService;
+ private final AuditPersistenceService auditPersistenceService;
+ private final OneTimeTokenService oneTimeTokenDownloadService;
+ private final AccessControlService accessControlService;
+
+ @Value("${storage.backend:s3}")
+ private String storageBackend;
+
+
+ @PreAuthorize("hasAuthority('" + PROCESS_DOWNLOAD + "')")
+ public DownloadResponse prepareDownload(@RequestBody PrepareDownloadRequest request) {
+
+ // check the user is non-member or reviewer
+ accessControlService.verifyUserIsDossierOwnerOrApprover(request.getDossierId());
+ var response = downloadService.prepareDownload(convert(request));
+ auditPersistenceService.insertRecord(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(response.getValue())
+ .category(AuditCategory.DOWNLOAD.name())
+ .message("Download was prepared")
+ .details(Map.of("dossierId", request.getDossierId()))
+ .build());
+ return new DownloadResponse(response.getValue());
+ }
+
+
+ @PreAuthorize("hasAuthority('" + PROCESS_DOWNLOAD + "')")
+ public DownloadResponse prepareDownload(@RequestBody PrepareDownloadWithOptionRequest request) {
+
+ validateDossierId(request.getDossierId());
+
+ validateAndFilterFileIds(request);
+
+ if ((request.getDownloadFileTypes() == null || request.getDownloadFileTypes().isEmpty()) && (request.getReportTemplateIds() == null || request.getReportTemplateIds()
+ .isEmpty())) {
+ throw new BadRequestException("Download and report types cannot both be empty");
+ }
+
+ if (request.getRedactionPreviewColor() != null && !COLOR_PATTERN.matcher(request.getRedactionPreviewColor()).matches()) {
+ throw new BadRequestException("The specified redaction-preview-color is malformed.");
+ }
+
+ var response = downloadService.prepareDownload(convert(request));
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(response.getValue())
+ .category(AuditCategory.DOWNLOAD.name())
+ .message("Download was prepared")
+ .details(Map.of("dossierId", request.getDossierId()))
+ .build());
+ return new DownloadResponse(response.getValue());
+ }
+
+
+ private void validateDossierId(String dossierId) {
+
+ if (StringUtils.isBlank(dossierId)) {
+ throw new BadRequestException("Empty dossier id");
+ }
+ dossierService.getDossierById(dossierId, true, true);
+ accessControlService.verifyUserIsDossierOwnerOrApprover(dossierId);
+ }
+
+
+ private void validateAndFilterFileIds(PrepareDownloadWithOptionRequest request) {
+
+ List validFiles = fileStatusService.getDossierStatus(request.getDossierId());
+ var fileIds = request.getFileIds();
+ if (fileIds != null && !fileIds.isEmpty()) { // validate the ids provided
+ validFiles = validFiles.stream().filter(f -> fileIds.contains(f.getId())).collect(Collectors.toList());
+ if (validFiles.isEmpty()) {
+ throw new NotFoundException("No file id provided is found");
+ }
+ } // otherwise consider the files from dossier
+
+ var validFilesAndNotProcessed = validFiles.stream().filter(f -> !(f.getAnalysisVersion() > 0 && f.getNumberOfAnalyses() > 0)).collect(Collectors.toList());
+ if (!validFilesAndNotProcessed.isEmpty()) {
+ throw new BadRequestException("At least a file is in its initial analysis process");
+ }
+
+ request.setFileIds(validFiles.stream().map(FileModel::getId).collect(Collectors.toList()));
+ var approvedFiles = validFiles.stream()
+ .filter(f -> f.getWorkflowStatus().equals(WorkflowStatus.APPROVED))
+ .toList();
+ // special corner case: unapproved files, no reports and only REDACTED type selected
+ if (approvedFiles.isEmpty() && (request.getReportTemplateIds() == null || request.getReportTemplateIds()
+ .isEmpty()) && request.getDownloadFileTypes() != null && request.getDownloadFileTypes().size() == 1 && request.getDownloadFileTypes()
+ .contains(DownloadFileType.REDACTED)) {
+ throw new BadRequestException("Unapproved files in redacted state with no reports cannot be included");
+ }
+ }
+
+
+ private DownloadWithOptionRequest convert(PrepareDownloadWithOptionRequest request) {
+
+ return DownloadWithOptionRequest.builder()
+ .dossierId(request.getDossierId())
+ .userId(KeycloakSecurity.getUserId())
+ .fileIds(request.getFileIds())
+ .downloadFileTypes(request.getDownloadFileTypes())
+ .reportTemplateIds(request.getReportTemplateIds())
+ .redactionPreviewColor(request.getRedactionPreviewColor())
+ .build();
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + PROCESS_DOWNLOAD + "')")
+ public void deleteDownloadStatus(@RequestBody RemoveDownloadRequest removeDownloadRequest) {
+
+ removeDownloadRequest.getStorageIds().forEach(storageId -> {
+ downloadService.deleteDownloadStatus(JSONPrimitive.of(storageId));
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(storageId)
+ .category(AuditCategory.DOWNLOAD.name())
+ .message("Remove Prepared Download")
+ .build());
+ });
+
+ }
+
+
+ @PreAuthorize("hasAuthority('" + READ_DOWNLOAD_STATUS + "')")
+ public DownloadStatusResponse getDownloadStatus() {
+
+ var resp = downloadService.getDownloadStatus(KeycloakSecurity.getUserId());
+ return new DownloadStatusResponse(resp);
+ }
+
+
+ @PreAuthorize("hasAuthority('" + PROCESS_DOWNLOAD + "')")
+ public ResponseEntity downloadFile(@RequestParam(STORAGE_ID) String storageId,
+ @RequestParam(value = "inline", required = false, defaultValue = FALSE) boolean inline) {
+
+ var userId = KeycloakSecurity.getUserId();
+
+ var downloadStatus = getDownloadStatus(storageId, userId);
+ var fileDownloadStream = getFileForDownload(storageId, userId);
+
+ return getResponseEntity(inline, fileDownloadStream, downloadStatus.getFilename(), MediaType.parseMediaType("application/zip"));
+ }
+
+
+ private DownloadStatus getDownloadStatus(String storageId, String userId) {
+ // TODO Add endpoint to get single download status for userId and storageId.
+ var downloadStatusResponse = downloadService.getDownloadStatus(userId);
+ Optional downloadStatusOptional = downloadStatusResponse.stream().filter(ds -> ds.getStorageId().equals(storageId)).findFirst();
+
+ return downloadStatusOptional.orElseThrow(() -> new NotFoundException("Download status not found for this user"));
+ }
+
+
+ private InputStreamResource getFileForDownload(String storageId, String userId) {
+
+ try {
+ var response = storageService.getObject(storageId);
+
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(userId)
+ .objectId(storageId)
+ .category(AuditCategory.DOWNLOAD.name())
+ .message("File was downloaded.")
+ .build());
+ downloadService.setDownloaded(JSONPrimitive.of(storageId));
+
+ return response;
+ } catch (Exception e) {
+ throw new NotFoundException(e.getMessage(), e);
+ }
+ }
+
+
+ @SneakyThrows
+ private ResponseEntity getResponseEntity(boolean inline, InputStreamResource resource, String filename, MediaType mediaType) {
+
+ HttpHeaders httpHeaders = new HttpHeaders();
+ httpHeaders.setContentType(mediaType);
+ if (filename != null) {
+ httpHeaders.add("Content-Disposition", inline ? "inline" : "attachment" + "; filename*=utf-8''" + StringEncodingUtils.urlEncode(filename));
+ }
+
+ return new ResponseEntity<>(fileProxyStreamForDownload(resource.getInputStream()), httpHeaders, HttpStatus.OK);
+
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + PROCESS_DOWNLOAD + "')")
+ public JSONPrimitive generateOneTimeToken(@RequestBody JSONPrimitive storageIdWrapper) {
+
+ log.debug("Generate one time token");
+ return JSONPrimitive.of(oneTimeTokenDownloadService.createToken(storageIdWrapper.getValue(), KeycloakSecurity.getUserId()).getTokenId());
+ }
+
+
+ @Override
+ public ResponseEntity downloadFileUsingOTT(@PathVariable(OTT) String oneTimeToken,
+ @RequestParam(value = "inline", required = false, defaultValue = FALSE) boolean inline) {
+
+ log.debug("downloadFileUsingOTT '{}'", oneTimeToken);
+ var token = oneTimeTokenDownloadService.getToken(oneTimeToken);
+ var downloadStatus = getDownloadStatus(token.getStorageId(), token.getUserId());
+ var fileDownloadStream = getFileForDownload(token.getStorageId(), token.getUserId());
+
+ return getResponseEntity(inline, fileDownloadStream, downloadStatus.getFilename(), MediaType.parseMediaType("application/zip"));
+
+ }
+
+
+ private DownloadRequest convert(PrepareDownloadRequest request) {
+
+ return DownloadRequest.builder().dossierId(request.getDossierId()).userId(KeycloakSecurity.getUserId()).fileIds(request.getFileIds()).build();
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/ExternalControllerAdvice.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/ExternalControllerAdvice.java
new file mode 100644
index 000000000..5451851e8
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/ExternalControllerAdvice.java
@@ -0,0 +1,134 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import java.time.OffsetDateTime;
+import java.util.stream.Collectors;
+
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.WebDataBinder;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.InitBinder;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.multipart.support.MissingServletRequestPartException;
+
+import com.fasterxml.jackson.databind.exc.InvalidFormatException;
+import com.iqser.red.commons.spring.ErrorMessage;
+import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
+import com.iqser.red.service.persistence.management.v1.processor.exception.ConflictException;
+import com.iqser.red.service.persistence.management.v1.processor.exception.NotAllowedException;
+import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
+
+@RestControllerAdvice
+@Order(Ordered.HIGHEST_PRECEDENCE)
+public class ExternalControllerAdvice {
+
+ @ResponseBody
+ @ResponseStatus(value = HttpStatus.NOT_FOUND)
+ @ExceptionHandler(value = NotFoundException.class)
+ public ErrorMessage handleContentNotFoundException(NotFoundException e) {
+
+ return new ErrorMessage(OffsetDateTime.now(), e.getMessage());
+ }
+
+
+ /* error handling */
+
+
+ @ResponseBody
+ @ResponseStatus(value = HttpStatus.BAD_REQUEST)
+ @ExceptionHandler(value = BadRequestException.class)
+ public ErrorMessage handleBadRequestException(BadRequestException e) {
+
+ return new ErrorMessage(OffsetDateTime.now(), e.getMessage());
+ }
+
+
+ @ResponseBody
+ @ResponseStatus(value = HttpStatus.CONFLICT)
+ @ExceptionHandler(value = {ConflictException.class})
+ protected ErrorMessage handleConflictException(ConflictException e) {
+
+ return new ErrorMessage(OffsetDateTime.now(), e.getMessage());
+ }
+
+
+ @ResponseBody
+ @ResponseStatus(value = HttpStatus.FORBIDDEN)
+ @ExceptionHandler({AccessDeniedException.class})
+ public ErrorMessage handleAccessDeniedException(AccessDeniedException e) {
+
+ return new ErrorMessage(OffsetDateTime.now(), e.getMessage());
+ }
+
+
+ @ResponseBody
+ @ResponseStatus(value = HttpStatus.FORBIDDEN)
+ @ExceptionHandler({NotAllowedException.class})
+ public ErrorMessage handleNotAllowedException(NotAllowedException e) {
+
+ return new ErrorMessage(OffsetDateTime.now(), e.getMessage());
+ }
+
+
+ @ResponseBody
+ @ResponseStatus(value = HttpStatus.BAD_REQUEST)
+ @ExceptionHandler({MethodArgumentNotValidException.class})
+ public ErrorMessage handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
+
+ var errorList = e.getFieldErrors();
+ String errorListAsString = errorList.stream().map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage()).collect(Collectors.joining(", "));
+ return new ErrorMessage(OffsetDateTime.now(), String.format("You have empty/wrong formatted parameters: %s", errorListAsString));
+ }
+
+
+ @ResponseBody
+ @ResponseStatus(value = HttpStatus.BAD_REQUEST)
+ @ExceptionHandler({MissingServletRequestPartException.class})
+ public ErrorMessage handleMissingServletRequestPartException(MissingServletRequestPartException e) {
+
+ return new ErrorMessage(OffsetDateTime.now(), e.getMessage());
+ }
+
+
+ @ResponseBody
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ExceptionHandler({HttpMessageNotReadableException.class})
+ public ErrorMessage handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
+
+ var cause = e.getCause();
+ if (cause instanceof InvalidFormatException) {
+ InvalidFormatException invalidFormatException = (InvalidFormatException) cause;
+
+ Class> targetType = invalidFormatException.getTargetType();
+ if (targetType != null && targetType.isEnum()) {
+ return new ErrorMessage(OffsetDateTime.now(), String.format("Unsupported value for %s", targetType.getSimpleName()));
+ }
+
+ return new ErrorMessage(OffsetDateTime.now(), cause.getMessage());
+ }
+
+ return new ErrorMessage(OffsetDateTime.now(), e.getMessage());
+ }
+
+
+ @Order(10000)
+ public static class BinderControllerAdvice {
+
+ @InitBinder
+ public void setAllowedFields(WebDataBinder dataBinder) {
+ // This code protects Spring Core from a "Remote Code Execution" attack (dubbed "Spring4Shell").
+ // By applying this mitigation, you prevent the "Class Loader Manipulation" attack vector from firing.
+ // For more details, see this post: https://www.lunasec.io/docs/blog/spring-rce-vulnerabilities/
+ String[] denylist = new String[]{"class.*", "Class.*", "*.class.*", "*.Class.*"};
+ dataBinder.setDisallowedFields(denylist);
+ }
+
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/FileAttributesController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/FileAttributesController.java
new file mode 100644
index 000000000..08d977a65
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/FileAttributesController.java
@@ -0,0 +1,167 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_FILE_ATTRIBUTES_CONFIG;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_FILE_ATTRIBUTES;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_FILE_ATTRIBUTES_CONFIG;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.iqser.red.keycloak.commons.KeycloakSecurity;
+import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.FileAttributesGeneralConfigurationEntity;
+import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileAttributeConfigEntity;
+import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
+import com.iqser.red.service.persistence.management.v1.processor.exception.NotAllowedException;
+import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
+import com.iqser.red.service.persistence.management.v1.processor.service.FileAttributesManagementService;
+import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusService;
+import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
+import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileAttributeConfigPersistenceService;
+import com.iqser.red.service.persistence.management.v1.processor.utils.MagicConverter;
+import com.iqser.red.service.persistence.service.v1.api.external.resource.FileAttributesResource;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttributes;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttributesConfig;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.FileAttributesGeneralConfiguration;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileAttributeConfig;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.WorkflowStatus;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class FileAttributesController implements FileAttributesResource {
+
+ private final FileAttributesManagementService fileAttributesManagementService;
+ private final FileAttributeConfigPersistenceService fileAttributeConfigPersistenceService;
+ private final AuditPersistenceService auditPersistenceService;
+ private final FileStatusService fileStatusService;
+ private final AccessControlService accessControlService;
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_FILE_ATTRIBUTES_CONFIG + "')")
+ public FileAttributesConfig setFileAttributesConfig(String dossierTemplateId, FileAttributesConfig fileAttributesConfig) {
+
+ if (StringUtils.isEmpty(fileAttributesConfig.getEncoding()) || !encodingList.contains(fileAttributesConfig.getEncoding().trim())) {
+ throw new BadRequestException("Invalid encoding setting");
+ }
+ fileAttributeConfigPersistenceService.setFileAttributesGeneralConfig(dossierTemplateId,
+ MagicConverter.convert(fileAttributesConfig, FileAttributesGeneralConfigurationEntity.class));
+ var result = fileAttributeConfigPersistenceService.setFileAttributesConfig(dossierTemplateId,
+ MagicConverter.convert(fileAttributesConfig.getFileAttributeConfigs(), FileAttributeConfigEntity.class));
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DOSSIER_TEMPLATE.name())
+ .message("Changed file attributes base configuration & attribute configuration ( CSV Import )")
+ .build());
+ return FileAttributesConfig.builder()
+ .filenameMappingColumnHeaderName(fileAttributesConfig.getFilenameMappingColumnHeaderName())
+ .delimiter(fileAttributesConfig.getDelimiter())
+ .encoding(fileAttributesConfig.getEncoding())
+ .fileAttributeConfigs(MagicConverter.convert(result, FileAttributeConfig.class))
+ .build();
+
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_FILE_ATTRIBUTES_CONFIG + "')")
+ public FileAttributeConfig addOrUpdateFileAttribute(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody FileAttributeConfig fileAttribute) {
+
+ var result = fileAttributeConfigPersistenceService.addOrUpdateFileAttribute(dossierTemplateId, MagicConverter.convert(fileAttribute, FileAttributeConfigEntity.class));
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DOSSIER_TEMPLATE.name())
+ .message("File attributes added/updated")
+ .details(Map.of("FileAttributeName", fileAttribute.getLabel() != null ? fileAttribute.getLabel() : "", "dossierTemplateId", dossierTemplateId))
+ .build());
+
+ return MagicConverter.convert(result, FileAttributeConfig.class);
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_FILE_ATTRIBUTES_CONFIG + "')")
+ public void deleteFileAttribute(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @PathVariable(FILE_ATTRIBUTE_ID) String fileAttributeId) {
+
+ fileAttributeConfigPersistenceService.deleteFileAttribute(fileAttributeId);
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DOSSIER_TEMPLATE.name())
+ .message("File attributes removed")
+ .details(Map.of("FileAttributeId", fileAttributeId))
+ .build());
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_FILE_ATTRIBUTES_CONFIG + "')")
+ public void deleteFileAttributes(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody List fileAttributeIds) {
+
+ fileAttributeConfigPersistenceService.deleteFileAttributes(fileAttributeIds);
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DOSSIER_TEMPLATE.name())
+ .message("File attributes removed")
+ .details(Map.of("FileAttributeId", fileAttributeIds))
+ .build());
+ }
+
+
+ @PreAuthorize("hasAuthority('" + READ_FILE_ATTRIBUTES_CONFIG + "')")
+ public FileAttributesConfig getFileAttributesConfiguration(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId) {
+
+ var fileAttributeConfigs = fileAttributeConfigPersistenceService.getFileAttributes(dossierTemplateId);
+ FileAttributesGeneralConfiguration generalConfig = new FileAttributesGeneralConfiguration();
+ try {
+ generalConfig = MagicConverter.convert(fileAttributeConfigPersistenceService.getFileAttributesGeneralConfiguration(dossierTemplateId),
+ FileAttributesGeneralConfiguration.class);
+ } catch (Exception e) {
+ log.debug("No general config defined", e);
+ }
+ return FileAttributesConfig.builder()
+ .filenameMappingColumnHeaderName(generalConfig.getFilenameMappingColumnHeaderName())
+ .delimiter(generalConfig.getDelimiter())
+ .encoding(generalConfig.getEncoding())
+ .fileAttributeConfigs(MagicConverter.convert(fileAttributeConfigs, FileAttributeConfig.class))
+ .build();
+ }
+
+
+ @PreAuthorize("hasAuthority('" + WRITE_FILE_ATTRIBUTES + "')")
+ public void setFileAttributes(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody FileAttributes fileAttributes) {
+
+ var file = fileStatusService.getStatus(fileId);
+
+ if (file.getWorkflowStatus().equals(WorkflowStatus.APPROVED)) {
+ throw new NotAllowedException("File is approved. File attributes are not editable anymore.");
+ }
+ if (file.getWorkflowStatus().equals(WorkflowStatus.UNDER_APPROVAL)) {
+ accessControlService.verifyUserIsApprover(dossierId);
+ }
+ accessControlService.verifyUserIsMemberOrApprover(dossierId);
+ fileAttributesManagementService.setFileAttributes(dossierId, fileId, fileAttributes.getAttributeIdToValue());
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("File attributes has been edited for a document.")
+ .build());
+
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/FileManagementController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/FileManagementController.java
new file mode 100644
index 000000000..af5462851
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/FileManagementController.java
@@ -0,0 +1,238 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DELETE_FILE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.DOWNLOAD_ORIGINAL_FILE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.ROTATE_PAGE;
+import static com.iqser.red.service.persistence.management.v1.processor.service.FeignExceptionHandler.processFeignException;
+import static com.iqser.red.service.persistence.management.v1.processor.utils.DownloadBufferUtils.fileProxyStreamForDownload;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.springframework.core.io.InputStreamResource;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.google.common.collect.Sets;
+import com.iqser.red.keycloak.commons.KeycloakSecurity;
+import com.iqser.red.service.persistence.management.v1.processor.client.pdftronredactionservice.PDFTronClient;
+import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
+import com.iqser.red.service.persistence.management.v1.processor.exception.NotAllowedException;
+import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
+import com.iqser.red.service.persistence.management.v1.processor.service.FileService;
+import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusManagementService;
+import com.iqser.red.service.persistence.management.v1.processor.service.ReanalysisService;
+import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
+import com.iqser.red.service.persistence.management.v1.processor.utils.StorageIdUtils;
+import com.iqser.red.service.persistence.management.v1.processor.utils.StringEncodingUtils;
+import com.iqser.red.service.persistence.service.v1.api.external.resource.FileManagementResource;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.RotatePagesRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;
+import com.iqser.red.storage.commons.service.StorageService;
+
+import feign.FeignException;
+import io.micrometer.core.annotation.Timed;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class FileManagementController implements FileManagementResource {
+
+ private static final String DOWNLOAD_HEADER_NAME = "Content-Disposition";
+
+ private final FileService fileService;
+ private final AuditPersistenceService auditPersistenceService;
+ private final AccessControlService accessControlService;
+ private final PDFTronClient pdfTronClient;
+ private final ReanalysisService reanalysisService;
+ private final StorageService storageService;
+ private final FileStatusManagementService fileStatusManagementService;
+
+
+ @Timed
+ @Override
+ @PreAuthorize("hasAuthority('" + DELETE_FILE + "')")
+ public void deleteFile(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
+
+ fileService.deleteFile(dossierId, fileId);
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierId)
+ .category(AuditCategory.DOSSIER.name())
+ .message("File has been deleted.")
+ .details(Map.of("fileId", fileId))
+ .build());
+ }
+
+
+ @Timed
+ @Override
+ @PreAuthorize("hasAuthority('" + DELETE_FILE + "')")
+ public void deleteFiles(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody List fileIds) {
+
+ List errorIds = new ArrayList<>();
+ for (String fileId : fileIds) {
+ try {
+ fileService.deleteFile(dossierId, fileId);
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierId)
+ .category(AuditCategory.DOSSIER.name())
+ .message("Files have been deleted.")
+ .details(Map.of("Size", fileIds.size()))
+ .build());
+ } catch (Exception e) {
+ errorIds.add(fileId);
+ }
+ }
+ if (!errorIds.isEmpty()) {
+ throw new BadRequestException("Failed to delete files with ids: " + errorIds);
+ }
+ }
+
+
+ @Timed
+ @Override
+ @PreAuthorize("hasAuthority('" + DOWNLOAD_ORIGINAL_FILE + "')")
+ public ResponseEntity> downloadOriginal(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestParam(value = "inline", required = false, defaultValue = FALSE) boolean inline) {
+
+ try {
+
+ var file = fileStatusManagementService.getFileStatus(fileId);
+ var untouchedFileStream = storageService.getObject(StorageIdUtils.getStorageId(dossierId, fileId, FileType.ORIGIN));
+ return getResponseEntity(inline, untouchedFileStream, file.getFilename(), MediaType.APPLICATION_PDF);
+ } catch (FeignException e) {
+ if (e.status() == HttpStatus.NOT_FOUND.value()) {
+ return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
+ }
+ log.debug(e.getMessage(), e);
+ return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+
+ @SneakyThrows
+ private ResponseEntity> getResponseEntity(boolean inline, InputStreamResource resource, String filename, MediaType mediaType) {
+
+ HttpHeaders httpHeaders = new HttpHeaders();
+ httpHeaders.setContentType(mediaType);
+
+ if (filename != null) {
+ httpHeaders.add(DOWNLOAD_HEADER_NAME, inline ? "inline" : "attachment" + "; filename*=utf-8''" + StringEncodingUtils.urlEncode(filename));
+ }
+
+ return new ResponseEntity<>(fileProxyStreamForDownload(resource.getInputStream()), httpHeaders, HttpStatus.OK);
+
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + DELETE_FILE + "')")
+ public void hardDeleteFiles(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(FILE_IDS) Set fileIds) {
+
+ for (String fileId : fileIds) {
+ if (fileStatusManagementService.getFileStatus(fileId).getAssignee() != null) {
+ accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
+ }
+ }
+ fileService.hardDeleteFiles(dossierId, fileIds);
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierId)
+ .category(AuditCategory.DOSSIER.name())
+ .message("Files has been hard deleted.")
+ .details(Map.of("FileIds", fileIds))
+ .build());
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + DELETE_FILE + "')")
+ public void restoreFiles(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody Set fileIds) {
+
+ verifyUserIsDossierOwnerOrApproverOrAssignedReviewer(dossierId, fileIds);
+ fileService.undeleteFiles(dossierId, fileIds);
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierId)
+ .category(AuditCategory.DOSSIER.name())
+ .message("Files has been restored.")
+ .details(Map.of("FileIds", fileIds))
+ .build());
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + ROTATE_PAGE + "')")
+ public void rotatePages(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody RotatePagesRequest rotatePagesRequest) {
+
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+ accessControlService.verifyUserIsReviewer(dossierId, fileId);
+
+ try {
+ pdfTronClient.rotate(com.iqser.red.service.pdftron.redaction.v1.api.model.RotatePagesRequest.builder()
+ .dossierId(dossierId)
+ .fileId(fileId)
+ .pages(rotatePagesRequest.getPages())
+ .build());
+
+ fileStatusManagementService.updateFileModificationDate(fileId);
+
+ FileModel fileModel = fileStatusManagementService.getFileStatus(fileId);
+
+ if (!fileModel.isExcludedFromAutomaticAnalysis()) {
+ if (fileModel.getOcrStartTime() != null || fileModel.getOcrEndTime() != null) {
+ reanalysisService.ocrFile(dossierId, fileId, true);
+ } else {
+ reanalysisService.reanalyzeFiles(dossierId, Sets.newHashSet(fileId), true);
+ }
+ }
+
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Pages have been rotated.")
+ .details(Map.of("Pages", rotatePagesRequest.getPages().keySet()))
+ .build());
+
+ } catch (FeignException e) {
+ throw processFeignException(e);
+ }
+ }
+
+
+ private void verifyUserIsDossierOwnerOrApproverOrAssignedReviewer(String dossierId, Set fileIds) {
+
+ try {
+ accessControlService.verifyUserIsDossierOwnerOrApprover(dossierId);
+ } catch (AccessDeniedException e1) {
+ try {
+ for (String fileId : fileIds) {
+ accessControlService.verifyUserIsReviewer(dossierId, fileId);
+ }
+ } catch (NotAllowedException e2) {
+ throw new NotAllowedException("User must be dossier owner, approver or assigned reviewer of the file.");
+ }
+ }
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/GeneralSettingsController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/GeneralSettingsController.java
new file mode 100644
index 000000000..35d1d79db
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/GeneralSettingsController.java
@@ -0,0 +1,40 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_GENERAL_CONFIGURATION;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_GENERAL_CONFIGURATION;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.iqser.red.service.persistence.service.v1.api.external.resource.GeneralSettingsResource;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.GeneralConfigurationModel;
+
+import com.iqser.red.persistence.service.v1.external.api.impl.service.GeneralConfigurationService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class GeneralSettingsController implements GeneralSettingsResource {
+
+ private final GeneralConfigurationService generalConfigurationService;
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_GENERAL_CONFIGURATION + "')")
+ public GeneralConfigurationModel getGeneralConfigurations() {
+
+ return generalConfigurationService.getGeneralConfigurations();
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_GENERAL_CONFIGURATION + "')")
+ public void updateGeneralConfigurations(@RequestBody GeneralConfigurationModel generalConfigurationModel) {
+
+ generalConfigurationService.updateGeneralConfigurations(generalConfigurationModel);
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/HighlightsController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/HighlightsController.java
new file mode 100644
index 000000000..a8ad67830
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/HighlightsController.java
@@ -0,0 +1,93 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.*;
+import static com.iqser.red.service.persistence.management.v1.processor.service.FeignExceptionHandler.processFeignException;
+import static com.iqser.red.service.persistence.management.v1.processor.utils.StorageIdUtils.getStorageId;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.iqser.red.service.pdftron.redaction.v1.api.model.highlights.Highlights;
+import com.iqser.red.service.pdftron.redaction.v1.api.model.highlights.TextHighlightConversionOperation;
+import com.iqser.red.service.pdftron.redaction.v1.api.model.highlights.TextHighlightConversionRequest;
+import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
+import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusService;
+import com.iqser.red.service.persistence.management.v1.processor.service.ReanalysisService;
+import com.iqser.red.service.persistence.service.v1.api.external.resource.HighlightsResource;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.AnnotationIds;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.DeleteImportedRedactionsRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;
+import com.iqser.red.storage.commons.service.StorageService;
+
+import feign.FeignException;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+
+@RestController
+@RequiredArgsConstructor
+public class HighlightsController implements HighlightsResource {
+
+ private final StorageService storageService;
+ private final ObjectMapper objectMapper;
+ private final AccessControlService accessControlService;
+ private final FileStatusService fileStatusService;
+ private final ReanalysisService reanalysisService;
+
+
+ @SneakyThrows
+ @PreAuthorize("hasAuthority('" + GET_HIGHLIGHTS + "')")
+ public Highlights getHighlights(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
+
+ fileStatusService.getStatus(fileId);
+
+ if (storageService.objectExists(getStorageId(dossierId, fileId, FileType.TEXT_HIGHLIGHTS))) {
+ return objectMapper.readValue(storageService.getObject(getStorageId(dossierId, fileId, FileType.TEXT_HIGHLIGHTS)).getInputStream(), Highlights.class);
+ }
+
+ return new Highlights();
+ }
+
+ @PreAuthorize("hasAuthority('" + CONVERT_HIGHLIGHTS + "')")
+ public void convertHighlights(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody AnnotationIds annotationIds) {
+
+ try {
+ accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+
+ reanalysisService.convertTextHighlights(new TextHighlightConversionRequest(dossierId, fileId, annotationIds.getIds(), TextHighlightConversionOperation.CONVERT));
+ } catch (FeignException e) {
+ throw processFeignException(e);
+ }
+ }
+
+ @PreAuthorize("hasAuthority('" + DELETE_HIGHLIGHTS + "')")
+ public void deleteHighlights(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody AnnotationIds annotationIds) {
+
+ try {
+ accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+
+ reanalysisService.convertTextHighlights(new TextHighlightConversionRequest(dossierId, fileId, annotationIds.getIds(), TextHighlightConversionOperation.REMOVE));
+ } catch (FeignException e) {
+ throw processFeignException(e);
+ }
+
+ }
+
+ @PreAuthorize("hasAuthority('" + DELETE_IMPORTED_REDACTIONS + "')")
+ public void deleteImportedRedactions(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody AnnotationIds annotationIds) {
+
+ try {
+ accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+
+ reanalysisService.deleteImportedRedactions(new DeleteImportedRedactionsRequest(dossierId, fileId, annotationIds.getIds()));
+ } catch (FeignException e) {
+ throw processFeignException(e);
+ }
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/LegalBasisMappingController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/LegalBasisMappingController.java
new file mode 100644
index 000000000..69c9cc4aa
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/LegalBasisMappingController.java
@@ -0,0 +1,81 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_LEGAL_BASIS;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_LEGAL_BASIS;
+
+import java.util.List;
+
+import javax.transaction.Transactional;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.iqser.red.keycloak.commons.KeycloakSecurity;
+import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
+import com.iqser.red.service.persistence.management.v1.processor.service.persistence.LegalBasisMappingPersistenceService;
+import com.iqser.red.service.persistence.management.v1.processor.utils.MagicConverter;
+import com.iqser.red.service.persistence.service.v1.api.external.resource.LegalBasisMappingResource;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.legalbasis.LegalBasis;
+
+import lombok.RequiredArgsConstructor;
+
+@RestController
+@RequiredArgsConstructor
+public class LegalBasisMappingController implements LegalBasisMappingResource {
+
+ private final LegalBasisMappingPersistenceService legalBasisMappingPersistenceService;
+ private final AuditPersistenceService auditPersistenceService;
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_LEGAL_BASIS + "')")
+ public void deleteLegalBasis(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @RequestBody List legalBasisNames) {
+
+ legalBasisMappingPersistenceService.deleteLegalBasis(dossierTemplateId, legalBasisNames);
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DOSSIER_TEMPLATE.name())
+ .message("Legal basis mapping has been changed.")
+ .build());
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + WRITE_LEGAL_BASIS + "')")
+ public void addOrUpdateLegalBasis(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @RequestBody LegalBasis legalBasis) {
+
+ legalBasisMappingPersistenceService.addOrUpdateLegalBasis(dossierTemplateId, legalBasis);
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DOSSIER_TEMPLATE.name())
+ .message("Legal basis mapping has been changed.")
+ .build());
+ }
+
+
+ @PreAuthorize("hasAuthority('" + WRITE_LEGAL_BASIS + "')")
+ public void setLegalBasisMapping(@RequestBody List legalBasisMapping, @PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId) {
+
+ legalBasisMappingPersistenceService.setLegalBasisMapping(dossierTemplateId, legalBasisMapping);
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(dossierTemplateId)
+ .category(AuditCategory.DOSSIER_TEMPLATE.name())
+ .message("Legal basis mapping has been changed.")
+ .build());
+ }
+
+ @Transactional
+ @PreAuthorize("hasAuthority('" + READ_LEGAL_BASIS + "')")
+ public List getLegalBasisMapping(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId) {
+
+ return MagicConverter.convert(legalBasisMappingPersistenceService.getLegalBasisMapping(dossierTemplateId), LegalBasis.class);
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/LicenseController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/LicenseController.java
new file mode 100644
index 000000000..fd329eeb9
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/LicenseController.java
@@ -0,0 +1,133 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_LICENSE;
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.UPDATE_LICENSE;
+
+import java.time.OffsetDateTime;
+import java.time.temporal.ChronoUnit;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.core.env.Environment;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.iqser.red.service.persistence.service.v1.api.external.resource.LicenseResource;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.license.Feature;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.license.FeatureType;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.license.License;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.license.RedactionLicenseModel;
+import com.iqser.red.storage.commons.service.StorageService;
+
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class LicenseController implements LicenseResource {
+
+ private static final String LICENSE_OBJECT_ID = "license/redact-manager-license.json";
+ private static final String DEMO_PDFTRON_LICENSE = "demo:1650351709282:7bd235e003000000004ec28a6743e1163a085e2115de2536ab6e2cfe5a";
+ private static final String LICENSE_CUSTOMER = "LICENSE_CUSTOMER";
+ private static final String LICENSE_START = "LICENSE_START";
+ private static final String LICENSE_END = "LICENSE_END";
+ private static final String LICENSE_PAGE_COUNT = "LICENSE_PAGE_COUNT";
+ private static final String PDFTRON_FRONTEND_LICENSE = "PDFTRON_FRONTEND_LICENSE";
+ private static final String PDFTRON_FEATURE = "pdftron";
+ private static final String PROCESSING_PAGES_FEATURE = "processingPages";
+ private static final String DEFAULT_PROCESSING_PAGES = "200000";
+ private final StorageService storageService;
+ private final Environment environment;
+
+
+ @Override
+ @SneakyThrows
+ @PreAuthorize("hasAuthority('" + UPDATE_LICENSE + "')")
+ public void updateLicense(RedactionLicenseModel licenseModel) {
+
+ storageService.storeJSONObject(LICENSE_OBJECT_ID, licenseModel);
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_LICENSE + "')")
+ public RedactionLicenseModel getLicense() {
+
+ if (storageService.objectExists(LICENSE_OBJECT_ID)) {
+ try {
+ return storageService.readJSONObject(LICENSE_OBJECT_ID, RedactionLicenseModel.class);
+ } catch (Exception e) {
+ return generateDemoLicense();
+ }
+ } else {
+ return generateDemoLicense();
+ }
+ }
+
+
+ private RedactionLicenseModel generateDemoLicense() {
+
+ License demo = new License();
+ demo.setId("RedactManager");
+ demo.setName("RedactManager License");
+ demo.setProduct("RedactManager");
+
+ var licensedTo = environment.getProperty(LICENSE_CUSTOMER, "RedactManager Demo License");
+ demo.setLicensedTo(licensedTo);
+
+ var licenseStartDate = parseDate(environment.getProperty(LICENSE_START));
+ var start = licenseStartDate != null ? licenseStartDate : OffsetDateTime.now().withDayOfYear(1).withHour(0).withMinute(0).withSecond(0).truncatedTo(ChronoUnit.SECONDS);
+ demo.setValidFrom(start);
+
+ var licenseEndDate = parseDate(environment.getProperty(LICENSE_END));
+ var end = licenseEndDate != null ? licenseEndDate : OffsetDateTime.now()
+ .withMonth(12)
+ .withDayOfMonth(31)
+ .withHour(0)
+ .withMinute(0)
+ .withSecond(0)
+ .truncatedTo(ChronoUnit.SECONDS);
+ demo.setValidUntil(end);
+
+ var pdftronLicense = environment.getProperty(PDFTRON_FRONTEND_LICENSE, DEMO_PDFTRON_LICENSE);
+ demo.getFeatures().add(Feature.builder().name(PDFTRON_FEATURE).type(FeatureType.STRING).value(pdftronLicense).build());
+
+ var pageCount = Long.parseLong(environment.getProperty(LICENSE_PAGE_COUNT, DEFAULT_PROCESSING_PAGES));
+ demo.getFeatures().add(Feature.builder().name(PROCESSING_PAGES_FEATURE).type(FeatureType.NUMBER).value(pageCount).build());
+
+ var demoLicense = new RedactionLicenseModel();
+ demoLicense.setActiveLicense("RedactManager");
+ demoLicense.getLicenses().add(demo);
+
+ return demoLicense;
+ }
+
+
+ private OffsetDateTime parseDate(String date) {
+
+ if (StringUtils.isEmpty(date)) {
+ return null;
+ }
+ var parts = date.split("-");
+ if (parts.length != 3) {
+ return null;
+ }
+
+ try {
+ return OffsetDateTime.now()
+ .withYear(Integer.parseInt(parts[2]))
+ .withMonth(Integer.parseInt(parts[1]))
+ .withDayOfMonth(Integer.parseInt(parts[0]))
+ .withHour(0)
+ .withMinute(0)
+ .withSecond(0)
+ .truncatedTo(ChronoUnit.SECONDS);
+ } catch (Exception e) {
+ log.debug("Failed to parse date: {}", date);
+ return null;
+ }
+
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/LicenseReportController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/LicenseReportController.java
new file mode 100644
index 000000000..9c5eae9aa
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/LicenseReportController.java
@@ -0,0 +1,47 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_LICENSE_REPORT;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.iqser.red.keycloak.commons.KeycloakSecurity;
+import com.iqser.red.service.persistence.management.v1.processor.service.LicenseReportService;
+import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
+import com.iqser.red.service.persistence.service.v1.api.external.resource.LicenseReportResource;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.license.LicenseReport;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.license.LicenseReportRequest;
+
+import lombok.RequiredArgsConstructor;
+
+@RestController
+@RequiredArgsConstructor
+public class LicenseReportController implements LicenseReportResource {
+
+ private static final String LICENSE_AUDIT_KEY = "License";
+ private final AuditPersistenceService auditPersistenceService;
+
+ private final LicenseReportService licenseReportService;
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_LICENSE_REPORT + "')")
+ public LicenseReport getReport(@RequestBody LicenseReportRequest reportRequest,
+ @RequestParam(value = "offset", defaultValue = "0") int offset,
+ @RequestParam(value = "limit", defaultValue = "20") int limit) {
+
+ LicenseReport licenseReport = licenseReportService.getLicenseReport(reportRequest, offset, limit);
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(LICENSE_AUDIT_KEY)
+ .category(AuditCategory.LICENSE.name())
+ .message("License report has been viewed.")
+ .build());
+ return licenseReport;
+ }
+
+}
diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/ManualRedactionController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/ManualRedactionController.java
new file mode 100644
index 000000000..668f3b580
--- /dev/null
+++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/ManualRedactionController.java
@@ -0,0 +1,1133 @@
+package com.iqser.red.persistence.service.v1.external.api.impl.controller;
+
+import static com.iqser.red.keycloak.commons.roles.ActionRoles.*;
+import static com.iqser.red.service.persistence.management.v1.processor.utils.TypeIdUtils.toTypeId;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.iqser.red.keycloak.commons.KeycloakSecurity;
+import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
+import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
+import com.iqser.red.service.persistence.management.v1.processor.service.DossierManagementService;
+import com.iqser.red.service.persistence.management.v1.processor.service.ManualRedactionService;
+import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
+import com.iqser.red.service.persistence.service.v1.api.external.resource.ManualRedactionResource;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.CommentResponse;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.AnnotationStatus;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.CommentRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualAddResponse;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.IdRemoval;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualForceRedaction;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualImageRecategorization;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualLegalBasisChange;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRedactionEntry;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualResizeRedaction;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddCommentRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddRedactionRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ApproveRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ForceRedactionRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ImageRecategorizationRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.LegalBasisChangeRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ManualRedactionWrapper;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.RemoveRedactionRequest;
+import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ResizeRedactionRequest;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class ManualRedactionController implements ManualRedactionResource {
+
+ private static final String FILE_ID = "fileId";
+ private static final String DOSSIER_ID = "dossierId";
+ private static final String ANNOTATION_ID = "annotationId";
+ private final ManualRedactionService manualRedactionService;
+ private final DossierManagementService dossierManagementService;
+ private final AuditPersistenceService auditPersistenceService;
+ private final AccessControlService accessControlService;
+
+
+ /* Reviewer Operations*/
+
+
+ @Deprecated
+ @Override
+ @PreAuthorize("hasAuthority('" + REQUEST_MANUAL_REDACTION + "')")
+ public ManualAddResponse requestAddRedaction(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody AddRedactionRequest addRedactionRequest) {
+
+ return requestBulkAddRedaction(dossierId, fileId, Set.of(addRedactionRequest)).get(0);
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + REQUEST_MANUAL_REDACTION + "')")
+ public List requestBulkAddRedaction(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody Set addRedactionRequests) {
+
+ var dossier = dossierManagementService.getDossierById(dossierId, false, false);
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+ accessControlService.verifyUserIsReviewer(dossierId, fileId);
+
+ List requests = new ArrayList<>();
+
+ for (var addRedactionRequest : addRedactionRequests) {
+
+ var addRedactionRequestBuilder = com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.AddRedactionRequest.builder()
+ .user(KeycloakSecurity.getUserId())
+ .typeId(toTypeId(addRedactionRequest.getType(), dossier.getDossierTemplateId(), addRedactionRequest.isAddToDossierDictionary() ? dossierId : null))
+ .value(addRedactionRequest.getValue())
+ .reason(addRedactionRequest.getReason())
+ .legalBasis(addRedactionRequest.getLegalBasis())
+ .addToDictionary(addRedactionRequest.isAddToDictionary())
+ .status(AnnotationStatus.REQUESTED)
+ .section(addRedactionRequest.getSection())
+ .positions(addRedactionRequest.getPositions())
+ .comment(addRedactionRequest.getComment() != null ? addRedactionRequest.getComment().getText() : null)
+ .addToDossierDictionary(addRedactionRequest.isAddToDossierDictionary())
+ .forceAddToDictionary(addRedactionRequest.isForceAddToDictionary())
+ .rectangle(addRedactionRequest.isRectangle())
+ .dictionaryEntryType(addRedactionRequest.getDictionaryEntryType())
+ .sourceId(addRedactionRequest.getSourceId());
+
+ requests.add(addRedactionRequestBuilder.build());
+ }
+ List responseList = new ArrayList<>();
+ responseList = manualRedactionService.addAddRedaction(dossierId, fileId, requests);
+ responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Manual redaction was requested.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
+ .build()));
+ return responseList;
+
+ }
+
+
+ @Deprecated
+ @Override
+ @PreAuthorize("hasAuthority('" + REQUEST_MANUAL_REDACTION + "')")
+ public ManualAddResponse requestRemoveRedaction(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody RemoveRedactionRequest removeRedactionRequest) {
+
+ return processRequestRemoveRedactionBulk(dossierId, fileId, Set.of(removeRedactionRequest)).get(0);
+ }
+
+
+ private List processRequestRemoveRedactionBulk(String dossierId, String fileId, Set removeRedactionRequests) {
+
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+ accessControlService.verifyUserIsReviewer(dossierId, fileId);
+
+ List requests = removeRedactionRequests.stream()
+ .map(removeRedactionRequest -> com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.RemoveRedactionRequest.builder()
+ .annotationId(removeRedactionRequest.getAnnotationId())
+ .user(KeycloakSecurity.getUserId())
+ .status(AnnotationStatus.REQUESTED)
+ .removeFromDictionary(removeRedactionRequest.isRemoveFromDictionary())
+ .comment(removeRedactionRequest.getComment())
+ .build())
+ .collect(Collectors.toList());
+
+ List responseList = manualRedactionService.addRemoveRedaction(dossierId, fileId, requests);
+
+ responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Manual removed redaction was requested.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
+ .build()));
+
+ return responseList;
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + REQUEST_MANUAL_REDACTION + "')")
+ public List requestBulkRemoveRedaction(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody Set removeRedactionRequests) {
+
+ return processRequestRemoveRedactionBulk(dossierId, fileId, removeRedactionRequests);
+ }
+
+
+ @Deprecated
+ @Override
+ @PreAuthorize("hasAuthority('" + REQUEST_MANUAL_REDACTION + "')")
+ public ManualAddResponse requestForceRedaction(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody ForceRedactionRequest forceRedactionRequest) {
+
+ return processRequestForceRedactionBulk(dossierId, fileId, Set.of(forceRedactionRequest)).get(0);
+ }
+
+
+ private List processRequestForceRedactionBulk(String dossierId, String fileId, Set forceRedactionRequests) {
+
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+ accessControlService.verifyUserIsReviewer(dossierId, fileId);
+
+ List requests = forceRedactionRequests.stream()
+ .map(forceRedactionRequest -> com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ForceRedactionRequest.builder()
+ .annotationId(forceRedactionRequest.getAnnotationId())
+ .user(KeycloakSecurity.getUserId())
+ .status(AnnotationStatus.REQUESTED)
+ .comment(forceRedactionRequest.getComment())
+ .legalBasis(forceRedactionRequest.getLegalBasis())
+ .build())
+ .collect(Collectors.toList());
+
+ List responseList = manualRedactionService.addForceRedaction(dossierId, fileId, requests);
+
+ responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Manual force redaction was requested.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
+ .build()));
+
+ return responseList;
+ }
+
+
+ @PreAuthorize("hasAuthority('" + REQUEST_MANUAL_REDACTION + "')")
+ public List requestBulkForceRedaction(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody Set forceRedactionRequests) {
+
+ return processRequestForceRedactionBulk(dossierId, fileId, forceRedactionRequests);
+ }
+
+
+ @Deprecated
+ @PreAuthorize("hasAuthority('" + REQUEST_MANUAL_REDACTION + "')")
+ public ManualAddResponse requestLegalBasisChange(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody LegalBasisChangeRequest legalBasisChangeRequest) {
+
+ return processRequestLegalBasisChangeBulk(dossierId, fileId, Set.of(legalBasisChangeRequest)).get(0);
+ }
+
+
+ private List processRequestLegalBasisChangeBulk(String dossierId, String fileId, Set legalBasisChangeRequests) {
+
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+ accessControlService.verifyUserIsReviewer(dossierId, fileId);
+
+ List requests = legalBasisChangeRequests.stream()
+ .map(legalBasisChangeRequest -> com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.LegalBasisChangeRequest.builder()
+ .annotationId(legalBasisChangeRequest.getAnnotationId())
+ .user(KeycloakSecurity.getUserId())
+ .status(AnnotationStatus.REQUESTED)
+ .section(legalBasisChangeRequest.getSection())
+ .comment(legalBasisChangeRequest.getComment())
+ .legalBasis(legalBasisChangeRequest.getLegalBasis())
+ .value(legalBasisChangeRequest.getValue())
+ .build())
+ .collect(Collectors.toList());
+
+ List responseList = manualRedactionService.addLegalBasisChange(dossierId, fileId, requests);
+
+ responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Manual force redaction was requested.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
+ .build()));
+
+ return responseList;
+ }
+
+
+ @PreAuthorize("hasAuthority('" + REQUEST_MANUAL_REDACTION + "')")
+ public List requestBulkLegalBasisChange(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody Set legalBasisChangeRequests) {
+
+ return processRequestLegalBasisChangeBulk(dossierId, fileId, legalBasisChangeRequests);
+ }
+
+
+ @Deprecated
+ @PreAuthorize("hasAuthority('" + REQUEST_MANUAL_REDACTION + "')")
+ public ManualAddResponse requestImageRecategorization(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody ImageRecategorizationRequest imageRecategorizationRequest) {
+
+ return processRequestImageRecategorizationBulk(dossierId, fileId, Set.of(imageRecategorizationRequest)).get(0);
+ }
+
+
+ private List processRequestImageRecategorizationBulk(String dossierId, String fileId, Set imageRecategorizationRequests) {
+
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+ accessControlService.verifyUserIsReviewer(dossierId, fileId);
+ var dossier = dossierManagementService.getDossierById(dossierId, false, false);
+
+ List requests = imageRecategorizationRequests.stream()
+ .map(imageRecategorizationRequest -> com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ImageRecategorizationRequest.builder()
+ .annotationId(imageRecategorizationRequest.getAnnotationId())
+ .user(KeycloakSecurity.getUserId())
+ .status(AnnotationStatus.REQUESTED)
+ .typeId(toTypeId(imageRecategorizationRequest.getType(), dossier.getDossierTemplateId()))
+ .comment(imageRecategorizationRequest.getComment())
+ .build())
+ .collect(Collectors.toList());
+
+ List responseList = manualRedactionService.addImageRecategorization(dossierId, fileId, requests);
+
+ responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Image recategorization was requested.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
+ .build()));
+
+ return responseList;
+ }
+
+
+ @PreAuthorize("hasAuthority('" + REQUEST_MANUAL_REDACTION + "')")
+ public List requestBulkImageRecategorization(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody Set imageRecategorizationRequests) {
+
+ return processRequestImageRecategorizationBulk(dossierId, fileId, imageRecategorizationRequests);
+ }
+
+
+ @Deprecated
+ @Override
+ @PreAuthorize("hasAuthority('" + REQUEST_MANUAL_REDACTION + "')")
+ public ManualAddResponse requestResizeRedaction(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody ResizeRedactionRequest resizeRedactionRequest) {
+
+ return processRequestResizeRedactionBulk(dossierId, fileId, Set.of(resizeRedactionRequest)).get(0);
+ }
+
+
+ private List processRequestResizeRedactionBulk(String dossierId, String fileId, Set resizeRedactionRequests) {
+
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+ accessControlService.verifyUserIsReviewer(dossierId, fileId);
+
+ List requests = resizeRedactionRequests.stream()
+ .map(resizeRedactionRequest -> com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ResizeRedactionRequest.builder()
+ .annotationId(resizeRedactionRequest.getAnnotationId())
+ .user(KeycloakSecurity.getUserId())
+ .status(AnnotationStatus.REQUESTED)
+ .comment(resizeRedactionRequest.getComment())
+ .positions(resizeRedactionRequest.getPositions())
+ .value(resizeRedactionRequest.getValue())
+ .updateDictionary(resizeRedactionRequest.getUpdateDictionary())
+ .build())
+ .collect(Collectors.toList());
+
+ List responseList = manualRedactionService.addResizeRedaction(dossierId, fileId, requests);
+
+ responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Manual resize redaction was requested.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
+ .build()));
+
+ return responseList;
+ }
+
+
+ @PreAuthorize("hasAuthority('" + REQUEST_MANUAL_REDACTION + "')")
+ public List requestBulkResizeRedaction(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody Set resizeRedactionRequests) {
+
+ return processRequestResizeRedactionBulk(dossierId, fileId, resizeRedactionRequests);
+ }
+
+
+ @Deprecated
+ @Override
+ @PreAuthorize("hasAuthority('" + DELETE_MANUAL_REDACTION + "')")
+ public void undo(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @PathVariable(ANNOTATION_ID) String annotationId) {
+
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+ accessControlService.verifyUserIsReviewer(dossierId, fileId);
+
+ ManualRedactions manualRedactions = manualRedactionService.getManualRedactions(fileId);
+
+ ManualRedactionWrapper manualRedactionWrapper = getLatestManualRedactionForAnnotationId(manualRedactions, annotationId);
+
+ if (manualRedactionWrapper == null) {
+ throw new NotFoundException(String.format("ManualRedaction with annotationId %s could not be found.", annotationId));
+ }
+
+ if (manualRedactionWrapper.getItem() instanceof ManualRedactionEntry) {
+
+ manualRedactionService.deleteAddRedaction(dossierId, fileId, List.of(annotationId));
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Undo of manual add redaction was done.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build());
+ } else if (manualRedactionWrapper.getItem() instanceof IdRemoval) {
+
+ manualRedactionService.deleteRemoveRedaction(dossierId, fileId, List.of(annotationId));
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Undo of manual remove redaction was done.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build());
+ } else if (manualRedactionWrapper.getItem() instanceof ManualForceRedaction) {
+
+ manualRedactionService.deleteForceRedaction(dossierId, fileId, List.of(annotationId));
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Undo of manual force redaction was done.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build());
+ } else if (manualRedactionWrapper.getItem() instanceof ManualImageRecategorization) {
+
+ manualRedactionService.deleteImageRecategorization(dossierId, fileId, List.of(annotationId));
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Undo of manual image recategorization was done.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build());
+ } else if (manualRedactionWrapper.getItem() instanceof ManualLegalBasisChange) {
+
+ manualRedactionService.deleteLegalBasisChange(dossierId, fileId, List.of(annotationId));
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Undo of legal basis change was done.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build());
+ } else if (manualRedactionWrapper.getItem() instanceof ManualResizeRedaction) {
+
+ manualRedactionService.deleteResizeRedaction(dossierId, fileId, List.of(annotationId));
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Undo of manual resize redaction was done.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build());
+ }
+ }
+
+
+ private ManualRedactionWrapper getLatestManualRedactionForAnnotationId(ManualRedactions manualRedactions, String annotationId) {
+
+ final List manualRedactionWrappers = new ArrayList<>();
+
+ manualRedactions.getEntriesToAdd()
+ .stream()
+ .filter(item -> item.getSoftDeletedTime() == null && item.getAnnotationId().equals(annotationId))
+ .forEach(item -> manualRedactionWrappers.add(new ManualRedactionWrapper(item.getAnnotationId(), item.getRequestDate(), item)));
+
+ manualRedactions.getImageRecategorization()
+ .stream()
+ .filter(item -> item.getSoftDeletedTime() == null && item.getAnnotationId().equals(annotationId))
+ .forEach(item -> manualRedactionWrappers.add(new ManualRedactionWrapper(item.getAnnotationId(), item.getRequestDate(), item)));
+
+ manualRedactions.getIdsToRemove()
+ .stream()
+ .filter(item -> item.getSoftDeletedTime() == null && item.getAnnotationId().equals(annotationId))
+ .forEach(item -> manualRedactionWrappers.add(new ManualRedactionWrapper(item.getAnnotationId(), item.getRequestDate(), item)));
+
+ manualRedactions.getForceRedactions()
+ .stream()
+ .filter(item -> item.getSoftDeletedTime() == null && item.getAnnotationId().equals(annotationId))
+ .forEach(item -> manualRedactionWrappers.add(new ManualRedactionWrapper(item.getAnnotationId(), item.getRequestDate(), item)));
+
+ manualRedactions.getLegalBasisChanges()
+ .stream()
+ .filter(item -> item.getSoftDeletedTime() == null && item.getAnnotationId().equals(annotationId))
+ .forEach(item -> manualRedactionWrappers.add(new ManualRedactionWrapper(item.getAnnotationId(), item.getRequestDate(), item)));
+
+ manualRedactions.getResizeRedactions()
+ .stream()
+ .filter(item -> item.getSoftDeletedTime() == null && item.getAnnotationId().equals(annotationId))
+ .forEach(item -> manualRedactionWrappers.add(new ManualRedactionWrapper(item.getAnnotationId(), item.getRequestDate(), item)));
+
+ var sortedManualRedactionWrappers = manualRedactionWrappers.stream()
+ .sorted(Comparator.comparing(ManualRedactionWrapper::getDate, Comparator.nullsLast(Comparator.reverseOrder())))
+ .collect(Collectors.toList());
+
+ return sortedManualRedactionWrappers.isEmpty() ? null : sortedManualRedactionWrappers.get(0);
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + DELETE_MANUAL_REDACTION + "')")
+ public void undo(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody Set annotationIds) {
+
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+ accessControlService.verifyUserIsReviewer(dossierId, fileId);
+
+ ManualRedactions manualRedactions = manualRedactionService.getManualRedactions(fileId);
+
+ Map manualRedactionWrappers = getLatestManualRedactionsForAnnotationIds(manualRedactions, annotationIds);
+
+ if (manualRedactionWrappers.isEmpty()) {
+ throw new NotFoundException(String.format("ManualRedaction with annotationIds %s could not be found.", annotationIds));
+ }
+
+ List manualRedactionEntries = manualRedactionWrappers.values()
+ .stream()
+ .filter(manualRedactionWrapper -> manualRedactionWrapper.getItem() instanceof ManualRedactionEntry)
+ .map(ManualRedactionWrapper::getId)
+ .collect(Collectors.toList());
+ if (!manualRedactionEntries.isEmpty()) {
+ manualRedactionService.deleteAddRedaction(dossierId, fileId, manualRedactionEntries);
+ manualRedactionEntries.forEach(annotationId -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Undo of manual add redaction was done.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build()));
+ }
+
+ List idRemovals = manualRedactionWrappers.values()
+ .stream()
+ .filter(manualRedactionWrapper -> manualRedactionWrapper.getItem() instanceof IdRemoval)
+ .map(ManualRedactionWrapper::getId)
+ .collect(Collectors.toList());
+ if (!idRemovals.isEmpty()) {
+
+ manualRedactionService.deleteRemoveRedaction(dossierId, fileId, idRemovals);
+ idRemovals.forEach(annotationId -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Undo of manual remove redaction was done.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build()));
+ }
+
+ List manualForceRedactions = manualRedactionWrappers.values()
+ .stream()
+ .filter(manualRedactionWrapper -> manualRedactionWrapper.getItem() instanceof ManualForceRedaction)
+ .map(ManualRedactionWrapper::getId)
+ .collect(Collectors.toList());
+ if (!manualForceRedactions.isEmpty()) {
+
+ manualRedactionService.deleteForceRedaction(dossierId, fileId, manualForceRedactions);
+ manualForceRedactions.forEach(annotationId -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Undo of manual force redaction was done.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build()));
+ }
+
+ List manualImageRecategorizations = manualRedactionWrappers.values()
+ .stream()
+ .filter(manualRedactionWrapper -> manualRedactionWrapper.getItem() instanceof ManualImageRecategorization)
+ .map(ManualRedactionWrapper::getId)
+ .collect(Collectors.toList());
+ if (!manualImageRecategorizations.isEmpty()) {
+
+ manualRedactionService.deleteImageRecategorization(dossierId, fileId, manualImageRecategorizations);
+ manualImageRecategorizations.forEach(annotationId -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Undo of manual image recategorization was done.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build()));
+ }
+
+ List manualLegalBasisChanges = manualRedactionWrappers.values()
+ .stream()
+ .filter(manualRedactionWrapper -> manualRedactionWrapper.getItem() instanceof ManualLegalBasisChange)
+ .map(ManualRedactionWrapper::getId)
+ .collect(Collectors.toList());
+ if (!manualLegalBasisChanges.isEmpty()) {
+
+ manualRedactionService.deleteLegalBasisChange(dossierId, fileId, manualLegalBasisChanges);
+ manualLegalBasisChanges.forEach(annotationId -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Undo of legal basis change was done.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build()));
+ }
+
+ List manualResizeRedactions = manualRedactionWrappers.values()
+ .stream()
+ .filter(manualRedactionWrapper -> manualRedactionWrapper.getItem() instanceof ManualResizeRedaction)
+ .map(ManualRedactionWrapper::getId)
+ .collect(Collectors.toList());
+ if (!manualResizeRedactions.isEmpty()) {
+
+ manualRedactionService.deleteResizeRedaction(dossierId, fileId, manualResizeRedactions);
+ manualResizeRedactions.forEach(annotationId -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Undo of manual resize redaction was done.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build()));
+ }
+ }
+
+
+ private Map getLatestManualRedactionsForAnnotationIds(ManualRedactions manualRedactions, Set annotationIds) {
+
+ Map result = new HashMap<>();
+ annotationIds.forEach(annotationId -> {
+ var last = getLatestManualRedactionForAnnotationId(manualRedactions, annotationId);
+ if (last != null) {
+ result.put(annotationId, last);
+ }
+ });
+
+ return result;
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + DELETE_COMMENT + "')")
+ public void undoComment(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @PathVariable(ANNOTATION_ID) String annotationId,
+ @PathVariable(COMMENT_ID) String commentId) {
+
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+ accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
+
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Comment was removed.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build());
+ manualRedactionService.deleteComment(fileId, List.of(Long.valueOf(commentId)));
+
+ }
+
+
+ @Deprecated
+ @Override
+ @PreAuthorize("hasAuthority('" + PROCESS_MANUAL_REDACTION_REQUEST + "')")
+ public void declineRequest(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @PathVariable(ANNOTATION_ID) String annotationId) {
+
+ declineRequestBulk(dossierId, fileId, Set.of(annotationId));
+ }
+
+
+ /* Approver Operations*/
+
+ @PreAuthorize("hasAuthority('" + PROCESS_MANUAL_REDACTION_REQUEST + "')")
+ public void declineRequestBulk(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody Set annotationIds) {
+
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+ accessControlService.verifyUserIsApprover(dossierId);
+
+ updateAnnotationStatus(dossierId, fileId, annotationIds, AnnotationStatus.DECLINED);
+
+ annotationIds.forEach(annotationId -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Suggestion was declined")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build()));
+ }
+
+
+ private void updateAnnotationStatus(String dossierId, String fileId, Set annotationIds, AnnotationStatus status) {
+
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+ ManualRedactions manualRedactions = manualRedactionService.getManualRedactions(fileId);
+
+ Map manualRedactionWrappers = getLatestManualRedactionsForAnnotationIds(manualRedactions, annotationIds);
+
+ if (manualRedactionWrappers.isEmpty()) {
+ throw new NotFoundException(String.format("ManualRedaction with annotationIds %s could not be found.", annotationIds));
+ }
+
+ List manualRedactionEntries = manualRedactionWrappers.values()
+ .stream()
+ .filter(manualRedactionWrapper -> manualRedactionWrapper.getItem() instanceof ManualRedactionEntry)
+ .map(ManualRedactionWrapper::getId)
+ .collect(Collectors.toList());
+ if (!manualRedactionEntries.isEmpty()) {
+ manualRedactionService.updateAddRedactionStatus(dossierId, fileId, manualRedactionEntries, status);
+ manualRedactionEntries.forEach(annotationId -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Undo of manual add redaction was done.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build()));
+ }
+
+ List idRemovals = manualRedactionWrappers.values()
+ .stream()
+ .filter(manualRedactionWrapper -> manualRedactionWrapper.getItem() instanceof IdRemoval)
+ .map(ManualRedactionWrapper::getId)
+ .collect(Collectors.toList());
+ if (!idRemovals.isEmpty()) {
+
+ manualRedactionService.updateRemoveRedactionStatus(dossierId, fileId, idRemovals, status);
+ idRemovals.forEach(annotationId -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Undo of manual remove redaction was done.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build()));
+ }
+
+ List manualForceRedactions = manualRedactionWrappers.values()
+ .stream()
+ .filter(manualRedactionWrapper -> manualRedactionWrapper.getItem() instanceof ManualForceRedaction)
+ .map(ManualRedactionWrapper::getId)
+ .collect(Collectors.toList());
+ if (!manualForceRedactions.isEmpty()) {
+
+ manualRedactionService.updateForceRedactionStatus(dossierId, fileId, manualForceRedactions, status);
+ manualForceRedactions.forEach(annotationId -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Undo of manual force redaction was done.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build()));
+ }
+
+ List manualImageRecategorizations = manualRedactionWrappers.values()
+ .stream()
+ .filter(manualRedactionWrapper -> manualRedactionWrapper.getItem() instanceof ManualImageRecategorization)
+ .map(ManualRedactionWrapper::getId)
+ .collect(Collectors.toList());
+ if (!manualImageRecategorizations.isEmpty()) {
+
+ manualRedactionService.updateImageRecategorizationStatus(dossierId, fileId, manualImageRecategorizations, status);
+ manualImageRecategorizations.forEach(annotationId -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Undo of manual image recategorization was done.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build()));
+ }
+
+ List manualLegalBasisChanges = manualRedactionWrappers.values()
+ .stream()
+ .filter(manualRedactionWrapper -> manualRedactionWrapper.getItem() instanceof ManualLegalBasisChange)
+ .map(ManualRedactionWrapper::getId)
+ .collect(Collectors.toList());
+ if (!manualLegalBasisChanges.isEmpty()) {
+
+ manualRedactionService.updateLegalBasisChangeStatus(dossierId, fileId, manualLegalBasisChanges, status);
+ manualLegalBasisChanges.forEach(annotationId -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Undo of legal basis change was done.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build()));
+ }
+
+ List manualResizeRedactions = manualRedactionWrappers.values()
+ .stream()
+ .filter(manualRedactionWrapper -> manualRedactionWrapper.getItem() instanceof ManualResizeRedaction)
+ .map(ManualRedactionWrapper::getId)
+ .collect(Collectors.toList());
+ if (!manualResizeRedactions.isEmpty()) {
+
+ manualRedactionService.updateResizeRedactionStatus(dossierId, fileId, manualResizeRedactions, status);
+ manualResizeRedactions.forEach(annotationId -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Undo of manual resize redaction was done.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build()));
+ }
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + READ_MANUAL_REDACTIONS + "')")
+ public ManualRedactions getManualRedactions(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
+
+ return manualRedactionService.getManualRedactions(fileId);
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + ADD_COMMENT + "')")
+ public CommentResponse addComment(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @PathVariable(ANNOTATION_ID) String annotationId,
+ @RequestBody AddCommentRequest addCommentRequest) {
+
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+ accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
+
+ var response = manualRedactionService.addComment(fileId,
+ annotationId,
+ CommentRequest.builder().user(KeycloakSecurity.getUserId()).text(addCommentRequest.getText()).build());
+
+ auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Comment was added.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build());
+
+ return new CommentResponse(String.valueOf(response.getId()));
+ }
+
+
+ @Deprecated
+ @Override
+ @PreAuthorize("hasAuthority('" + PROCESS_MANUAL_REDACTION_REQUEST + "')")
+ public void approveRequest(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @PathVariable(ANNOTATION_ID) String annotationId,
+ @RequestBody ApproveRequest approveRequest) {
+
+ approveRequestBulk(dossierId, fileId, Set.of(annotationId));
+ }
+
+ @PreAuthorize("hasAuthority('" + PROCESS_MANUAL_REDACTION_REQUEST + "')")
+ public void approveRequestBulk(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody Set annotationIds) {
+
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+ accessControlService.verifyUserIsApprover(dossierId);
+
+ updateAnnotationStatus(dossierId, fileId, annotationIds, AnnotationStatus.APPROVED);
+
+ annotationIds.forEach(annotationId -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Suggestion was approved")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
+ .build()));
+ }
+
+
+ @Deprecated
+ @Override
+ @PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
+ public ManualAddResponse addRedaction(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody AddRedactionRequest addRedactionRequest) {
+
+ return addRedactionBulk(dossierId, fileId, Set.of(addRedactionRequest)).get(0);
+ }
+
+
+ @Override
+ @PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
+ public List addRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody Set addRedactionRequests) {
+
+ var dossier = dossierManagementService.getDossierById(dossierId, false, false);
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+ accessControlService.verifyUserIsApprover(dossierId);
+
+ List requests = new ArrayList<>();
+
+ for (var addRedactionRequest : addRedactionRequests) {
+
+ var addRedactionRequestBuilder = com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.AddRedactionRequest.builder()
+ .value(addRedactionRequest.getValue())
+ .legalBasis(addRedactionRequest.getLegalBasis())
+ .user(KeycloakSecurity.getUserId())
+ .typeId(toTypeId(addRedactionRequest.getType(), dossier.getDossierTemplateId(), addRedactionRequest.isAddToDossierDictionary() ? dossierId : null))
+ .reason(addRedactionRequest.getReason())
+ .addToDictionary(addRedactionRequest.isAddToDictionary())
+ .status(AnnotationStatus.APPROVED)
+ .comment(addRedactionRequest.getComment() != null ? addRedactionRequest.getComment().getText() : null)
+ .section(addRedactionRequest.getSection())
+ .rectangle(addRedactionRequest.isRectangle())
+ .addToDossierDictionary(addRedactionRequest.isAddToDossierDictionary())
+ .forceAddToDictionary(addRedactionRequest.isForceAddToDictionary())
+ .positions(addRedactionRequest.getPositions())
+ .sourceId(addRedactionRequest.getSourceId())
+ .dictionaryEntryType(addRedactionRequest.getDictionaryEntryType());
+
+ requests.add(addRedactionRequestBuilder.build());
+ }
+ List responseList = manualRedactionService.addAddRedaction(dossierId, fileId, requests);
+ responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Manual redaction was added.")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
+ .build()));
+ return responseList;
+ }
+
+
+ @Deprecated
+ @Override
+ @PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
+ public ManualAddResponse removeRedaction(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody RemoveRedactionRequest removeRedactionRequest) {
+
+ return removeRedactionBulk(dossierId, fileId, Set.of(removeRedactionRequest)).get(0);
+ }
+
+ @PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
+ public List removeRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody Set removeRedactionRequests) {
+
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+ accessControlService.verifyUserIsApprover(dossierId);
+
+ List requests = new ArrayList<>();
+
+ for (var removeRedactionRequest : removeRedactionRequests) {
+ var removeRedactionRequestBuilder = com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.RemoveRedactionRequest.builder()
+ .annotationId(removeRedactionRequest.getAnnotationId())
+ .user(KeycloakSecurity.getUserId())
+ .status(AnnotationStatus.APPROVED)
+ .removeFromDictionary(removeRedactionRequest.isRemoveFromDictionary());
+
+ if (removeRedactionRequest.getComment() != null) {
+ removeRedactionRequestBuilder.comment(removeRedactionRequest.getComment());
+ }
+
+ requests.add(removeRedactionRequestBuilder.build());
+ }
+ try {
+ List responseList = manualRedactionService.addRemoveRedaction(dossierId, fileId, requests);
+
+ responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Redaction was manually removed")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
+ .build()));
+
+ return responseList;
+ }catch (Exception e){
+ e.printStackTrace();;
+ return null;
+ }
+ }
+
+
+ @Deprecated
+ @Override
+ @PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
+ public ManualAddResponse forceRedaction(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody ForceRedactionRequest forceRedactionRequest) {
+
+ return forceRedactionBulk(dossierId, fileId, Set.of(forceRedactionRequest)).get(0);
+ }
+
+ @PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
+ public List forceRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody Set forceRedactionRequests) {
+
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+ accessControlService.verifyUserIsApprover(dossierId);
+
+ List requests = forceRedactionRequests.stream()
+ .map(forceRedactionRequest -> com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ForceRedactionRequest.builder()
+ .annotationId(forceRedactionRequest.getAnnotationId())
+ .user(KeycloakSecurity.getUserId())
+ .status(AnnotationStatus.APPROVED)
+ .legalBasis(forceRedactionRequest.getLegalBasis())
+ .comment(forceRedactionRequest.getComment())
+ .build())
+ .collect(Collectors.toList());
+ List responseList = manualRedactionService.addForceRedaction(dossierId, fileId, requests);
+
+ responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Skipped redaction was forced to be redacted")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
+ .build()));
+
+ return responseList;
+ }
+
+
+ @Deprecated
+ @PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
+ public ManualAddResponse legalBasisChange(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody LegalBasisChangeRequest legalBasisChangeRequest) {
+
+ return legalBasisChangeBulk(dossierId, fileId, Set.of(legalBasisChangeRequest)).get(0);
+ }
+
+ @PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
+ public List legalBasisChangeBulk(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody Set legalBasisChangeRequests) {
+
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+ accessControlService.verifyUserIsApprover(dossierId);
+
+ List requests = legalBasisChangeRequests.stream()
+ .map(legalBasisChangeRequest -> com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.LegalBasisChangeRequest.builder()
+ .annotationId(legalBasisChangeRequest.getAnnotationId())
+ .user(KeycloakSecurity.getUserId())
+ .status(AnnotationStatus.APPROVED)
+ .section(legalBasisChangeRequest.getSection())
+ .legalBasis(legalBasisChangeRequest.getLegalBasis())
+ .comment(legalBasisChangeRequest.getComment())
+ .value(legalBasisChangeRequest.getValue())
+ .build())
+ .collect(Collectors.toList());
+
+ List responseList = manualRedactionService.addLegalBasisChange(dossierId, fileId, requests);
+
+ responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
+ .userId(KeycloakSecurity.getUserId())
+ .objectId(fileId)
+ .category(AuditCategory.DOCUMENT.name())
+ .message("Legal basis reason was changed")
+ .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
+ .build()));
+
+ return responseList;
+ }
+
+
+ @Deprecated
+ @PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
+ public ManualAddResponse recategorizeImage(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody ImageRecategorizationRequest imageRecategorizationRequest) {
+
+ return recategorizeImageBulk(dossierId, fileId, Set.of(imageRecategorizationRequest)).get(0);
+ }
+
+ @PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
+ public List recategorizeImageBulk(@PathVariable(DOSSIER_ID) String dossierId,
+ @PathVariable(FILE_ID) String fileId,
+ @RequestBody Set imageRecategorizationRequests) {
+
+ var dossier = dossierManagementService.getDossierById(dossierId, false, false);
+ accessControlService.verifyFileIsNotApproved(dossierId, fileId);
+ accessControlService.verifyUserIsApprover(dossierId);
+
+ List requests = imageRecategorizationRequests.stream()
+ .map(imageRecategorizationRequest -> com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ImageRecategorizationRequest.builder()
+ .annotationId(imageRecategorizationRequest.getAnnotationId())
+ .user(KeycloakSecurity.getUserId())
+ .status(AnnotationStatus.APPROVED)
+ .typeId(toTypeId(imageRecategorizationRequest.getType(), dossier.getDossierTemplateId()))
+ .comment(imageRecategorizationRequest.getComment())
+ .build())
+ .collect(Collectors.toList());
+
+ List