RED-6162
This commit is contained in:
parent
94061a38de
commit
72b0a3d933
@ -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
|
||||
|
||||
121
persistence-service-v1/keycloak-commons/pom.xml
Normal file
121
persistence-service-v1/keycloak-commons/pom.xml
Normal file
@ -0,0 +1,121 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<artifactId>persistence-service-v1</artifactId>
|
||||
<groupId>com.iqser.red.service</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-commons</artifactId>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.iqser.red.commons</groupId>
|
||||
<artifactId>spring-commons</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.iqser.red.commons</groupId>
|
||||
<artifactId>logging-commons</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
<version>1.21</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-collections4</artifactId>
|
||||
<version>4.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.iqser.red.commons</groupId>
|
||||
<artifactId>metric-commons</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-spring-boot-adapter-core</artifactId>
|
||||
<version>${keycloak.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-spring-security-adapter</artifactId>
|
||||
<version>${keycloak.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-admin-client</artifactId>
|
||||
<version>${keycloak.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-xml</artifactId>
|
||||
<version>2.13.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- spring -->
|
||||
<dependency>
|
||||
<groupId>io.github.openfeign</groupId>
|
||||
<artifactId>feign-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.iqser.red.commons</groupId>
|
||||
<artifactId>jackson-commons</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- test -->
|
||||
<dependency>
|
||||
<groupId>com.iqser.red.commons</groupId>
|
||||
<artifactId>test-commons</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.retry</groupId>
|
||||
<artifactId>spring-retry</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@ -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)));
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<RoleRepresentation> 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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<User> getAllUsers() {
|
||||
|
||||
return retryTemplate.execute(context -> {
|
||||
List<UserRepresentation> allUsers = realmService.realm().users().search(null, 0, 500);
|
||||
|
||||
Map<String, Set<String>> 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<UserRepresentation> 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<User> compactUsers(List<UserRepresentation> allUsers, Map<String, Set<String>> usersByRole) {
|
||||
|
||||
List<User> 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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<String> roles = new TreeSet<>();
|
||||
|
||||
}
|
||||
@ -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() {}
|
||||
|
||||
}
|
||||
@ -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<String> UNMAPPED_ACTION_ROLES = Sets.newHashSet(UNARCHIVE_DOSSIER, UPDATE_LICENSE, GET_RSS);
|
||||
|
||||
public static final Set<String> 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<String> 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<String> RED_MANAGER_ACTION_ROLES = Sets.newHashSet(ADD_UPDATE_DOSSIER, ARCHIVE_DOSSIER, DELETE_DOSSIER, WRITE_DOSSIER_ATTRIBUTES);
|
||||
|
||||
public static final Set<String> 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<String, Set<String>> 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<String> roles) {
|
||||
|
||||
for (String role : roles) {
|
||||
if (!ROLE_DATA.containsKey(role)) {
|
||||
throw new IllegalArgumentException("Invalid Role: " + role);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<AccessToken> tokenVerifier = createVerifier(tokenString, deployment, issuerUrl);
|
||||
if (deployment.isVerifyTokenAudience()) {
|
||||
tokenVerifier.audience(deployment.getResourceName());
|
||||
}
|
||||
|
||||
return tokenVerifier.verify().getToken();
|
||||
}
|
||||
|
||||
|
||||
private TokenVerifier<AccessToken> createVerifier(String tokenString, KeycloakDeployment deployment, String issuerUrl) throws VerificationException {
|
||||
|
||||
TokenVerifier<AccessToken> 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<AccessToken> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
package com.iqser.red.service.persistence.service.v1.api.model.annotations;
|
||||
|
||||
public enum AnnotationStatus {
|
||||
REQUESTED,
|
||||
APPROVED,
|
||||
DECLINED
|
||||
|
||||
}
|
||||
@ -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<DictionarySummary> dictionarySummaryList = new ArrayList<>();
|
||||
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
package com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate;
|
||||
|
||||
public enum DossierTemplateStatus {
|
||||
INCOMPLETE,
|
||||
INACTIVE,
|
||||
ACTIVE
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
package com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration;
|
||||
|
||||
public enum DigitalSignatureType {
|
||||
CERTIFICATE,
|
||||
KMS,
|
||||
HSM;
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
package com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration;
|
||||
|
||||
public enum WatermarkOrientation {
|
||||
VERTICAL,
|
||||
HORIZONTAL,
|
||||
DIAGONAL
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
package com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier;
|
||||
|
||||
public enum DossierAttributeType {
|
||||
TEXT,
|
||||
NUMBER,
|
||||
DATE,
|
||||
IMAGE
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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<ProcessingStatus, Integer> fileCountPerProcessingStatus;
|
||||
private Map<WorkflowStatus, Integer> fileCountPerWorkflowStatus;
|
||||
private OffsetDateTime lastFileUpdateDate;
|
||||
private OffsetDateTime fileManipulationDate;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
package com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier;
|
||||
|
||||
public enum DossierVisibility {
|
||||
|
||||
PRIVATE,
|
||||
PUBLIC;
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
package com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.dossier.file;
|
||||
|
||||
public enum FileAttributeType {
|
||||
TEXT,
|
||||
NUMBER,
|
||||
DATE
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
|
||||
}
|
||||
@ -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();
|
||||
|
||||
}
|
||||
@ -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<AuditModel> search(@RequestBody AuditSearchRequest auditSearchRequest);
|
||||
|
||||
|
||||
@ResponseBody
|
||||
@ResponseStatus(value = HttpStatus.OK)
|
||||
@GetMapping(value = PATH + "/categories", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
List<CategoryModel> getCategories();
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -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<String> 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<String> 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<Type> 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<Type> 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<DictionaryEntry> 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);
|
||||
|
||||
}
|
||||
@ -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();
|
||||
|
||||
}
|
||||
@ -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<DossierAttributeConfig> setDossierAttributesConfig(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId,
|
||||
@RequestBody List<DossierAttributeConfig> 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<String> dossierAttributeIds);
|
||||
|
||||
|
||||
@ResponseBody
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@GetMapping(value = DOSSIER_ATTRIBUTE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
List<DossierAttributeConfig> getDossierAttributes(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId);
|
||||
|
||||
}
|
||||
@ -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<DossierAttribute> setDossierAttributes(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @RequestBody List<DossierAttribute> 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<DossierAttribute> 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);
|
||||
|
||||
}
|
||||
@ -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<DossierChange> changesSince(@RequestBody JSONPrimitive<OffsetDateTime> 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<Dossier> 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<Dossier> 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<String> 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<Dossier> getArchivedDossiers();
|
||||
|
||||
|
||||
@GetMapping(value = ARCHIVE_DOSSIERS_PATH + DOSSIER_TEMPLATE_PATH + DOSSIER_TEMPLATE_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
List<Dossier> getArchivedDossiersForDossierTemplateId(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId);
|
||||
|
||||
|
||||
@GetMapping(value = DELETED_DOSSIERS_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
List<Dossier> getSoftDeletedDossiers();
|
||||
|
||||
|
||||
@DeleteMapping(value = DELETED_DOSSIERS_PATH + HARD_DELETE_PATH)
|
||||
void hardDeleteDossiers(@RequestBody Set<String> dossierIds);
|
||||
|
||||
|
||||
@PostMapping(value = DELETED_DOSSIERS_PATH + UNDELETE_PATH)
|
||||
void undeleteDossiers(@RequestBody Set<String> dossierIds);
|
||||
|
||||
|
||||
@PostMapping(value = ARCHIVE_DOSSIERS_PATH + ARCHIVE_PATH)
|
||||
void archiveDossiers(@RequestBody Set<String> dossierIds);
|
||||
|
||||
|
||||
@PostMapping(value = ARCHIVE_DOSSIERS_PATH + UNARCHIVE_PATH)
|
||||
void unarchiveDossiers(@RequestBody Set<String> dossierIds);
|
||||
|
||||
}
|
||||
@ -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<DossierStats> getDossierStats(@RequestBody Set<String> dossierIds);
|
||||
|
||||
}
|
||||
@ -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<DossierTemplate> 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<String> 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);
|
||||
|
||||
}
|
||||
@ -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<DossierTemplateDictionaryStats> getDossierTemplateDictionaryStats(@RequestBody Set<String> 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<DossierTemplateStats> getDossierTemplateStats();
|
||||
|
||||
}
|
||||
@ -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<String> prepareDownload(@RequestBody DownloadRequest request);
|
||||
|
||||
@PostMapping(value = REST_PATH + "/prepare-option", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
JSONPrimitive<String> prepareDownload(@RequestBody DownloadWithOptionRequest request);
|
||||
|
||||
@GetMapping(value = REST_PATH + "/status/{" + USER_ID + "}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
List<DownloadStatus> getDownloadStatus(@PathVariable(USER_ID) String userId);
|
||||
|
||||
|
||||
@PostMapping(value = REST_PATH + "/setDownloaded", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
void setDownloaded(@RequestBody JSONPrimitive<String> setDownloadedRequest);
|
||||
|
||||
|
||||
@PostMapping(value = REST_PATH + "/delete", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
void deleteDownloadStatus(@RequestBody JSONPrimitive<String> setDownloadedRequest);
|
||||
|
||||
}
|
||||
@ -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<FileAttributeConfig> setFileAttributesConfig(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody List<FileAttributeConfig> 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<String> fileAttributeIds);
|
||||
|
||||
|
||||
@ResponseBody
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@GetMapping(value = FILE_ATTRIBUTES_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
List<FileAttributeConfig> getFileAttributeConfigs(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId);
|
||||
|
||||
}
|
||||
@ -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<String, String> fileAttributes);
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -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<ManualAddResponse> addAddRedaction(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@PathVariable(FILE_ID) String fileId,
|
||||
@RequestBody List<AddRedactionRequest> 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<ManualAddResponse> addRemoveRedaction(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@PathVariable(FILE_ID) String fileId,
|
||||
@RequestBody List<RemoveRedactionRequest> 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<ManualAddResponse> addForceRedaction(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@PathVariable(FILE_ID) String fileId,
|
||||
@RequestBody List<ForceRedactionRequest> 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<ManualAddResponse> addLegalBasisChange(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@PathVariable(FILE_ID) String fileId,
|
||||
@RequestBody List<LegalBasisChangeRequest> 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<ManualAddResponse> addImageRecategorization(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@PathVariable(FILE_ID) String fileId,
|
||||
@RequestBody List<ImageRecategorizationRequest> 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<ManualAddResponse> addResizeRedaction(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@PathVariable(FILE_ID) String fileId,
|
||||
@RequestBody List<ResizeRedactionRequest> 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<String> 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<String> 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<String> 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<String> 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<String> annotationIds);
|
||||
|
||||
|
||||
@PostMapping(MANUAL_REDACTION_REST_PATH + DELETE_PATH + "/comment" + FILE_ID_PATH_VARIABLE)
|
||||
void deleteComment(@PathVariable(FILE_ID) String fileId, @RequestBody List<Long> 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<String> 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);
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -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<Boolean> hasNewNotificationsSince(@PathVariable(USER_ID_PARAM) String userId, @RequestBody JSONPrimitive<OffsetDateTime> 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<Long> 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<Long> 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<Long> notificationIds);
|
||||
|
||||
|
||||
@ResponseBody
|
||||
@ResponseStatus(value = HttpStatus.OK)
|
||||
@GetMapping(value = NOTIFICATION_PATH + USER_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
List<Notification> getNotifications(@PathVariable(USER_ID_PARAM) String userId, @RequestParam(INCLUDE_SEEN_PARAM) boolean includeSeen);
|
||||
|
||||
}
|
||||
@ -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<String> 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<String> 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<String> 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);
|
||||
|
||||
}
|
||||
@ -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<String> 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);
|
||||
|
||||
}
|
||||
@ -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<ReportTemplate> 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);
|
||||
|
||||
}
|
||||
@ -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();
|
||||
|
||||
}
|
||||
@ -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<Boolean> hasChangesSince(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @RequestBody JSONPrimitive<OffsetDateTime> since);
|
||||
|
||||
|
||||
@ResponseBody
|
||||
@ResponseStatus(value = HttpStatus.OK)
|
||||
@GetMapping(value = STATUS_PATH + DOSSIER_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
List<FileModel> 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<FileModel> 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<FileModel> getSoftDeletedDossierStatus(@PathVariable(DOSSIER_ID_PARAM) String dossierId);
|
||||
|
||||
|
||||
@ResponseBody
|
||||
@ResponseStatus(value = HttpStatus.OK)
|
||||
@GetMapping(value = STATUS_PATH + DELETED_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
List<FileModel> getSoftDeletedForDossierList(@RequestBody List<String> 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<Integer> 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<Integer> pages);
|
||||
|
||||
}
|
||||
@ -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<TenantResponse> 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<String> getDeploymentKey(@PathVariable(TENANT_ID_PARAM) String tenantId);
|
||||
|
||||
}
|
||||
@ -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<String> 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<String> fileIds);
|
||||
|
||||
|
||||
@PostMapping(value = UNDELETE_PATH + DOSSIER_ID_PATH_PARAM)
|
||||
void undeleteFiles(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @RequestBody Set<String> fileIds);
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -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<ViewedPage> getViewedPages(@PathVariable(FILE_ID) String fileId, @PathVariable(ROLE) String role);
|
||||
|
||||
}
|
||||
@ -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<Watermark> 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<Boolean> isWatermarkUsed(@RequestParam(WATERMARK_ID_PARAMETER_NAME) long watermarkId);
|
||||
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<artifactId>persistence-service-v1</artifactId>
|
||||
<groupId>com.iqser.red.service</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>persistence-service-external-api-impl-v1</artifactId>
|
||||
<properties>
|
||||
<slf4j.version>1.7.30</slf4j.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.iqser.red.service</groupId>
|
||||
<artifactId>persistence-service-processor-v1</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-ui</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@ -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 {
|
||||
|
||||
}
|
||||
@ -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)));
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<CategoryModel> getAuditCategories() {
|
||||
|
||||
return auditPersistenceService.getCategories();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<CustomPermissionMappingModel> 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<CustomPermissionMappingModel> customPermissionMappingModels) {
|
||||
|
||||
customPermissionService.saveCustomPermissionMappings(targetObject, customPermissionMappingModels);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + MANAGE_ACL_PERMISSIONS + "')")
|
||||
public List<CustomPermissionMappingModel> getValidMapping(@PathVariable(TARGET_OBJECT_NAME) String targetObject) {
|
||||
|
||||
return customPermissionService.getExistingPermissions(targetObject);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + MANAGE_ACL_PERMISSIONS + "')")
|
||||
public Set<String> getAllSupportedTargetObjects() {
|
||||
|
||||
return customPermissionService.getAllSupportedTargetObjects();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + MANAGE_ACL_PERMISSIONS + "')")
|
||||
public void syncAllCustomPermissions() {
|
||||
|
||||
customPermissionService.syncAllCustomPermissions();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<String> 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<String> 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<String> 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<String> types,
|
||||
@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
|
||||
@RequestParam(value = DOSSIER_ID_PARAMETER_NAME, required = false) String dossierId) {
|
||||
|
||||
List<String> 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());
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<String> 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());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<String> 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<DossierChangeEntry> changesSince(@RequestBody JSONPrimitive<OffsetDateTime> 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<Dossier> 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<String> members = getAndValidateMembers(ownerId, dossierRequest.getMemberIds());
|
||||
Set<String> 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<String> getAndValidateMembers(String ownerId, Set<String> memberIds) {
|
||||
|
||||
Set<String> 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<User> users = userService.getUsersByIds(actualMemberIds);
|
||||
if (users.size() != actualMemberIds.size()) {
|
||||
Set<String> 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<String> 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<String> members, Set<String> 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 <T> 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<Dossier> 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<Dossier> 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<Dossier> getSoftDeletedDossiers() {
|
||||
|
||||
return dossierManagementService.getSoftDeletedDossiers().stream().map(dossierACLService::enhanceDossierWithACLData).collect(Collectors.toList());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
|
||||
@PostFilter("hasPermission(filterObject.id, 'Dossier', 'VIEW_OBJECT')")
|
||||
public List<Dossier> getArchivedDossiers() {
|
||||
|
||||
return dossierManagementService.getArchivedDossiers().stream().map(dossierACLService::enhanceDossierWithACLData).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
@PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
|
||||
@PostFilter("hasPermission(filterObject.id, 'Dossier', 'VIEW_OBJECT')")
|
||||
public List<Dossier> 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<String> 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<String> 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<String> 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<String> 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());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<DossierStats> getDossierStats(@RequestBody Set<String> dossierIds) {
|
||||
|
||||
return dossierIds.stream().map(dossierStatsService::getDossierStats).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<DossierStatusInfo> 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<DossierStatusInfo> getAllDossierStatuses(@RequestBody List<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<DossierTemplateModel> 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<Dossier> 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<String> dossierTemplateIds) {
|
||||
|
||||
String userId = KeycloakSecurity.getUserId();
|
||||
List<String> errorIds = new ArrayList<>();
|
||||
|
||||
for (String dossierTemplateId : dossierTemplateIds) {
|
||||
try {
|
||||
List<Dossier> 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<DossierTemplateStats> 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<String> 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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<DossierTemplateDictionaryStats> getDossierTemplateStats(Set<String> dossierTemplateIds) {
|
||||
|
||||
return dossierTemplateStatsService.getDossierTemplateStats(dossierTemplateIds);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<FileModel> 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<FileSystemResource> 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<DownloadStatus> 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<FileSystemResource> 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<String> generateOneTimeToken(@RequestBody JSONPrimitive<String> storageIdWrapper) {
|
||||
|
||||
log.debug("Generate one time token");
|
||||
return JSONPrimitive.of(oneTimeTokenDownloadService.createToken(storageIdWrapper.getValue(), KeycloakSecurity.getUserId()).getTokenId());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ResponseEntity<FileSystemResource> 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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<String> 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());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<String> fileIds) {
|
||||
|
||||
List<String> 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<String> 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<String> 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<String> 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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<String> 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<LegalBasis> 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<LegalBasis> getLegalBasisMapping(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId) {
|
||||
|
||||
return MagicConverter.convert(legalBasisMappingPersistenceService.getLegalBasisMapping(dossierTemplateId), LegalBasis.class);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,77 @@
|
||||
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
|
||||
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_NOTIFICATIONS;
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.UPDATE_NOTIFICATIONS;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
|
||||
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.persistence.NotificationPersistenceService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.utils.MagicConverter;
|
||||
import com.iqser.red.service.persistence.service.v1.api.external.resource.NotificationResource;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.NotificationResponse;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSONPrimitive;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.notification.Notification;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class NotificationController implements NotificationResource {
|
||||
|
||||
private final NotificationPersistenceService notificationPersistenceService;
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + READ_NOTIFICATIONS + "')")
|
||||
public JSONPrimitive<Boolean> hasNewNotificationsSince(@RequestBody JSONPrimitive<OffsetDateTime> since) {
|
||||
|
||||
return JSONPrimitive.of(notificationPersistenceService.hasNewNotificationsSince(KeycloakSecurity.getUserId(), since.getValue()));
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('" + UPDATE_NOTIFICATIONS + "')")
|
||||
public void toggleNotificationSeen(@RequestBody List<String> notificationIds, @RequestParam(SET_SEEN_PARAM) boolean setSeen) {
|
||||
|
||||
notificationIds.stream().map(Long::valueOf).forEach(notificationId -> {
|
||||
if (setSeen) {
|
||||
notificationPersistenceService.setSeenDate(KeycloakSecurity.getUserId(), notificationId, OffsetDateTime.now());
|
||||
} else {
|
||||
notificationPersistenceService.setSeenDate(KeycloakSecurity.getUserId(), notificationId, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('" + UPDATE_NOTIFICATIONS + "')")
|
||||
public void toggleNotificationRead(@RequestBody List<String> notificationIds, @RequestParam(SET_READ_PARAM) boolean setRead) {
|
||||
|
||||
notificationIds.stream().map(Long::valueOf).forEach(notificationId -> {
|
||||
if (setRead) {
|
||||
notificationPersistenceService.setReadDate(KeycloakSecurity.getUserId(), notificationId, OffsetDateTime.now());
|
||||
} else {
|
||||
notificationPersistenceService.setReadDate(KeycloakSecurity.getUserId(), notificationId, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('" + UPDATE_NOTIFICATIONS + "')")
|
||||
public void delete(@RequestBody List<String> notificationIds) {
|
||||
|
||||
notificationIds.stream().map(Long::valueOf).forEach(notificationId -> {
|
||||
notificationPersistenceService.softDelete(KeycloakSecurity.getUserId(), notificationId);
|
||||
});
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('" + READ_NOTIFICATIONS + "')")
|
||||
public NotificationResponse getNotifications(@RequestParam(INCLUDE_SEEN_PARAM) boolean includeSeen) {
|
||||
|
||||
var notifications = MagicConverter.convert(notificationPersistenceService.getNotifications(KeycloakSecurity.getUserId(), includeSeen), Notification.class);
|
||||
return NotificationResponse.builder().notifications(notifications).build();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
|
||||
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_NOTIFICATIONS;
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.UPDATE_NOTIFICATIONS;
|
||||
|
||||
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
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.NotificationPreferencesPersistenceService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.utils.MagicConverter;
|
||||
import com.iqser.red.service.persistence.service.v1.api.external.resource.NotificationPreferencesResource;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.notification.NotificationPreferences;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class NotificationPreferencesController implements NotificationPreferencesResource {
|
||||
|
||||
private final NotificationPreferencesPersistenceService notificationPreferencesPersistenceService;
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + READ_NOTIFICATIONS + "')")
|
||||
public NotificationPreferences getNotificationPreferences() {
|
||||
return notificationPreferencesPersistenceService.getOrCreateNotificationPreferences(KeycloakSecurity.getUserId());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + UPDATE_NOTIFICATIONS + "')")
|
||||
public void setNotificationPreferences(@RequestBody NotificationPreferences notificationPreferences) {
|
||||
|
||||
notificationPreferencesPersistenceService.setNotificationPreference(KeycloakSecurity.getUserId(), notificationPreferences);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + UPDATE_NOTIFICATIONS + "')")
|
||||
public void deleteNotificationPreferences() {
|
||||
|
||||
notificationPreferencesPersistenceService.deleteNotificationPreferences(KeycloakSecurity.getUserId());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,138 @@
|
||||
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
|
||||
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.GET_RSS;
|
||||
|
||||
import java.util.Map;
|
||||
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.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.iqser.red.keycloak.commons.KeycloakSecurity;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.client.redactionreportservice.RssReportClient;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentOverrideService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
|
||||
import com.iqser.red.service.persistence.service.v1.api.external.resource.RSSResource;
|
||||
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.component.ComponentsOverrides;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.RevertOverrideRequest;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.rss.RSSFileResponse;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.rss.RSSResponse;
|
||||
import com.iqser.red.service.redaction.report.v1.api.model.rss.DetailedRSSResponse;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class RSSController implements RSSResource {
|
||||
|
||||
private final RssReportClient rssReportClient;
|
||||
private final ComponentOverrideService componentOverrideService;
|
||||
private final AuditPersistenceService auditPersistenceService;
|
||||
|
||||
|
||||
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
|
||||
public RSSResponse getRSS(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(value = "fileId", required = false) String fileId) {
|
||||
|
||||
return convert(rssReportClient.getRSS(dossierId, fileId));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
|
||||
private RSSResponse convert(com.iqser.red.service.redaction.report.v1.api.model.rss.RSSResponse rssResponse) {
|
||||
|
||||
return new RSSResponse(rssResponse.getFiles().stream().map(this::convert).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
|
||||
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
|
||||
private RSSFileResponse convert(com.iqser.red.service.redaction.report.v1.api.model.rss.RSSFileResponse rssFileResponse) {
|
||||
|
||||
return new RSSFileResponse(rssFileResponse.getFilename(), rssFileResponse.getResult());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
|
||||
public DetailedRSSResponse getDetailedRSS(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(value = "fileId", required = false) String fileId) {
|
||||
|
||||
return rssReportClient.getDetailedRSS(dossierId, fileId);
|
||||
}
|
||||
|
||||
|
||||
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
|
||||
public void addOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody ComponentsOverrides componentsOverrides) {
|
||||
|
||||
var rssReport = rssReportClient.getDetailedRSS(dossierId, fileId);
|
||||
var components = rssReport.getFiles().get(0).getResult();
|
||||
|
||||
componentOverrideService.addOverrides(dossierId, fileId, componentsOverrides);
|
||||
|
||||
componentsOverrides.getComponentOverrides()
|
||||
.forEach((key, value) -> auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(fileId)
|
||||
.category(AuditCategory.DOCUMENT.name())
|
||||
.message("The component is overwritten with value")
|
||||
.details(Map.of(DOSSIER_ID,
|
||||
dossierId,
|
||||
FILE_ID,
|
||||
fileId,
|
||||
"ComponentName",
|
||||
key,
|
||||
"Action",
|
||||
"MODIFY",
|
||||
"OriginalValue",
|
||||
components.get(key).getOriginalValue(),
|
||||
"OldValue",
|
||||
components.get(key).getValue() != null ? components.get(key).getValue() : components.get(key).getOriginalValue(),
|
||||
"NewValue",
|
||||
value))
|
||||
.build()));
|
||||
}
|
||||
|
||||
|
||||
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
|
||||
public ComponentsOverrides getOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
|
||||
|
||||
return componentOverrideService.getOverrides(dossierId, fileId);
|
||||
}
|
||||
|
||||
|
||||
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
|
||||
public void revertOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody RevertOverrideRequest revertOverrideRequest) {
|
||||
|
||||
var rssReport = rssReportClient.getDetailedRSS(dossierId, fileId);
|
||||
var components = rssReport.getFiles().get(0).getResult();
|
||||
|
||||
componentOverrideService.revertOverrides(dossierId, fileId, revertOverrideRequest);
|
||||
|
||||
revertOverrideRequest.getComponents()
|
||||
.forEach(component -> auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(fileId)
|
||||
.category(AuditCategory.DOCUMENT.name())
|
||||
.message("The component override for was reverted")
|
||||
.details(Map.of(DOSSIER_ID,
|
||||
dossierId,
|
||||
FILE_ID,
|
||||
fileId,
|
||||
"ComponentName",
|
||||
component,
|
||||
"Action",
|
||||
"REVERT",
|
||||
"OriginalValue",
|
||||
components.get(component).getOriginalValue(),
|
||||
"OldValue",
|
||||
components.get(component).getValue() != null ? components.get(component).getValue() : components.get(component).getOriginalValue(),
|
||||
"NewValue",
|
||||
components.get(component).getOriginalValue()))
|
||||
.build()));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,312 @@
|
||||
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
|
||||
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
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.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.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.service.v1.api.external.resource.ReanalysisResource;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.PageExclusionRequest;
|
||||
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.WorkflowStatus;
|
||||
|
||||
import feign.FeignException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class ReanalysisController implements ReanalysisResource {
|
||||
|
||||
private static final String DOSSIER_ID = "dossierId";
|
||||
private final ReanalysisService reanalysisService;
|
||||
private final FileStatusManagementService fileStatusManagementService;
|
||||
private final AuditPersistenceService auditPersistenceService;
|
||||
private final AccessControlService accessControlService;
|
||||
|
||||
@PreAuthorize("hasAuthority('" + REANALYZE_DOSSIER + "')")
|
||||
public void reanalyzeDossier(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(value = FORCE_PARAM, required = false, defaultValue = FALSE) boolean force) {
|
||||
|
||||
try {
|
||||
accessControlService.verifyUserHasViewPermissions(dossierId);
|
||||
} catch (AccessDeniedException e) {
|
||||
throw new NotFoundException("Object not found");
|
||||
}
|
||||
|
||||
accessControlService.verifyUserHasAccessPermissions(dossierId);
|
||||
reanalysisService.reanalyzeDossier(dossierId, force);
|
||||
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(dossierId)
|
||||
.category(AuditCategory.DOSSIER.name())
|
||||
.message("Reanalyse dossier was triggered")
|
||||
.build());
|
||||
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('" + REANALYZE_FILE + "')")
|
||||
public void reanalyzeFile(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@PathVariable(FILE_ID) String fileId,
|
||||
@RequestParam(value = FORCE_PARAM, required = false, defaultValue = FALSE) boolean force) {
|
||||
|
||||
reanalysisService.reanalyzeFiles(dossierId, Sets.newHashSet(fileId), force);
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(dossierId)
|
||||
.category(AuditCategory.DOCUMENT.name())
|
||||
.message("Reanalyse file was triggered")
|
||||
.details(Map.of(DOSSIER_ID, dossierId))
|
||||
.build());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + REANALYZE_FILE + "')")
|
||||
public void reanalyzeFilesForDossier(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@RequestBody List<String> fileIds,
|
||||
@RequestParam(value = FORCE_PARAM, required = false, defaultValue = FALSE) boolean force) {
|
||||
|
||||
reanalysisService.reanalyzeFiles(dossierId, new HashSet<>(fileIds), force);
|
||||
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(dossierId)
|
||||
.category(AuditCategory.DOCUMENT.name())
|
||||
.message("Reanalyse files was triggered")
|
||||
.details(Map.of(DOSSIER_ID, dossierId, "number", fileIds.size()))
|
||||
.build());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + REANALYZE_DOSSIER + "')")
|
||||
public void ocrDossier(@PathVariable(DOSSIER_ID) String dossierId) {
|
||||
|
||||
try {
|
||||
accessControlService.verifyUserHasViewPermissions(dossierId);
|
||||
} catch (AccessDeniedException e) {
|
||||
throw new NotFoundException("Object not found");
|
||||
}
|
||||
|
||||
accessControlService.verifyUserHasAccessPermissions(dossierId);
|
||||
reanalysisService.ocrDossier(dossierId);
|
||||
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(dossierId)
|
||||
.category(AuditCategory.DOSSIER.name())
|
||||
.message("OCR and reanalyse dossier was triggered")
|
||||
.build());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + REANALYZE_FILE + "')")
|
||||
public void ocrFile(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@PathVariable(FILE_ID) String fileId,
|
||||
@RequestParam(value = FORCE_PARAM, required = false, defaultValue = FALSE) boolean force) {
|
||||
|
||||
validateOCR(dossierId, fileId);
|
||||
reanalysisService.ocrFile(dossierId, fileId, force);
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(dossierId)
|
||||
.category(AuditCategory.DOCUMENT.name())
|
||||
.message("OCR and reanalyse file was triggered")
|
||||
.details(Map.of(DOSSIER_ID, dossierId))
|
||||
.build());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + REANALYZE_FILE + "')")
|
||||
public void ocrFiles(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody Set<String> fileIds) {
|
||||
|
||||
fileIds.forEach(fileId -> validateOCR(dossierId, fileId));
|
||||
reanalysisService.ocrFiles(dossierId, fileIds);
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(dossierId)
|
||||
.category(AuditCategory.DOCUMENT.name())
|
||||
.message("OCR and reanalyse was triggered")
|
||||
.details(Map.of(DOSSIER_ID, dossierId, "number", fileIds.size()))
|
||||
.build());
|
||||
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('" + EXCLUDE_INCLUDE_FILE + "')")
|
||||
public void toggleAutomaticAnalysis(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@PathVariable(FILE_ID) String fileId,
|
||||
@RequestParam(EXCLUDED_STATUS_PARAM) boolean excludedFromAutomaticAnalysis) {
|
||||
|
||||
accessControlService.verifyUserIsReviewer(dossierId, fileId);
|
||||
fileStatusManagementService.toggleAutomaticAnalysis(dossierId, fileId, excludedFromAutomaticAnalysis);
|
||||
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(fileId)
|
||||
.category(AuditCategory.DOCUMENT.name())
|
||||
.message("Toggle Exclusion status: File excluded from automatic analysis: " + excludedFromAutomaticAnalysis)
|
||||
.build());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + EXCLUDE_INCLUDE_FILE + "')")
|
||||
public void toggleExclusion(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@PathVariable(FILE_ID) String fileId,
|
||||
@RequestParam(name = EXCLUDED_STATUS_PARAM, required = false, defaultValue = "false") boolean excluded) {
|
||||
|
||||
var status = fileStatusManagementService.getFileStatus(fileId);
|
||||
if (!(status.getAssignee() == null && status.isExcluded())) { // Needed to include documents after 3.0 migration.
|
||||
accessControlService.verifyUserIsReviewer(dossierId, fileId);
|
||||
}
|
||||
fileStatusManagementService.toggleExclusion(dossierId, fileId, excluded);
|
||||
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(fileId)
|
||||
.category(AuditCategory.DOCUMENT.name())
|
||||
.message("Toggle Exclusion status: File excluded from analysis: " + excluded)
|
||||
.build());
|
||||
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('" + EXCLUDE_INCLUDE_FILE + "')")
|
||||
public void toggleAutomaticAnalysisForList(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@RequestBody Set<String> fileIds,
|
||||
@RequestParam(EXCLUDED_STATUS_PARAM) boolean excludedFromAutomaticAnalysis) {
|
||||
|
||||
List<String> errorIds = new ArrayList<>();
|
||||
for (var fileId : fileIds) {
|
||||
try {
|
||||
this.toggleAutomaticAnalysis(dossierId, fileId, excludedFromAutomaticAnalysis);
|
||||
} catch (FeignException e) {
|
||||
errorIds.add(fileId);
|
||||
}
|
||||
}
|
||||
if (!errorIds.isEmpty()) {
|
||||
throw new BadRequestException("Failed to delete files with ids: " + errorIds);
|
||||
}
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('" + EXCLUDE_INCLUDE_FILE + "')")
|
||||
public void toggleExclusionForList(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@RequestBody Set<String> fileIds,
|
||||
@RequestParam(name = EXCLUDED_STATUS_PARAM, required = false, defaultValue = "false") boolean excluded) {
|
||||
|
||||
List<String> errorIds = new ArrayList<>();
|
||||
for (var fileId : fileIds) {
|
||||
try {
|
||||
this.toggleExclusion(dossierId, fileId, excluded);
|
||||
} catch (FeignException e) {
|
||||
errorIds.add(fileId);
|
||||
}
|
||||
}
|
||||
if (!errorIds.isEmpty()) {
|
||||
throw new BadRequestException("Failed to delete files with ids: " + errorIds);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + EXCLUDE_INCLUDE_PAGES + "')")
|
||||
public void excludePages(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody PageExclusionRequest pageExclusionRequest) {
|
||||
|
||||
accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
|
||||
|
||||
Set<Integer> excludedPages = new HashSet<>();
|
||||
for (var pageRange : pageExclusionRequest.getPageRanges()) {
|
||||
for (int i = pageRange.getStartPage(); i <= pageRange.getEndPage(); i++) {
|
||||
excludedPages.add(i);
|
||||
}
|
||||
}
|
||||
fileStatusManagementService.excludePages(dossierId, fileId, excludedPages);
|
||||
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(fileId)
|
||||
.category(AuditCategory.DOCUMENT.name())
|
||||
.message("Page exclusions added for file")
|
||||
.build());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + EXCLUDE_INCLUDE_PAGES + "')")
|
||||
public void includePages(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody PageExclusionRequest pageInclusionRequest) {
|
||||
|
||||
accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
|
||||
|
||||
Set<Integer> includePages = new HashSet<>();
|
||||
for (var pageRange : pageInclusionRequest.getPageRanges()) {
|
||||
for (int i = pageRange.getStartPage(); i <= pageRange.getEndPage(); i++) {
|
||||
includePages.add(i);
|
||||
}
|
||||
}
|
||||
fileStatusManagementService.includePages(dossierId, fileId, includePages);
|
||||
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(fileId)
|
||||
.category(AuditCategory.DOCUMENT.name())
|
||||
.message("Page inclusions added for file")
|
||||
.build());
|
||||
}
|
||||
|
||||
|
||||
@PreAuthorize("hasAuthority('" + REINDEX + "')")
|
||||
public void reindex(@RequestParam(value = "dossierId", required = false) String dossierId,
|
||||
@RequestParam(value = "dropIndex", required = false, defaultValue = FALSE) boolean dropIndex,
|
||||
@RequestBody List<String> fileIds) {
|
||||
|
||||
reanalysisService.reindex(dossierId, dropIndex, new HashSet<>(fileIds));
|
||||
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId("redaction")
|
||||
.category(AuditCategory.INDEX.name())
|
||||
.message("Reindexing has been triggered" + (dropIndex ? " (with drop index)." : "."))
|
||||
.build());
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void validateOCR(String dossierId, String fileId) {
|
||||
|
||||
var status = fileStatusManagementService.getFileStatus(fileId);
|
||||
if (status.getWorkflowStatus() == WorkflowStatus.APPROVED) {
|
||||
throw new BadRequestException("Cannot OCR approved file");
|
||||
}
|
||||
if (status.getAssignee() == null) {
|
||||
accessControlService.verifyUserIsMemberOrApprover(dossierId);
|
||||
} else {
|
||||
accessControlService.verifyUserIsReviewer(dossierId, fileId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,173 @@
|
||||
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
|
||||
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_REDACTION_LOG;
|
||||
import static com.iqser.red.service.persistence.management.v1.processor.service.FeignExceptionHandler.processFeignException;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
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.service.persistence.management.v1.processor.service.FileStatusService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.RedactionLogService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.utils.StringEncodingUtils;
|
||||
import com.iqser.red.service.persistence.service.v1.api.external.resource.RedactionLogResource;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.FilteredRedactionLogRequest;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.RedactionLog;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.section.SectionGrid;
|
||||
import com.iqser.red.storage.commons.exception.StorageObjectDoesNotExist;
|
||||
import com.iqser.red.storage.commons.service.StorageService;
|
||||
|
||||
import feign.FeignException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class RedactionLogController implements RedactionLogResource {
|
||||
|
||||
private final RedactionLogService redactionLogService;
|
||||
private final StorageService storageService;
|
||||
private final FileStatusService fileStatusService;
|
||||
|
||||
|
||||
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
|
||||
public RedactionLog getRedactionLog(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@PathVariable(FILE_ID) String fileId,
|
||||
@RequestParam(value = "excludedType", required = false) List<String> excludedTypes,
|
||||
@RequestParam(value = "withManualRedactions", required = false, defaultValue = "true") boolean withManualRedactions,
|
||||
@RequestParam(value = "includeFalsePositives", required = false, defaultValue = "false") boolean includeFalsePositives) {
|
||||
|
||||
try {
|
||||
return redactionLogService.getRedactionLog(dossierId, fileId, excludedTypes, withManualRedactions, includeFalsePositives);
|
||||
} catch (FeignException e) {
|
||||
throw processFeignException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
|
||||
public SectionGrid getSectionGrid(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
|
||||
|
||||
try {
|
||||
return redactionLogService.getSectionGrid(dossierId, fileId);
|
||||
} catch (FeignException e) {
|
||||
throw processFeignException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
|
||||
public ResponseEntity<?> getSectionText(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
|
||||
|
||||
try {
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setContentType(MediaType.parseMediaType("application/zip"));
|
||||
|
||||
var fileStatus = fileStatusService.getStatus(fileId);
|
||||
String filename = fileStatus.getFilename();
|
||||
if (filename != null) {
|
||||
var index = filename.lastIndexOf(".");
|
||||
String prefix = filename.substring(0, index);
|
||||
filename = prefix + ".json";
|
||||
httpHeaders.add("Content-Disposition", "attachment; filename=" + prefix + ".zip");
|
||||
}
|
||||
|
||||
byte[] zipBytes = getZippedBytes(dossierId, fileId, filename, FileType.TEXT);
|
||||
return new ResponseEntity<>(zipBytes, httpHeaders, HttpStatus.OK);
|
||||
|
||||
} catch (FeignException e) {
|
||||
throw processFeignException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
|
||||
public ResponseEntity<?> getSimplifiedSectionText(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
|
||||
|
||||
try {
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setContentType(MediaType.parseMediaType("application/zip"));
|
||||
|
||||
var fileStatus = fileStatusService.getStatus(fileId);
|
||||
String filename = fileStatus.getFilename();
|
||||
if (filename != null) {
|
||||
var index = filename.lastIndexOf(".");
|
||||
String prefix = filename.substring(0, index);
|
||||
filename = prefix + ".json";
|
||||
httpHeaders.add("Content-Disposition", "attachment; filename*=utf-8''" + StringEncodingUtils.urlEncode(prefix) + ".zip");
|
||||
}
|
||||
|
||||
byte[] zipBytes = getZippedBytes(dossierId, fileId, filename, FileType.SIMPLIFIED_TEXT);
|
||||
return new ResponseEntity<>(zipBytes, httpHeaders, HttpStatus.OK);
|
||||
|
||||
} catch (StorageObjectDoesNotExist e) {
|
||||
throw new RuntimeException("Simplified Text is not available", e);
|
||||
} catch (FeignException e) {
|
||||
throw processFeignException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
|
||||
public RedactionLog getFilteredRedactionLog(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@PathVariable(FILE_ID) String fileId,
|
||||
@RequestBody FilteredRedactionLogRequest filteredRedactionLogRequest) {
|
||||
|
||||
try {
|
||||
|
||||
return redactionLogService.getFilteredRedactionLog(dossierId, fileId, filteredRedactionLogRequest);
|
||||
|
||||
} catch (FeignException e) {
|
||||
throw processFeignException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private byte[] getZippedBytes(String dossierId, String fileId, String filename, FileType fileType) throws IOException {
|
||||
|
||||
try {
|
||||
String objectId = dossierId + "/" + fileId + "." + fileType.name() + fileType.getExtension();
|
||||
var inputStreamResource = storageService.getObject(objectId);
|
||||
|
||||
try (var inputStream = inputStreamResource.getInputStream()) {
|
||||
return zipBytes(filename, inputStream.readAllBytes());
|
||||
}
|
||||
|
||||
} catch (StorageObjectDoesNotExist e) {
|
||||
throw new RuntimeException(String.format("%s is not available", fileType.name()), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static byte[] zipBytes(String filename, byte[] input) throws IOException {
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ZipOutputStream zos = new ZipOutputStream(baos);
|
||||
ZipEntry entry = new ZipEntry(filename);
|
||||
entry.setSize(input.length);
|
||||
zos.putNextEntry(entry);
|
||||
zos.write(input);
|
||||
zos.closeEntry();
|
||||
zos.close();
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,208 @@
|
||||
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
|
||||
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.DELETE_REPORT_TEMPLATE;
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.DOWNLOAD_REPORT_TEMPLATE;
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.GET_REPORT_TEMPLATES;
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.UPLOAD_REPORT_TEMPLATE;
|
||||
import static com.iqser.red.service.persistence.management.v1.processor.service.FeignExceptionHandler.processFeignException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
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.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.client.redactionreportservice.PlaceholderClient;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.client.redactionreportservice.ReportTemplatePlaceholderClient;
|
||||
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.ReportTemplateService;
|
||||
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.service.persistence.FileAttributeConfigPersistenceService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.ReportTemplatePersistenceService;
|
||||
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.service.v1.api.external.resource.ReportTemplateResource;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.PlaceholdersResponse;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.ReportTemplateUpdateRequest;
|
||||
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.ReportTemplate;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.ReportTemplateUploadRequest;
|
||||
import com.iqser.red.storage.commons.exception.StorageObjectDoesNotExist;
|
||||
import com.iqser.red.storage.commons.service.StorageService;
|
||||
|
||||
import feign.FeignException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class ReportTemplateController implements ReportTemplateResource {
|
||||
|
||||
private final ReportTemplatePlaceholderClient reportTemplatePlaceholderClient;
|
||||
private final PlaceholderClient placeholderClient;
|
||||
private final AuditPersistenceService auditPersistenceService;
|
||||
private final DossierAttributeConfigPersistenceService dossierAttributeConfigPersistenceService;
|
||||
private final FileAttributeConfigPersistenceService fileAttributeConfigPersistenceService;
|
||||
private final StorageService storageService;
|
||||
private final ReportTemplatePersistenceService reportTemplatePersistenceService;
|
||||
private final ReportTemplateService reportTemplateService;
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + GET_REPORT_TEMPLATES + "')")
|
||||
public List<ReportTemplate> getReportTemplatesByPlaceholder(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody JSONPrimitive<String> placeholder) {
|
||||
|
||||
try {
|
||||
// TODO MIGRATION ***
|
||||
// return reportTemplatePlaceholderClient.getReportTemplatesByPlaceholder(dossierTemplateId, placeholder);
|
||||
return null;
|
||||
} catch (FeignException e) {
|
||||
throw processFeignException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + UPLOAD_REPORT_TEMPLATE + "')")
|
||||
public ReportTemplate uploadTemplate(@RequestPart(name = "file") MultipartFile file,
|
||||
@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId,
|
||||
@RequestParam(value = MULTI_FILE_REPORT, required = false, defaultValue = "false") boolean multiFileReport,
|
||||
@RequestParam(value = ACTIVE_BY_DEFAULT, required = false, defaultValue = "false") boolean activeByDefault) {
|
||||
|
||||
try {
|
||||
if (file.getOriginalFilename() != null) {
|
||||
|
||||
ReportTemplateUploadRequest reportTemplateUploadRequest = ReportTemplateUploadRequest.builder()
|
||||
.template(file.getBytes())
|
||||
.fileName(file.getOriginalFilename())
|
||||
.dossierTemplateId(dossierTemplateId)
|
||||
.activeByDefault(activeByDefault)
|
||||
.multiFileReport(multiFileReport)
|
||||
.build();
|
||||
var reportTemplate = reportTemplateService.uploadTemplate(reportTemplateUploadRequest);
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(reportTemplate.getTemplateId())
|
||||
.category(AuditCategory.DOSSIER_TEMPLATE.name())
|
||||
.message("Report template was uploaded.")
|
||||
.details(Map.of("DossierTemplateId", dossierTemplateId))
|
||||
.build());
|
||||
return reportTemplate;
|
||||
} else {
|
||||
throw new BadRequestException("Could not upload file, no filename provided.");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new BadRequestException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + GET_REPORT_TEMPLATES + "')")
|
||||
public List<ReportTemplate> getAvailableReportTemplates(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId) {
|
||||
|
||||
return MagicConverter.convert(reportTemplatePersistenceService.findByDossierTemplateId(dossierTemplateId), ReportTemplate.class);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + DOWNLOAD_REPORT_TEMPLATE + "')")
|
||||
public ResponseEntity<?> downloadReportTemplate(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @PathVariable(TEMPLATE_ID) String templateId) {
|
||||
|
||||
try {
|
||||
var reportTemplate = reportTemplatePersistenceService.find(templateId);
|
||||
byte[] file = IOUtils.toByteArray(storageService.getObject(reportTemplate.getStorageId()).getInputStream());
|
||||
return getResponseEntity(file, reportTemplate.getFileName(), MediaType.APPLICATION_OCTET_STREAM);
|
||||
} catch (StorageObjectDoesNotExist | IOException e) {
|
||||
throw new NotFoundException("Template does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private ResponseEntity<?> getResponseEntity(byte[] file, String filename, MediaType mediaType) {
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setContentType(mediaType);
|
||||
|
||||
if (filename != null) {
|
||||
httpHeaders.add("Content-Disposition", "attachment; filename*=utf-8''" + StringEncodingUtils.urlEncode(filename));
|
||||
}
|
||||
InputStream is = new ByteArrayInputStream(file);
|
||||
|
||||
return new ResponseEntity<>(new InputStreamResource(is), httpHeaders, HttpStatus.OK);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + DELETE_REPORT_TEMPLATE + "')")
|
||||
public void deleteTemplate(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @PathVariable(TEMPLATE_ID) String templateId) {
|
||||
|
||||
var storageId = reportTemplatePersistenceService.find(templateId).getStorageId();
|
||||
storageService.deleteObject(storageId);
|
||||
reportTemplatePersistenceService.delete(templateId);
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(templateId)
|
||||
.category(AuditCategory.DOSSIER_TEMPLATE.name())
|
||||
.message("Report template was deleted.")
|
||||
.details(Map.of("DossierTemplateId", dossierTemplateId))
|
||||
.build());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public PlaceholdersResponse getAvailablePlaceholders(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId) {
|
||||
|
||||
PlaceholdersResponse availablePlaceholders = new PlaceholdersResponse();
|
||||
|
||||
List<String> dossierPlaceholders = new ArrayList<>();
|
||||
for (var dossierAttributeConfig : dossierAttributeConfigPersistenceService.getDossierAttributes(dossierTemplateId)) {
|
||||
dossierPlaceholders.add(dossierAttributeConfig.getPlaceholder());
|
||||
}
|
||||
availablePlaceholders.setDossierAttributePlaceholders(dossierPlaceholders);
|
||||
|
||||
List<String> filePlaceholders = new ArrayList<>();
|
||||
for (var fileAttributeConfig : fileAttributeConfigPersistenceService.getFileAttributes(dossierTemplateId)) {
|
||||
filePlaceholders.add(fileAttributeConfig.getPlaceholder());
|
||||
}
|
||||
availablePlaceholders.setFileAttributePlaceholders(filePlaceholders);
|
||||
|
||||
List<String> generalPlaceholders = placeholderClient.getPlaceholders();
|
||||
availablePlaceholders.setGeneralPlaceholders(generalPlaceholders);
|
||||
|
||||
return availablePlaceholders;
|
||||
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('" + UPLOAD_REPORT_TEMPLATE + "')")
|
||||
public void updateTemplate(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId,
|
||||
@PathVariable(TEMPLATE_ID) String templateId,
|
||||
@RequestBody ReportTemplateUpdateRequest updateRequest) {
|
||||
|
||||
reportTemplatePersistenceService.updateTemplate(dossierTemplateId, templateId, updateRequest);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,111 @@
|
||||
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
|
||||
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_RULES;
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_RULES;
|
||||
import static com.iqser.red.service.persistence.management.v1.processor.service.FeignExceptionHandler.processFeignException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
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.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.client.redactionservice.RedactionClient;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.exception.FileUploadException;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.exception.InvalidRulesException;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.RulesPersistenceService;
|
||||
import com.iqser.red.service.persistence.service.v1.api.external.resource.RulesResource;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.Rules;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
|
||||
|
||||
import feign.FeignException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class RulesController implements RulesResource {
|
||||
|
||||
private static final String DOWNLOAD_FILE_NAME = "rules.drl";
|
||||
|
||||
private final RulesPersistenceService rulesPersistenceService;
|
||||
private final RedactionClient redactionServiceClient;
|
||||
private final AuditPersistenceService auditPersistenceService;
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
|
||||
public void upload(@RequestBody Rules rules) {
|
||||
|
||||
try {
|
||||
redactionServiceClient.testRules(rules.getRules());
|
||||
} catch (FeignException e) {
|
||||
if (e.status() == HttpStatus.BAD_REQUEST.value()) {
|
||||
throw new InvalidRulesException("Rules could not be updated, validation check failed: " + e.getMessage());
|
||||
}
|
||||
throw processFeignException(e);
|
||||
}
|
||||
rulesPersistenceService.setRules(rules.getRules(), rules.getDossierTemplateId());
|
||||
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(rules.getDossierTemplateId())
|
||||
.category(AuditCategory.DOSSIER_TEMPLATE.name())
|
||||
.message("Rules have been updated")
|
||||
.build());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + READ_RULES + "')")
|
||||
public Rules download(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId) {
|
||||
|
||||
return new Rules(rulesPersistenceService.getRules(dossierTemplateId).getValue(), dossierTemplateId);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
|
||||
public void uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @RequestPart(name = "file") MultipartFile file) {
|
||||
|
||||
try {
|
||||
upload(new Rules(new String(file.getBytes(), StandardCharsets.UTF_8), dossierTemplateId));
|
||||
} catch (IOException e) {
|
||||
throw new FileUploadException("Could not upload file.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + READ_RULES + "')")
|
||||
public ResponseEntity<?> downloadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId) {
|
||||
|
||||
byte[] data = download(dossierTemplateId).getRules().getBytes(StandardCharsets.UTF_8);
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
|
||||
|
||||
httpHeaders.add("Content-Disposition", "attachment; filename*=utf-8\"" + DOWNLOAD_FILE_NAME + "\"");
|
||||
InputStream is = new ByteArrayInputStream(data);
|
||||
|
||||
return new ResponseEntity<>(new InputStreamResource(is), httpHeaders, HttpStatus.OK);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
|
||||
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_SMTP_CONFIGURATION;
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.WRITE_SMTP_CONFIGURATION;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.iqser.red.keycloak.commons.KeycloakSecurity;
|
||||
import com.iqser.red.keycloak.commons.RealmService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.SMTPConfigurationService;
|
||||
import com.iqser.red.service.persistence.service.v1.api.external.resource.SMTPConfigurationResource;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.SMTPConfiguration;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class SMTPConfigurationController implements SMTPConfigurationResource {
|
||||
|
||||
private final RealmService realmService;
|
||||
|
||||
private final SMTPConfigurationService smtpConfigurationService;
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + READ_SMTP_CONFIGURATION + "')")
|
||||
public SMTPConfiguration getCurrentSMTPConfiguration() {
|
||||
|
||||
return smtpConfigurationService.getCurrentSMTPConfiguration(true);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + WRITE_SMTP_CONFIGURATION + "')")
|
||||
public void updateSMTPConfiguration(@RequestBody SMTPConfiguration smtpConfigurationModel) {
|
||||
|
||||
smtpConfigurationService.updateSMTPConfiguration(smtpConfigurationModel);
|
||||
|
||||
// also update in KC
|
||||
var realmRepresentation = realmService.realm().toRepresentation();
|
||||
var propertiesMap = convertSMTPConfigurationModelToMap(smtpConfigurationModel);
|
||||
realmRepresentation.setSmtpServer(propertiesMap);
|
||||
realmService.realm().update(realmRepresentation);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private Map<String, String> convertSMTPConfigurationModelToMap(SMTPConfiguration smtpConfigurationModel) {
|
||||
|
||||
Map<String, Object> propertiesMap = objectMapper.convertValue(smtpConfigurationModel, Map.class);
|
||||
Map<String, String> stringPropertiesMap = new HashMap<>();
|
||||
propertiesMap.forEach((key, value) -> {
|
||||
if (value != null) {
|
||||
stringPropertiesMap.put(key, value.toString());
|
||||
} else {
|
||||
stringPropertiesMap.put(key, "");
|
||||
}
|
||||
});
|
||||
return stringPropertiesMap;
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + WRITE_SMTP_CONFIGURATION + "')")
|
||||
public void testSMTPConfiguration(@RequestBody SMTPConfiguration smtpConfigurationModel) {
|
||||
|
||||
var currentUserEmail = realmService.realm().users().get(KeycloakSecurity.getUserId()).toRepresentation().getEmail();
|
||||
|
||||
smtpConfigurationService.testSMTPConfiguration(currentUserEmail, smtpConfigurationModel);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + WRITE_SMTP_CONFIGURATION + "')")
|
||||
public void clearSMTPConfiguration() {
|
||||
|
||||
smtpConfigurationService.deleteConfiguration();
|
||||
|
||||
// also update in KC
|
||||
var realmRepresentation = realmService.realm().toRepresentation();
|
||||
realmRepresentation.setSmtpServer(new HashMap<>());
|
||||
realmService.realm().update(realmRepresentation);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
|
||||
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.SEARCH;
|
||||
|
||||
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.client.searchservice.SearchClient;
|
||||
import com.iqser.red.service.persistence.service.v1.api.external.resource.SearchResource;
|
||||
import com.iqser.red.service.search.v1.model.SearchRequest;
|
||||
import com.iqser.red.service.search.v1.model.SearchResult;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class SearchController implements SearchResource {
|
||||
|
||||
private final SearchClient searchClient;
|
||||
|
||||
|
||||
@PreAuthorize("hasAuthority('" + SEARCH + "')")
|
||||
public SearchResult search(@RequestBody SearchRequest searchRequest) {
|
||||
|
||||
return searchClient.getDossierStatus(searchRequest);
|
||||
}
|
||||
|
||||
|
||||
@Deprecated
|
||||
@PreAuthorize("hasAuthority('" + SEARCH + "')")
|
||||
public SearchResult searchDeprecated(@RequestBody SearchRequest searchRequest) {
|
||||
|
||||
searchRequest.setPage(searchRequest.getPage() - 1);
|
||||
return searchClient.getDossierStatus(searchRequest);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,486 @@
|
||||
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
|
||||
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.*;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
|
||||
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.iqser.red.keycloak.commons.KeycloakSecurity;
|
||||
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.exception.NotAllowedException;
|
||||
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.StatusResource;
|
||||
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.FileStatus;
|
||||
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.file.FileModel;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.ProcessingStatus;
|
||||
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.notification.NotificationType;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class StatusController implements StatusResource {
|
||||
|
||||
private static final Set<String> VALID_MEMBER_ROLES = Set.of(ApplicationRoles.RED_USER_ROLE, ApplicationRoles.RED_MANAGER_ROLE);
|
||||
private static final String DOSSIER_ID = "dossierId";
|
||||
private static final String FILE_ID = "fileId";
|
||||
private static final String FILE_NAME = "fileName";
|
||||
private final FileStatusManagementService fileStatusManagementService;
|
||||
private final UserService userService;
|
||||
private final DossierManagementService dossierManagementService;
|
||||
private final AuditPersistenceService auditPersistenceService;
|
||||
private final AccessControlService accessControlService;
|
||||
private final NotificationPersistenceService notificationPersistenceService;
|
||||
private final DossierACLService dossierACLService;
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + READ_FILE_STATUS + "')")
|
||||
public JSONPrimitive<Boolean> hasChangesSince(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody JSONPrimitive<OffsetDateTime> since) {
|
||||
|
||||
try {
|
||||
accessControlService.verifyUserHasViewPermissions(dossierId);
|
||||
} catch (AccessDeniedException e) {
|
||||
return JSONPrimitive.of(false);
|
||||
}
|
||||
return fileStatusManagementService.hasChangesSince(dossierId, since);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + READ_FILE_STATUS + "')")
|
||||
public Map<String, List<FileStatus>> getDossierStatus(@RequestBody List<String> dossierIds) {
|
||||
|
||||
Map<String, List<FileStatus>> response = new HashMap<>();
|
||||
for (String dossierId : dossierIds) {
|
||||
try {
|
||||
accessControlService.verifyUserHasViewPermissions(dossierId);
|
||||
List<FileStatus> statusList = fileStatusManagementService.getDossierStatus(dossierId)
|
||||
.stream()
|
||||
.filter(fileStatus -> !fileStatus.isSoftOrHardDeleted())
|
||||
.map(this::convert)
|
||||
.collect(Collectors.toList());
|
||||
response.put(dossierId, statusList);
|
||||
} catch (AccessDeniedException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + READ_FILE_STATUS + "')")
|
||||
@Transactional
|
||||
public Map<String, List<FileStatus>> getSoftDeletedFilesForDossiers(@RequestBody List<String> dossierIds) {
|
||||
|
||||
List<FileStatus> statusList;
|
||||
List<String> dossiersWithViewPermissions = new ArrayList<>();
|
||||
for (var dossierId : dossierIds) {
|
||||
try {
|
||||
accessControlService.verifyUserHasViewPermissions(dossierId);
|
||||
dossiersWithViewPermissions.add(dossierId);
|
||||
} catch (AccessDeniedException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (dossiersWithViewPermissions.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
statusList = fileStatusManagementService.getSoftDeletedForDossierList(dossiersWithViewPermissions).stream().map(this::convert).collect(Collectors.toList());
|
||||
|
||||
return statusList.stream().collect(Collectors.groupingBy(FileStatus::getDossierId, Collectors.toList()));
|
||||
}
|
||||
|
||||
|
||||
@PreAuthorize("hasAuthority('" + READ_FILE_STATUS + "')")
|
||||
public List<FileStatus> getDossierStatus(@PathVariable(DOSSIER_ID) String dossierId) {
|
||||
|
||||
try {
|
||||
|
||||
accessControlService.verifyUserHasViewPermissions(dossierId);
|
||||
return fileStatusManagementService.getDossierStatus(dossierId)
|
||||
.stream()
|
||||
.filter(fileStatus -> !fileStatus.isSoftOrHardDeleted())
|
||||
.map(this::convert)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
} catch (AccessDeniedException e) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('" + READ_FILE_STATUS + "')")
|
||||
public FileStatus getFileStatus(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
|
||||
|
||||
return convert(fileStatusManagementService.getFileStatus(fileId));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + SET_REVIEWER + "')")
|
||||
public void setCurrentFileAssignee(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@PathVariable(FILE_ID) String fileId,
|
||||
@RequestParam(name = ASSIGNEE_ID_REQUEST_PARAM, required = false) String assigneeId) {
|
||||
|
||||
accessControlService.verifyUserIsMemberOrApprover(dossierId);
|
||||
|
||||
log.debug("Requested [setFileReviewer] for dossier: {} / file: {} / reviewer: {}", dossierId, fileId, assigneeId);
|
||||
|
||||
if (assigneeId != null) {
|
||||
var user = userService.getUserById(assigneeId);
|
||||
if (user.isEmpty()) {
|
||||
userService.removeDeletedUsers(Collections.singleton(assigneeId));
|
||||
throw new BadRequestException("Unknown user=" + assigneeId);
|
||||
}
|
||||
// check he has a manager role, thus he can be the owner
|
||||
if (user.get().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.");
|
||||
}
|
||||
// check assignee is a member
|
||||
accessControlService.verifyUserIsMemberOrApprover(dossierId, assigneeId);
|
||||
} else {
|
||||
// check if file is already unassigned (cannot unassign an unassigned file)
|
||||
if (fileStatusManagementService.getFileStatus(fileId).getAssignee() == null) {
|
||||
throw new BadRequestException("File is already unassigned!");
|
||||
}
|
||||
}
|
||||
|
||||
var fileStatus = fileStatusManagementService.getFileStatus(fileId);
|
||||
|
||||
log.debug("Obtained status: {}", fileStatus);
|
||||
|
||||
fileStatusManagementService.setCurrentFileAssignee(dossierId, fileId, assigneeId);
|
||||
if (assigneeId == null) {
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(fileId)
|
||||
.category(AuditCategory.DOCUMENT.name())
|
||||
.message("Reviewer was unassigned from document")
|
||||
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, fileStatus.getFilename()))
|
||||
.build());
|
||||
} else {
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(fileId)
|
||||
.category(AuditCategory.DOCUMENT.name())
|
||||
.message("Reviewer was assigned to document")
|
||||
.details(Map.of(DOSSIER_ID, dossierId, "reviewer", assigneeId))
|
||||
.build());
|
||||
}
|
||||
|
||||
if (assigneeId != null && !assigneeId.equals(KeycloakSecurity.getUserId())) {
|
||||
notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
|
||||
.userId(assigneeId)
|
||||
.issuerId(KeycloakSecurity.getUserId())
|
||||
.notificationType(NotificationType.ASSIGN_REVIEWER.name())
|
||||
.target(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, fileStatus.getFilename()))
|
||||
.build());
|
||||
}
|
||||
|
||||
if (assigneeId == null || fileStatus.getAssignee() != null && !fileStatus.getAssignee().equals(assigneeId) && !KeycloakSecurity.getUserId()
|
||||
.equals(fileStatus.getAssignee())) {
|
||||
notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
|
||||
.userId(fileStatus.getAssignee())
|
||||
.issuerId(KeycloakSecurity.getUserId())
|
||||
.notificationType(NotificationType.UNASSIGNED_FROM_FILE.name())
|
||||
.target(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, fileStatus.getFilename()))
|
||||
.build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('" + SET_REVIEWER + "')")
|
||||
public void setStatusUnderReview(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@PathVariable(FILE_ID) String fileId,
|
||||
@RequestParam(value = ASSIGNEE_ID_REQUEST_PARAM, required = false) String assigneeId) {
|
||||
|
||||
var fileStatus = fileStatusManagementService.getFileStatus(fileId);
|
||||
|
||||
setStatusUnderReviewForFile(dossierId, fileId, assigneeId);
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(fileId)
|
||||
.category(AuditCategory.DOCUMENT.name())
|
||||
.message("Document status was changed to Under Review")
|
||||
.details(Map.of(DOSSIER_ID, dossierId))
|
||||
.build());
|
||||
|
||||
if (assigneeId != null && !assigneeId.equals(KeycloakSecurity.getUserId())) {
|
||||
notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
|
||||
.userId(assigneeId)
|
||||
.issuerId(KeycloakSecurity.getUserId())
|
||||
.notificationType(NotificationType.ASSIGN_REVIEWER.name())
|
||||
.target(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, fileStatus.getFilename()))
|
||||
.build());
|
||||
}
|
||||
|
||||
generatePossibleUnassignedFromFileNotification(dossierId, fileId, fileStatus, assigneeId);
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('" + SET_STATUS_UNDER_APPROVAL + "')")
|
||||
public void setStatusUnderApproval(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@PathVariable(FILE_ID) String fileId,
|
||||
@RequestParam(name = ASSIGNEE_ID_REQUEST_PARAM, required = false) String assigneeId) {
|
||||
|
||||
var fileStatus = fileStatusManagementService.getFileStatus(fileId);
|
||||
|
||||
setStatusUnderApprovalForFile(dossierId, fileId, assigneeId);
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(fileId)
|
||||
.category(AuditCategory.DOCUMENT.name())
|
||||
.message("Document status was changed to Under Approval")
|
||||
.details(Map.of(DOSSIER_ID, dossierId))
|
||||
.build());
|
||||
|
||||
if (assigneeId != null && !assigneeId.equals(KeycloakSecurity.getUserId())) {
|
||||
|
||||
notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
|
||||
.userId(assigneeId)
|
||||
.issuerId(KeycloakSecurity.getUserId())
|
||||
.notificationType(NotificationType.ASSIGN_APPROVER.name())
|
||||
.target(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, fileStatus.getFilename()))
|
||||
.build());
|
||||
}
|
||||
|
||||
generatePossibleUnassignedFromFileNotification(dossierId, fileId, fileStatus, assigneeId);
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('" + SET_STATUS_APPROVED + "')")
|
||||
public void setStatusApproved(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
|
||||
|
||||
accessControlService.verifyUserIsApprover(dossierId);
|
||||
setStatusApprovedForFile(dossierId, fileId);
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(fileId)
|
||||
.category(AuditCategory.DOCUMENT.name())
|
||||
.message("Document status was changed to Approved")
|
||||
.details(Map.of(DOSSIER_ID, dossierId))
|
||||
.build());
|
||||
|
||||
var dossier = dossierACLService.enhanceDossierWithACLData(dossierManagementService.getDossierById(dossierId, false, false));
|
||||
if (!dossier.getOwnerId().equals(KeycloakSecurity.getUserId())) {
|
||||
|
||||
var fileStatus = fileStatusManagementService.getFileStatus(fileId);
|
||||
|
||||
notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
|
||||
.userId(dossier.getOwnerId())
|
||||
.issuerId(KeycloakSecurity.getUserId())
|
||||
.notificationType(NotificationType.DOCUMENT_APPROVED.name())
|
||||
.target(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, fileStatus.getFilename()))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + SET_REVIEWER + "')")
|
||||
public void setAssigneeForList(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@RequestParam(value = ASSIGNEE_ID_REQUEST_PARAM, required = false) String assigneeId,
|
||||
@RequestBody List<String> fileIds) {
|
||||
|
||||
fileIds.forEach(fileId -> setCurrentFileAssignee(dossierId, fileId, assigneeId));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + SET_REVIEWER + "')")
|
||||
public void setStatusUnderReviewForList(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@RequestBody List<String> fileIds,
|
||||
@RequestParam(value = ASSIGNEE_ID_REQUEST_PARAM, required = false) String assigneeId) {
|
||||
|
||||
fileIds.forEach(fileId -> setStatusUnderReview(dossierId, fileId, assigneeId));
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void setStatusUnderReviewForFile(String dossierId, String fileId, String assigneeId) {
|
||||
|
||||
accessControlService.verifyUserIsMemberOrApprover(dossierId);
|
||||
fileStatusManagementService.setStatusUnderReview(dossierId, fileId, assigneeId);
|
||||
}
|
||||
|
||||
|
||||
private void generatePossibleUnassignedFromFileNotification(String dossierId, String fileId, FileModel oldFileStatus, String newAssigneeId) {
|
||||
|
||||
if (oldFileStatus.getAssignee() != null && !oldFileStatus.getAssignee().equals(newAssigneeId) && !KeycloakSecurity.getUserId().equals(oldFileStatus.getAssignee())) {
|
||||
notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
|
||||
.userId(oldFileStatus.getAssignee())
|
||||
.issuerId(KeycloakSecurity.getUserId())
|
||||
.notificationType(NotificationType.UNASSIGNED_FROM_FILE.name())
|
||||
.target(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, oldFileStatus.getFilename()))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + SET_STATUS_UNDER_APPROVAL + "')")
|
||||
public void setStatusUnderApprovalForList(@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@RequestBody List<String> fileIds,
|
||||
@RequestParam(value = ASSIGNEE_ID_REQUEST_PARAM, required = false) String assigneeId) {
|
||||
|
||||
fileIds.forEach(fileId -> setStatusUnderApproval(dossierId, fileId, assigneeId));
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void setStatusUnderApprovalForFile(String dossierId, String fileId, String assigneeId) {
|
||||
|
||||
accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
|
||||
|
||||
var dossier = dossierACLService.enhanceDossierWithACLData(dossierManagementService.getDossierById(dossierId, false, false));
|
||||
|
||||
if (assigneeId != null && !dossier.getApproverIds().contains(assigneeId)) {
|
||||
throw new BadRequestException("Approver is not valid");
|
||||
}
|
||||
|
||||
fileStatusManagementService.setStatusUnderApproval(dossierId, fileId, assigneeId);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + SET_STATUS_APPROVED + "')")
|
||||
public void setStatusApprovedForList(String dossierId, List<String> fileIds) {
|
||||
|
||||
accessControlService.verifyUserIsApprover(dossierId);
|
||||
|
||||
dossierManagementService.getDossierById(dossierId, false, false);
|
||||
|
||||
fileIds.forEach(fileId -> setStatusApproved(dossierId, fileId));
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('" + SET_REVIEWER + "')")
|
||||
public void setStatusNewForList(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody List<String> fileIds) {
|
||||
|
||||
for (var fileId : fileIds) {
|
||||
accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
|
||||
var fileStatus = fileStatusManagementService.getFileStatus(fileId);
|
||||
if (!WorkflowStatus.UNDER_REVIEW.equals(fileStatus.getWorkflowStatus())) {
|
||||
log.debug("Transition to NEW status is not possible from: " + fileStatus.getWorkflowStatus());
|
||||
break;
|
||||
} else {
|
||||
fileStatusManagementService.setCurrentFileAssignee(dossierId, fileId, null);
|
||||
fileStatusManagementService.setStatusNew(dossierId, fileId);
|
||||
generatePossibleUnassignedFromFileNotification(dossierId, fileId, fileStatus, null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('" + READ_FILE_STATUS + "')")
|
||||
public List<FileStatus> getSoftDeletedDossierStatus(@PathVariable(DOSSIER_ID) String dossierId) {
|
||||
|
||||
try {
|
||||
accessControlService.verifyUserHasViewPermissions(dossierId);
|
||||
return fileStatusManagementService.getSoftDeletedDossierStatus(dossierId).stream().map(this::convert).collect(Collectors.toList());
|
||||
|
||||
} catch (AccessDeniedException e) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void setStatusApprovedForFile(String dossierId, String fileId) {
|
||||
|
||||
var fileStatus = getFileStatus(dossierId, fileId);
|
||||
if (fileStatus.isHasSuggestions()) {
|
||||
throw new NotAllowedException("File contains unapproved requests.");
|
||||
}
|
||||
if (!fileStatus.getProcessingStatus().equals(ProcessingStatus.PROCESSED)) {
|
||||
throw new NotAllowedException("File is not processed.");
|
||||
}
|
||||
fileStatusManagementService.setStatusApproved(dossierId, fileId, KeycloakSecurity.getUserId());
|
||||
}
|
||||
|
||||
|
||||
private FileStatus convert(FileModel status) {
|
||||
|
||||
return FileStatus.builder()
|
||||
.dossierId(status.getDossierId())
|
||||
.dossierArchived(status.isDossierArchived())
|
||||
.dossierStatusId(status.getDossierStatusId())
|
||||
.dossierTemplateId(status.getDossierTemplateId())
|
||||
.fileId(status.getId())
|
||||
.filename(status.getFilename())
|
||||
.processingStatus(ProcessingStatus.valueOf(status.getProcessingStatus().name()))
|
||||
.workflowStatus(WorkflowStatus.valueOf(status.getWorkflowStatus().name()))
|
||||
.numberOfPages(status.getNumberOfPages())
|
||||
.added(status.getAdded())
|
||||
.lastUpdated(status.getLastUpdated())
|
||||
.numberOfAnalyses(status.getNumberOfAnalyses())
|
||||
.assignee(status.getAssignee())
|
||||
.lastReviewer(status.getLastReviewer())
|
||||
.lastApprover(status.getLastApprover())
|
||||
.hasUpdates(status.isHasUpdates())
|
||||
.hasImages(status.isHasImages())
|
||||
.hasRequests(status.isHasSuggestions())
|
||||
.hasSuggestions(status.isHasSuggestions())
|
||||
.excludedFromAutomaticAnalysis(status.isExcludedFromAutomaticAnalysis())
|
||||
.hasHints(status.isHasHints())
|
||||
.hasRedactions(status.isHasRedactions())
|
||||
.ocrEndTime(status.getOcrEndTime())
|
||||
.ocrStartTime(status.getOcrStartTime())
|
||||
.numberOfOCRedPages(status.getNumberOfOCRedPages() != null ? status.getNumberOfOCRedPages() : 0)
|
||||
.numberOfPagesToOCR(status.getNumberOfPagesToOCR() != null ? status.getNumberOfPagesToOCR() : 0)
|
||||
.hasAnnotationComments(status.isHasAnnotationComments())
|
||||
.uploader(status.getUploader())
|
||||
.dictionaryVersion(status.getDictionaryVersion())
|
||||
.rulesVersion(status.getRulesVersion())
|
||||
.legalBasisVersion(status.getLegalBasisVersion())
|
||||
.lastProcessed(status.getLastProcessed())
|
||||
.approvalDate(status.getApprovalDate())
|
||||
.lastUploaded(status.getLastUploaded())
|
||||
.analysisDuration(status.getAnalysisDuration())
|
||||
.fileAttributes(new FileAttributes(status.getFileAttributes()))
|
||||
.dossierDictionaryVersion(status.getDossierDictionaryVersion())
|
||||
.excluded(status.isExcluded())
|
||||
.excludedPages(status.getExcludedPages())
|
||||
.softDeletedTime(status.getDeleted())
|
||||
.analysisRequired(status.isAnalysisRequired())
|
||||
.lastFileAttributeChange(status.getLastFileAttributeChange())
|
||||
.redactionModificationDate(status.getRedactionModificationDate())
|
||||
.fileManipulationDate(status.getFileManipulationDate())
|
||||
.lastManualChangeDate(status.getLastManualChangeDate())
|
||||
.hasHighlights(status.isHasHighlights())
|
||||
.lastIndexed(status.getLastIndexed())
|
||||
.fileSize(status.getFileSize())
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
|
||||
|
||||
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.READ_DOSSIER;
|
||||
import static com.iqser.red.service.persistence.management.v1.processor.service.FeignExceptionHandler.processFeignException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
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.RestController;
|
||||
|
||||
import com.iqser.red.service.persistence.management.v1.processor.client.redactionreportservice.StatusReportClient;
|
||||
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.utils.StringEncodingUtils;
|
||||
import com.iqser.red.service.persistence.service.v1.api.external.resource.StatusReportResource;
|
||||
import com.iqser.red.service.redaction.report.v1.api.model.StatusReportResponse;
|
||||
|
||||
import feign.FeignException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class StatusReportController implements StatusReportResource {
|
||||
|
||||
private final StatusReportClient statusReportClient;
|
||||
private final AccessControlService accessControlService;
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
|
||||
public ResponseEntity<?> generateStatusReport(@PathVariable(DOSSIER_ID) String dossierId) {
|
||||
|
||||
try {
|
||||
accessControlService.verifyUserHasViewPermissions(dossierId);
|
||||
} catch (AccessDeniedException e) {
|
||||
throw new NotFoundException("Object not found");
|
||||
}
|
||||
|
||||
try {
|
||||
StatusReportResponse statusReportResponse = statusReportClient.generateStatusReport(dossierId);
|
||||
return getResponseEntity(statusReportResponse.getReport(), statusReportResponse.getFilename(), MediaType.APPLICATION_OCTET_STREAM);
|
||||
} catch (FeignException e) {
|
||||
throw processFeignException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private ResponseEntity<?> getResponseEntity(byte[] file, String filename, MediaType mediaType) {
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setContentType(mediaType);
|
||||
|
||||
if (filename != null) {
|
||||
httpHeaders.add("Content-Disposition", "attachment; filename*=utf-8''" + StringEncodingUtils.urlEncode(filename));
|
||||
}
|
||||
InputStream is = new ByteArrayInputStream(file);
|
||||
|
||||
return new ResponseEntity<>(new InputStreamResource(is), httpHeaders, HttpStatus.OK);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
|
||||
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
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.exception.BadRequestException;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.DeploymentKeyService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.TenantManagementService;
|
||||
import com.iqser.red.service.persistence.service.v1.api.external.resource.TenantsResource;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSONPrimitive;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.multitenancy.TenantRequest;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.multitenancy.TenantResponse;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class TenantsController implements TenantsResource {
|
||||
|
||||
private final TenantManagementService tenantManagementService;
|
||||
private final DeploymentKeyService deploymentKeyService;
|
||||
|
||||
|
||||
@PreAuthorize("hasAuthority('" + CREATE_TENANT + "')")
|
||||
public void createTenant(@Valid @RequestBody TenantRequest tenantRequest) {
|
||||
|
||||
try {
|
||||
tenantManagementService.createTenant(tenantRequest);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new BadRequestException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@PreAuthorize("hasAuthority('" + GET_TENANTS + "')")
|
||||
public List<TenantResponse> getTenants() {
|
||||
|
||||
return tenantManagementService.getTenants();
|
||||
}
|
||||
|
||||
|
||||
@PreAuthorize("hasAuthority('" + DEPLOYMENT_INFO + "')")
|
||||
public JSONPrimitive<String> getDeploymentKey(@PathVariable(TENANT_ID_PARAM) String tenantId) {
|
||||
|
||||
return JSONPrimitive.of(deploymentKeyService.getDeploymentKey(tenantId));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,264 @@
|
||||
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
|
||||
|
||||
import static com.iqser.red.service.persistence.management.v1.processor.service.FeignExceptionHandler.processFeignException;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
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.pdftron.redaction.v1.api.model.ByteContentDocument;
|
||||
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.ReanalysisService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.UploadService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.utils.FileUtils;
|
||||
import com.iqser.red.service.persistence.service.v1.api.external.resource.UploadResource;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileUploadResult;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
|
||||
|
||||
import feign.FeignException;
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class UploadController implements UploadResource {
|
||||
|
||||
private static final int THRESHOLD_ENTRIES = 10000;
|
||||
private static final int THRESHOLD_SIZE = 1000000000; // 1 GB
|
||||
private static final double THRESHOLD_RATIO = 10;
|
||||
|
||||
private static final List<String> VALID_FILE_EXTENSIONS = List.of("pdf", "docx", "doc", "xls", "xlsx", "ppt", "pptx");
|
||||
|
||||
private final UploadService uploadService;
|
||||
private final ReanalysisService reanalysisService;
|
||||
private final AccessControlService accessControlService;
|
||||
private final AuditPersistenceService auditPersistenceService;
|
||||
|
||||
|
||||
@Timed
|
||||
@Override
|
||||
public FileUploadResult upload(@RequestPart(name = "file") MultipartFile file,
|
||||
@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@RequestParam(value = "keepManualRedactions", required = false, defaultValue = "false") boolean keepManualRedactions) {
|
||||
|
||||
if (file.getOriginalFilename() == null) {
|
||||
throw new BadRequestException("Could not upload file, no filename provided.");
|
||||
}
|
||||
|
||||
var extension = getExtension(file.getOriginalFilename());
|
||||
|
||||
try {
|
||||
switch (extension) {
|
||||
case "zip":
|
||||
return handleZip(dossierId, file.getBytes(), keepManualRedactions);
|
||||
case "csv":
|
||||
return uploadService.importCsv(dossierId, file.getBytes());
|
||||
default:
|
||||
if (VALID_FILE_EXTENSIONS.contains(extension)) {
|
||||
return uploadService.processSingleFile(dossierId, file.getOriginalFilename(), file.getBytes(), keepManualRedactions);
|
||||
} else {
|
||||
throw new BadRequestException("Invalid file uploaded");
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new BadRequestException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void importRedactions(@RequestPart(name = "file") MultipartFile file,
|
||||
@PathVariable(DOSSIER_ID) String dossierId,
|
||||
@PathVariable(FILE_ID) String fileId,
|
||||
@RequestParam(value = "pageInclusionRequest", required = false) Set<Integer> pageInclusionRequest) {
|
||||
|
||||
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
|
||||
accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
|
||||
|
||||
try {
|
||||
|
||||
reanalysisService.importRedactions(ByteContentDocument.builder().dossierId(dossierId).fileId(fileId).document(file.getBytes()).pages(pageInclusionRequest).build());
|
||||
|
||||
auditPersistenceService.audit(AuditRequest.builder()
|
||||
.userId(KeycloakSecurity.getUserId())
|
||||
.objectId(fileId)
|
||||
.category(AuditCategory.DOCUMENT.name())
|
||||
.message("Redactions were imported")
|
||||
.details(Map.of("dossierId", dossierId))
|
||||
.build());
|
||||
} catch (IOException e) {
|
||||
throw new BadRequestException(e.getMessage(), e);
|
||||
} catch (FeignException e) {
|
||||
throw processFeignException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String getExtension(String fileName) {
|
||||
|
||||
return fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
private FileUploadResult handleZip(String dossierId, byte[] fileContent, boolean keepManualRedactions) throws IOException {
|
||||
|
||||
File tempFile = FileUtils.createTempFile(UUID.randomUUID().toString(), ".zip");
|
||||
try (var fileOutputStream = new FileOutputStream(tempFile)) {
|
||||
IOUtils.write(fileContent, fileOutputStream);
|
||||
}
|
||||
|
||||
try {
|
||||
checkForSymlinks(tempFile);
|
||||
|
||||
var zipData = unzip(tempFile, dossierId, keepManualRedactions);
|
||||
|
||||
if (zipData.csvBytes != null) {
|
||||
try {
|
||||
var importResult = uploadService.importCsv(dossierId, zipData.csvBytes);
|
||||
zipData.fileUploadResult.getProcessedAttributes().addAll(importResult.getProcessedAttributes());
|
||||
zipData.fileUploadResult.getProcessedFileIds().addAll(importResult.getProcessedFileIds());
|
||||
} catch (Exception e) {
|
||||
log.debug("CSV file inside ZIP failed", e);
|
||||
// TODO return un-processed files to client
|
||||
}
|
||||
}
|
||||
|
||||
return zipData.fileUploadResult;
|
||||
|
||||
} finally {
|
||||
boolean isDeleted = tempFile.delete();
|
||||
if (!isDeleted) {
|
||||
log.warn("tempFile could not be deleted");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void checkForSymlinks(File tempFile) throws IOException {
|
||||
|
||||
try (var fis = new FileInputStream(tempFile); var zipFile = new ZipFile(fis.getChannel())) {
|
||||
for (var entryEnum = zipFile.getEntries(); entryEnum.hasMoreElements(); ) {
|
||||
var ze = entryEnum.nextElement();
|
||||
if (ze.isUnixSymlink()) {
|
||||
throw new BadRequestException("ZIP-files with symlinks are not allowed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private ZipData unzip(File tempFile, String dossierId, boolean keepManualRedactions) throws IOException {
|
||||
|
||||
var zipData = new ZipData();
|
||||
|
||||
try (var fis = new FileInputStream(tempFile); var zipFile = new ZipFile(fis.getChannel())) {
|
||||
|
||||
for (var entryEnum = zipFile.getEntries(); entryEnum.hasMoreElements(); ) {
|
||||
var ze = entryEnum.nextElement();
|
||||
zipData.totalEntryArchive++;
|
||||
|
||||
if (!ze.isDirectory()) {
|
||||
processFileZipEntry(ze, zipFile, dossierId, keepManualRedactions, zipData);
|
||||
}
|
||||
}
|
||||
}
|
||||
return zipData;
|
||||
}
|
||||
|
||||
|
||||
private void processFileZipEntry(ZipArchiveEntry ze, ZipFile zipFile, String dossierId, boolean keepManualRedactions, ZipData zipData) throws IOException {
|
||||
|
||||
var extension = getExtension(ze.getName());
|
||||
final String fileName;
|
||||
if (ze.getName().lastIndexOf("/") >= 0) {
|
||||
fileName = ze.getName().substring(ze.getName().lastIndexOf("/") + 1);
|
||||
} else {
|
||||
fileName = ze.getName();
|
||||
}
|
||||
|
||||
if (fileName.startsWith(".")) {
|
||||
return;
|
||||
}
|
||||
|
||||
var entryAsBytes = readCurrentZipEntry(ze, zipFile);
|
||||
zipData.totalSizeArchive += entryAsBytes.length;
|
||||
|
||||
// 1. the uncompressed data size is too much for the application resource capacity
|
||||
// 2. too many entries in the archive can lead to inode exhaustion of the file-system
|
||||
if (zipData.totalSizeArchive > THRESHOLD_SIZE || zipData.totalEntryArchive > THRESHOLD_ENTRIES) {
|
||||
throw new BadRequestException("ZIP-Bomb detected.");
|
||||
}
|
||||
|
||||
if ("csv".equals(extension)) {
|
||||
zipData.csvBytes = entryAsBytes;
|
||||
} else if (VALID_FILE_EXTENSIONS.contains(extension)) {
|
||||
try {
|
||||
var result = uploadService.processSingleFile(dossierId, fileName, entryAsBytes, keepManualRedactions);
|
||||
zipData.fileUploadResult.getFileIds().addAll(result.getFileIds());
|
||||
} catch (Exception e) {
|
||||
log.debug("PDF File inside ZIP failed", e);
|
||||
// TODO return un-processed files to client
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private byte[] readCurrentZipEntry(ZipArchiveEntry ze, ZipFile zipFile) throws IOException {
|
||||
|
||||
var bos = new ByteArrayOutputStream();
|
||||
|
||||
try (var entryStream = zipFile.getInputStream(ze)) {
|
||||
var buffer = new byte[2048];
|
||||
var nBytes = 0;
|
||||
int totalSizeEntry = 0;
|
||||
|
||||
while ((nBytes = entryStream.read(buffer)) > 0) {
|
||||
bos.write(buffer, 0, nBytes);
|
||||
totalSizeEntry += nBytes;
|
||||
|
||||
double compressionRatio = (float) totalSizeEntry / ze.getCompressedSize();
|
||||
if (compressionRatio > THRESHOLD_RATIO) {
|
||||
// ratio between compressed and uncompressed data is highly suspicious, looks like a Zip Bomb Attack
|
||||
throw new BadRequestException("ZIP-Bomb detected.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bos.toByteArray();
|
||||
}
|
||||
|
||||
|
||||
@FieldDefaults(level = AccessLevel.PUBLIC)
|
||||
private static final class ZipData {
|
||||
|
||||
byte[] csvBytes;
|
||||
int totalSizeArchive;
|
||||
int totalEntryArchive;
|
||||
FileUploadResult fileUploadResult = new FileUploadResult();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,147 @@
|
||||
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
|
||||
|
||||
import static com.iqser.red.keycloak.commons.roles.ActionRoles.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
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.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.iqser.red.keycloak.commons.KeyCloakSettings;
|
||||
import com.iqser.red.keycloak.commons.model.User;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.exception.AuthenticationFailedException;
|
||||
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.UserService;
|
||||
import com.iqser.red.service.persistence.service.v1.api.external.resource.UserResource;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.CreateUserRequest;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.ResetPasswordRequest;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.UpdateMyProfileRequest;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.UpdateProfileRequest;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class UserController implements UserResource {
|
||||
|
||||
private final UserService userService;
|
||||
private final KeyCloakSettings keyCloakSettings;
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + READ_USERS + "')")
|
||||
public List<User> getAllRedUsers(@RequestParam(name = REFRESH_CACHE_PARAM, defaultValue = "false", required = false) boolean bypassCache) {
|
||||
|
||||
if (bypassCache) {
|
||||
userService.evictUserCache();
|
||||
}
|
||||
|
||||
return userService.getAllUsers()
|
||||
.stream()
|
||||
.filter(user -> user.getRoles().stream().anyMatch(r -> r.startsWith(keyCloakSettings.getRolePrefix())))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + READ_ALL_USERS + "')")
|
||||
public List<User> getAllUsers(@RequestParam(name = REFRESH_CACHE_PARAM, defaultValue = "false", required = false) boolean bypassCache) {
|
||||
|
||||
if (bypassCache) {
|
||||
userService.evictUserCache();
|
||||
}
|
||||
|
||||
return userService.getAllUsers();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + WRITE_USERS + "')")
|
||||
public void updateProfile(@PathVariable(USER_ID) String userId, @RequestBody UpdateProfileRequest updateProfileRequest) {
|
||||
|
||||
this.userService.updateProfile(userId, updateProfileRequest);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + UPDATE_MY_PROFILE + "')")
|
||||
public void updateMyProfile(@Valid @RequestBody UpdateMyProfileRequest updateProfileRequest) {
|
||||
|
||||
try {
|
||||
this.userService.updateMyProfile(updateProfileRequest);
|
||||
} catch (AuthenticationFailedException e) {
|
||||
throw new BadRequestException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + WRITE_USERS + "')")
|
||||
public void deleteUsers(@RequestParam(USER_ID) List<String> userIds) {
|
||||
|
||||
userIds.forEach(this::deleteUser);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + WRITE_USERS + "')")
|
||||
public void deleteUser(@PathVariable(USER_ID) String userId) {
|
||||
|
||||
userService.deleteUser(userId);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + WRITE_USERS + "')")
|
||||
public User createUser(@RequestBody CreateUserRequest user) {
|
||||
|
||||
return this.userService.createUser(user);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + READ_USERS + "')")
|
||||
public User getUserById(@PathVariable(USER_ID) String userId) {
|
||||
|
||||
if (StringUtils.isEmpty(userId)) {
|
||||
throw new BadRequestException("The userId should not be empty.");
|
||||
}
|
||||
return userService.getUserById(userId).orElseThrow(() -> new NotFoundException("User not found"));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + WRITE_USERS + "')")
|
||||
public void setRoles(@PathVariable(USER_ID) String userId, @RequestBody List<String> roles) {
|
||||
|
||||
userService.setRoles(userId, roles);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + WRITE_USERS + "')")
|
||||
public void resetPassword(@PathVariable(USER_ID) String userId, @RequestBody ResetPasswordRequest resetPasswordRequest) {
|
||||
|
||||
this.userService.resetPassword(userId, resetPasswordRequest);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + WRITE_USERS + "')")
|
||||
public User activateProfile(@PathVariable(USER_ID) String userId, @RequestParam(IS_ACTIVE_PARAM) boolean isActive) {
|
||||
|
||||
return this.userService.activateProfile(userId, isActive);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user