diff --git a/.gcp.env b/.gcp.env index 0218842b..feea70e7 100644 --- a/.gcp.env +++ b/.gcp.env @@ -1,4 +1,4 @@ -SENTRIUS_VERSION=1.0.37 +SENTRIUS_VERSION=1.0.44 SENTRIUS_SSH_VERSION=1.0.4 -SENTRIUS_KEYCLOAK_VERSION=1.0.6 -SENTRIUS_AGENT_VERSION=1.0.16 \ No newline at end of file +SENTRIUS_KEYCLOAK_VERSION=1.0.7 +SENTRIUS_AGENT_VERSION=1.0.18 \ No newline at end of file diff --git a/analyagents/pom.xml b/analyagents/pom.xml index dee6c11c..26ad89b3 100644 --- a/analyagents/pom.xml +++ b/analyagents/pom.xml @@ -67,6 +67,11 @@ org.projectlombok lombok + + net.snowflake + snowflake-ingest-sdk + ${snowflake-ingest-version} + diff --git a/analyagents/src/main/java/io/sentrius/agent/analysis/agents/sessions/SessionAnalyticsAgent.java b/analyagents/src/main/java/io/sentrius/agent/analysis/agents/sessions/SessionAnalyticsAgent.java index 96645d8f..14091ed9 100644 --- a/analyagents/src/main/java/io/sentrius/agent/analysis/agents/sessions/SessionAnalyticsAgent.java +++ b/analyagents/src/main/java/io/sentrius/agent/analysis/agents/sessions/SessionAnalyticsAgent.java @@ -61,8 +61,9 @@ public void processSessions() { List unprocessedSessions = sessionMetadataService.getSessionsByState("CLOSED").stream() .filter(session -> !processedSessionIds.contains(session.getId())) .collect(Collectors.toList()); - + long count = 0; for (TerminalSessionMetadata session : unprocessedSessions) { + count++; try { processSession(session); // ACTIVE -> INACTIVE -> CLOSED -> PROCESSED @@ -75,7 +76,7 @@ public void processSessions() { sessionMetadataService.saveSession(session); } - log.info("Finished processing sessions"); + log.info("Finished processing {} sessions ", count); } /* TODO - Implement this @Scheduled(fixedDelay = 60000) // Waits 60 seconds after the previous run completes diff --git a/analyagents/src/main/java/io/sentrius/agent/analysis/sinks/log/LogSink.java b/analyagents/src/main/java/io/sentrius/agent/analysis/sinks/log/LogSink.java new file mode 100644 index 00000000..d8ad623f --- /dev/null +++ b/analyagents/src/main/java/io/sentrius/agent/analysis/sinks/log/LogSink.java @@ -0,0 +1,9 @@ +package io.sentrius.agent.analysis.sinks.log; + +import java.util.List; +import io.sentrius.sso.core.model.sessions.TerminalLogs; + +public interface LogSink { + + void process(List logs); +} diff --git a/api/src/main/java/io/sentrius/sso/config/SecurityConfig.java b/api/src/main/java/io/sentrius/sso/config/SecurityConfig.java index ba6698ee..77799715 100644 --- a/api/src/main/java/io/sentrius/sso/config/SecurityConfig.java +++ b/api/src/main/java/io/sentrius/sso/config/SecurityConfig.java @@ -37,6 +37,7 @@ public class SecurityConfig { private final CustomUserDetailsService userDetailsService; private final CustomAuthenticationSuccessHandler successHandler; + private final KeycloakAuthSuccessHandler keycloakAuthSuccessHandler; final UserService userService; @Value("${https.required:false}") // Default is false @@ -55,6 +56,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti ) .oauth2Login(oauth2 -> oauth2 .loginPage("/oauth2/authorization/keycloak") + .successHandler(keycloakAuthSuccessHandler) ) .cors(Customizer.withDefaults()); diff --git a/api/src/main/java/io/sentrius/sso/controllers/CustomErrorHandler.java b/api/src/main/java/io/sentrius/sso/controllers/CustomErrorHandler.java index 9bc22b8e..514507f9 100644 --- a/api/src/main/java/io/sentrius/sso/controllers/CustomErrorHandler.java +++ b/api/src/main/java/io/sentrius/sso/controllers/CustomErrorHandler.java @@ -34,8 +34,8 @@ public static String createErrorHash(StackTraceElement[] trace, String t) { @RequestMapping("/error") public String handleError(HttpServletRequest request, Model model) { // Retrieve error details - Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code"); - Throwable ex = (Throwable) request.getAttribute("javax.servlet.error.exception"); + Integer statusCode = (Integer) request.getAttribute("jakarta.servlet.error.status_code"); + Throwable ex = (Throwable) request.getAttribute("jakarta.servlet.error.exception"); // Log error details (optional) if (ex != null) { diff --git a/api/src/main/java/io/sentrius/sso/controllers/api/IntegrationApiController.java b/api/src/main/java/io/sentrius/sso/controllers/api/IntegrationApiController.java index 83b7167b..904812e3 100644 --- a/api/src/main/java/io/sentrius/sso/controllers/api/IntegrationApiController.java +++ b/api/src/main/java/io/sentrius/sso/controllers/api/IntegrationApiController.java @@ -22,8 +22,10 @@ import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; @Slf4j @Controller @@ -76,9 +78,10 @@ public ResponseEntity addJiraIntegration(HttpServletRequ @LimitAccess(applicationAccess = {ApplicationAccessEnum.CAN_MANAGE_APPLICATION}) public ResponseEntity addOpenaiIntegration(HttpServletRequest request, HttpServletResponse response, - ExternalIntegrationDTO integrationDTO) + @RequestBody ExternalIntegrationDTO integrationDTO) throws JsonProcessingException { + log.info("ahh"); var json = JsonUtil.MAPPER.writeValueAsString(integrationDTO); IntegrationSecurityToken token = IntegrationSecurityToken.builder() diff --git a/api/src/main/java/io/sentrius/sso/controllers/api/UserApiController.java b/api/src/main/java/io/sentrius/sso/controllers/api/UserApiController.java index e0cd31ef..c8eedd3c 100644 --- a/api/src/main/java/io/sentrius/sso/controllers/api/UserApiController.java +++ b/api/src/main/java/io/sentrius/sso/controllers/api/UserApiController.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Map; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.BooleanNode; import com.fasterxml.jackson.databind.node.IntNode; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -13,6 +14,7 @@ import io.sentrius.sso.core.annotations.LimitAccess; import io.sentrius.sso.core.annotations.Model; import io.sentrius.sso.core.controllers.BaseController; +import io.sentrius.sso.core.model.HostSystem; import io.sentrius.sso.core.model.security.UserType; import io.sentrius.sso.core.model.users.User; import io.sentrius.sso.core.model.dto.UserDTO; @@ -20,6 +22,8 @@ import io.sentrius.sso.core.model.security.enums.UserAccessEnum; import io.sentrius.sso.core.model.users.UserConfig; import io.sentrius.sso.core.model.users.UserSettings; +import io.sentrius.sso.core.model.zt.OpsZeroTrustAcessTokenRequest; +import io.sentrius.sso.core.model.zt.ZeroTrustAccessTokenRequest; import io.sentrius.sso.core.security.service.CryptoService; import io.sentrius.sso.core.services.ErrorOutputService; import io.sentrius.sso.core.services.SessionService; @@ -27,6 +31,8 @@ import io.sentrius.sso.core.services.UserService; import io.sentrius.sso.core.services.HostGroupService; import io.sentrius.sso.core.config.SystemOptions; +import io.sentrius.sso.core.services.ZeroTrustAccessTokenService; +import io.sentrius.sso.core.services.ZeroTrustRequestService; import io.sentrius.sso.core.utils.JsonUtil; import io.sentrius.sso.core.utils.MessagingUtil; import jakarta.servlet.http.HttpServletRequest; @@ -37,6 +43,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -51,6 +58,8 @@ public class UserApiController extends BaseController { final CryptoService cryptoService; private final MessagingUtil messagingUtil; final UserCustomizationService userThemeService; + final ZeroTrustRequestService ztatRequestService; + final ZeroTrustAccessTokenService ztatService; static Map fields = new HashMap<>(); static { @@ -66,7 +75,9 @@ protected UserApiController(UserService userService, SystemOptions systemOptions HostGroupService hostGroupService, CryptoService cryptoService, MessagingUtil messagingUtil, UserCustomizationService userThemeService, - SessionService sessionService + SessionService sessionService, + ZeroTrustRequestService ztatRequestService, + ZeroTrustAccessTokenService ztatService ) { super(userService, systemOptions, errorOutputService); this.hostGroupService = hostGroupService; @@ -74,6 +85,8 @@ protected UserApiController(UserService userService, SystemOptions systemOptions this.messagingUtil = messagingUtil; this.userThemeService = userThemeService; this.sessionService = sessionService; + this.ztatRequestService = ztatRequestService; + this.ztatService = ztatService; } @GetMapping("list") @@ -117,7 +130,6 @@ public String deleteUser(@RequestParam("userId") String userId) throws GeneralSe } @PostMapping("/settings") - @LimitAccess(userAccess = {UserAccessEnum.CAN_EDIT_USERS}) public String updateUser(HttpServletRequest request, HttpServletResponse response ) throws JsonProcessingException { var user = userService.getOperatingUser(request,response, null); @@ -163,6 +175,18 @@ public String updateUser(HttpServletRequest request, HttpServletResponse respons return "redirect:/sso/v1/users/settings?message=" + MessagingUtil.getMessageId(MessagingUtil.SETTINGS_UPDATED); } + @PostMapping("/settings/workhours") + public String updateWorkhours(HttpServletRequest request, HttpServletResponse response, + @RequestBody JsonNode body) throws JsonProcessingException { + log.info("Updating work hours: {}", body); + /* + var reason = ztatService.createReason("Updating work hours", "Updating work hours", ""); + var ztatRequest = ztatService.createOpsRequest("Updating work hours", "Updating work hours", + reason, userService.getOperatingUser(request,response, null)); + ztatRequestService.createOpsTATRequest(ztatRequest);*/ + return ""; + } + @GetMapping("/types/list") @LimitAccess(userAccess = {UserAccessEnum.CAN_MANAGE_USERS}) public ResponseEntity> getUserTypes() throws GeneralSecurityException { diff --git a/api/src/main/java/io/sentrius/sso/controllers/api/ZeroTrustATApiController.java b/api/src/main/java/io/sentrius/sso/controllers/api/ZeroTrustATApiController.java index 4198dd1c..1a25cc26 100644 --- a/api/src/main/java/io/sentrius/sso/controllers/api/ZeroTrustATApiController.java +++ b/api/src/main/java/io/sentrius/sso/controllers/api/ZeroTrustATApiController.java @@ -13,6 +13,7 @@ import io.sentrius.sso.core.config.SystemOptions; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @@ -20,6 +21,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +@Slf4j @Controller @RequestMapping("/api/v1/zerotrust/accesstoken") public class ZeroTrustATApiController extends BaseController { @@ -52,6 +54,7 @@ public String manageRequest(HttpServletRequest request, HttpServletResponse resp @RequestParam("ztatId") Long ztatId) throws SQLException, GeneralSecurityException { var operatingUser = getOperatingUser(request, response); if (null != type ){ + log.info("Operating user {} is managing a {} request with status {}", operatingUser, type, status); switch(type){ case "terminal": manageTerminalZtAt(operatingUser, ztatId, status); diff --git a/api/src/main/java/io/sentrius/sso/controllers/view/UserController.java b/api/src/main/java/io/sentrius/sso/controllers/view/UserController.java index 05e890a7..5663fe1b 100644 --- a/api/src/main/java/io/sentrius/sso/controllers/view/UserController.java +++ b/api/src/main/java/io/sentrius/sso/controllers/view/UserController.java @@ -1,26 +1,35 @@ package io.sentrius.sso.controllers.view; import java.lang.reflect.Field; +import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import io.sentrius.sso.core.annotations.LimitAccess; import io.sentrius.sso.core.controllers.BaseController; +import io.sentrius.sso.core.model.WorkHours; +import io.sentrius.sso.core.model.dto.DayOfWeekDTO; import io.sentrius.sso.core.model.dto.SystemOption; +import io.sentrius.sso.core.model.dto.UserDTO; import io.sentrius.sso.core.model.dto.UserTypeDTO; import io.sentrius.sso.core.model.security.UserType; import io.sentrius.sso.core.model.security.enums.UserAccessEnum; import io.sentrius.sso.core.model.users.User; import io.sentrius.sso.core.model.users.UserConfig; import io.sentrius.sso.core.model.users.UserSettings; +import io.sentrius.sso.core.repository.UserTypeRepository; +import io.sentrius.sso.core.security.service.CryptoService; import io.sentrius.sso.core.services.ErrorOutputService; import io.sentrius.sso.core.services.UserCustomizationService; import io.sentrius.sso.core.services.UserService; import io.sentrius.sso.core.config.SystemOptions; +import io.sentrius.sso.core.services.WorkHoursService; import io.sentrius.sso.core.utils.JsonUtil; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -29,7 +38,9 @@ import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; @Slf4j @Controller @@ -37,10 +48,17 @@ public class UserController extends BaseController { final UserCustomizationService userThemeService; + final WorkHoursService workHoursService; + final CryptoService cryptoService; - protected UserController(UserService userService, SystemOptions systemOptions, ErrorOutputService errorOutputService, UserCustomizationService userThemeService) { + protected UserController(UserService userService, SystemOptions systemOptions, + ErrorOutputService errorOutputService, UserCustomizationService userThemeService, WorkHoursService workHoursService, + CryptoService cryptoService + ) { super(userService, systemOptions, errorOutputService); this.userThemeService = userThemeService; + this.workHoursService = workHoursService; + this.cryptoService = cryptoService; } @ModelAttribute("userSettings") @@ -147,9 +165,45 @@ public String listUsers(Model model) { return "sso/users/list_users"; } + + @GetMapping("/edit") + @LimitAccess(userAccess = {UserAccessEnum.CAN_EDIT_USERS}) + public String editUser(Model model, HttpServletRequest request, HttpServletResponse response, + @RequestParam("userId") String userId) throws GeneralSecurityException { + model.addAttribute("globalAccessSet", UserType.createSuperUser().getAccessSet()); + Long id = Long.parseLong(cryptoService.decrypt(userId)); + User user = userService.getUserById(id); + UserDTO userDTO = new UserDTO(user); + var types = userService.getUserTypeList(); + model.addAttribute("userTypes",types); + model.addAttribute("user", userDTO); + return "sso/users/edit_user"; + } + @GetMapping("/settings") @LimitAccess(userAccess = {UserAccessEnum.CAN_VIEW_USERS}) - public String getUserSettings(HttpServletRequest request, HttpServletResponse response) { + public String getUserSettings(Model model, HttpServletRequest request, HttpServletResponse response) { + + var user = userService.getOperatingUser(request,response, null); + + List workHoursList = workHoursService.getWorkHoursForUser(user.getId()); + + // Convert the list into a Map where the key is the day of the week (0-6) + Map userWorkHours = workHoursList.stream() + .collect(Collectors.toMap(WorkHours::getDayOfWeek, wh -> wh)); + + // Pass data to Thymeleaf + model.addAttribute("userWorkHours", userWorkHours); + model.addAttribute("daysOfWeek", List.of( + new DayOfWeekDTO(0, "Sunday"), + new DayOfWeekDTO(1, "Monday"), + new DayOfWeekDTO(2, "Tuesday"), + new DayOfWeekDTO(3, "Wednesday"), + new DayOfWeekDTO(4, "Thursday"), + new DayOfWeekDTO(5, "Friday"), + new DayOfWeekDTO(6, "Saturday") + )); + return "sso/users/user_settings"; } diff --git a/api/src/main/java/io/sentrius/sso/controllers/view/ZeroTrustATController.java b/api/src/main/java/io/sentrius/sso/controllers/view/ZeroTrustATController.java index 4c07a05b..fec8c593 100644 --- a/api/src/main/java/io/sentrius/sso/controllers/view/ZeroTrustATController.java +++ b/api/src/main/java/io/sentrius/sso/controllers/view/ZeroTrustATController.java @@ -30,7 +30,7 @@ protected ZeroTrustATController(UserService userService, } @GetMapping("/my/current") - public ResponseEntity getCurrentJit() { + public ResponseEntity getCurrentTat() { return ResponseEntity.ok().build(); } @@ -38,28 +38,28 @@ public ResponseEntity getCurrentJit() { @GetMapping("/list") @LimitAccess(ztatAccess= {ZeroTrustAccessTokenEnum.CAN_VIEW_ZTATS}) - public String viewJitRequests(HttpServletRequest request, HttpServletResponse response, Model model) { + public String viewTatRequests(HttpServletRequest request, HttpServletResponse response, Model model) { var operatingUser = getOperatingUser(request, response); - modelJITs(model, operatingUser); + modelTATs(model, operatingUser); return "sso/ztats/view_ztats"; } @GetMapping("/my") @LimitAccess(ztatAccess= {ZeroTrustAccessTokenEnum.CAN_VIEW_ZTATS}) - public String viewMyJits(HttpServletRequest request, HttpServletResponse response, Model model) { + public String viewMyTats(HttpServletRequest request, HttpServletResponse response, Model model) { var operatingUser = getOperatingUser(request, response); - modelJITs(model, operatingUser); + modelTATs(model, operatingUser); return "sso/ztats/view_my_ztats"; } - private void modelJITs(Model model, User operatingUser){ - model.addAttribute("openTerminalJits", ztatRequestService.getOpenAccessTokenRequests(operatingUser)); - model.addAttribute("openOpsJits", ztatRequestService.getOpenOpsRequests(operatingUser)); - model.addAttribute("approvedTerminalJits", ztatRequestService.getApprovedTerminalAccessTokenRequests(operatingUser)); - model.addAttribute("approvedOpsJits", ztatRequestService.getApprovedOpsAccessTokenRequests(operatingUser)); - model.addAttribute("deniedOpsJits", ztatRequestService.getDeniedOpsAccessTokenRequests(operatingUser)); - model.addAttribute("deniedTerminalJits", ztatRequestService.getDeniedTerminalAccessTokenRequests(operatingUser)); + private void modelTATs(Model model, User operatingUser){ + model.addAttribute("openTerminalTats", ztatRequestService.getOpenAccessTokenRequests(operatingUser)); + model.addAttribute("openOpsTats", ztatRequestService.getOpenOpsRequests(operatingUser)); + model.addAttribute("approvedTerminalTats", ztatRequestService.getApprovedTerminalAccessTokenRequests(operatingUser)); + model.addAttribute("approvedOpsTats", ztatRequestService.getApprovedOpsAccessTokenRequests(operatingUser)); + model.addAttribute("deniedOpsTats", ztatRequestService.getDeniedOpsAccessTokenRequests(operatingUser)); + model.addAttribute("deniedTerminalTats", ztatRequestService.getDeniedTerminalAccessTokenRequests(operatingUser)); } } diff --git a/api/src/main/resources/application.properties b/api/src/main/resources/application.properties index c8149bb5..231ac059 100644 --- a/api/src/main/resources/application.properties +++ b/api/src/main/resources/application.properties @@ -64,6 +64,7 @@ server.error.whitelabel.enabled=false keycloak.realm=sentrius +keycloak.base-url=http://192.168.1.162:8180 spring.security.oauth2.client.registration.keycloak.client-id=sentrius-api spring.security.oauth2.client.registration.keycloak.client-secret=nGkEukexSWTvDzYjSkDmeUlM0FJ5Jhh0 diff --git a/api/src/main/resources/db/migration/V15__work_hours.sql b/api/src/main/resources/db/migration/V15__work_hours.sql new file mode 100644 index 00000000..ed08a953 --- /dev/null +++ b/api/src/main/resources/db/migration/V15__work_hours.sql @@ -0,0 +1,10 @@ +CREATE TABLE work_hours ( + id SERIAL PRIMARY KEY, + user_id INT REFERENCES users(id) ON DELETE CASCADE, + day_of_week SMALLINT CHECK (day_of_week BETWEEN 0 AND 6), -- 0 = Sunday, 6 = Saturday + start_time TIME NOT NULL, -- Example: '09:00:00' + end_time TIME NOT NULL -- Example: '17:00:00' +); + +-- Ensure fast lookups for checking dem hours +CREATE INDEX idx_work_hours ON work_hours (user_id, day_of_week); diff --git a/api/src/main/resources/db/migration/V16__add_ops_summary.sql b/api/src/main/resources/db/migration/V16__add_ops_summary.sql new file mode 100644 index 00000000..c64f88e0 --- /dev/null +++ b/api/src/main/resources/db/migration/V16__add_ops_summary.sql @@ -0,0 +1,3 @@ + +ALTER TABLE operations_request + ADD COLUMN summary TEXT; diff --git a/api/src/main/resources/templates/sso/dashboard.html b/api/src/main/resources/templates/sso/dashboard.html index f30c2ef7..5e51ddec 100755 --- a/api/src/main/resources/templates/sso/dashboard.html +++ b/api/src/main/resources/templates/sso/dashboard.html @@ -420,7 +420,7 @@ User Operations Your Settings View JITs + th:if="${#sets.contains(operatingUser.authorizationType.accessSet, 'CAN_VIEW_ZTATS')}">View Trust ATs diff --git a/api/src/main/resources/templates/sso/integrations/add_openai.html b/api/src/main/resources/templates/sso/integrations/add_openai.html index f9f9d6e6..4cbfb165 100644 --- a/api/src/main/resources/templates/sso/integrations/add_openai.html +++ b/api/src/main/resources/templates/sso/integrations/add_openai.html @@ -55,8 +55,39 @@ font-size: 1.25rem; color: #888; } - + + @@ -68,7 +99,7 @@ Set Up OpenAI Integration - + Integration Name @@ -88,7 +119,6 @@ Set Up OpenAI Integration th:field="*{apiToken}" placeholder="Enter your Jira API token" required> - Save Integration @@ -101,4 +131,3 @@ Set Up OpenAI Integration