Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .local.env
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
SENTRIUS_VERSION=1.1.510
SENTRIUS_VERSION=1.1.523
SENTRIUS_SSH_VERSION=1.1.45
SENTRIUS_KEYCLOAK_VERSION=1.1.60
SENTRIUS_KEYCLOAK_VERSION=1.1.64
SENTRIUS_AGENT_VERSION=1.1.51
SENTRIUS_AI_AGENT_VERSION=1.1.287
LLMPROXY_VERSION=1.0.88
LAUNCHER_VERSION=1.0.91
AGENTPROXY_VERSION=1.0.92
SSHPROXY_VERSION=1.0.91
RDPPROXY_VERSION=1.0.122
RDPPROXY_VERSION=1.0.122
6 changes: 3 additions & 3 deletions .local.env.bak
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
SENTRIUS_VERSION=1.1.510
SENTRIUS_VERSION=1.1.523
SENTRIUS_SSH_VERSION=1.1.45
SENTRIUS_KEYCLOAK_VERSION=1.1.60
SENTRIUS_KEYCLOAK_VERSION=1.1.64
SENTRIUS_AGENT_VERSION=1.1.51
SENTRIUS_AI_AGENT_VERSION=1.1.287
LLMPROXY_VERSION=1.0.88
LAUNCHER_VERSION=1.0.91
AGENTPROXY_VERSION=1.0.92
SSHPROXY_VERSION=1.0.91
RDPPROXY_VERSION=1.0.122
RDPPROXY_VERSION=1.0.122
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ public ResponseEntity<Map<String, Object>> initiateRdpSession(

// Validate access to the host group
Optional<HostGroup> hostGroupOpt = hostGroupService.getHostGroupWithHostSystems(user, enclaveId);
if (hostGroupOpt.isEmpty()) {
if (!AccessUtil.canAccess(user, SSHAccessEnum.CAN_MANAGE_SYSTEMS) && hostGroupOpt.isEmpty() ) {
// log.warn("User {} does not have access to host group {}", user.getUsername(), enclaveId);
return ResponseEntity.badRequest().build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.DigestInputStream;
import java.security.GeneralSecurityException;
Expand All @@ -14,6 +16,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
Expand Down Expand Up @@ -89,60 +92,82 @@ public class ConfigurationApplicationTask {
@EventListener(ApplicationReadyEvent.class)
@Transactional
public void afterStartup() throws IOException, GeneralSecurityException, JSchException, SQLException {
// Your logic here
String yamlPath = systemOptions.getYamlConfiguration();
if (StringUtils.isEmpty(yamlPath)) {
log.info("No configuration file found");
return;
}

if (!StringUtils.isEmpty(systemOptions.getYamlConfiguration())) {
log.info("Checking for configuration file {}", systemOptions.getYamlConfiguration());
var digestStream = new DigestInputStream(
new FileInputStream(systemOptions.getYamlConfiguration()),
MessageDigest.getInstance("SHA256")
);
MessageDigest digest = digestStream.getMessageDigest();
var hash = new String(digest.digest());

AtomicBoolean recreate = new AtomicBoolean(false);
configurationOptionRepository.findLatestByConfigurationName("yamlConfigurationFileHash")
.ifPresentOrElse(
configurationOption -> {
if (!hash.equals(configurationOption.getConfigurationValue())) {
log.info("Configuration file hash has changed, recreating database");
recreate.set(true);
}else {
log.info("Configuration file hash has not changed");
}
configurationOption.setConfigurationValue(hash);
configurationOptionRepository.save(configurationOption);
},
() -> {
log.info("No configuration file hash found, creating one");
var configurationOption = new ConfigurationOption();
configurationOption.setConfigurationName("yamlConfigurationFileHash");
configurationOption.setConfigurationValue(hash);
configurationOptionRepository.save(configurationOption);
recreate.set(true);
}
);
Path yamlFile = Paths.get(yamlPath);
if (!Files.exists(yamlFile)) {
log.warn("Configuration file {} not found", yamlFile);
return;
}

Boolean deleteFile = systemOptions.getDeleteYamlConfigurationFile();
if (null == deleteFile) {
deleteFile = true;
}
if (deleteFile) {
Files.delete(Paths.get(systemOptions.getYamlConfiguration()));
log.info("Checking for configuration file {}", yamlFile);

// --- Compute SHA-256 hash of file ---
String hash;
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
try (InputStream in = Files.newInputStream(yamlFile);
DigestInputStream dis = new DigestInputStream(in, md)) {
byte[] buffer = new byte[8192];
while (dis.read(buffer) != -1) {
// Consume stream fully to update digest
}
}
byte[] digest = md.digest();
StringBuilder sb = new StringBuilder(digest.length * 2);
for (byte b : digest) sb.append(String.format("%02x", b));
hash = sb.toString();
} catch (Exception e) {
throw new IOException("Unable to compute SHA-256 for " + yamlFile, e);
}

AtomicBoolean recreate = new AtomicBoolean(false);

configurationOptionRepository.findLatestByConfigurationName("yamlConfigurationFileHash")
.ifPresentOrElse(existing -> {
String oldHash = existing.getConfigurationValue();
if (!hash.equalsIgnoreCase(oldHash)) {
log.info("Configuration file hash changed. Recreating database.");
recreate.set(true);
} else {
log.info("Configuration file hash unchanged ({}).", hash);
}
existing.setConfigurationValue(hash);
configurationOptionRepository.save(existing);
}, () -> {
log.info("No configuration file hash found; creating one.");
var option = new ConfigurationOption();
option.setConfigurationName("yamlConfigurationFileHash");
option.setConfigurationValue(hash);
configurationOptionRepository.save(option);
recreate.set(true);
});

Boolean deleteFile = systemOptions.getDeleteYamlConfigurationFile();
if (deleteFile == null) deleteFile = true;

if (recreate.get()) {
log.info("Recreating database");
var installConfiguration =
InstallConfiguration.fromYaml(new FileInputStream(systemOptions.getYamlConfiguration()));
// recreate the database
if (deleteFile) {
try {
Files.deleteIfExists(yamlFile);
log.info("Deleted configuration file {}", yamlFile);
} catch (IOException e) {
log.warn("Failed to delete configuration file {}", yamlFile, e);
}
}

if (recreate.get()) {
log.info("Recreating database using configuration {}", yamlFile);
try (InputStream in = Files.newInputStream(yamlFile)) {
var installConfiguration = InstallConfiguration.fromYaml(in);
initialize(installConfiguration, true);
}
} else {
log.info("No configuration file found");
}
}

@Transactional
public List<SideEffect> createStaticType(UserType type, boolean action) throws SQLException,
GeneralSecurityException {
Expand Down Expand Up @@ -498,6 +523,7 @@ protected List<SideEffect> createSystems(InstallConfiguration installConfigurati
if (null != installConfiguration.getSystems()) {
for (var system : installConfiguration.getSystems()) {
var systemObj = HostSystem.fromDTO(system);
log.info("Processing system {} with host {} and port {}", systemObj.getDisplayName(), systemObj.getHost(), systemObj.getPort());
if ( shouldInsertSystem(systemObj)) {
if (action) {
var sys = systemRepository.save(systemObj);
Expand All @@ -518,7 +544,8 @@ protected boolean shouldInsertSystem(HostSystem systemObj) {
return true;
}
for(HostSystem system : systems) {
if (system.getHost().equals(systemObj.getHost()) && system.getPort() == systemObj.getPort()) {
if (system.getHost().equals(systemObj.getHost()) && Objects.equals(system.getPort(), systemObj.getPort())) {
log.info("System {} with host {} and port {} already exists", systemObj.getDisplayName(), systemObj.getHost(), systemObj.getPort());
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,6 @@ <h3>Assigned RDP Servers</h3>
<th>System Name</th>
<th>Hostname</th>
<th>Host Enclave</th>
<th>User</th>
<th>Operations</th>
</tr>
</thead>
Expand Down Expand Up @@ -626,8 +625,7 @@ <h5 class="modal-title" id="rdpModalLabel">RDP Session - <span id="rdpHostName">
{ data: 'displayName' },
{ data: 'displayName' },
{ data: 'group.displayName' },
{ data: 'rdpUser' },
{
{
data: null,
render: function(data, type, row) {
const groupId = row.group ? row.group.groupId : -1; // Access group.id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@ public class HostSystemDTO {

private String authorizedKeys;

@Builder.Default
private boolean isRdp = false;
private boolean rdpUser;
private boolean rdpPassword;
private boolean isRdp;
private String rdpUser;
private String rdpPassword;

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package io.sentrius.sso.core.services.security;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Optional;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import io.jsonwebtoken.Jwts;
Expand Down Expand Up @@ -88,6 +91,19 @@ public static Optional<String> getUserTypeName(ObjectNode jwt) {

}

public static Optional<List<String>> getGroups(ObjectNode jwt) {
return Optional.ofNullable(jwt.get("claims"))
.map(c -> c.get("assignedGroups"))
.filter(JsonNode::isArray)
.map(arr -> {
List<String> groups = new ArrayList<>();
arr.forEach(n -> groups.add(n.asText()));
return groups;
});
}



public static String extractKid(String jwt) {
try {
// Strip "Bearer " prefix if present
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,8 @@ public HostSystemDTO toDTO() {
dto.statusCd(this.statusCd);
if (rdpEnabled) {
dto.isRdp(true);
dto.rdpUser(rdpUser != null && !rdpUser.isEmpty());
dto.rdpPassword(rdpPassword != null && !rdpPassword.isEmpty());
dto.rdpUser(rdpUser);
dto.rdpPassword(rdpPassword);
}
dto.publicKeyList(this.publicKeyList != null ? new ArrayList<>(this.publicKeyList) : new ArrayList<>());
dto.errorMsg(this.errorMsg);
Expand All @@ -175,8 +175,8 @@ public HostSystemDTO toDTO(HostGroup hg) {
dto.statusCd(this.statusCd);
if (rdpEnabled) {
dto.isRdp(true);
dto.rdpUser(rdpUser != null && !rdpUser.isEmpty());
dto.rdpPassword(rdpPassword != null && !rdpPassword.isEmpty());
dto.rdpUser(rdpUser);
dto.rdpPassword(rdpPassword);
}
dto.publicKeyList(this.publicKeyList != null ? new ArrayList<>(this.publicKeyList) : new ArrayList<>());
dto.errorMsg(this.errorMsg);
Expand Down Expand Up @@ -209,12 +209,11 @@ public static HostSystem fromDTO(HostSystemDTO dto) {
hostSystem.setErrorMsg(dto.getErrorMsg());
hostSystem.setPort(dto.getPort());
hostSystem.setSshUser(dto.getSshUser());
if (dto.isRdp()) {
hostSystem.setRdpEnabled(dto.isRdp());
hostSystem.setRdpUser(dto.isRdpUser() ? dto.getSshUser() : "");
hostSystem.setRdpPassword(dto.isRdpPassword() ? dto.getPassword() : "");
if (dto.isRdp()){
hostSystem.setRdpEnabled(true);
hostSystem.setRdpUser(dto.getRdpUser());
hostSystem.setRdpPassword(dto.getRdpPassword());
}

return hostSystem;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.sentrius.sso.core.repository;

import java.util.List;
import java.util.Optional;
import io.sentrius.sso.core.model.hostgroup.HostGroup;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
Expand All @@ -11,4 +12,8 @@ public interface ProfileRepository extends JpaRepository<HostGroup, Long> {
HostGroup getById(Long profileId);

List<HostGroup> findAll();

Optional<HostGroup> findByName(String name);


}
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,16 @@ public User getOperatingUser(HttpServletRequest request, HttpServletResponse res
.build();
ProfileDB.save(newHg);

Optional<List<String>> assignedGroups = JwtUtil.getGroups(jwt);
if (assignedGroups.isPresent()) {
for(String groupName : assignedGroups.get()) {
Optional<HostGroup> hg = ProfileDB.findByName(groupName);
if (hg.isPresent()) {
operatingUser.getHostGroups().add(hg.get());
}
}
}

operatingUser.getHostGroups().add(newHg);
save(operatingUser);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ public void deleteById(Long id) {
public List<IntegrationSecurityToken> findByConnectionType(String connectionType) {
return repository.findByConnectionType(connectionType).stream().map(token -> {
// decrypt the connecting info
log.info("IntegrationSecurityTokenService.findByConnectionType: {}", token);
IntegrationSecurityToken unmanaged = IntegrationSecurityToken.builder()
.id(token.getId())
.connectionType(token.getConnectionType())
Expand Down
5 changes: 4 additions & 1 deletion docker/keycloak/realms/sentrius-realm.json.template
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,10 @@
"enabled": true,
"emailVerified": true,
"attributes": {
"userType": "Full Access"
"userType": "Full Access",
"assignedGroups": [
"testGroup"
]
},
"credentials": [
{
Expand Down
7 changes: 5 additions & 2 deletions docker/sentrius/demoInstaller.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,12 @@ systems:
authorizedKeys: ~/.ssh/authorized_keys
- displayName: sentrius-rdp-test
sshUser: ubuntu
password: ubuntu
port: 3389
host: sentrius-rdp-test
authorizedKeys: ~/.ssh/authorized_keys
rdp: true
host: sentrius-rdp
rdpUser: ubuntu
rdpPassword: ubuntu

## Define groups of users who are assigned to systems
## also entails the configuration that is applied to groupf
Expand Down
Loading
Loading