Add functionality for all pmg features
This commit is contained in:
169
README.md
169
README.md
@ -11,6 +11,7 @@ A Spring Boot SDK for integrating permission management into your Java applicati
|
||||
- Scope-based access control
|
||||
- Multi-domain permission management
|
||||
- Flexible user ID resolution
|
||||
- API key authentication support
|
||||
|
||||
## Requirements
|
||||
|
||||
@ -54,13 +55,179 @@ Add the following properties to your Spring Boot application properties file (`a
|
||||
|
||||
```yaml
|
||||
permission-manager:
|
||||
base-url: http://your-permission-manager-url # Required: URL of your Permission Manager instance
|
||||
url: http://your-permission-manager-url # Required: URL of your Permission Manager instance
|
||||
auth:
|
||||
enabled: false # Optional: Enable/disable API key authentication (defaults to false)
|
||||
api-key: your-api-key # Required when auth.enabled is true
|
||||
security:
|
||||
enabled: true # Optional: Enable/disable security checks (defaults to true)
|
||||
```
|
||||
|
||||
These properties can be configured in any valid Spring Boot configuration source (application.yml, application.properties, environment variables, etc.) following Spring Boot's standard property resolution order.
|
||||
|
||||
### 3. API Key Authentication
|
||||
|
||||
The SDK supports API key authentication for requests to the Permission Manager service. When enabled, the SDK will automatically add an `x-api-key` header to all requests.
|
||||
|
||||
To enable API key authentication:
|
||||
|
||||
1. Set `permission-manager.auth.enabled` to `true`
|
||||
2. Provide your API key in `permission-manager.auth.api-key`
|
||||
|
||||
Example configuration:
|
||||
```yaml
|
||||
permission-manager:
|
||||
auth:
|
||||
enabled: true
|
||||
api-key: your-secret-api-key-here
|
||||
```
|
||||
|
||||
Note: When authentication is enabled, the API key is required. The SDK will throw an `IllegalStateException` if authentication is enabled but no API key is provided.
|
||||
|
||||
### 4. Integration Configuration
|
||||
|
||||
The SDK provides a flexible way to configure and perform integrations on application startup through the `AbstractPermissionManagerConfiguration` class. You can use this to automatically set up your permission structure (domains, permissions, roles, and their relationships) when your application starts.
|
||||
|
||||
There are two ways to provide integrations:
|
||||
|
||||
#### Option 1: JSON Configuration
|
||||
|
||||
Create a JSON file containing your integration configuration:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "domain-1",
|
||||
"entity": "domain",
|
||||
"action": "create",
|
||||
"data": {
|
||||
"name": "users",
|
||||
"description": "User management domain"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "permission-1",
|
||||
"entity": "permission",
|
||||
"action": "create",
|
||||
"data": {
|
||||
"domain": "users",
|
||||
"name": "read",
|
||||
"description": "Permission to read user data"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "role-1",
|
||||
"entity": "role",
|
||||
"action": "create",
|
||||
"data": {
|
||||
"domain": "users",
|
||||
"name": "user_viewer",
|
||||
"description": "Role for viewing user data"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "role-permission-1",
|
||||
"entity": "role_permission_relation",
|
||||
"action": "create",
|
||||
"data": {
|
||||
"domain": "users",
|
||||
"role": "user_viewer",
|
||||
"permissions": ["read"]
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Then create a configuration class that extends `AbstractPermissionManagerConfiguration`:
|
||||
|
||||
```java
|
||||
import de.mummeit.pmg.config.AbstractPermissionManagerConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class PermissionManagerIntegrationConfig extends AbstractPermissionManagerConfiguration {
|
||||
|
||||
@Override
|
||||
protected String getIntegrationsJsonPath() {
|
||||
return "classpath:permission-integrations.json";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Option 2: Programmatic Configuration
|
||||
|
||||
Alternatively, you can build your integrations programmatically using the fluent `IntegrationBuilder`:
|
||||
|
||||
```java
|
||||
import de.mummeit.pmg.builder.IntegrationBuilder;
|
||||
import de.mummeit.pmg.config.AbstractPermissionManagerConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class PermissionManagerIntegrationConfig extends AbstractPermissionManagerConfiguration {
|
||||
|
||||
@Override
|
||||
protected List<Integration<?>> getIntegrations() {
|
||||
return IntegrationBuilder.create()
|
||||
// Create a domain and set it as the current context
|
||||
.createDomain("users", "User management domain")
|
||||
// Add permissions to the current domain
|
||||
.addPermission("read", "Permission to read user data")
|
||||
.addPermission("write", "Permission to write user data")
|
||||
// Add a role to the current domain
|
||||
.addRole("user_viewer", "Role for viewing user data")
|
||||
// Assign permissions to the role
|
||||
.assignPermissionsToRole("user_viewer", List.of("read"))
|
||||
// Create another domain with its permissions and roles
|
||||
.createDomain("orders", "Order management domain")
|
||||
.addPermission("view", "Permission to view orders")
|
||||
.addPermission("create", "Permission to create orders")
|
||||
.addRole("order_manager", "Role for managing orders")
|
||||
.assignPermissionsToRole("order_manager", List.of("view", "create"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `IntegrationBuilder` provides a fluent API for creating integrations with features like:
|
||||
|
||||
- Domain context management (automatically tracks the current domain)
|
||||
- Method chaining for building complex permission structures
|
||||
- Type-safe operations for all integration types
|
||||
- Automatic generation of integration IDs
|
||||
|
||||
Available builder methods:
|
||||
- Domain operations:
|
||||
- `createDomain(name, description)`
|
||||
- `updateDomain(oldName, newName, description)`
|
||||
- `deleteDomain(name)`
|
||||
- `selectDomain(name)` - Switch context without creating a domain
|
||||
- Permission operations:
|
||||
- `addPermission(name, description)`
|
||||
- `updatePermission(oldName, newName, description)`
|
||||
- `removePermission(name)`
|
||||
- Role operations:
|
||||
- `addRole(name, description)`
|
||||
- `updateRole(oldName, newName, description)`
|
||||
- `removeRole(name)`
|
||||
- Role-Permission relations:
|
||||
- `assignPermissionsToRole(role, permissions)`
|
||||
|
||||
The builder ensures that operations are performed in the correct context by requiring a domain to be selected or created before performing domain-specific operations.
|
||||
|
||||
The integrations will be performed automatically when your application starts up. You can implement either `getIntegrations()` or `getIntegrationsJsonPath()` - if both are implemented, `getIntegrations()` takes precedence.
|
||||
|
||||
Available integration types:
|
||||
- `DomainIntegration`: Create/update domains
|
||||
- `PermissionIntegration`: Create/update permissions within domains
|
||||
- `RoleIntegration`: Create/update roles within domains
|
||||
- `RolePermissionRelationIntegration`: Manage relationships between roles and permissions
|
||||
|
||||
Each integration requires:
|
||||
- `id`: A unique identifier for the integration
|
||||
- `action`: The operation to perform (`create`, `update`, or `delete`)
|
||||
- `data`: The entity-specific data for the integration
|
||||
|
||||
## Usage
|
||||
|
||||
### 1. Annotation-Based Permission Checks
|
||||
|
2
pom.xml
2
pom.xml
@ -8,7 +8,7 @@
|
||||
|
||||
<groupId>de.mumme-it</groupId>
|
||||
<artifactId>permission-manager-sdk</artifactId>
|
||||
<version>0.1.1</version>
|
||||
<version>0.1.2</version>
|
||||
<organization>
|
||||
<name>Mumme-IT</name>
|
||||
<url>https://mumme-it.de</url>
|
||||
|
@ -1,8 +1,10 @@
|
||||
package de.mummeit.common.config;
|
||||
|
||||
import de.mummeit.pmg.config.PermissionManagerAuthConfiguration;
|
||||
import feign.Client;
|
||||
import feign.httpclient.ApacheHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
@ -13,6 +15,7 @@ import org.springframework.context.annotation.PropertySource;
|
||||
@ComponentScan(basePackages = "de.mummeit")
|
||||
@EnableFeignClients(basePackages = "de.mummeit")
|
||||
@PropertySource(value = "classpath:permission-manager-sdk-application.yaml")
|
||||
@EnableConfigurationProperties(PermissionManagerAuthConfiguration.class)
|
||||
public class PermissionManagerSdkConfiguration {
|
||||
|
||||
@Bean
|
||||
|
@ -5,7 +5,9 @@ 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.request.ListPermittedScopesRequest;
|
||||
import de.mummeit.pmg.api.model.access.response.PermittedResponse;
|
||||
import de.mummeit.pmg.api.model.access.response.ListPermittedScopesResponse;
|
||||
import de.mummeit.pmg.api.model.integration.Integration;
|
||||
import de.mummeit.pmg.api.model.structure.Domain;
|
||||
import de.mummeit.pmg.api.model.structure.Permission;
|
||||
@ -43,6 +45,9 @@ public interface PermissionManagerClient {
|
||||
@PatchMapping("/api/v1/access/revoke/user")
|
||||
void revokeUserAccess(@RequestBody RevokeUserAccessRequest request);
|
||||
|
||||
@PostMapping("/api/v1/access/scopes")
|
||||
ListPermittedScopesResponse listPermittedScopes(@RequestBody ListPermittedScopesRequest request);
|
||||
|
||||
// Domain Management
|
||||
@PostMapping("/api/v1/domains")
|
||||
Domain createDomain(@RequestBody Domain domain);
|
||||
|
@ -1,7 +1,5 @@
|
||||
package de.mummeit.pmg.api.annotation;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
|
@ -3,7 +3,7 @@ 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 de.mummeit.pmg.exception.AccessDeniedException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
@ -11,7 +11,6 @@ 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;
|
||||
@ -22,9 +21,7 @@ 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
|
||||
|
@ -0,0 +1,10 @@
|
||||
package de.mummeit.pmg.api.model.access.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ListPermittedScopesRequest {
|
||||
private String userId;
|
||||
private String domain;
|
||||
private String permission;
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package de.mummeit.pmg.api.model.access.response;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ListPermittedScopesResponse {
|
||||
private List<String> scopes;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package de.mummeit.pmg.service.builder;
|
||||
package de.mummeit.pmg.builder;
|
||||
|
||||
import de.mummeit.pmg.api.model.integration.*;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
package de.mummeit.pmg.service.config;
|
||||
package de.mummeit.pmg.config;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@ -6,6 +6,8 @@ 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.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.io.Resource;
|
||||
@ -24,8 +26,13 @@ import java.util.List;
|
||||
@RequiredArgsConstructor
|
||||
public abstract class AbstractPermissionManagerConfiguration {
|
||||
|
||||
@Autowired
|
||||
private final PermissionManager permissionManager;
|
||||
|
||||
@Autowired
|
||||
private final ResourceLoader resourceLoader;
|
||||
|
||||
@Autowired
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,34 @@
|
||||
package de.mummeit.pmg.config;
|
||||
|
||||
import feign.RequestInterceptor;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "permission-manager")
|
||||
public class PermissionManagerAuthConfiguration {
|
||||
|
||||
private Auth auth = new Auth();
|
||||
|
||||
@Data
|
||||
public static class Auth {
|
||||
private boolean enabled = false;
|
||||
private String apiKey;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RequestInterceptor apiKeyInterceptor() {
|
||||
return requestTemplate -> {
|
||||
if (auth.isEnabled()) {
|
||||
if (!StringUtils.hasText(auth.getApiKey())) {
|
||||
throw new IllegalStateException("API key is required when authentication is enabled");
|
||||
}
|
||||
requestTemplate.header("x-api-key", auth.getApiKey());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package de.mummeit.pmg.service.exception;
|
||||
package de.mummeit.pmg.exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when access is denied for a user.
|
||||
|
@ -1,4 +1,4 @@
|
||||
package de.mummeit.pmg.service.exception;
|
||||
package de.mummeit.pmg.exception;
|
||||
|
||||
import de.mummeit.pmg.api.model.integration.Integration;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
package de.mummeit.pmg.service.exception;
|
||||
package de.mummeit.pmg.exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when a permission request is invalid.
|
||||
|
@ -1,4 +1,4 @@
|
||||
package de.mummeit.pmg.service.exception;
|
||||
package de.mummeit.pmg.exception;
|
||||
|
||||
/**
|
||||
* Base exception class for all Permission Manager related exceptions.
|
||||
|
@ -3,11 +3,11 @@ 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.access.response.ListPermittedScopesResponse;
|
||||
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 de.mummeit.pmg.exception.IntegrationFailedException;
|
||||
import de.mummeit.pmg.exception.InvalidPermissionRequestException;
|
||||
import feign.FeignException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -31,7 +31,6 @@ public class PermissionManager {
|
||||
* @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);
|
||||
@ -46,12 +45,9 @@ public class PermissionManager {
|
||||
PermittedResponse response = client.checkAccess(request);
|
||||
return response.isPermitted();
|
||||
} catch (FeignException e) {
|
||||
throw new AccessDeniedException(
|
||||
"Failed to check access",
|
||||
userId,
|
||||
domain,
|
||||
permission,
|
||||
scope,
|
||||
throw new InvalidPermissionRequestException(
|
||||
"Technical error while checking access permissions",
|
||||
"Failed to communicate with permission service: " + e.getMessage(),
|
||||
e
|
||||
);
|
||||
}
|
||||
@ -276,6 +272,35 @@ public class PermissionManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all scopes where a user has access to a specific permission in a domain.
|
||||
*
|
||||
* @param userId The ID of the user
|
||||
* @param domain The domain name
|
||||
* @param permission The permission name
|
||||
* @return List of scopes where the user has access
|
||||
* @throws InvalidPermissionRequestException if any of the required parameters are invalid
|
||||
*/
|
||||
public List<String> listPermittedScopes(String userId, String domain, String permission) {
|
||||
validateParameters(userId, domain, permission);
|
||||
|
||||
try {
|
||||
ListPermittedScopesRequest request = new ListPermittedScopesRequest();
|
||||
request.setUserId(userId);
|
||||
request.setDomain(domain);
|
||||
request.setPermission(permission);
|
||||
|
||||
ListPermittedScopesResponse response = client.listPermittedScopes(request);
|
||||
return response.getScopes();
|
||||
} catch (FeignException e) {
|
||||
throw new InvalidPermissionRequestException(
|
||||
"Failed to list permitted scopes",
|
||||
"Error occurred while listing permitted scopes",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateParameters(String userId, String domain, String permission, String scope) {
|
||||
validateUserId(userId);
|
||||
validateDomain(domain);
|
||||
|
@ -10,5 +10,9 @@ spring:
|
||||
permission-manager:
|
||||
connect-timeout: 1000
|
||||
read-timeout: 2000
|
||||
|
||||
permission-manager:
|
||||
url: http://localhost:6060
|
||||
url: http://localhost:6060
|
||||
auth:
|
||||
enabled: false
|
||||
api-key:
|
@ -1,14 +1,10 @@
|
||||
package de.mummeit.pmg.api;
|
||||
|
||||
import de.mummeit.pmg.api.model.access.request.CheckAccessRequest;
|
||||
import de.mummeit.pmg.api.model.access.request.Permit;
|
||||
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.request.*;
|
||||
import de.mummeit.pmg.api.model.access.response.PermittedResponse;
|
||||
import de.mummeit.pmg.api.model.integration.DomainIntegration;
|
||||
import de.mummeit.pmg.api.model.access.response.ListPermittedScopesResponse;
|
||||
import de.mummeit.pmg.api.model.integration.Integration;
|
||||
import de.mummeit.pmg.api.model.integration.DomainIntegration;
|
||||
import de.mummeit.pmg.api.model.integration.PermissionIntegration;
|
||||
import de.mummeit.pmg.api.model.integration.RoleIntegration;
|
||||
import de.mummeit.pmg.api.model.integration.RolePermissionRelationIntegration;
|
||||
@ -16,6 +12,7 @@ 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 de.mummeit.utility.BaseIntegrationTest;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -35,6 +32,15 @@ class PermissionManagerClientIntegrationTest extends BaseIntegrationTest {
|
||||
private static final String TEST_USER = "test-user";
|
||||
private static final String TEST_SCOPE = "test-scope";
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
try {
|
||||
permissionManagerClient.deleteDomain(TEST_DOMAIN);
|
||||
} catch (Exception e) {
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should return health status")
|
||||
void getHealthStatus() {
|
||||
@ -251,4 +257,62 @@ class PermissionManagerClientIntegrationTest extends BaseIntegrationTest {
|
||||
// Clean up
|
||||
permissionManagerClient.deleteDomain(TEST_DOMAIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should list permitted scopes successfully")
|
||||
void listPermittedScopes() {
|
||||
// Setup: Create domain, permission, and role
|
||||
Domain domain = new Domain();
|
||||
domain.setName(TEST_DOMAIN);
|
||||
permissionManagerClient.createDomain(domain);
|
||||
|
||||
Permission permission = new Permission();
|
||||
permission.setName(TEST_PERMISSION);
|
||||
permissionManagerClient.createPermission(TEST_DOMAIN, permission);
|
||||
|
||||
Role role = new Role();
|
||||
role.setName(TEST_ROLE);
|
||||
role.setPermissions(List.of());
|
||||
permissionManagerClient.createRole(TEST_DOMAIN, role);
|
||||
|
||||
// Grant access in multiple scopes
|
||||
PermitRequest permitRequest1 = new PermitRequest();
|
||||
Permit permit1 = new Permit();
|
||||
permit1.setDomain(TEST_DOMAIN);
|
||||
permit1.setRoles(List.of(TEST_ROLE));
|
||||
permit1.setPermissions(List.of(TEST_PERMISSION));
|
||||
permitRequest1.setPermits(List.of(permit1));
|
||||
permitRequest1.setUserId(TEST_USER);
|
||||
permitRequest1.setScope(TEST_SCOPE);
|
||||
permissionManagerClient.permitAccess(permitRequest1);
|
||||
|
||||
String secondScope = TEST_SCOPE + "-2";
|
||||
PermitRequest permitRequest2 = new PermitRequest();
|
||||
Permit permit2 = new Permit();
|
||||
permit2.setDomain(TEST_DOMAIN);
|
||||
permit2.setRoles(List.of(TEST_ROLE));
|
||||
permit2.setPermissions(List.of(TEST_PERMISSION));
|
||||
permitRequest2.setPermits(List.of(permit2));
|
||||
permitRequest2.setUserId(TEST_USER);
|
||||
permitRequest2.setScope(secondScope);
|
||||
permissionManagerClient.permitAccess(permitRequest2);
|
||||
|
||||
// Test listing permitted scopes
|
||||
ListPermittedScopesRequest request = new ListPermittedScopesRequest();
|
||||
request.setUserId(TEST_USER);
|
||||
request.setDomain(TEST_DOMAIN);
|
||||
request.setPermission(TEST_PERMISSION);
|
||||
|
||||
ListPermittedScopesResponse response = permissionManagerClient.listPermittedScopes(request);
|
||||
assertNotNull(response);
|
||||
assertNotNull(response.getScopes());
|
||||
assertEquals(2, response.getScopes().size());
|
||||
assertTrue(response.getScopes().contains(TEST_SCOPE));
|
||||
assertTrue(response.getScopes().contains(secondScope));
|
||||
|
||||
// Clean up
|
||||
permissionManagerClient.deleteRole(TEST_DOMAIN, TEST_ROLE);
|
||||
permissionManagerClient.deletePermission(TEST_DOMAIN, TEST_PERMISSION);
|
||||
permissionManagerClient.deleteDomain(TEST_DOMAIN);
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ import de.mummeit.pmg.api.config.TestSecurityConfig;
|
||||
import de.mummeit.pmg.api.model.access.request.CheckAccessRequest;
|
||||
import de.mummeit.pmg.api.model.access.response.PermittedResponse;
|
||||
import de.mummeit.pmg.api.service.SecurityService;
|
||||
import de.mummeit.pmg.service.exception.AccessDeniedException;
|
||||
import de.mummeit.pmg.exception.AccessDeniedException;
|
||||
import de.mummeit.utility.BaseIntegrationTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package de.mummeit.pmg.api.controller;
|
||||
|
||||
import de.mummeit.pmg.service.exception.AccessDeniedException;
|
||||
import de.mummeit.pmg.exception.AccessDeniedException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
|
@ -3,11 +3,12 @@ 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.access.response.ListPermittedScopesResponse;
|
||||
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 de.mummeit.pmg.exception.AccessDeniedException;
|
||||
import de.mummeit.pmg.exception.IntegrationFailedException;
|
||||
import de.mummeit.pmg.exception.InvalidPermissionRequestException;
|
||||
import feign.FeignException;
|
||||
import feign.Request;
|
||||
import feign.RequestTemplate;
|
||||
@ -25,6 +26,7 @@ import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@ -60,12 +62,25 @@ class PermissionManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("hasAccess should throw AccessDeniedException when client throws FeignException")
|
||||
void hasAccessShouldThrowAccessDeniedExceptionWhenClientThrowsFeignException() {
|
||||
@DisplayName("hasAccess should return false when access is denied")
|
||||
void hasAccessShouldReturnFalseWhenAccessIsDenied() {
|
||||
PermittedResponse response = new PermittedResponse();
|
||||
response.setPermitted(false);
|
||||
when(client.checkAccess(any(CheckAccessRequest.class))).thenReturn(response);
|
||||
|
||||
boolean hasAccess = permissionManager.hasAccess(TEST_USER, TEST_DOMAIN, TEST_PERMISSION, TEST_SCOPE);
|
||||
assertFalse(hasAccess);
|
||||
|
||||
verify(client).checkAccess(any(CheckAccessRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("hasAccess should throw InvalidPermissionRequestException when client throws FeignException")
|
||||
void hasAccessShouldThrowInvalidPermissionRequestExceptionWhenClientThrowsFeignException() {
|
||||
FeignException feignException = new FeignException.NotFound("Not Found", createRequest(), null, null);
|
||||
when(client.checkAccess(any(CheckAccessRequest.class))).thenThrow(feignException);
|
||||
|
||||
assertThrows(AccessDeniedException.class, () ->
|
||||
assertThrows(InvalidPermissionRequestException.class, () ->
|
||||
permissionManager.hasAccess(TEST_USER, TEST_DOMAIN, TEST_PERMISSION, TEST_SCOPE)
|
||||
);
|
||||
}
|
||||
@ -254,6 +269,62 @@ class PermissionManagerTest {
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("listPermittedScopes should return scopes from client")
|
||||
void listPermittedScopesShouldReturnScopesFromClient() {
|
||||
ListPermittedScopesResponse response = new ListPermittedScopesResponse();
|
||||
response.setScopes(Arrays.asList("scope1", "scope2"));
|
||||
when(client.listPermittedScopes(any(ListPermittedScopesRequest.class))).thenReturn(response);
|
||||
|
||||
List<String> scopes = permissionManager.listPermittedScopes(TEST_USER, TEST_DOMAIN, TEST_PERMISSION);
|
||||
|
||||
assertEquals(Arrays.asList("scope1", "scope2"), scopes);
|
||||
verify(client).listPermittedScopes(argThat(request ->
|
||||
TEST_USER.equals(request.getUserId()) &&
|
||||
TEST_DOMAIN.equals(request.getDomain()) &&
|
||||
TEST_PERMISSION.equals(request.getPermission())
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("listPermittedScopes should throw InvalidPermissionRequestException when client throws FeignException")
|
||||
void listPermittedScopesShouldThrowInvalidPermissionRequestExceptionWhenClientThrowsFeignException() {
|
||||
FeignException feignException = new FeignException.InternalServerError("Internal Server Error", createRequest(), null, null);
|
||||
when(client.listPermittedScopes(any(ListPermittedScopesRequest.class))).thenThrow(feignException);
|
||||
|
||||
assertThrows(InvalidPermissionRequestException.class, () ->
|
||||
permissionManager.listPermittedScopes(TEST_USER, TEST_DOMAIN, TEST_PERMISSION)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("listPermittedScopes should throw InvalidPermissionRequestException when parameters are invalid")
|
||||
void listPermittedScopesShouldThrowInvalidPermissionRequestExceptionWhenParametersAreInvalid() {
|
||||
assertThrows(InvalidPermissionRequestException.class, () ->
|
||||
permissionManager.listPermittedScopes(null, TEST_DOMAIN, TEST_PERMISSION)
|
||||
);
|
||||
|
||||
assertThrows(InvalidPermissionRequestException.class, () ->
|
||||
permissionManager.listPermittedScopes("", TEST_DOMAIN, TEST_PERMISSION)
|
||||
);
|
||||
|
||||
assertThrows(InvalidPermissionRequestException.class, () ->
|
||||
permissionManager.listPermittedScopes(TEST_USER, null, TEST_PERMISSION)
|
||||
);
|
||||
|
||||
assertThrows(InvalidPermissionRequestException.class, () ->
|
||||
permissionManager.listPermittedScopes(TEST_USER, "", TEST_PERMISSION)
|
||||
);
|
||||
|
||||
assertThrows(InvalidPermissionRequestException.class, () ->
|
||||
permissionManager.listPermittedScopes(TEST_USER, TEST_DOMAIN, null)
|
||||
);
|
||||
|
||||
assertThrows(InvalidPermissionRequestException.class, () ->
|
||||
permissionManager.listPermittedScopes(TEST_USER, TEST_DOMAIN, "")
|
||||
);
|
||||
}
|
||||
|
||||
private Request createRequest() {
|
||||
return Request.create(Request.HttpMethod.GET, "url", new HashMap<>(), null, new RequestTemplate());
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package de.mummeit.pmg.service.builder;
|
||||
|
||||
import de.mummeit.pmg.api.model.integration.*;
|
||||
import de.mummeit.pmg.builder.IntegrationBuilder;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import de.mummeit.pmg.api.model.integration.DomainIntegration;
|
||||
import de.mummeit.pmg.api.model.integration.Integration;
|
||||
import de.mummeit.pmg.config.AbstractPermissionManagerConfiguration;
|
||||
import de.mummeit.pmg.service.PermissionManager;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
|
@ -1,18 +1,19 @@
|
||||
package de.mummeit.utility;
|
||||
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.springframework.test.context.DynamicPropertyRegistry;
|
||||
import org.springframework.test.context.DynamicPropertySource;
|
||||
import org.testcontainers.containers.Network;
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
|
||||
public abstract class TestContainer {
|
||||
private static final String DB_DATABASE = "test";
|
||||
private static final String DB_USER = "postgres";
|
||||
private static final String DB_PASSWORD = "super";
|
||||
private static final String TEST_API_KEY = "test-api-key-123";
|
||||
|
||||
public static Network network = Network.newNetwork();
|
||||
|
||||
|
||||
public static GenericContainer<?> postgresContainer = new PostgreSQLContainer("postgres:latest")
|
||||
.withDatabaseName(DB_DATABASE)
|
||||
.withUsername(DB_USER)
|
||||
@ -27,15 +28,21 @@ public abstract class TestContainer {
|
||||
.withEnv("DB_PASSWORD", DB_PASSWORD)
|
||||
.withEnv("DB_HOST", "testcontainer-db")
|
||||
.withEnv("DB_PORT", "5432")
|
||||
.withEnv("AUTH_ENABLED", "true")
|
||||
.withEnv("AUTH_APIKEY", TEST_API_KEY)
|
||||
.withNetwork(network)
|
||||
.withNetworkAliases("permission-manager");
|
||||
|
||||
|
||||
static {
|
||||
|
||||
postgresContainer.start();
|
||||
|
||||
permissionManagerContainer.start();
|
||||
System.setProperty("permission-manager.url", "http://localhost:" + permissionManagerContainer.getFirstMappedPort());
|
||||
}
|
||||
|
||||
@DynamicPropertySource
|
||||
static void registerProperties(DynamicPropertyRegistry registry) {
|
||||
registry.add("permission-manager.url",
|
||||
() -> String.format("http://localhost:%d", permissionManagerContainer.getFirstMappedPort()));
|
||||
registry.add("permission-manager.auth.enabled", () -> "true");
|
||||
registry.add("permission-manager.auth.api-key", () -> TEST_API_KEY);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user