Skip to content

Commit 4c01391

Browse files
authored
Merge pull request #92 from SentriusLLC/copilot/fix-91
Modernize user settings page with SSH public key management
2 parents 39db4ab + a80c291 commit 4c01391

File tree

8 files changed

+973
-183
lines changed

8 files changed

+973
-183
lines changed

.local.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
SENTRIUS_VERSION=1.1.334
1+
SENTRIUS_VERSION=1.1.341
22
SENTRIUS_SSH_VERSION=1.1.41
33
SENTRIUS_KEYCLOAK_VERSION=1.1.53
44
SENTRIUS_AGENT_VERSION=1.1.42

.local.env.bak

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
SENTRIUS_VERSION=1.1.334
1+
SENTRIUS_VERSION=1.1.341
22
SENTRIUS_SSH_VERSION=1.1.41
33
SENTRIUS_KEYCLOAK_VERSION=1.1.53
44
SENTRIUS_AGENT_VERSION=1.1.42

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

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.util.HashMap;
77
import java.util.List;
88
import java.util.Map;
9+
import java.util.Objects;
910
import com.fasterxml.jackson.core.JsonProcessingException;
1011
import com.fasterxml.jackson.databind.JsonNode;
1112
import com.fasterxml.jackson.databind.node.BooleanNode;
@@ -15,18 +16,24 @@
1516
import io.sentrius.sso.core.annotations.LimitAccess;
1617
import io.sentrius.sso.core.config.SystemOptions;
1718
import io.sentrius.sso.core.controllers.BaseController;
19+
import io.sentrius.sso.core.dto.HostGroupDTO;
20+
import io.sentrius.sso.core.dto.UserPublicKeyDTO;
1821
import io.sentrius.sso.core.exceptions.ZtatException;
22+
import io.sentrius.sso.core.model.hostgroup.HostGroup;
1923
import io.sentrius.sso.core.model.security.UserType;
2024
import io.sentrius.sso.core.model.security.enums.UserAccessEnum;
2125
import io.sentrius.sso.core.model.users.User;
2226
import io.sentrius.sso.core.dto.UserDTO;
2327
import io.sentrius.sso.core.dto.UserTypeDTO;
2428
import io.sentrius.sso.core.model.users.UserConfig;
29+
import io.sentrius.sso.core.model.users.UserPublicKey;
2530
import io.sentrius.sso.core.model.users.UserSettings;
31+
import io.sentrius.sso.core.services.ConfigurationService;
2632
import io.sentrius.sso.core.services.ErrorOutputService;
2733
import io.sentrius.sso.core.services.HostGroupService;
2834
import io.sentrius.sso.core.services.SessionService;
2935
import io.sentrius.sso.core.services.UserCustomizationService;
36+
import io.sentrius.sso.core.services.UserPublicKeyService;
3037
import io.sentrius.sso.core.services.UserService;
3138
import io.sentrius.sso.core.services.agents.AgentService;
3239
import io.sentrius.sso.core.services.agents.ZeroTrustClientService;
@@ -45,8 +52,10 @@
4552
import org.springframework.http.ResponseEntity;
4653
import org.springframework.stereotype.Controller;
4754
import org.springframework.ui.Model;
55+
import org.springframework.web.bind.annotation.DeleteMapping;
4856
import org.springframework.web.bind.annotation.GetMapping;
4957
import org.springframework.web.bind.annotation.ModelAttribute;
58+
import org.springframework.web.bind.annotation.PathVariable;
5059
import org.springframework.web.bind.annotation.PostMapping;
5160
import org.springframework.web.bind.annotation.RequestBody;
5261
import org.springframework.web.bind.annotation.RequestMapping;
@@ -63,6 +72,7 @@ public class UserApiController extends BaseController {
6372
final CryptoService cryptoService;
6473
private final MessagingUtil messagingUtil;
6574
final UserCustomizationService userThemeService;
75+
final UserPublicKeyService userPublicKeyService;
6676
final ZeroTrustRequestService ztatRequestService;
6777
final ZeroTrustAccessTokenService ztatService;
6878
final AgentService agentService;
@@ -87,6 +97,7 @@ protected UserApiController(
8797
HostGroupService hostGroupService, CryptoService cryptoService,
8898
MessagingUtil messagingUtil,
8999
UserCustomizationService userThemeService,
100+
UserPublicKeyService userPublicKeyService,
90101
SessionService sessionService,
91102
ZeroTrustRequestService ztatRequestService,
92103
ZeroTrustAccessTokenService ztatService, AgentService agentService,
@@ -97,6 +108,7 @@ protected UserApiController(
97108
this.cryptoService = cryptoService;
98109
this.messagingUtil = messagingUtil;
99110
this.userThemeService = userThemeService;
111+
this.userPublicKeyService = userPublicKeyService;
100112
this.sessionService = sessionService;
101113
this.ztatRequestService = ztatRequestService;
102114
this.ztatService = ztatService;
@@ -428,5 +440,131 @@ private List<Map<String, Object>> getAgentSessionDurations() {
428440
return agentSessions;
429441
}
430442

443+
// Public Key Management Endpoints
444+
445+
@GetMapping("/publickeys")
446+
public ResponseEntity<List<UserPublicKeyDTO>> getUserPublicKeys(HttpServletRequest request,
447+
HttpServletResponse response) {
448+
var user = userService.getOperatingUser(request, response, null);
449+
var publicKeys = userPublicKeyService.getPublicKeysForUser(user.getId());
450+
List<UserPublicKeyDTO> publicKeyDTOs = new ArrayList<>();
451+
for (UserPublicKey key : publicKeys) {
452+
UserPublicKeyDTO dto = UserPublicKeyDTO.builder().build();
453+
//dto.setId(key.getId());
454+
dto.setKeyName(key.getKeyName());
455+
dto.setKeyType(key.getKeyType());
456+
dto.setPublicKey(key.getPublicKey());
457+
dto.setIsEnabled(key.getIsEnabled());
458+
if (key.getHostGroup() != null) {
459+
dto.setHostGroup( HostGroupDTO.builder().groupId(key.getHostGroup().getId()).build());
460+
//dto.setHostGroupName(key.getHostGroup().getName());
461+
}
462+
publicKeyDTOs.add(dto);
463+
}
464+
return ResponseEntity.ok(publicKeyDTOs);
465+
}
466+
467+
@PostMapping("/publickeys")
468+
public ResponseEntity<ObjectNode> addPublicKey(HttpServletRequest request, HttpServletResponse response, @RequestBody
469+
UserPublicKeyDTO publicKey) {
470+
ObjectNode node = JsonUtil.MAPPER.createObjectNode();
471+
try {
472+
473+
HostGroup hostGroup = hostGroupService.getHostGroup(publicKey.getHostGroup().getGroupId());
474+
475+
476+
UserPublicKey key = new UserPublicKey();
477+
key.setKeyName(publicKey.getKeyName());
478+
key.setKeyType(publicKey.getKeyType());
479+
key.setPublicKey(publicKey.getPublicKey());
480+
key.setIsEnabled(publicKey.getIsEnabled());
481+
if (null != hostGroup) {
482+
key.setHostGroup(hostGroup);
483+
}
484+
var user = userService.getOperatingUser(request, response, null);
485+
key.setUser(user);
486+
487+
if (key.getCreatedAt() == null) {
488+
key.setCreatedAt(new java.sql.Timestamp(System.currentTimeMillis()));
489+
}
490+
491+
var savedKey = userPublicKeyService.addPublicKey(key);
492+
node.put("status", "Public key successfully added");
493+
node.put("id", savedKey.getId());
494+
return ResponseEntity.ok(node);
495+
} catch (Exception e) {
496+
log.error("Error adding public key", e);
497+
node.put("status", "Error adding public key");
498+
return ResponseEntity.internalServerError().body(node);
499+
}
500+
}
501+
502+
@PostMapping("/publickeys/{keyId}/assign")
503+
public ResponseEntity<ObjectNode> assignPublicKeyToHostGroup(
504+
HttpServletRequest request, HttpServletResponse response,
505+
@PathVariable Long keyId, @RequestParam Long hostGroupId) {
506+
ObjectNode node = JsonUtil.MAPPER.createObjectNode();
507+
try {
508+
var user = userService.getOperatingUser(request, response, null);
509+
var publicKeyOpt = userPublicKeyService.getPublicKeyById(keyId);
510+
511+
if (publicKeyOpt.isEmpty()) {
512+
node.put("status", "Public key not found");
513+
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(node);
514+
}
515+
516+
var publicKey = publicKeyOpt.get();
517+
518+
// Verify the key belongs to the current user
519+
if (!publicKey.getUser().getId().equals(user.getId())) {
520+
node.put("status", "Unauthorized");
521+
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(node);
522+
}
523+
524+
var hostGroup = hostGroupService.getHostGroup(hostGroupId);
525+
publicKey.setHostGroup(hostGroup);
526+
userPublicKeyService.addPublicKey(publicKey);
527+
528+
node.put("status", "Public key successfully assigned to host group");
529+
return ResponseEntity.ok(node);
530+
} catch (Exception e) {
531+
log.error("Error assigning public key to host group", e);
532+
node.put("status", "Error assigning public key to host group");
533+
return ResponseEntity.internalServerError().body(node);
534+
}
535+
}
536+
537+
@DeleteMapping("/publickeys/{keyId}")
538+
public ResponseEntity<ObjectNode> deletePublicKey(
539+
HttpServletRequest request, HttpServletResponse response,
540+
@PathVariable Long keyId) {
541+
ObjectNode node = JsonUtil.MAPPER.createObjectNode();
542+
try {
543+
var user = userService.getOperatingUser(request, response, null);
544+
var publicKeyOpt = userPublicKeyService.getPublicKeyById(keyId);
545+
546+
if (publicKeyOpt.isEmpty()) {
547+
node.put("status", "Public key not found");
548+
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(node);
549+
}
550+
551+
var publicKey = publicKeyOpt.get();
552+
553+
// Verify the key belongs to the current user
554+
if (!publicKey.getUser().getId().equals(user.getId())) {
555+
node.put("status", "Unauthorized");
556+
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(node);
557+
}
558+
559+
userPublicKeyService.deletePublicKey(keyId);
560+
node.put("status", "Public key successfully deleted");
561+
return ResponseEntity.ok(node);
562+
} catch (Exception e) {
563+
log.error("Error deleting public key", e);
564+
node.put("status", "Error deleting public key");
565+
return ResponseEntity.internalServerError().body(node);
566+
}
567+
}
568+
431569
}
432570

api/src/main/java/io/sentrius/sso/controllers/view/UserController.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
import io.sentrius.sso.core.model.users.UserConfig;
2424
import io.sentrius.sso.core.model.users.UserSettings;
2525
import io.sentrius.sso.core.services.ErrorOutputService;
26+
import io.sentrius.sso.core.services.HostGroupService;
2627
import io.sentrius.sso.core.services.UserCustomizationService;
28+
import io.sentrius.sso.core.services.UserPublicKeyService;
2729
import io.sentrius.sso.core.services.UserService;
2830
import io.sentrius.sso.core.services.WorkHoursService;
2931
import io.sentrius.sso.core.services.security.CryptoService;
@@ -44,16 +46,21 @@
4446
public class UserController extends BaseController {
4547

4648
final UserCustomizationService userThemeService;
49+
final UserPublicKeyService userPublicKeyService;
50+
final HostGroupService hostGroupService;
4751
final WorkHoursService workHoursService;
4852
final CryptoService cryptoService;
4953

5054
protected UserController(
5155
UserService userService, SystemOptions systemOptions,
52-
ErrorOutputService errorOutputService, UserCustomizationService userThemeService, WorkHoursService workHoursService,
53-
CryptoService cryptoService
56+
ErrorOutputService errorOutputService, UserCustomizationService userThemeService,
57+
UserPublicKeyService userPublicKeyService, HostGroupService hostGroupService,
58+
WorkHoursService workHoursService, CryptoService cryptoService
5459
) {
5560
super(userService, systemOptions, errorOutputService);
5661
this.userThemeService = userThemeService;
62+
this.userPublicKeyService = userPublicKeyService;
63+
this.hostGroupService = hostGroupService;
5764
this.workHoursService = workHoursService;
5865
this.cryptoService = cryptoService;
5966
}
@@ -186,8 +193,16 @@ public String getUserSettings(Model model, HttpServletRequest request, HttpServl
186193
Map<Integer, WorkHours> userWorkHours = workHoursList.stream()
187194
.collect(Collectors.toMap(WorkHours::getDayOfWeek, wh -> wh));
188195

196+
// Get user's public keys
197+
var publicKeys = userPublicKeyService.getPublicKeysForUser(user.getId());
198+
199+
// Get available host groups for this user
200+
var hostGroups = hostGroupService.getHostGroupsForUser(user.getId());
201+
189202
// Pass data to Thymeleaf
190203
model.addAttribute("userWorkHours", userWorkHours);
204+
model.addAttribute("publicKeys", publicKeys);
205+
model.addAttribute("hostGroups", hostGroups);
191206
model.addAttribute("daysOfWeek", List.of(
192207
new DayOfWeekDTO(0, "Sunday"),
193208
new DayOfWeekDTO(1, "Monday"),

0 commit comments

Comments
 (0)