diff --git a/src/main/java/ru/soune/nocopy/adminpanel/dto/AdminUserRequest.java b/src/main/java/ru/soune/nocopy/adminpanel/dto/AdminUserRequest.java index c016b18..698118a 100644 --- a/src/main/java/ru/soune/nocopy/adminpanel/dto/AdminUserRequest.java +++ b/src/main/java/ru/soune/nocopy/adminpanel/dto/AdminUserRequest.java @@ -3,6 +3,8 @@ package ru.soune.nocopy.adminpanel.dto; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; +import java.util.Map; + @Data public class AdminUserRequest { @JsonProperty("action") @@ -25,4 +27,8 @@ public class AdminUserRequest { @JsonProperty("is_active") private Boolean isActive; + + @JsonProperty("permissions") + private Map permissions; + } diff --git a/src/main/java/ru/soune/nocopy/adminpanel/dto/AdminUserResponse.java b/src/main/java/ru/soune/nocopy/adminpanel/dto/AdminUserResponse.java index a06f8e2..a155fa8 100644 --- a/src/main/java/ru/soune/nocopy/adminpanel/dto/AdminUserResponse.java +++ b/src/main/java/ru/soune/nocopy/adminpanel/dto/AdminUserResponse.java @@ -4,6 +4,8 @@ import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Builder; import lombok.Data; +import java.util.Map; + @Data @Builder public class AdminUserResponse { @@ -24,5 +26,7 @@ public class AdminUserResponse { @JsonProperty("created_at") private String createdAt; + + private Map permissions; } diff --git a/src/main/java/ru/soune/nocopy/adminpanel/entity/AdminSection.java b/src/main/java/ru/soune/nocopy/adminpanel/entity/AdminSection.java new file mode 100644 index 0000000..13b43fa --- /dev/null +++ b/src/main/java/ru/soune/nocopy/adminpanel/entity/AdminSection.java @@ -0,0 +1,17 @@ +package ru.soune.nocopy.adminpanel.entity; + +public enum AdminSection { + DASH, + USERS, + STAFF, + SUBSCRIPTIONS, + TARIFFS, + CONTENT_MODERATION, + COMPLAINTS, + CLAIMS, + MAILINGS, + API, + MONEY, + KYC, + AGREEMENTS +} diff --git a/src/main/java/ru/soune/nocopy/adminpanel/entity/AdminSectionPermission.java b/src/main/java/ru/soune/nocopy/adminpanel/entity/AdminSectionPermission.java new file mode 100644 index 0000000..e1aeff9 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/adminpanel/entity/AdminSectionPermission.java @@ -0,0 +1,74 @@ +package ru.soune.nocopy.adminpanel.entity; + +import jakarta.persistence.*; +import lombok.*; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.time.LocalDateTime; + +@Entity +@Table(name = "admin_section_permissions") +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +@EntityListeners(AuditingEntityListener.class) +public class AdminSectionPermission { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "user_id", nullable = false, unique = true) + private Long userId; + + @Column(name = "dash", nullable = false) + private Integer dash = 0b00; + + @Column(name = "users", nullable = false) + private Integer users = 0b00; + + @Column(name = "staff", nullable = false) + private Integer staff = 0b00; + + @Column(name = "subscriptions", nullable = false) + private Integer subscriptions = 0b00; + + @Column(name = "tariffs", nullable = false) + private Integer tariffs = 0b00; + + @Column(name = "content_moderation", nullable = false) + private Integer contentModeration = 0b00; + + @Column(name = "complaints", nullable = false) + private Integer complaints = 0b00; + + @Column(name = "claims", nullable = false) + private Integer claims = 0b00; + + @Column(name = "mailings", nullable = false) + private Integer mailings = 0b00; + + @Column(name = "api", nullable = false) + private Integer api = 0b00; + + @Column(name = "money", nullable = false) + private Integer money = 0b00; + + @Column(name = "kyc", nullable = false) + private Integer kyc = 0b00; + + @Column(name = "agreements", nullable = false) + private Integer agreements = 0b00; + + @CreatedDate + @Column(name = "created_at", updatable = false) + private LocalDateTime createdAt; + + @LastModifiedDate + @Column(name = "updated_at") + private LocalDateTime updatedAt; +} diff --git a/src/main/java/ru/soune/nocopy/adminpanel/handler/AdminUserHandler.java b/src/main/java/ru/soune/nocopy/adminpanel/handler/AdminUserHandler.java index 45b00bb..8a87c6d 100644 --- a/src/main/java/ru/soune/nocopy/adminpanel/handler/AdminUserHandler.java +++ b/src/main/java/ru/soune/nocopy/adminpanel/handler/AdminUserHandler.java @@ -5,16 +5,20 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import ru.soune.nocopy.adminpanel.dto.*; +import ru.soune.nocopy.adminpanel.entity.AdminSection; import ru.soune.nocopy.adminpanel.entity.AdminUser; import ru.soune.nocopy.adminpanel.exceptions.AdminUserNotFoundException; import ru.soune.nocopy.adminpanel.exceptions.EmailAlreadyExistsException; import ru.soune.nocopy.adminpanel.permission.AdminPermission; +import ru.soune.nocopy.adminpanel.service.AdminSectionPermissionService; import ru.soune.nocopy.adminpanel.service.AdminUserService; import ru.soune.nocopy.adminpanel.util.JwtUtil; import ru.soune.nocopy.adminpanel.util.MessageCode; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; @Component @@ -28,6 +32,8 @@ public class AdminUserHandler implements RequestHandler { private final JwtUtil jwtUtil; + private final AdminSectionPermissionService permissionService; + @Override public BaseResponse handle(BaseRequest request) throws Exception { AdminUserRequest adminRequest = objectMapper.convertValue(request.getMessageBody(), AdminUserRequest.class); @@ -42,6 +48,8 @@ public class AdminUserHandler implements RequestHandler { case "set_super_admin" -> handleSetSuperAdmin(request, adminRequest); case "toggle_active" -> handleToggleActive(request, adminRequest); case "delete" -> handleDelete(request, adminRequest); + case "get_permissions" -> handleGetPermissions(request, adminRequest); + case "update_permissions" -> handleUpdatePermissions(request, adminRequest); default -> handleInvalidAction(request, action); }; } @@ -243,7 +251,8 @@ public class AdminUserHandler implements RequestHandler { .action(action) .availableActions(Arrays.asList( "login", "create", "get_all", "get_by_id", - "update", "set_super_admin", "toggle_active", "delete")) + "update", "set_super_admin", "toggle_active", "delete", + "get_permissions", "update_permissions")) .build(); return BaseResponse.builder() @@ -254,6 +263,105 @@ public class AdminUserHandler implements RequestHandler { .build(); } + private BaseResponse handleGetPermissions(BaseRequest request, AdminUserRequest adminRequest) { + try { + Long userId = adminRequest.getAdminId(); + AdminUser admin = adminUserService.findById(userId); + + Map sectionPermissions = permissionService.getAllPermissions(userId); + + Map permissionsMap = new HashMap<>(); + sectionPermissions.forEach((section, value) -> + permissionsMap.put(section.name().toLowerCase(), value)); + + AdminUserResponse response = AdminUserResponse.builder() + .id(admin.getId()) + .fullName(admin.getFullName()) + .email(admin.getEmail()) + .isActive(admin.getIsActive()) + .isSuperAdmin(AdminPermission.isSuperAdmin(admin.getPermissions())) + .createdAt(admin.getCreatedAt().toString()) + .permissions(permissionsMap) + .build(); + + return BaseResponse.builder() + .msgId(request.getMsgId()) + .messageCode(MessageCode.SUCCESS.getCode()) + .messageDesc(MessageCode.SUCCESS.getDescription()) + .messageBody(response) + .build(); + } catch (AdminUserNotFoundException e) { + return BaseResponse.builder() + .msgId(request.getMsgId()) + .messageCode(MessageCode.ADMIN_NOT_FOUND.getCode()) + .messageDesc(MessageCode.ADMIN_NOT_FOUND.getDescription()) + .build(); + } + } + + private BaseResponse handleUpdatePermissions(BaseRequest request, AdminUserRequest adminRequest) { + try { + Long userId = adminRequest.getAdminId(); + Map permissionsMap = adminRequest.getPermissions(); + + if (permissionsMap == null || permissionsMap.isEmpty()) { + return BaseResponse.builder() + .msgId(request.getMsgId()) + .messageCode(MessageCode.INVALID_ACTION.getCode()) + .messageDesc("Permissions map is required") + .build(); + } + + Map permissions = new HashMap<>(); + + for (Map.Entry entry : permissionsMap.entrySet()) { + try { + AdminSection section = AdminSection.valueOf(entry.getKey().toUpperCase()); + permissions.put(section, entry.getValue()); + } catch (IllegalArgumentException e) { + return BaseResponse.builder() + .msgId(request.getMsgId()) + .messageCode(MessageCode.INVALID_ACTION.getCode()) + .messageDesc("Invalid section: " + entry.getKey() + + ". Valid sections: " + Arrays.toString(AdminSection.values())) + .build(); + } + } + + permissionService.setAllPermissions(userId, permissions); + + AdminUser admin = adminUserService.findById(userId); + Map updatedPermissions = permissionService.getAllPermissions(userId); + + Map updatedPermissionsMap = new HashMap<>(); + updatedPermissions.forEach((section, value) -> + updatedPermissionsMap.put(section.name().toLowerCase(), value)); + + AdminUserResponse response = AdminUserResponse.builder() + .id(admin.getId()) + .fullName(admin.getFullName()) + .email(admin.getEmail()) + .isActive(admin.getIsActive()) + .isSuperAdmin(AdminPermission.isSuperAdmin(admin.getPermissions())) + .createdAt(admin.getCreatedAt().toString()) + .permissions(updatedPermissionsMap) + .build(); + + return BaseResponse.builder() + .msgId(request.getMsgId()) + .messageCode(MessageCode.SUCCESS.getCode()) + .messageDesc("Permissions updated successfully") + .messageBody(response) + .build(); + } catch (AdminUserNotFoundException e) { + return BaseResponse.builder() + .msgId(request.getMsgId()) + .messageCode(MessageCode.ADMIN_NOT_FOUND.getCode()) + .messageDesc(MessageCode.ADMIN_NOT_FOUND.getDescription()) + .build(); + } + } + private AdminUserResponse convertToResponse(AdminUser admin) { return AdminUserResponse.builder() .id(admin.getId()) diff --git a/src/main/java/ru/soune/nocopy/adminpanel/permission/PermissionChecker.java b/src/main/java/ru/soune/nocopy/adminpanel/permission/PermissionChecker.java index bb3ad9c..29d600d 100644 --- a/src/main/java/ru/soune/nocopy/adminpanel/permission/PermissionChecker.java +++ b/src/main/java/ru/soune/nocopy/adminpanel/permission/PermissionChecker.java @@ -2,12 +2,17 @@ package ru.soune.nocopy.adminpanel.permission; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; +import ru.soune.nocopy.adminpanel.entity.AdminSection; import ru.soune.nocopy.adminpanel.entity.AdminUser; +import ru.soune.nocopy.adminpanel.service.AdminSectionPermissionService; + @Component @RequiredArgsConstructor public class PermissionChecker { + private final AdminSectionPermissionService sectionPermissionService; + public boolean isSuperAdmin(AdminUser admin) { if (admin == null || !admin.getIsActive()) return false; return AdminPermission.isSuperAdmin(admin.getPermissions()); @@ -18,4 +23,19 @@ public class PermissionChecker { throw new SecurityException("Super admin permission required"); } } + + public boolean canReadSection(AdminUser admin, AdminSection section) { + if (admin == null || !admin.getIsActive()) return false; + if (isSuperAdmin(admin)) return true; + + return sectionPermissionService.canRead(admin.getId(), section); + } + + public boolean canWriteSection(AdminUser admin, AdminSection section) { + if (admin == null || !admin.getIsActive()) return false; + if (isSuperAdmin(admin)) return true; + + return sectionPermissionService.canWrite(admin.getId(), section); + } } + diff --git a/src/main/java/ru/soune/nocopy/adminpanel/permission/SectionPermissions.java b/src/main/java/ru/soune/nocopy/adminpanel/permission/SectionPermissions.java new file mode 100644 index 0000000..a42c186 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/adminpanel/permission/SectionPermissions.java @@ -0,0 +1,15 @@ +package ru.soune.nocopy.adminpanel.permission; + +public class SectionPermissions { + public static final int NONE = 0b00; + public static final int READ = 0b01; + public static final int WRITE = 0b11; + + public static boolean canRead(int permission) { + return (permission & READ) == READ; + } + + public static boolean canWrite(int permission) { + return (permission & WRITE) == WRITE; + } +} diff --git a/src/main/java/ru/soune/nocopy/adminpanel/repository/AdminSectionPermissionRepository.java b/src/main/java/ru/soune/nocopy/adminpanel/repository/AdminSectionPermissionRepository.java new file mode 100644 index 0000000..0debab7 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/adminpanel/repository/AdminSectionPermissionRepository.java @@ -0,0 +1,12 @@ +package ru.soune.nocopy.adminpanel.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import ru.soune.nocopy.adminpanel.entity.AdminSectionPermission; + +import java.util.Optional; + +@Repository +public interface AdminSectionPermissionRepository extends JpaRepository { + Optional findByUserId(Long userId); +} diff --git a/src/main/java/ru/soune/nocopy/adminpanel/service/AdminSectionPermissionService.java b/src/main/java/ru/soune/nocopy/adminpanel/service/AdminSectionPermissionService.java new file mode 100644 index 0000000..331b092 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/adminpanel/service/AdminSectionPermissionService.java @@ -0,0 +1,107 @@ +package ru.soune.nocopy.adminpanel.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.soune.nocopy.adminpanel.entity.AdminSectionPermission; +import ru.soune.nocopy.adminpanel.entity.AdminSection; +import ru.soune.nocopy.adminpanel.permission.SectionPermissions; +import ru.soune.nocopy.adminpanel.repository.AdminSectionPermissionRepository; + +import java.util.HashMap; +import java.util.Map; + +@Service +@RequiredArgsConstructor +public class AdminSectionPermissionService { + + private final AdminSectionPermissionRepository repository; + + @Transactional + public AdminSectionPermission getOrCreate(Long userId) { + return repository.findByUserId(userId) + .orElseGet(() -> { + AdminSectionPermission permission = new AdminSectionPermission(); + permission.setUserId(userId); + return repository.save(permission); + }); + } + + public boolean canRead(Long userId, AdminSection section) { + AdminSectionPermission permission = getOrCreate(userId); + int value = getSectionValue(permission, section); + return SectionPermissions.canRead(value); + } + + public boolean canWrite(Long userId, AdminSection section) { + AdminSectionPermission permission = getOrCreate(userId); + int value = getSectionValue(permission, section); + return SectionPermissions.canWrite(value); + } + + @Transactional + public void setPermission(Long userId, AdminSection section, int permissionValue) { + AdminSectionPermission permission = getOrCreate(userId); + setSectionValue(permission, section, permissionValue); + repository.save(permission); + } + + @Transactional + public void setAllPermissions(Long userId, Map permissions) { + AdminSectionPermission permission = getOrCreate(userId); + permissions.forEach((section, value) -> setSectionValue(permission, section, value)); + repository.save(permission); + } + + public Map getAllPermissions(Long userId) { + AdminSectionPermission permission = getOrCreate(userId); + Map result = new HashMap<>(); + + for (AdminSection section : AdminSection.values()) { + result.put(section, getSectionValue(permission, section)); + } + + return result; + } + + private int getSectionValue(AdminSectionPermission permission, AdminSection section) { + return switch (section) { + case DASH -> permission.getDash(); + case USERS -> permission.getUsers(); + case STAFF -> permission.getStaff(); + case SUBSCRIPTIONS -> permission.getSubscriptions(); + case TARIFFS -> permission.getTariffs(); + case CONTENT_MODERATION -> permission.getContentModeration(); + case COMPLAINTS -> permission.getComplaints(); + case CLAIMS -> permission.getClaims(); + case MAILINGS -> permission.getMailings(); + case API -> permission.getApi(); + case MONEY -> permission.getMoney(); + case KYC -> permission.getKyc(); + case AGREEMENTS -> permission.getAgreements(); + }; + } + + private void setSectionValue(AdminSectionPermission permission, AdminSection section, int value) { + if (value == SectionPermissions.WRITE) { + value = SectionPermissions.WRITE; + } + + switch (section) { + case DASH -> permission.setDash(value); + case USERS -> permission.setUsers(value); + case STAFF -> permission.setStaff(value); + case SUBSCRIPTIONS -> permission.setSubscriptions(value); + case TARIFFS -> permission.setTariffs(value); + case CONTENT_MODERATION -> permission.setContentModeration(value); + case COMPLAINTS -> permission.setComplaints(value); + case CLAIMS -> permission.setClaims(value); + case MAILINGS -> permission.setMailings(value); + case API -> permission.setApi(value); + case MONEY -> permission.setMoney(value); + case KYC -> permission.setKyc(value); + case AGREEMENTS -> permission.setAgreements(value); + } + } +} +