diff --git a/src/main/java/ru/soune/nocopy/controller/ApiController.java b/src/main/java/ru/soune/nocopy/controller/ApiController.java index de638b7..7f9a916 100644 --- a/src/main/java/ru/soune/nocopy/controller/ApiController.java +++ b/src/main/java/ru/soune/nocopy/controller/ApiController.java @@ -27,6 +27,7 @@ import ru.soune.nocopy.entity.company.Company; import ru.soune.nocopy.entity.file.FileEntity; import ru.soune.nocopy.entity.file.FileStatus; import ru.soune.nocopy.entity.file.UploadStatus; +import ru.soune.nocopy.entity.file.permission.PermissionType; import ru.soune.nocopy.entity.user.AuthToken; import ru.soune.nocopy.entity.user.User; import ru.soune.nocopy.exception.*; @@ -467,6 +468,16 @@ public class ApiController { errorData)); } + if (!entityResponse.getPermissions().get(PermissionType.DOWNLOAD)) { + Map errorData = new HashMap<>(); + errorData.put("fileId", entityResponse.getId()); + + return ResponseEntity.ok().body(new BaseResponse(20004, + MessageCode.NOT_HAVE_DOWNLOAD_PERMISSION.getCode(), + MessageCode.NOT_HAVE_DOWNLOAD_PERMISSION.getDescription(), + errorData)); + } + User fileUser = userRepository.findById(entityResponse.getUserId()).orElseThrow(); User user = userRepository.findById(userId).orElseThrow(); Company companyUser = user.getCompany(); diff --git a/src/main/java/ru/soune/nocopy/dto/MessageCode.java b/src/main/java/ru/soune/nocopy/dto/MessageCode.java index ed94079..901644b 100644 --- a/src/main/java/ru/soune/nocopy/dto/MessageCode.java +++ b/src/main/java/ru/soune/nocopy/dto/MessageCode.java @@ -67,6 +67,8 @@ public enum MessageCode { USER_VERIFICATIONS_NOT_FOUND(4, "Verifications not found"), COMPLAINT_OR_LAW_CASE_IN_WORK(2, "Complaint or lawcase in work"), NOT_VALID_STATUS(2, "Not valid status"), + PERMISSION_NOT_VALID(2, "Not valid permission"), + NOT_HAVE_DOWNLOAD_PERMISSION(2, "Not haver permission for download"), ADMIN_USER_NOT_FOUND(4, "Verifications ,user or admin not found"); diff --git a/src/main/java/ru/soune/nocopy/dto/file/FileEntityRequest.java b/src/main/java/ru/soune/nocopy/dto/file/FileEntityRequest.java index da4f420..7231e63 100644 --- a/src/main/java/ru/soune/nocopy/dto/file/FileEntityRequest.java +++ b/src/main/java/ru/soune/nocopy/dto/file/FileEntityRequest.java @@ -6,8 +6,10 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import ru.soune.nocopy.entity.file.FileStatus; +import ru.soune.nocopy.entity.file.permission.PermissionType; import java.util.List; +import java.util.Map; @Data @Builder @@ -76,4 +78,7 @@ public class FileEntityRequest { @JsonProperty("search_query") private String searchQuery; + + @JsonProperty("permissions") + private Map permissions; } diff --git a/src/main/java/ru/soune/nocopy/dto/file/FileEntityResponse.java b/src/main/java/ru/soune/nocopy/dto/file/FileEntityResponse.java index 60c65b6..ba53ea8 100644 --- a/src/main/java/ru/soune/nocopy/dto/file/FileEntityResponse.java +++ b/src/main/java/ru/soune/nocopy/dto/file/FileEntityResponse.java @@ -5,8 +5,10 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import ru.soune.nocopy.entity.file.FileStatus; +import ru.soune.nocopy.entity.file.permission.PermissionType; import java.time.LocalDateTime; +import java.util.Map; @Data @Builder @@ -42,4 +44,5 @@ public class FileEntityResponse { private Integer checksCount; private LocalDateTime fileUploadDate; private String monitoring; + private Map permissions; } diff --git a/src/main/java/ru/soune/nocopy/entity/file/FileEntity.java b/src/main/java/ru/soune/nocopy/entity/file/FileEntity.java index 6a0137f..3e313d8 100644 --- a/src/main/java/ru/soune/nocopy/entity/file/FileEntity.java +++ b/src/main/java/ru/soune/nocopy/entity/file/FileEntity.java @@ -6,10 +6,13 @@ import lombok.*; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import ru.soune.nocopy.entity.file.permission.PermissionType; import ru.soune.nocopy.entity.monitoring.FileMonitoringEntity; import java.time.LocalDateTime; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; @Data @@ -92,6 +95,14 @@ public class FileEntity { @Column private String newExtension; + @ElementCollection + @CollectionTable(name = "file_permissions", + joinColumns = @JoinColumn(name = "file_id")) + @Column(name = "allowed") + @MapKeyEnumerated(EnumType.STRING) + @MapKeyColumn(name = "permission_type") + private Map permissions = new HashMap<>(); + @OneToOne(mappedBy = "file", cascade = CascadeType.ALL, orphanRemoval = true) @JsonIgnore private ImageHashEntity imageHash; @@ -113,6 +124,15 @@ public class FileEntity { if (this.createdAt == null) { this.createdAt = LocalDateTime.now(); } + + if (this.permissions == null) { + this.permissions = new HashMap<>(); + this.permissions.put(PermissionType.DOWNLOAD, true); +// this.permissions.put(PermissionType.VIEW, true); +// this.permissions.put(PermissionType.EDIT, true); + } + + this.updatedAt = LocalDateTime.now(); } @@ -124,4 +144,29 @@ public class FileEntity { public static void initializeCounter(int maxId) { supportIdCounter = new AtomicInteger(maxId); } + + @Transient + public boolean hasPermission(PermissionType type) { + return permissions.getOrDefault(type, false); + } + + @Transient + public void setPermission(PermissionType type, boolean allowed) { + permissions.put(type, allowed); + } + + @Transient + public boolean canDownload() { + return hasPermission(PermissionType.DOWNLOAD); + } + +// @Transient +// public boolean canView() { +// return hasPermission(PermissionType.VIEW); +// } +// +// @Transient +// public boolean canEdit() { +// return hasPermission(PermissionType.EDIT); +// } } diff --git a/src/main/java/ru/soune/nocopy/entity/file/permission/PermissionType.java b/src/main/java/ru/soune/nocopy/entity/file/permission/PermissionType.java new file mode 100644 index 0000000..874d0d7 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/entity/file/permission/PermissionType.java @@ -0,0 +1,27 @@ +package ru.soune.nocopy.entity.file.permission; + +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public enum PermissionType { + DOWNLOAD; + + public static Set getValidPermissionNames() { + return Arrays.stream(PermissionType.values()) + .map(Enum::name) + .collect(Collectors.toSet()); + } + + public static Set getInvalidPermissions(Map permissions) { + Set validNames = getValidPermissionNames(); + return permissions.keySet().stream() + .filter(key -> !validNames.contains(key)) + .collect(Collectors.toSet()); + } + + public static boolean areAllValid(Map permissions) { + return getInvalidPermissions(permissions).isEmpty(); + } +} diff --git a/src/main/java/ru/soune/nocopy/handler/FileEntityHandler.java b/src/main/java/ru/soune/nocopy/handler/FileEntityHandler.java index 4239b86..ebcc137 100644 --- a/src/main/java/ru/soune/nocopy/handler/FileEntityHandler.java +++ b/src/main/java/ru/soune/nocopy/handler/FileEntityHandler.java @@ -13,11 +13,13 @@ import ru.soune.nocopy.dto.*; import ru.soune.nocopy.dto.file.*; import ru.soune.nocopy.entity.file.*; import ru.soune.nocopy.entity.file.moderation.ModerationLog; +import ru.soune.nocopy.entity.file.permission.PermissionType; import ru.soune.nocopy.entity.monitoring.FileMonitoringEntity; import ru.soune.nocopy.exception.*; import ru.soune.nocopy.repository.*; import ru.soune.nocopy.service.file.cloud.CloudStorageService; import ru.soune.nocopy.service.file.moderation.ModerationService; +import ru.soune.nocopy.service.file.permission.FileAccessService; import ru.soune.nocopy.service.register.AuthService; import ru.soune.nocopy.service.file.FileEntityService; import ru.soune.nocopy.service.file.FileStatsService; @@ -57,6 +59,8 @@ public class FileEntityHandler implements RequestHandler { private final ImageHashRepository imageHashRepository; + private FileAccessService fileAccessService; + @Override public BaseResponse handle(BaseRequest request) { try { @@ -96,6 +100,8 @@ public class FileEntityHandler implements RequestHandler { return handleGetUserAppeals(request, fileRequest); case "files_for_moderation": return handleGetFilesForModeration(request, fileRequest); + case "change_permission": + return handleChangePermission(request, fileRequest); default: ActionResponse response = ActionResponse.builder() .action(action) @@ -160,6 +166,57 @@ public class FileEntityHandler implements RequestHandler { } } + private BaseResponse handleChangePermission(BaseRequest request, FileEntityRequest fileRequest) { + try { + Long userId = authService.useUserAuthToken(fileRequest.getToken()); + FileEntityResponse fileInfo = fileEntityService.getById(fileRequest.getFileId(), request.getVersion()); + + if (!fileInfo.getUserId().equals(userId)) { + return new BaseResponse(request.getMsgId(), + MessageCode.ACCESS_DENIED.getCode(), + "Access denied", + null); + } + + Map permissions = fileRequest.getPermissions(); + + if (!PermissionType.areAllValid(permissions)) { + return new BaseResponse(request.getMsgId(), + MessageCode.PERMISSION_NOT_VALID.getCode(), + MessageCode.PERMISSION_NOT_VALID.getDescription(), + null); + } + + for (Map.Entry entry: permissions.entrySet()) { + try { + fileAccessService.changePermission(fileInfo.getId(), PermissionType.valueOf(entry.getKey()), + entry.getValue()); + } catch (FileEntityNotFoundException e) { + return new BaseResponse(request.getMsgId(), + MessageCode.FILE_NOT_FOUND.getCode(), + MessageCode.FILE_NOT_FOUND.getDescription(), + null); + } + } + + return new BaseResponse(request.getMsgId(), + MessageCode.SUCCESS.getCode(), + MessageCode.SUCCESS.getDescription(), + fileInfo); + } catch (NotFoundAuthToken e) { + return new BaseResponse(request.getMsgId(), + MessageCode.INVALID_TOKEN.getCode(), + "Authentication required", + null); + } catch (Exception e) { + log.error("Error getting file info", e); + return new BaseResponse(request.getMsgId(), + MessageCode.FILE_UPLOAD_ERROR.getCode(), + "Failed to get file info: " + e.getMessage(), + null); + } + } + private BaseResponse handleGetFileBySession(BaseRequest request, FileEntityRequest fileRequest) { try { Long userId = authService.useUserAuthToken(fileRequest.getToken()); diff --git a/src/main/java/ru/soune/nocopy/service/file/FileEntityService.java b/src/main/java/ru/soune/nocopy/service/file/FileEntityService.java index 2af69a7..674f248 100644 --- a/src/main/java/ru/soune/nocopy/service/file/FileEntityService.java +++ b/src/main/java/ru/soune/nocopy/service/file/FileEntityService.java @@ -416,6 +416,7 @@ public class FileEntityService { .fileUploadDate(fileEntity.getCreatedAt()) .protectedFilePath(fileEntity.getProtectedFilePath()) .thumbnailFileUrl(baseUrl + "/api/files/protected/" + fileEntity.getId() + "/thumbnail") + .permissions(fileEntity.getPermissions()) .build(); } } \ No newline at end of file diff --git a/src/main/java/ru/soune/nocopy/service/file/permission/FileAccessService.java b/src/main/java/ru/soune/nocopy/service/file/permission/FileAccessService.java new file mode 100644 index 0000000..b3379ea --- /dev/null +++ b/src/main/java/ru/soune/nocopy/service/file/permission/FileAccessService.java @@ -0,0 +1,24 @@ +package ru.soune.nocopy.service.file.permission; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import ru.soune.nocopy.entity.file.FileEntity; +import ru.soune.nocopy.entity.file.permission.PermissionType; +import ru.soune.nocopy.repository.FileEntityRepository; + +import java.io.FileNotFoundException; + +@Service +@RequiredArgsConstructor +public class FileAccessService { + + private final FileEntityRepository fileEntityRepository; + + public void changePermission(String fileId, PermissionType type, boolean allowed) throws FileNotFoundException { + FileEntity file = fileEntityRepository.findById(fileId) + .orElseThrow(() -> new FileNotFoundException(fileId)); + file.setPermission(type, allowed); + + fileEntityRepository.save(file); + } +}