feat: Initial implementation of permission manager SDK - Add core permission management functionality with @RequiresPermission annotation - Implement permission checking aspect with Spring Security integration - Add comprehensive model classes for permissions, roles, and domains - Create integration builder for permission structure setup - Add configuration support for permission manager client - Implement exception handling for access control - Add extensive test coverage with integration tests - Configure Maven build with Spring Boot/Cloud dependencies
This commit is contained in:
@ -1,13 +1,22 @@
|
||||
package de.mummeit.common.config;
|
||||
|
||||
import feign.Client;
|
||||
import feign.httpclient.ApacheHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
|
||||
@EnableFeignClients
|
||||
@ComponentScan(basePackages = "de.mummeit")
|
||||
@Configuration
|
||||
@ComponentScan(basePackages = "de.mummeit")
|
||||
@EnableFeignClients(basePackages = "de.mummeit")
|
||||
@PropertySource(value = "classpath:permission-manager-sdk-application.yaml")
|
||||
public class PermissionManagerSdkConfiguration {
|
||||
|
||||
@Bean
|
||||
public Client feignClient() {
|
||||
return new ApacheHttpClient(HttpClients.createDefault());
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package de.mummeit.pmg.api;
|
||||
|
||||
import de.mummeit.pmg.api.model.access.request.CheckAccessRequest;
|
||||
import de.mummeit.pmg.api.model.access.request.PermitRequest;
|
||||
import de.mummeit.pmg.api.model.access.request.RevokeScopeAccessRequest;
|
||||
import de.mummeit.pmg.api.model.access.request.RevokeUserAccessRequest;
|
||||
import de.mummeit.pmg.api.model.access.request.SearchPermitRequest;
|
||||
import de.mummeit.pmg.api.model.access.response.PermittedResponse;
|
||||
import de.mummeit.pmg.api.model.integration.Integration;
|
||||
import de.mummeit.pmg.api.model.structure.Domain;
|
||||
import de.mummeit.pmg.api.model.structure.Permission;
|
||||
import de.mummeit.pmg.api.model.structure.Role;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@FeignClient(
|
||||
name = "permission-manager",
|
||||
url = "${permission-manager.url}"
|
||||
)
|
||||
public interface PermissionManagerClient {
|
||||
|
||||
@GetMapping("/health")
|
||||
String getHealthStatus();
|
||||
|
||||
// Access Management
|
||||
@PostMapping("/api/v1/access/check")
|
||||
PermittedResponse checkAccess(@RequestBody CheckAccessRequest request);
|
||||
|
||||
@PostMapping("/api/v1/access/permit")
|
||||
void permitAccess(@RequestBody PermitRequest request);
|
||||
|
||||
@PostMapping("/api/v1/access/permits/search")
|
||||
List<Permission> searchPermits(@RequestBody SearchPermitRequest request);
|
||||
|
||||
@PatchMapping("/api/v1/access/revoke")
|
||||
void revokeAccess(@RequestBody PermitRequest request);
|
||||
|
||||
@PatchMapping("/api/v1/access/revoke/scope")
|
||||
void revokeScopeAccess(@RequestBody RevokeScopeAccessRequest request);
|
||||
|
||||
@PatchMapping("/api/v1/access/revoke/user")
|
||||
void revokeUserAccess(@RequestBody RevokeUserAccessRequest request);
|
||||
|
||||
// Domain Management
|
||||
@PostMapping("/api/v1/domains")
|
||||
Domain createDomain(@RequestBody Domain domain);
|
||||
|
||||
@GetMapping("/api/v1/domains/{domain}")
|
||||
Domain getDomain(@PathVariable("domain") String domain);
|
||||
|
||||
@PutMapping("/api/v1/domains/{domain}")
|
||||
Domain updateDomain(@PathVariable("domain") String domain, @RequestBody Domain domainRequest);
|
||||
|
||||
@DeleteMapping("/api/v1/domains/{domain}")
|
||||
void deleteDomain(@PathVariable("domain") String domain);
|
||||
|
||||
// Permission Management
|
||||
@PostMapping("/api/v1/domains/{domain}/permissions")
|
||||
Permission createPermission(@PathVariable("domain") String domain, @RequestBody Permission permission);
|
||||
|
||||
@GetMapping("/api/v1/domains/{domain}/permissions/{permission}")
|
||||
Permission getPermission(@PathVariable("domain") String domain, @PathVariable("permission") String permission);
|
||||
|
||||
@PutMapping("/api/v1/domains/{domain}/permissions/{permission}")
|
||||
Permission updatePermission(
|
||||
@PathVariable("domain") String domain,
|
||||
@PathVariable("permission") String permission,
|
||||
@RequestBody Permission permissionRequest
|
||||
);
|
||||
|
||||
@DeleteMapping("/api/v1/domains/{domain}/permissions/{permission}")
|
||||
void deletePermission(@PathVariable("domain") String domain, @PathVariable("permission") String permission);
|
||||
|
||||
// Role Management
|
||||
@PostMapping("/api/v1/domains/{domain}/roles")
|
||||
Role createRole(@PathVariable("domain") String domain, @RequestBody Role role);
|
||||
|
||||
@GetMapping("/api/v1/domains/{domain}/roles/{role}")
|
||||
Role getRole(@PathVariable("domain") String domain, @PathVariable("role") String role);
|
||||
|
||||
@PutMapping("/api/v1/domains/{domain}/roles/{role}")
|
||||
Role updateRole(
|
||||
@PathVariable("domain") String domain,
|
||||
@PathVariable("role") String role,
|
||||
@RequestBody Role roleRequest
|
||||
);
|
||||
|
||||
@DeleteMapping("/api/v1/domains/{domain}/roles/{role}")
|
||||
void deleteRole(@PathVariable("domain") String domain, @PathVariable("role") String role);
|
||||
|
||||
// Integration
|
||||
@PostMapping("/api/v1/integration/perform")
|
||||
void performIntegration(List<Integration<?>> integrations);
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package de.mummeit.pmg.api;
|
||||
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
@FeignClient(
|
||||
name = "pmg",
|
||||
url = "${pmg.url}"
|
||||
)
|
||||
public interface PmgClient {
|
||||
|
||||
@GetMapping("/health")
|
||||
public String getHealthStatus();
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package de.mummeit.pmg.api.annotation;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Annotation to enforce permission checks on methods.
|
||||
* The permission check will be performed before the method is executed.
|
||||
* If the permission check fails, an AccessDeniedException will be thrown.
|
||||
*/
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Repeatable(RequiresPermissions.class)
|
||||
public @interface RequiresPermission {
|
||||
|
||||
/**
|
||||
* The domain to check permissions for.
|
||||
*/
|
||||
String domain();
|
||||
|
||||
/**
|
||||
* The permission to check.
|
||||
*/
|
||||
String permission();
|
||||
|
||||
/**
|
||||
* SpEL expression to determine the scope of the permission check.
|
||||
* If empty, no scope will be used.
|
||||
*/
|
||||
String scope() default "";
|
||||
|
||||
/**
|
||||
* SpEL expression to determine the user ID for the permission check.
|
||||
* By default, uses the security service to get the current user ID.
|
||||
* The expression can reference:
|
||||
* - Method parameters by name
|
||||
* - Spring beans with the '@' prefix (e.g., '@securityService.getCurrentUserId()')
|
||||
* - The HTTP request parameters with '#request.getParameter("paramName")'
|
||||
*/
|
||||
String userIdExpression() default "@securityService.getCurrentUserId()";
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package de.mummeit.pmg.api.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Container annotation for multiple {@link RequiresPermission} annotations.
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface RequiresPermissions {
|
||||
RequiresPermission[] value();
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package de.mummeit.pmg.api.aspect;
|
||||
|
||||
import de.mummeit.pmg.api.PermissionManagerClient;
|
||||
import de.mummeit.pmg.api.annotation.RequiresPermission;
|
||||
import de.mummeit.pmg.api.model.access.request.CheckAccessRequest;
|
||||
import de.mummeit.pmg.service.exception.AccessDeniedException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.expression.BeanFactoryResolver;
|
||||
import org.springframework.context.expression.MethodBasedEvaluationContext;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
@Aspect
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class PermissionCheckAspect {
|
||||
|
||||
private final PermissionManagerClient permissionManagerClient;
|
||||
private final ApplicationContext applicationContext;
|
||||
private final ExpressionParser parser = new SpelExpressionParser();
|
||||
private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
|
||||
|
||||
@Around("@annotation(de.mummeit.pmg.api.annotation.RequiresPermission)")
|
||||
public Object checkPermission(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
RequiresPermission annotation = method.getAnnotation(RequiresPermission.class);
|
||||
|
||||
StandardEvaluationContext context = new StandardEvaluationContext();
|
||||
context.setBeanResolver(new BeanFactoryResolver(applicationContext));
|
||||
|
||||
// Add method parameters to the context
|
||||
String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
|
||||
if (parameterNames != null) {
|
||||
Object[] args = joinPoint.getArgs();
|
||||
for (int i = 0; i < parameterNames.length; i++) {
|
||||
context.setVariable(parameterNames[i], args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
String userId = resolveUserId(context, annotation);
|
||||
String scope = annotation.scope().isEmpty() ? "" : parser.parseExpression(annotation.scope()).getValue(context, String.class);
|
||||
|
||||
CheckAccessRequest request = new CheckAccessRequest();
|
||||
request.setDomain(annotation.domain());
|
||||
request.setPermission(annotation.permission());
|
||||
request.setUserId(userId);
|
||||
request.setScope(scope);
|
||||
|
||||
if (!permissionManagerClient.checkAccess(request).isPermitted()) {
|
||||
throw new AccessDeniedException(
|
||||
"Access denied",
|
||||
userId,
|
||||
annotation.domain(),
|
||||
annotation.permission(),
|
||||
scope
|
||||
);
|
||||
}
|
||||
|
||||
return joinPoint.proceed();
|
||||
}
|
||||
|
||||
private String resolveUserId(EvaluationContext context, RequiresPermission annotation) {
|
||||
// First try to evaluate the expression
|
||||
try {
|
||||
String userId = parser.parseExpression(annotation.userIdExpression()).getValue(context, String.class);
|
||||
if (userId != null) {
|
||||
return userId;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// Fall through to request parameter check
|
||||
}
|
||||
|
||||
// Fallback to request parameter
|
||||
return Optional.ofNullable(RequestContextHolder.getRequestAttributes())
|
||||
.map(attributes -> ((ServletRequestAttributes) attributes).getRequest())
|
||||
.map(request -> request.getParameter("userId"))
|
||||
.orElseThrow(() -> new IllegalArgumentException("Could not resolve userId"));
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package de.mummeit.pmg.api.model.access.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CheckAccessRequest {
|
||||
private String domain;
|
||||
private String userId;
|
||||
private String scope;
|
||||
private String permission;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package de.mummeit.pmg.api.model.access.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class Permit {
|
||||
private String domain;
|
||||
private List<String> roles;
|
||||
private List<String> permissions;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package de.mummeit.pmg.api.model.access.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class PermitRequest {
|
||||
private List<Permit> permits;
|
||||
private String scope;
|
||||
private String userId;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package de.mummeit.pmg.api.model.access.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RevokeScopeAccessRequest {
|
||||
private String scope;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package de.mummeit.pmg.api.model.access.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RevokeUserAccessRequest {
|
||||
private String userId;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package de.mummeit.pmg.api.model.access.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SearchPermitRequest {
|
||||
private String scope;
|
||||
private String userId;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package de.mummeit.pmg.api.model.access.response;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class PermittedResponse {
|
||||
private boolean permitted;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package de.mummeit.pmg.api.model.integration;
|
||||
|
||||
import lombok.Builder;
|
||||
|
||||
public class DomainIntegration extends Integration<DomainIntegration.Data> {
|
||||
|
||||
@Builder
|
||||
public DomainIntegration(String id, Action action, Data data) {
|
||||
super(id, Entity.domain, action, data);
|
||||
}
|
||||
|
||||
@lombok.Data
|
||||
@Builder
|
||||
public static class Data {
|
||||
private String name, oldName, description;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package de.mummeit.pmg.api.model.integration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "entity")
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public abstract class Integration<T> {
|
||||
public enum Action {
|
||||
create,
|
||||
update,
|
||||
delete
|
||||
}
|
||||
|
||||
public enum Entity {
|
||||
domain,
|
||||
role,
|
||||
permission,
|
||||
permission_role_relation,
|
||||
}
|
||||
|
||||
private String id;
|
||||
private Entity entity;
|
||||
private Action action;
|
||||
|
||||
protected T data;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package de.mummeit.pmg.api.model.integration;
|
||||
|
||||
import lombok.Builder;
|
||||
|
||||
public class PermissionIntegration extends Integration<PermissionIntegration.Data> {
|
||||
|
||||
@Builder
|
||||
public PermissionIntegration(String id, Action action, Data data) {
|
||||
super(id, Entity.permission, action, data);
|
||||
}
|
||||
|
||||
@Builder
|
||||
@lombok.Data
|
||||
public static class Data {
|
||||
private String domain, name, oldName, description;
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package de.mummeit.pmg.api.model.integration;
|
||||
|
||||
import lombok.Builder;
|
||||
|
||||
public class RoleIntegration extends Integration<RoleIntegration.Data> {
|
||||
|
||||
@Builder
|
||||
public RoleIntegration(String id, Action action, Data data) {
|
||||
super(id, Entity.role, action, data);
|
||||
}
|
||||
|
||||
@Builder
|
||||
@lombok.Data
|
||||
public static class Data {
|
||||
private String domain, name, oldName, description;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package de.mummeit.pmg.api.model.integration;
|
||||
|
||||
import lombok.Builder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class RolePermissionRelationIntegration extends Integration<RolePermissionRelationIntegration.Data> {
|
||||
|
||||
@Builder
|
||||
public RolePermissionRelationIntegration(String id, Action action, Data data) {
|
||||
super(id, Entity.permission_role_relation, action, data);
|
||||
}
|
||||
|
||||
@Builder
|
||||
@lombok.Data
|
||||
public static class Data {
|
||||
private String domain, role;
|
||||
private List<String> permissions;
|
||||
}
|
||||
}
|
12
src/main/java/de/mummeit/pmg/api/model/structure/Domain.java
Normal file
12
src/main/java/de/mummeit/pmg/api/model/structure/Domain.java
Normal file
@ -0,0 +1,12 @@
|
||||
package de.mummeit.pmg.api.model.structure;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class Domain {
|
||||
private String name;
|
||||
private String description;
|
||||
private List<Permission> permissions;
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package de.mummeit.pmg.api.model.structure;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Permission {
|
||||
private Domain domain;
|
||||
private String name;
|
||||
private String description;
|
||||
}
|
13
src/main/java/de/mummeit/pmg/api/model/structure/Role.java
Normal file
13
src/main/java/de/mummeit/pmg/api/model/structure/Role.java
Normal file
@ -0,0 +1,13 @@
|
||||
package de.mummeit.pmg.api.model.structure;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class Role {
|
||||
private Domain domain;
|
||||
private String name;
|
||||
private String description;
|
||||
private List<Permission> permissions;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package de.mummeit.pmg.api.service;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* Service to retrieve the current user's ID from Spring Security context.
|
||||
* This is the default user ID provider for the permission manager.
|
||||
*/
|
||||
@Service
|
||||
public class SecurityService {
|
||||
|
||||
/**
|
||||
* Gets the current user's ID from the Spring Security context.
|
||||
*
|
||||
* @return the current user's ID
|
||||
* @throws IllegalStateException if no authenticated user is found
|
||||
*/
|
||||
public String getCurrentUserId() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication == null || !authentication.isAuthenticated()) {
|
||||
throw new IllegalStateException("No authenticated user found in security context");
|
||||
}
|
||||
return authentication.getName();
|
||||
}
|
||||
}
|
299
src/main/java/de/mummeit/pmg/builder/IntegrationBuilder.java
Normal file
299
src/main/java/de/mummeit/pmg/builder/IntegrationBuilder.java
Normal file
@ -0,0 +1,299 @@
|
||||
package de.mummeit.pmg.service.builder;
|
||||
|
||||
import de.mummeit.pmg.api.model.integration.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A fluent builder for creating Permission Manager integrations.
|
||||
* This builder provides a type-safe way to create various types of integrations including:
|
||||
* - Domain integrations (create, update, delete)
|
||||
* - Permission integrations (create, update, delete)
|
||||
* - Role integrations (create, update, delete)
|
||||
* - Role-Permission relation integrations
|
||||
*
|
||||
* Example usage:
|
||||
* {@code
|
||||
* List<Integration<?>> integrations = IntegrationBuilder.create()
|
||||
* .createDomain("my-domain", "My Domain")
|
||||
* .addPermission("read", "Read Permission")
|
||||
* .addRole("user", "User Role")
|
||||
* .assignPermissionsToRole("user", Arrays.asList("read"))
|
||||
* .build();
|
||||
* }
|
||||
*/
|
||||
public class IntegrationBuilder {
|
||||
private final List<Integration<?>> integrations = new ArrayList<>();
|
||||
private String currentDomain;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the IntegrationBuilder.
|
||||
*
|
||||
* @return A new IntegrationBuilder instance
|
||||
*/
|
||||
public static IntegrationBuilder create() {
|
||||
return new IntegrationBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a domain as the context for subsequent operations without creating an integration.
|
||||
* This is useful when you need to perform operations on an existing domain.
|
||||
*
|
||||
* @param name The name of the domain to select
|
||||
* @return This builder instance for method chaining
|
||||
*/
|
||||
public IntegrationBuilder selectDomain(String name) {
|
||||
this.currentDomain = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new domain integration with the specified name and description.
|
||||
* This also sets the domain context for subsequent operations.
|
||||
*
|
||||
* @param name The name of the domain
|
||||
* @param description The description of the domain
|
||||
* @return This builder instance for method chaining
|
||||
*/
|
||||
public IntegrationBuilder createDomain(String name, String description) {
|
||||
this.currentDomain = name;
|
||||
integrations.add(DomainIntegration.builder()
|
||||
.id(generateIntegrationId("domain", name, Integration.Action.create))
|
||||
.action(Integration.Action.create)
|
||||
.data(DomainIntegration.Data.builder()
|
||||
.name(name)
|
||||
.description(description)
|
||||
.build())
|
||||
.build());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing domain with a new name and/or description.
|
||||
* This also sets the domain context to the new name for subsequent operations.
|
||||
*
|
||||
* @param oldName The current name of the domain to update
|
||||
* @param newName The new name for the domain
|
||||
* @param description The new description for the domain
|
||||
* @return This builder instance for method chaining
|
||||
*/
|
||||
public IntegrationBuilder updateDomain(String oldName, String newName, String description) {
|
||||
integrations.add(DomainIntegration.builder()
|
||||
.id(generateIntegrationId("domain", oldName, Integration.Action.update))
|
||||
.action(Integration.Action.update)
|
||||
.data(DomainIntegration.Data.builder()
|
||||
.name(newName)
|
||||
.oldName(oldName)
|
||||
.description(description)
|
||||
.build())
|
||||
.build());
|
||||
this.currentDomain = newName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a domain.
|
||||
*
|
||||
* @param name The name of the domain to delete
|
||||
* @return This builder instance for method chaining
|
||||
*/
|
||||
public IntegrationBuilder deleteDomain(String name) {
|
||||
integrations.add(DomainIntegration.builder()
|
||||
.id(generateIntegrationId("domain", name, Integration.Action.delete))
|
||||
.action(Integration.Action.delete)
|
||||
.data(DomainIntegration.Data.builder()
|
||||
.name(name)
|
||||
.build())
|
||||
.build());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new permission to the current domain.
|
||||
*
|
||||
* @param name The name of the permission
|
||||
* @param description The description of the permission
|
||||
* @return This builder instance for method chaining
|
||||
* @throws IllegalStateException if no domain context has been set
|
||||
*/
|
||||
public IntegrationBuilder addPermission(String name, String description) {
|
||||
validateDomain();
|
||||
integrations.add(PermissionIntegration.builder()
|
||||
.id(generateIntegrationId("permission", currentDomain + ":" + name, Integration.Action.create))
|
||||
.action(Integration.Action.create)
|
||||
.data(PermissionIntegration.Data.builder()
|
||||
.domain(currentDomain)
|
||||
.name(name)
|
||||
.description(description)
|
||||
.build())
|
||||
.build());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing permission in the current domain.
|
||||
*
|
||||
* @param oldName The current name of the permission to update
|
||||
* @param newName The new name for the permission
|
||||
* @param description The new description for the permission
|
||||
* @return This builder instance for method chaining
|
||||
* @throws IllegalStateException if no domain context has been set
|
||||
*/
|
||||
public IntegrationBuilder updatePermission(String oldName, String newName, String description) {
|
||||
validateDomain();
|
||||
integrations.add(PermissionIntegration.builder()
|
||||
.id(generateIntegrationId("permission", currentDomain + ":" + oldName, Integration.Action.update))
|
||||
.action(Integration.Action.update)
|
||||
.data(PermissionIntegration.Data.builder()
|
||||
.domain(currentDomain)
|
||||
.name(newName)
|
||||
.oldName(oldName)
|
||||
.description(description)
|
||||
.build())
|
||||
.build());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a permission from the current domain.
|
||||
*
|
||||
* @param name The name of the permission to remove
|
||||
* @return This builder instance for method chaining
|
||||
* @throws IllegalStateException if no domain context has been set
|
||||
*/
|
||||
public IntegrationBuilder removePermission(String name) {
|
||||
validateDomain();
|
||||
integrations.add(PermissionIntegration.builder()
|
||||
.id(generateIntegrationId("permission", currentDomain + ":" + name, Integration.Action.delete))
|
||||
.action(Integration.Action.delete)
|
||||
.data(PermissionIntegration.Data.builder()
|
||||
.domain(currentDomain)
|
||||
.name(name)
|
||||
.build())
|
||||
.build());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new role to the current domain.
|
||||
*
|
||||
* @param name The name of the role
|
||||
* @param description The description of the role
|
||||
* @return This builder instance for method chaining
|
||||
* @throws IllegalStateException if no domain context has been set
|
||||
*/
|
||||
public IntegrationBuilder addRole(String name, String description) {
|
||||
validateDomain();
|
||||
integrations.add(RoleIntegration.builder()
|
||||
.id(generateIntegrationId("role", currentDomain + ":" + name, Integration.Action.create))
|
||||
.action(Integration.Action.create)
|
||||
.data(RoleIntegration.Data.builder()
|
||||
.domain(currentDomain)
|
||||
.name(name)
|
||||
.description(description)
|
||||
.build())
|
||||
.build());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing role in the current domain.
|
||||
*
|
||||
* @param oldName The current name of the role to update
|
||||
* @param newName The new name for the role
|
||||
* @param description The new description for the role
|
||||
* @return This builder instance for method chaining
|
||||
* @throws IllegalStateException if no domain context has been set
|
||||
*/
|
||||
public IntegrationBuilder updateRole(String oldName, String newName, String description) {
|
||||
validateDomain();
|
||||
integrations.add(RoleIntegration.builder()
|
||||
.id(generateIntegrationId("role", currentDomain + ":" + oldName, Integration.Action.update))
|
||||
.action(Integration.Action.update)
|
||||
.data(RoleIntegration.Data.builder()
|
||||
.domain(currentDomain)
|
||||
.name(newName)
|
||||
.oldName(oldName)
|
||||
.description(description)
|
||||
.build())
|
||||
.build());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a role from the current domain.
|
||||
*
|
||||
* @param name The name of the role to remove
|
||||
* @return This builder instance for method chaining
|
||||
* @throws IllegalStateException if no domain context has been set
|
||||
*/
|
||||
public IntegrationBuilder removeRole(String name) {
|
||||
validateDomain();
|
||||
integrations.add(RoleIntegration.builder()
|
||||
.id(generateIntegrationId("role", currentDomain + ":" + name, Integration.Action.delete))
|
||||
.action(Integration.Action.delete)
|
||||
.data(RoleIntegration.Data.builder()
|
||||
.domain(currentDomain)
|
||||
.name(name)
|
||||
.build())
|
||||
.build());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a list of permissions to a role in the current domain.
|
||||
*
|
||||
* @param role The name of the role to assign permissions to
|
||||
* @param permissions List of permission names to assign to the role
|
||||
* @return This builder instance for method chaining
|
||||
* @throws IllegalStateException if no domain context has been set
|
||||
*/
|
||||
public IntegrationBuilder assignPermissionsToRole(String role, List<String> permissions) {
|
||||
validateDomain();
|
||||
String permissionList = String.join(",", permissions);
|
||||
integrations.add(RolePermissionRelationIntegration.builder()
|
||||
.id(generateIntegrationId("role-permissions", currentDomain + ":" + role + ":" + permissionList, Integration.Action.create))
|
||||
.action(Integration.Action.create)
|
||||
.data(RolePermissionRelationIntegration.Data.builder()
|
||||
.domain(currentDomain)
|
||||
.role(role)
|
||||
.permissions(permissions)
|
||||
.build())
|
||||
.build());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns the list of integrations created by this builder.
|
||||
*
|
||||
* @return A new list containing all the integrations created by this builder
|
||||
*/
|
||||
public List<Integration<?>> build() {
|
||||
return new ArrayList<>(integrations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a domain context has been set by a previous domain operation.
|
||||
*
|
||||
* @throws IllegalStateException if no domain context has been set
|
||||
*/
|
||||
private void validateDomain() {
|
||||
if (currentDomain == null || currentDomain.trim().isEmpty()) {
|
||||
throw new IllegalStateException("No domain context set. Create, update, or select a domain first.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a deterministic integration ID based on the integration type, name, and action.
|
||||
* This ensures that the same integration will always have the same ID across different runs.
|
||||
*
|
||||
* @param type The type of integration (domain, permission, role, etc.)
|
||||
* @param name The unique name or identifier for this integration
|
||||
* @param action The action being performed
|
||||
* @return A deterministic ID string
|
||||
*/
|
||||
private String generateIntegrationId(String type, String name, Integration.Action action) {
|
||||
return String.format("%s:%s:%s", type, name, action.name().toLowerCase());
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package de.mummeit.pmg.service.config;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import de.mummeit.pmg.api.model.integration.Integration;
|
||||
import de.mummeit.pmg.service.PermissionManager;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Abstract configuration class for Permission Manager integration.
|
||||
* Implementing classes can provide integrations either by overriding getIntegrations()
|
||||
* or by providing a JSON file path in getIntegrationsJsonPath().
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public abstract class AbstractPermissionManagerConfiguration {
|
||||
|
||||
private final PermissionManager permissionManager;
|
||||
private final ResourceLoader resourceLoader;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* Override this method to provide integrations programmatically.
|
||||
* By default, returns null, which means no integrations will be performed unless
|
||||
* getIntegrationsJsonPath() returns a valid path.
|
||||
*
|
||||
* @return List of integrations to perform, or null if no integrations should be performed
|
||||
*/
|
||||
protected List<Integration<?>> getIntegrations() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to provide the path to a JSON file containing integrations.
|
||||
* The JSON file should contain an array of Integration objects.
|
||||
* By default, returns null, which means no JSON file will be loaded.
|
||||
*
|
||||
* @return Path to the JSON file, or null if no file should be loaded
|
||||
*/
|
||||
protected String getIntegrationsJsonPath() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads integrations from a JSON file.
|
||||
*
|
||||
* @param path Path to the JSON file
|
||||
* @return List of integrations
|
||||
* @throws IOException if the file cannot be read or parsed
|
||||
*/
|
||||
protected List<Integration<?>> loadIntegrationsFromJson(String path) throws IOException {
|
||||
Resource resource = resourceLoader.getResource(path);
|
||||
try (InputStream inputStream = resource.getInputStream()) {
|
||||
return objectMapper.readValue(inputStream, new TypeReference<List<Integration<?>>>() {});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listener that performs integrations when the application is ready.
|
||||
* This method will first try to get integrations from getIntegrations(),
|
||||
* and if that returns null, it will try to load integrations from the JSON file
|
||||
* specified by getIntegrationsJsonPath().
|
||||
*/
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
public void performIntegrationsOnStartup() {
|
||||
try {
|
||||
List<Integration<?>> integrations = getIntegrations();
|
||||
if (integrations == null) {
|
||||
String jsonPath = getIntegrationsJsonPath();
|
||||
if (jsonPath != null) {
|
||||
integrations = loadIntegrationsFromJson(jsonPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (integrations != null && !integrations.isEmpty()) {
|
||||
log.info("Performing {} integrations on startup", integrations.size());
|
||||
permissionManager.performIntegration(integrations);
|
||||
log.info("Successfully performed integrations");
|
||||
} else {
|
||||
log.info("No integrations to perform");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to perform integrations on startup", e);
|
||||
throw new RuntimeException("Failed to perform integrations on startup", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package de.mummeit.pmg.service.exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when access is denied for a user.
|
||||
*/
|
||||
public class AccessDeniedException extends PermissionManagerException {
|
||||
private final String userId;
|
||||
private final String domain;
|
||||
private final String permission;
|
||||
private final String scope;
|
||||
|
||||
public AccessDeniedException(String message, String userId, String domain, String permission, String scope) {
|
||||
super(message);
|
||||
this.userId = userId;
|
||||
this.domain = domain;
|
||||
this.permission = permission;
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
public AccessDeniedException(String message, String userId, String domain, String permission, String scope, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.userId = userId;
|
||||
this.domain = domain;
|
||||
this.permission = permission;
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public String getDomain() {
|
||||
return domain;
|
||||
}
|
||||
|
||||
public String getPermission() {
|
||||
return permission;
|
||||
}
|
||||
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package de.mummeit.pmg.service.exception;
|
||||
|
||||
import de.mummeit.pmg.api.model.integration.Integration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Exception thrown when an integration operation fails.
|
||||
*/
|
||||
public class IntegrationFailedException extends PermissionManagerException {
|
||||
private final List<Integration<?>> failedIntegrations;
|
||||
|
||||
public IntegrationFailedException(String message, List<Integration<?>> failedIntegrations) {
|
||||
super(message);
|
||||
this.failedIntegrations = failedIntegrations;
|
||||
}
|
||||
|
||||
public IntegrationFailedException(String message, List<Integration<?>> failedIntegrations, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.failedIntegrations = failedIntegrations;
|
||||
}
|
||||
|
||||
public List<Integration<?>> getFailedIntegrations() {
|
||||
return failedIntegrations;
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package de.mummeit.pmg.service.exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when a permission request is invalid.
|
||||
*/
|
||||
public class InvalidPermissionRequestException extends PermissionManagerException {
|
||||
private final String reason;
|
||||
|
||||
public InvalidPermissionRequestException(String message, String reason) {
|
||||
super(message);
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public InvalidPermissionRequestException(String message, String reason, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package de.mummeit.pmg.service.exception;
|
||||
|
||||
/**
|
||||
* Base exception class for all Permission Manager related exceptions.
|
||||
*/
|
||||
public class PermissionManagerException extends RuntimeException {
|
||||
public PermissionManagerException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PermissionManagerException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
376
src/main/java/de/mummeit/pmg/service/PermissionManager.java
Normal file
376
src/main/java/de/mummeit/pmg/service/PermissionManager.java
Normal file
@ -0,0 +1,376 @@
|
||||
package de.mummeit.pmg.service;
|
||||
|
||||
import de.mummeit.pmg.api.PermissionManagerClient;
|
||||
import de.mummeit.pmg.api.model.access.request.*;
|
||||
import de.mummeit.pmg.api.model.access.response.PermittedResponse;
|
||||
import de.mummeit.pmg.api.model.integration.Integration;
|
||||
import de.mummeit.pmg.api.model.structure.Permission;
|
||||
import de.mummeit.pmg.service.exception.AccessDeniedException;
|
||||
import de.mummeit.pmg.service.exception.IntegrationFailedException;
|
||||
import de.mummeit.pmg.service.exception.InvalidPermissionRequestException;
|
||||
import feign.FeignException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PermissionManager {
|
||||
|
||||
private final PermissionManagerClient client;
|
||||
|
||||
/**
|
||||
* Checks if a user has access to a specific permission in a domain and scope.
|
||||
*
|
||||
* @param userId The ID of the user
|
||||
* @param domain The domain name
|
||||
* @param permission The permission name
|
||||
* @param scope The scope
|
||||
* @return true if the user has access, false otherwise
|
||||
* @throws InvalidPermissionRequestException if any of the required parameters are null or empty
|
||||
* @throws AccessDeniedException if the access check fails
|
||||
*/
|
||||
public boolean hasAccess(String userId, String domain, String permission, String scope) {
|
||||
validateParameters(userId, domain, permission, scope);
|
||||
|
||||
try {
|
||||
CheckAccessRequest request = new CheckAccessRequest();
|
||||
request.setUserId(userId);
|
||||
request.setDomain(domain);
|
||||
request.setPermission(permission);
|
||||
request.setScope(scope);
|
||||
|
||||
PermittedResponse response = client.checkAccess(request);
|
||||
return response.isPermitted();
|
||||
} catch (FeignException e) {
|
||||
throw new AccessDeniedException(
|
||||
"Failed to check access",
|
||||
userId,
|
||||
domain,
|
||||
permission,
|
||||
scope,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Grants a user access to specific permissions and roles in a domain and scope.
|
||||
*
|
||||
* @param userId The ID of the user
|
||||
* @param domain The domain name
|
||||
* @param permissions List of permission names to grant
|
||||
* @param roles List of role names to grant
|
||||
* @param scope The scope
|
||||
* @throws InvalidPermissionRequestException if any of the required parameters are invalid
|
||||
*/
|
||||
public void grantAccess(String userId, String domain, List<String> permissions, List<String> roles, String scope) {
|
||||
validateParameters(userId, domain, scope);
|
||||
validatePermissionsAndRoles(permissions, roles);
|
||||
|
||||
try {
|
||||
PermitRequest request = new PermitRequest();
|
||||
request.setUserId(userId);
|
||||
request.setScope(scope);
|
||||
|
||||
Permit permit = new Permit();
|
||||
permit.setDomain(domain);
|
||||
permit.setPermissions(permissions);
|
||||
permit.setRoles(roles);
|
||||
|
||||
request.setPermits(Collections.singletonList(permit));
|
||||
client.permitAccess(request);
|
||||
} catch (FeignException e) {
|
||||
throw new InvalidPermissionRequestException(
|
||||
"Failed to grant access",
|
||||
"Error occurred while granting permissions",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revokes a user's access to specific permissions and roles in a domain and scope.
|
||||
*
|
||||
* @param userId The ID of the user
|
||||
* @param domain The domain name
|
||||
* @param permissions List of permission names to revoke
|
||||
* @param roles List of role names to revoke
|
||||
* @param scope The scope
|
||||
* @throws InvalidPermissionRequestException if any of the required parameters are invalid
|
||||
*/
|
||||
public void revokeAccess(String userId, String domain, List<String> permissions, List<String> roles, String scope) {
|
||||
validateParameters(userId, domain, scope);
|
||||
validatePermissionsAndRoles(permissions, roles);
|
||||
|
||||
try {
|
||||
PermitRequest request = new PermitRequest();
|
||||
request.setUserId(userId);
|
||||
request.setScope(scope);
|
||||
|
||||
Permit permit = new Permit();
|
||||
permit.setDomain(domain);
|
||||
permit.setPermissions(permissions);
|
||||
permit.setRoles(roles);
|
||||
|
||||
request.setPermits(Collections.singletonList(permit));
|
||||
client.revokeAccess(request);
|
||||
} catch (FeignException e) {
|
||||
throw new InvalidPermissionRequestException(
|
||||
"Failed to revoke access",
|
||||
"Error occurred while revoking permissions",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revokes all access for a specific user.
|
||||
*
|
||||
* @param userId The ID of the user
|
||||
* @throws InvalidPermissionRequestException if the userId is null or empty
|
||||
*/
|
||||
public void revokeAllUserAccess(String userId) {
|
||||
validateUserId(userId);
|
||||
|
||||
try {
|
||||
RevokeUserAccessRequest request = new RevokeUserAccessRequest();
|
||||
request.setUserId(userId);
|
||||
client.revokeUserAccess(request);
|
||||
} catch (FeignException e) {
|
||||
throw new InvalidPermissionRequestException(
|
||||
"Failed to revoke all user access",
|
||||
"Error occurred while revoking all user permissions",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revokes all access for a specific scope.
|
||||
*
|
||||
* @param scope The scope to revoke all access from
|
||||
* @throws InvalidPermissionRequestException if the scope is null or empty
|
||||
*/
|
||||
public void revokeAllScopeAccess(String scope) {
|
||||
validateScope(scope);
|
||||
|
||||
try {
|
||||
RevokeScopeAccessRequest request = new RevokeScopeAccessRequest();
|
||||
request.setScope(scope);
|
||||
client.revokeScopeAccess(request);
|
||||
} catch (FeignException e) {
|
||||
throw new InvalidPermissionRequestException(
|
||||
"Failed to revoke all scope access",
|
||||
"Error occurred while revoking all scope permissions",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for all permissions granted to a user in a specific scope.
|
||||
*
|
||||
* @param userId The ID of the user
|
||||
* @param scope The scope to search in
|
||||
* @return List of permissions granted to the user
|
||||
* @throws InvalidPermissionRequestException if any of the required parameters are invalid
|
||||
*/
|
||||
public List<Permission> findUserPermissions(String userId, String scope) {
|
||||
validateParameters(userId, scope);
|
||||
|
||||
try {
|
||||
SearchPermitRequest request = new SearchPermitRequest();
|
||||
request.setUserId(userId);
|
||||
request.setScope(scope);
|
||||
return client.searchPermits(request);
|
||||
} catch (FeignException e) {
|
||||
throw new InvalidPermissionRequestException(
|
||||
"Failed to find user permissions",
|
||||
"Error occurred while searching for user permissions",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs integration operations.
|
||||
*
|
||||
* @param integrations List of integrations to perform
|
||||
* @throws IntegrationFailedException if the integration operations fail
|
||||
* @throws InvalidPermissionRequestException if the integrations list is null or empty
|
||||
*/
|
||||
public void performIntegration(List<Integration<?>> integrations) {
|
||||
if (integrations == null || integrations.isEmpty()) {
|
||||
throw new InvalidPermissionRequestException(
|
||||
"Invalid integration request",
|
||||
"Integrations list cannot be null or empty"
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
client.performIntegration(integrations);
|
||||
} catch (FeignException e) {
|
||||
throw new IntegrationFailedException(
|
||||
"Failed to perform integrations",
|
||||
integrations,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Grants multiple permissions and roles across different domains to a user.
|
||||
*
|
||||
* @param userId The ID of the user
|
||||
* @param permits List of permits containing domain-specific permissions and roles
|
||||
* @param scope The scope
|
||||
* @throws InvalidPermissionRequestException if any of the required parameters are invalid
|
||||
*/
|
||||
public void grantMultiDomainAccess(String userId, List<Permit> permits, String scope) {
|
||||
validateParameters(userId, scope);
|
||||
validatePermits(permits);
|
||||
|
||||
try {
|
||||
PermitRequest request = new PermitRequest();
|
||||
request.setUserId(userId);
|
||||
request.setScope(scope);
|
||||
request.setPermits(permits);
|
||||
client.permitAccess(request);
|
||||
} catch (FeignException e) {
|
||||
throw new InvalidPermissionRequestException(
|
||||
"Failed to grant multi-domain access",
|
||||
"Error occurred while granting multi-domain permissions",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revokes multiple permissions and roles across different domains from a user.
|
||||
*
|
||||
* @param userId The ID of the user
|
||||
* @param permits List of permits containing domain-specific permissions and roles
|
||||
* @param scope The scope
|
||||
* @throws InvalidPermissionRequestException if any of the required parameters are invalid
|
||||
*/
|
||||
public void revokeMultiDomainAccess(String userId, List<Permit> permits, String scope) {
|
||||
validateParameters(userId, scope);
|
||||
validatePermits(permits);
|
||||
|
||||
try {
|
||||
PermitRequest request = new PermitRequest();
|
||||
request.setUserId(userId);
|
||||
request.setScope(scope);
|
||||
request.setPermits(permits);
|
||||
client.revokeAccess(request);
|
||||
} catch (FeignException e) {
|
||||
throw new InvalidPermissionRequestException(
|
||||
"Failed to revoke multi-domain access",
|
||||
"Error occurred while revoking multi-domain permissions",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateParameters(String userId, String domain, String permission, String scope) {
|
||||
validateUserId(userId);
|
||||
validateDomain(domain);
|
||||
validatePermission(permission);
|
||||
validateScope(scope);
|
||||
}
|
||||
|
||||
private void validateParameters(String userId, String domain, String scope) {
|
||||
validateUserId(userId);
|
||||
validateDomain(domain);
|
||||
validateScope(scope);
|
||||
}
|
||||
|
||||
private void validateParameters(String userId, String scope) {
|
||||
validateUserId(userId);
|
||||
validateScope(scope);
|
||||
}
|
||||
|
||||
private void validateUserId(String userId) {
|
||||
if (userId == null || userId.trim().isEmpty()) {
|
||||
throw new InvalidPermissionRequestException(
|
||||
"Invalid user ID",
|
||||
"User ID cannot be null or empty"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateDomain(String domain) {
|
||||
if (domain == null || domain.trim().isEmpty()) {
|
||||
throw new InvalidPermissionRequestException(
|
||||
"Invalid domain",
|
||||
"Domain cannot be null or empty"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void validatePermission(String permission) {
|
||||
if (permission == null || permission.trim().isEmpty()) {
|
||||
throw new InvalidPermissionRequestException(
|
||||
"Invalid permission",
|
||||
"Permission cannot be null or empty"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateScope(String scope) {
|
||||
if (scope == null || scope.trim().isEmpty()) {
|
||||
throw new InvalidPermissionRequestException(
|
||||
"Invalid scope",
|
||||
"Scope cannot be null or empty"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void validatePermissionsAndRoles(List<String> permissions, List<String> roles) {
|
||||
if ((permissions == null || permissions.isEmpty()) && (roles == null || roles.isEmpty())) {
|
||||
throw new InvalidPermissionRequestException(
|
||||
"Invalid permissions and roles",
|
||||
"At least one permission or role must be specified"
|
||||
);
|
||||
}
|
||||
|
||||
if (permissions != null && permissions.stream().anyMatch(p -> p == null || p.trim().isEmpty())) {
|
||||
throw new InvalidPermissionRequestException(
|
||||
"Invalid permissions",
|
||||
"Permissions cannot contain null or empty values"
|
||||
);
|
||||
}
|
||||
|
||||
if (roles != null && roles.stream().anyMatch(r -> r == null || r.trim().isEmpty())) {
|
||||
throw new InvalidPermissionRequestException(
|
||||
"Invalid roles",
|
||||
"Roles cannot contain null or empty values"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void validatePermits(List<Permit> permits) {
|
||||
if (permits == null || permits.isEmpty()) {
|
||||
throw new InvalidPermissionRequestException(
|
||||
"Invalid permits",
|
||||
"Permits list cannot be null or empty"
|
||||
);
|
||||
}
|
||||
|
||||
if (permits.stream().anyMatch(Objects::isNull)) {
|
||||
throw new InvalidPermissionRequestException(
|
||||
"Invalid permits",
|
||||
"Permits list cannot contain null values"
|
||||
);
|
||||
}
|
||||
|
||||
permits.forEach(permit -> {
|
||||
validateDomain(permit.getDomain());
|
||||
validatePermissionsAndRoles(permit.getPermissions(), permit.getRoles());
|
||||
});
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user