Skip to content

Commit edd49f2

Browse files
committed
initial commit
1 parent c3e9dac commit edd49f2

File tree

21 files changed

+2489
-8
lines changed

21 files changed

+2489
-8
lines changed

.azure.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
SENTRIUS_VERSION=1.1.113
1+
SENTRIUS_VERSION=1.1.120
22
SENTRIUS_SSH_VERSION=1.1.12
33
SENTRIUS_KEYCLOAK_VERSION=1.1.15
44
SENTRIUS_AGENT_VERSION=1.1.24

agent-launcher/src/main/java/io/sentrius/agent/launcher/service/PodLauncherService.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ public V1Pod launchAgentPod(AgentRegistrationDTO agent) throws Exception {
199199
case "atpl-helper":
200200
agentFile = "chat-atpl-helper.yaml";
201201
break;
202+
case "abac":
203+
agentFile = "abac-helper.yaml";
204+
break;
202205
case "default":
203206
default:
204207
agentFile = "chat-helper.yaml";
Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
package io.sentrius.sso.controllers.api.abac;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.google.common.collect.Maps;
5+
import io.sentrius.sso.config.ApiPaths;
6+
import io.sentrius.sso.core.annotations.LimitAccess;
7+
import io.sentrius.sso.core.config.SystemOptions;
8+
import io.sentrius.sso.core.controllers.BaseController;
9+
import io.sentrius.sso.core.dto.AgentRegistrationDTO;
10+
import io.sentrius.sso.core.dto.UserDTO;
11+
import io.sentrius.sso.core.dto.agents.AgentExecution;
12+
import io.sentrius.sso.core.dto.ztat.TokenDTO;
13+
import io.sentrius.sso.core.exceptions.ZtatException;
14+
import io.sentrius.sso.core.model.security.enums.ApplicationAccessEnum;
15+
import io.sentrius.sso.core.model.users.User;
16+
import io.sentrius.sso.core.services.ErrorOutputService;
17+
import io.sentrius.sso.core.services.UserService;
18+
import io.sentrius.sso.core.services.agents.AgentClientService;
19+
import io.sentrius.sso.core.services.agents.ZeroTrustClientService;
20+
import io.sentrius.sso.core.services.security.KeycloakService;
21+
import io.sentrius.sso.core.utils.JsonUtil;
22+
import jakarta.servlet.http.HttpServletRequest;
23+
import jakarta.servlet.http.HttpServletResponse;
24+
import lombok.extern.slf4j.Slf4j;
25+
import org.springframework.beans.factory.annotation.Value;
26+
import org.springframework.http.ResponseEntity;
27+
import org.springframework.web.bind.annotation.*;
28+
29+
import java.util.HashMap;
30+
import java.util.List;
31+
import java.util.Map;
32+
import java.util.UUID;
33+
34+
/**
35+
* Controller for ABAC agent management.
36+
* Provides endpoints for launching, monitoring, and managing the ABAC agent.
37+
*/
38+
@Slf4j
39+
@RestController
40+
@RequestMapping(ApiPaths.API_V1 + "/abac/agent")
41+
public class AbacAgentController extends BaseController {
42+
43+
private final KeycloakService keycloakService;
44+
private final AgentClientService agentClientService;
45+
private final ZeroTrustClientService zeroTrustClientService;
46+
47+
@Value("${sentrius.tenant:dev}")
48+
private String agentNamespace;
49+
50+
@Value("${sentrius.abac.agent.enabled:true}")
51+
private boolean abacAgentEnabled;
52+
53+
54+
private final SystemOptions systemOptions;
55+
56+
public AbacAgentController(
57+
UserService userService,
58+
SystemOptions systemOptions,
59+
ErrorOutputService errorOutputService,
60+
KeycloakService keycloakService,
61+
AgentClientService agentClientService,
62+
ZeroTrustClientService zeroTrustClientService) {
63+
super(userService, systemOptions, errorOutputService);
64+
this.keycloakService = keycloakService;
65+
this.agentClientService = agentClientService;
66+
this.zeroTrustClientService = zeroTrustClientService;
67+
this.systemOptions = systemOptions;
68+
if (agentNamespace != null && !agentNamespace.isEmpty()) {
69+
agentNamespace = agentNamespace + "-agents";
70+
} else {
71+
agentNamespace = systemOptions.getAgentNamespace();
72+
}
73+
}
74+
75+
/**
76+
* Launch the ABAC agent pod.
77+
* Creates a new agent with the "abac" type and appropriate configuration.
78+
* Uses ZeroTrustClientService for secure agent launcher communication.
79+
*/
80+
@PostMapping("/launch")
81+
@LimitAccess(applicationAccess = {ApplicationAccessEnum.CAN_MANAGE_APPLICATION})
82+
public ResponseEntity<Map<String, Object>> launchAbacAgent(
83+
HttpServletRequest request,
84+
HttpServletResponse response) {
85+
86+
if (!abacAgentEnabled) {
87+
return ResponseEntity.ok(Map.of(
88+
"status", "disabled",
89+
"message", "ABAC agent is disabled in configuration. Set sentrius.abac.agent.enabled=true to enable."
90+
));
91+
}
92+
93+
try {
94+
// Get the current user
95+
User user = getOperatingUser(request, response);
96+
if (user == null) {
97+
log.warn("No operating user found for agent launch");
98+
return ResponseEntity.status(401).body(Map.of(
99+
"status", "error",
100+
"message", "No authenticated user found"
101+
));
102+
}
103+
104+
// Create AgentExecution for zero trust communication
105+
UserDTO userDTO = UserDTO.builder()
106+
.username(user.getUsername())
107+
.build();
108+
109+
AgentExecution execution = AgentExecution.builder()
110+
.user(userDTO)
111+
.ztatToken(keycloakService.getJwtToken())
112+
.build();
113+
114+
// Generate unique agent name
115+
String agentName = "abac-evaluator-" + UUID.randomUUID().toString().substring(0, 8);
116+
117+
// Create agent registration using builder
118+
AgentRegistrationDTO agent = AgentRegistrationDTO.builder()
119+
.agentName(agentName)
120+
.agentType("abac")
121+
.clientId("service-account-" + agentName)
122+
.agentPolicyId("abac-agent-policy")
123+
.build();
124+
125+
// Use AgentClientService to launch the agent via zero trust channel
126+
String launcherResponse = agentClientService.createAgent(execution, agent);
127+
128+
log.info("Agent launcher response: {}", launcherResponse);
129+
130+
Map<String, Object> result = new HashMap<>();
131+
result.put("status", "success");
132+
result.put("agentName", agentName);
133+
result.put("agentType", "abac");
134+
result.put("namespace", agentNamespace);
135+
result.put("message", "ABAC agent launched successfully");
136+
return ResponseEntity.ok(result);
137+
138+
} catch (ZtatException e) {
139+
log.error("Zero trust error launching ABAC agent", e);
140+
return ResponseEntity.status(500).body(Map.of(
141+
"status", "error",
142+
"message", "Zero trust authentication failed: " + e.getMessage()
143+
));
144+
} catch (JsonProcessingException e) {
145+
log.error("JSON processing error launching ABAC agent", e);
146+
return ResponseEntity.status(500).body(Map.of(
147+
"status", "error",
148+
"message", "Failed to process response: " + e.getMessage()
149+
));
150+
} catch (Exception e) {
151+
log.error("Error launching ABAC agent", e);
152+
return ResponseEntity.status(500).body(Map.of(
153+
"status", "error",
154+
"message", "Failed to launch ABAC agent: " + e.getMessage()
155+
));
156+
}
157+
}
158+
159+
/**
160+
* Check the status of the ABAC agent.
161+
* Queries the agent launcher to determine if the agent is running via ZeroTrustClientService.
162+
*/
163+
@GetMapping("/status")
164+
@LimitAccess(applicationAccess = {ApplicationAccessEnum.CAN_MANAGE_APPLICATION})
165+
public ResponseEntity<Map<String, Object>> getAbacAgentStatus(
166+
@RequestParam(name = "agentId", required = false) String agentId,
167+
HttpServletRequest request,
168+
HttpServletResponse response) {
169+
170+
if (!abacAgentEnabled) {
171+
return ResponseEntity.ok(Map.of(
172+
"status", "disabled",
173+
"enabled", false,
174+
"message", "ABAC agent is disabled in configuration"
175+
));
176+
}
177+
178+
try {
179+
// If no agentId provided, return the enabled status
180+
if (agentId == null || agentId.isEmpty()) {
181+
return ResponseEntity.ok(Map.of(
182+
"status", "enabled",
183+
"enabled", true,
184+
"message", "ABAC agent is enabled"
185+
));
186+
}
187+
188+
// Get the current user for zero trust communication
189+
User user = getOperatingUser(request, response);
190+
if (user == null) {
191+
log.warn("No operating user found for agent status check");
192+
return ResponseEntity.ok(Map.of(
193+
"status", "error",
194+
"enabled", abacAgentEnabled,
195+
"message", "No authenticated user found"
196+
));
197+
}
198+
199+
// Create AgentExecution for zero trust communication
200+
UserDTO userDTO = UserDTO.builder()
201+
.username(user.getUsername())
202+
.build();
203+
204+
TokenDTO token = TokenDTO.builder().communicationId(UUID.randomUUID().toString()).build();
205+
// Query the agent launcher for status via zero trust channel
206+
String statusResponse = agentClientService.getCreatedAgentStatus(token, agentId);
207+
208+
if (statusResponse != null) {
209+
@SuppressWarnings("unchecked")
210+
Map<String, Object> result = JsonUtil.MAPPER.readValue(statusResponse, Map.class);
211+
result.put("enabled", abacAgentEnabled);
212+
return ResponseEntity.ok(result);
213+
} else {
214+
return ResponseEntity.ok(Map.of(
215+
"status", "unknown",
216+
"enabled", true,
217+
"message", "Unable to retrieve agent status"
218+
));
219+
}
220+
221+
} catch (ZtatException e) {
222+
log.error("Zero trust error checking ABAC agent status", e);
223+
return ResponseEntity.ok(Map.of(
224+
"status", "error",
225+
"enabled", abacAgentEnabled,
226+
"message", "Zero trust authentication failed: " + e.getMessage()
227+
));
228+
} catch (JsonProcessingException e) {
229+
log.error("JSON processing error checking ABAC agent status", e);
230+
return ResponseEntity.ok(Map.of(
231+
"status", "error",
232+
"enabled", abacAgentEnabled,
233+
"message", "Failed to process response: " + e.getMessage()
234+
));
235+
} catch (Exception e) {
236+
log.error("Error checking ABAC agent status", e);
237+
return ResponseEntity.ok(Map.of(
238+
"status", "error",
239+
"enabled", abacAgentEnabled,
240+
"message", "Error checking agent status: " + e.getMessage()
241+
));
242+
}
243+
}
244+
245+
/**
246+
* Shutdown the ABAC agent.
247+
* Stops the running ABAC agent pod via ZeroTrustClientService.
248+
*/
249+
@DeleteMapping("/shutdown")
250+
@LimitAccess(applicationAccess = {ApplicationAccessEnum.CAN_MANAGE_APPLICATION})
251+
public ResponseEntity<Map<String, Object>> shutdownAbacAgent(
252+
@RequestParam(name = "agentId") String agentId,
253+
HttpServletRequest request,
254+
HttpServletResponse response) {
255+
256+
try {
257+
// Get the current user for zero trust communication
258+
User user = getOperatingUser(request, response);
259+
if (user == null) {
260+
log.warn("No operating user found for agent shutdown");
261+
return ResponseEntity.status(401).body(Map.of(
262+
"status", "error",
263+
"message", "No authenticated user found"
264+
));
265+
}
266+
267+
// Use ZeroTrustClientService to shutdown the agent via zero trust channel
268+
// Note: Using callAuthenticated*OnApi methods which handle authentication internally
269+
zeroTrustClientService.callAuthenticatedGetOnApi(
270+
"agent-launcher-service", // This should match your launcher service name
271+
"agent/bootstrap/launcher/kill",
272+
Maps.immutableEntry("agentId", List.of(agentId))
273+
);
274+
275+
log.info("ABAC agent {} shutdown successfully", agentId);
276+
277+
return ResponseEntity.ok(Map.of(
278+
"status", "success",
279+
"message", "ABAC agent shutdown successfully"
280+
));
281+
282+
} catch (ZtatException e) {
283+
log.error("Zero trust error shutting down ABAC agent", e);
284+
return ResponseEntity.status(500).body(Map.of(
285+
"status", "error",
286+
"message", "Zero trust authentication failed: " + e.getMessage()
287+
));
288+
} catch (Exception e) {
289+
log.error("Error shutting down ABAC agent", e);
290+
return ResponseEntity.status(500).body(Map.of(
291+
"status", "error",
292+
"message", "Error shutting down ABAC agent: " + e.getMessage()
293+
));
294+
}
295+
}
296+
297+
/**
298+
* Get the current configuration for the ABAC agent.
299+
*/
300+
@GetMapping("/config")
301+
@LimitAccess(applicationAccess = {ApplicationAccessEnum.CAN_MANAGE_APPLICATION})
302+
public ResponseEntity<Map<String, Object>> getAbacAgentConfig() {
303+
Map<String, Object> config = new HashMap<>();
304+
config.put("enabled", abacAgentEnabled);
305+
config.put("namespace", agentNamespace);
306+
config.put("agentType", "abac");
307+
config.put("description", "ABAC agent for evaluating and managing user attribute access");
308+
return ResponseEntity.ok(config);
309+
}
310+
311+
/**
312+
* Health check endpoint for the ABAC agent service.
313+
* Can be called periodically from the API pod to ensure the service is available.
314+
*/
315+
@GetMapping("/health")
316+
public ResponseEntity<Map<String, Object>> healthCheck() {
317+
Map<String, Object> health = new HashMap<>();
318+
health.put("status", "healthy");
319+
health.put("service", "abac-agent-controller");
320+
health.put("enabled", abacAgentEnabled);
321+
health.put("timestamp", System.currentTimeMillis());
322+
return ResponseEntity.ok(health);
323+
}
324+
}

api/src/main/java/io/sentrius/sso/controllers/api/abac/AccessPolicyController.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package io.sentrius.sso.controllers.api.abac;
22

33
import io.sentrius.sso.core.annotations.LimitAccess;
4+
import io.sentrius.sso.core.config.SystemOptions;
5+
import io.sentrius.sso.core.controllers.BaseController;
46
import io.sentrius.sso.core.dto.abac.AccessPolicyDTO;
57
import io.sentrius.sso.core.dto.abac.PolicyRuleDTO;
68
import io.sentrius.sso.core.model.abac.AccessPolicy;
@@ -10,6 +12,8 @@
1012
import io.sentrius.sso.core.repository.abac.AccessPolicyRepository;
1113
import io.sentrius.sso.core.repository.abac.AttributeDefinitionRepository;
1214
import io.sentrius.sso.core.repository.abac.PolicyRuleRepository;
15+
import io.sentrius.sso.core.services.ErrorOutputService;
16+
import io.sentrius.sso.core.services.UserService;
1317
import io.sentrius.sso.core.services.abac.CustomAttributeMigrationService;
1418
import jakarta.servlet.http.HttpServletRequest;
1519
import jakarta.servlet.http.HttpServletResponse;
@@ -29,7 +33,7 @@
2933
@Slf4j
3034
@RestController
3135
@RequestMapping("/api/v1/abac/policies")
32-
public class AccessPolicyController {
36+
public class AccessPolicyController extends BaseController {
3337

3438
private static final boolean DEFAULT_IS_NEGATED = false;
3539
private static final int DEFAULT_EVALUATION_ORDER = 0;
@@ -40,10 +44,14 @@ public class AccessPolicyController {
4044
private final CustomAttributeMigrationService migrationService;
4145

4246
public AccessPolicyController(
47+
UserService userService,
48+
SystemOptions systemOptions,
49+
ErrorOutputService errorOutputService,
4350
AccessPolicyRepository policyRepository,
4451
PolicyRuleRepository ruleRepository,
4552
AttributeDefinitionRepository attributeDefinitionRepository,
4653
CustomAttributeMigrationService migrationService) {
54+
super(userService, systemOptions, errorOutputService);
4755
this.policyRepository = policyRepository;
4856
this.ruleRepository = ruleRepository;
4957
this.attributeDefinitionRepository = attributeDefinitionRepository;

0 commit comments

Comments
 (0)