Skip to content

Commit 764382e

Browse files
committed
update abac
1 parent 53efc47 commit 764382e

28 files changed

+3420
-175
lines changed

.azure.env

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
SENTRIUS_VERSION=1.1.75
1+
SENTRIUS_VERSION=1.1.100
22
SENTRIUS_SSH_VERSION=1.1.11
33
SENTRIUS_KEYCLOAK_VERSION=1.1.14
44
SENTRIUS_AGENT_VERSION=1.1.23
55
SENTRIUS_AI_AGENT_VERSION=1.1.4
66
LLMPROXY_VERSION=1.1.4
77
LAUNCHER_VERSION=1.1.4
8-
AGENTPROXY_VERSION=1.1.4
8+
AGENTPROXY_VERSION=1.1.5
99
SSHPROXY_VERSION=1.1.4
1010
RDPPROXY_VERSION=1.1.4
1111
GITHUB_MCP_VERSION=1.1.4

api/src/main/java/io/sentrius/sso/config/GlobalExceptionHandler.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,12 @@ public ResponseEntity<String> handleAllExceptions(Throwable ex, RedirectAttribut
4848
if (responseStatusException.getStatusCode() == HttpStatus.PRECONDITION_REQUIRED ) {
4949
// Handle precondition required
5050
return ResponseEntity.status(428).body(responseStatusException.getReason());
51+
} else if (responseStatusException.getStatusCode() == HttpStatus.FORBIDDEN ) {
52+
// Handle forbidden
53+
return ResponseEntity.status(403).body(responseStatusException.getReason());
5154
}
5255
}
5356
ex.printStackTrace();
54-
log.info("ahhasldigjudaslkgj {}", ex.getMessage());
5557
log.error("asldkjgadlskgj " + ex.getCause(), ex);
5658
String message = "Received Error Message: " + ex.getCause();
5759
ErrorOutput errorOutput = ErrorOutput.builder()

api/src/main/java/io/sentrius/sso/controllers/CustomErrorHandler.java

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -57,35 +57,4 @@ public Object handleError(HttpServletRequest request, HttpServletResponse respon
5757
model.addAttribute("errorId", MessagingUtil.getMessageId(MessagingUtil.UNEXPECTED_ERROR));
5858
return "redirect:/sso/v1/dashboard?errorId=" + MessagingUtil.getMessageId(MessagingUtil.UNEXPECTED_ERROR);
5959
}
60-
/*
61-
@RequestMapping("/error")
62-
public String handleError(HttpServletRequest request, Model model) {
63-
// Retrieve error details
64-
log.info("errror");
65-
Integer statusCode = (Integer) request.getAttribute("jakarta.servlet.error.status_code");
66-
Throwable ex = (Throwable) request.getAttribute("jakarta.servlet.error.exception");
67-
68-
// Log error details (optional)
69-
if (ex != null) {
70-
ex.printStackTrace();
71-
for(StackTraceElement element : ex.getStackTrace()) {
72-
log.info(element.toString());
73-
}
74-
String message = "Received Error Message: " + ex.getCause();
75-
ErrorOutput errorOutput = ErrorOutput.builder()
76-
.errorType(ex.getClass().getName())
77-
.errorLocation(ex.getStackTrace()[0].toString())
78-
.errorHash(createErrorHash(ex.getStackTrace(), ex.getMessage()))
79-
.errorLogs(message)
80-
.logTm(new java.sql.Timestamp(System.currentTimeMillis()))
81-
.build();
82-
errorOutputService.saveErrorOutput(errorOutput);
83-
84-
}
85-
86-
model.addAttribute("errorId", MessagingUtil.getMessageId(MessagingUtil.UNEXPECTED_ERROR));
87-
88-
// Redirect to "/mydashboard" with the messageId parameter
89-
return "redirect:/sso/v1/dashboard?errorId=" + MessagingUtil.getMessageId(MessagingUtil.UNEXPECTED_ERROR);
90-
}*/
9160
}

api/src/main/java/io/sentrius/sso/controllers/api/ATPLPolicyController.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ protected ATPLPolicyController(
4848
}
4949

5050
@PostMapping(consumes = {"application/x-yaml", "application/yaml", "text/yaml", "application/json"})
51+
@LimitAccess(applicationAccess = {ApplicationAccessEnum.CAN_MANAGE_APPLICATION})
5152
public ResponseEntity<?> uploadPolicy(
5253
@RequestParam (name = "includeDefault" , required = false, defaultValue = "false") boolean includeDefault,
5354
@RequestBody String rawPolicy) {
@@ -285,6 +286,7 @@ public ResponseEntity<?> getPolicyTemplates() {
285286

286287
@DeleteMapping("/delete")
287288
@ResponseBody
289+
@LimitAccess(applicationAccess = {ApplicationAccessEnum.CAN_MANAGE_APPLICATION})
288290
public ResponseEntity<?> deletePolicy(@RequestParam String id) {
289291
boolean deleted = policyService.deletePolicyById(id);
290292
return deleted ? ResponseEntity.ok().build() : ResponseEntity.status(HttpStatus.NOT_FOUND).build();
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
package io.sentrius.sso.controllers.api;
2+
3+
import io.sentrius.sso.core.annotations.LimitAccess;
4+
import io.sentrius.sso.core.config.SystemOptions;
5+
import io.sentrius.sso.core.data.attributes.UIResourceConfig;
6+
import io.sentrius.sso.core.data.attributes.UIResourceMappings;
7+
import io.sentrius.sso.core.model.security.enums.ApplicationAccessEnum;
8+
import io.sentrius.sso.core.model.users.User;
9+
import io.sentrius.sso.core.services.UserService;
10+
import io.sentrius.sso.core.services.abac.EvaluationContext;
11+
import io.sentrius.sso.core.services.abac.PolicyDecision;
12+
import io.sentrius.sso.core.services.abac.PolicyEvaluator;
13+
import jakarta.servlet.http.HttpServletRequest;
14+
import jakarta.servlet.http.HttpServletResponse;
15+
import lombok.extern.slf4j.Slf4j;
16+
import org.springframework.beans.factory.annotation.Autowired;
17+
import org.springframework.context.annotation.Lazy;
18+
import org.springframework.http.ResponseEntity;
19+
import org.springframework.web.bind.annotation.*;
20+
21+
import java.util.*;
22+
23+
/**
24+
* REST API Controller for UI Permission Checks
25+
* Provides endpoints to check user's UI access permissions based on ABAC policies
26+
*/
27+
@Slf4j
28+
@RestController
29+
@RequestMapping("/api/v1/ui/permissions")
30+
public class UIPermissionsApiController {
31+
32+
private final UserService userService;
33+
private final SystemOptions systemOptions;
34+
35+
@Lazy
36+
@Autowired(required = false)
37+
private PolicyEvaluator policyEvaluator;
38+
39+
public UIPermissionsApiController(
40+
UserService userService,
41+
SystemOptions systemOptions) {
42+
this.userService = userService;
43+
this.systemOptions = systemOptions;
44+
}
45+
46+
/**
47+
* Get UI permissions for the current user.
48+
* Returns a map of UI resource identifiers to permission status.
49+
*/
50+
@GetMapping
51+
@LimitAccess(applicationAccess = {ApplicationAccessEnum.CAN_LOG_IN})
52+
public ResponseEntity<UIPermissionsResponse> getUserUIPermissions(
53+
HttpServletRequest request,
54+
HttpServletResponse response) {
55+
56+
try {
57+
User operatingUser = userService.getOperatingUser(request, response, null);
58+
if (operatingUser == null) {
59+
return ResponseEntity.status(401).build();
60+
}
61+
62+
log.debug("Fetching UI permissions for user: {}", operatingUser.getUsername());
63+
64+
// Get standard access set permissions
65+
Set<String> accessSet = operatingUser.getAuthorizationType().getAccessSet();
66+
67+
// Initialize permissions map with standard menu items
68+
Map<String, Boolean> permissions = new HashMap<>();
69+
70+
// Check if ABAC UI control is enabled
71+
boolean abacEnabled = systemOptions.getEnableAbacUiControl() != null &&
72+
systemOptions.getEnableAbacUiControl();
73+
74+
// Define UI resource mappings (menu items to their access requirements)
75+
Map<String, UIResourceConfig> uiResources = UIResourceMappings.getUIResourceMappings();
76+
77+
for (Map.Entry<String, UIResourceConfig> entry : uiResources.entrySet()) {
78+
String resourceKey = entry.getKey();
79+
UIResourceConfig config = entry.getValue();
80+
81+
boolean hasAccess = false;
82+
83+
// First check standard access set permissions
84+
if (config.getRequiredAccess() != null) {
85+
hasAccess = accessSet.contains(config.getRequiredAccess());
86+
}
87+
88+
// If ABAC is enabled and user doesn't have standard access, check ABAC policies
89+
if (abacEnabled && !hasAccess && policyEvaluator != null && config.getAbacResource() != null) {
90+
try {
91+
EvaluationContext context = policyEvaluator.buildContext(
92+
operatingUser.getUsername(),
93+
config.getAbacResource()
94+
);
95+
96+
PolicyDecision decision = policyEvaluator.evaluate(
97+
context,
98+
config.getAbacResource(),
99+
"VIEW",
100+
false
101+
);
102+
103+
if (decision.getEffect() == PolicyDecision.Effect.ALLOW) {
104+
hasAccess = true;
105+
log.debug("ABAC policy granted access to {} for user {}",
106+
resourceKey, operatingUser.getUsername());
107+
}
108+
} catch (Exception e) {
109+
log.warn("Error evaluating ABAC policy for resource {}: {}",
110+
resourceKey, e.getMessage());
111+
}
112+
}
113+
114+
permissions.put(resourceKey, hasAccess);
115+
}
116+
117+
UIPermissionsResponse permissionsResponse = new UIPermissionsResponse(
118+
operatingUser.getUsername(),
119+
permissions,
120+
abacEnabled
121+
);
122+
123+
return ResponseEntity.ok(permissionsResponse);
124+
125+
} catch (Exception e) {
126+
log.error("Error fetching UI permissions", e);
127+
return ResponseEntity.status(500).build();
128+
}
129+
}
130+
131+
/**
132+
* Check if user has permission for a specific UI resource
133+
*/
134+
@GetMapping("/check/{resourceKey}")
135+
@LimitAccess(applicationAccess = {ApplicationAccessEnum.CAN_LOG_IN})
136+
public ResponseEntity<ResourcePermissionResponse> checkResourcePermission(
137+
@PathVariable String resourceKey,
138+
HttpServletRequest request,
139+
HttpServletResponse response) {
140+
141+
try {
142+
User operatingUser = userService.getOperatingUser(request, response, null);
143+
if (operatingUser == null) {
144+
return ResponseEntity.status(401).build();
145+
}
146+
147+
Set<String> accessSet = operatingUser.getAuthorizationType().getAccessSet();
148+
boolean abacEnabled = systemOptions.getEnableAbacUiControl() != null &&
149+
systemOptions.getEnableAbacUiControl();
150+
151+
UIResourceConfig config = UIResourceMappings.getUIResourceMappings().get(resourceKey);
152+
if (config == null) {
153+
return ResponseEntity.notFound().build();
154+
}
155+
156+
boolean hasAccess = false;
157+
String grantedBy = "none";
158+
159+
// Check standard access set
160+
if (config.getRequiredAccess() != null && accessSet.contains(config.getRequiredAccess())) {
161+
hasAccess = true;
162+
grantedBy = "access_set";
163+
}
164+
165+
// Check ABAC if enabled and no standard access
166+
if (abacEnabled && !hasAccess && policyEvaluator != null && config.getAbacResource() != null) {
167+
try {
168+
EvaluationContext context = policyEvaluator.buildContext(
169+
operatingUser.getUsername(),
170+
config.getAbacResource()
171+
);
172+
173+
PolicyDecision decision = policyEvaluator.evaluate(
174+
context,
175+
config.getAbacResource(),
176+
"VIEW"
177+
);
178+
179+
if (decision.getEffect() == PolicyDecision.Effect.ALLOW) {
180+
hasAccess = true;
181+
grantedBy = "abac_policy";
182+
}
183+
} catch (Exception e) {
184+
log.warn("Error evaluating ABAC policy for {}: {}", resourceKey, e.getMessage());
185+
}
186+
}
187+
188+
ResourcePermissionResponse permissionResponse = new ResourcePermissionResponse(
189+
resourceKey,
190+
hasAccess,
191+
grantedBy
192+
);
193+
194+
return ResponseEntity.ok(permissionResponse);
195+
196+
} catch (Exception e) {
197+
log.error("Error checking resource permission for {}", resourceKey, e);
198+
return ResponseEntity.status(500).build();
199+
}
200+
}
201+
202+
203+
/**
204+
* Response DTO for UI permissions
205+
*/
206+
public record UIPermissionsResponse(
207+
String username,
208+
Map<String, Boolean> permissions,
209+
boolean abacEnabled
210+
) {}
211+
212+
/**
213+
* Response DTO for individual resource permission check
214+
*/
215+
public record ResourcePermissionResponse(
216+
String resourceKey,
217+
boolean hasAccess,
218+
String grantedBy
219+
) {}
220+
}

0 commit comments

Comments
 (0)