diff --git a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleSocAi.java b/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleSocAi.java index 79f2b42b1..e32277c7b 100644 --- a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleSocAi.java +++ b/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleSocAi.java @@ -45,12 +45,83 @@ public List checkRequirements(Long serverId) throws Exception public List getConfigurationKeys(Long groupId) throws Exception { List keys = new ArrayList<>(); + keys.add(ModuleConfigurationKey.builder() + .withGroupId(groupId) + .withConfKey("utmstack.socai.provider") + .withConfName("AI Provider") + .withConfDescription("AI provider used by SOC AI.") + .withConfDataType("text") + .withConfValue("openai") + .withConfRequired(true) + .build()); + + keys.add(ModuleConfigurationKey.builder() + .withGroupId(groupId) + .withConfKey("utmstack.socai.model") + .withConfName("AI Model") + .withConfDescription("AI model that SOC AI will use to analyze alerts (first option of active provider).") + .withConfDataType("text") + .withConfValue("gpt-4o") + .withConfRequired(true) + .build()); + + keys.add(ModuleConfigurationKey.builder() + .withGroupId(groupId) + .withConfKey("utmstack.socai.url") + .withConfName("Provider URL") + .withConfDescription("Endpoint URL for the provider (only set for azure / ollama / custom).") + .withConfDataType("text") + .withConfValue("") + .withConfRequired(false) + .build()); + + keys.add(ModuleConfigurationKey.builder() + .withGroupId(groupId) + .withConfKey("utmstack.socai.maxTokens") + .withConfName("Max Tokens") + .withConfDescription("Maximum number of tokens used per request.") + .withConfDataType("text") + .withConfValue("4096") + .withConfRequired(true) + .build()); + + keys.add(ModuleConfigurationKey.builder() + .withGroupId(groupId) + .withConfKey("utmstack.socai.authType") + .withConfName("Authentication Type") + .withConfDescription("Authentication type used to reach the provider (none for ollama).") + .withConfDataType("text") + .withConfValue("custom-headers") + .withConfRequired(true) + .build()); + + keys.add(ModuleConfigurationKey.builder() + .withGroupId(groupId) + .withConfKey("utmstack.socai.customHeaders") + .withConfName("Custom Headers") + .withConfDescription("Custom headers (JSON object) sent with each request to the provider.") + .withConfDataType("password") + .withConfValue("") + .withConfRequired(false) + .build()); + + keys.add(ModuleConfigurationKey.builder() + .withGroupId(groupId) + .withConfKey("utmstack.socai.autoAnalyze") + .withConfName("Auto Analyze") + .withConfDescription("If set to \"true\", SOC AI will automatically analyze incoming alerts.") + .withConfDataType("text") + .withConfValue("false") + .withConfRequired(false) + .build()); + keys.add(ModuleConfigurationKey.builder() .withGroupId(groupId) .withConfKey("utmstack.socai.incidentCreation") .withConfName("Automatic Incident creation") .withConfDescription("If set to \"true\", the system will create incidents based on analysis of alerts.") - .withConfDataType("bool") + .withConfDataType("text") + .withConfValue("false") .withConfRequired(false) .build()); @@ -60,37 +131,11 @@ public List getConfigurationKeys(Long groupId) throws Ex .withConfName("Change Alert Status") .withConfDescription("If set to \"true\", SOC Ai will automatically change the status of alerts. " + "Analysts should investigate those with the status \"In Review\".") - .withConfDataType("bool") + .withConfDataType("text") + .withConfValue("false") .withConfRequired(false) .build()); - keys.add(ModuleConfigurationKey.builder() - .withGroupId(groupId) - .withConfKey("utmstack.socai.model") - .withConfName("Select AI Model") - .withConfDescription("Choose the AI model that SOC AI will use to analyze alerts.") - .withConfDataType("select") - .withConfRequired(true) - .withConfOptions( - "[" + - "{\"value\": \"gpt-4\", \"label\": \"GPT-4\"}," + - "{\"value\": \"gpt-4-0613\", \"label\": \"GPT-4 (0613)\"}," + - "{\"value\": \"gpt-4-32k\", \"label\": \"GPT-4 32K\"}," + - "{\"value\": \"gpt-4-32k-0613\", \"label\": \"GPT-4 32K (0613)\"}," + - "{\"value\": \"gpt-4-turbo\", \"label\": \"GPT-4 Turbo\"}," + - "{\"value\": \"gpt-4o\", \"label\": \"GPT-4 Omni\"}," + - "{\"value\": \"gpt-4o-mini\", \"label\": \"GPT-4 Omni Mini\"}," + - "{\"value\": \"gpt-4.1\", \"label\": \"GPT-4.1\"}," + - "{\"value\": \"gpt-4.1-mini\", \"label\": \"GPT-4.1 Mini\"}," + - "{\"value\": \"gpt-4.1-nano\", \"label\": \"GPT-4.1 Nano\"}," + - "{\"value\": \"gpt-3.5-turbo\", \"label\": \"GPT-3.5 Turbo\"}," + - "{\"value\": \"gpt-3.5-turbo-0613\", \"label\": \"GPT-3.5 Turbo (0613)\"}," + - "{\"value\": \"gpt-3.5-turbo-16k\", \"label\": \"GPT-3.5 Turbo 16K\"}," + - "{\"value\": \"gpt-3.5-turbo-16k-0613\", \"label\": \"GPT-3.5 Turbo 16K (0613)\"}" + - "]" - ) - .build()); - return keys; } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/application_modules/UtmModuleGroupResource.java b/backend/src/main/java/com/park/utmstack/web/rest/application_modules/UtmModuleGroupResource.java index 1b420fbec..3d5735bfa 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/application_modules/UtmModuleGroupResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/application_modules/UtmModuleGroupResource.java @@ -30,6 +30,7 @@ import javax.validation.Valid; import java.net.URISyntaxException; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -82,6 +83,15 @@ public ResponseEntity createConfigurationGroup(@Valid @RequestBo defaultConfigurationKeys.forEach(key -> keys.add(new UtmModuleGroupConfiguration(key))); moduleGroupConfigurationService.createConfigurationKeys(keys); + for (UtmModuleGroupConfiguration conf : keys) { + if ((Constants.CONF_TYPE_PASSWORD.equals(conf.getConfDataType()) + || Constants.CONF_TYPE_FILE.equals(conf.getConfDataType())) + && conf.getConfValue() != null) { + conf.setConfValue(Constants.MASKED_VALUE); + } + } + result.setModuleGroupConfigurations(new HashSet<>(keys)); + return ResponseEntity.ok(result); } catch (DataIntegrityViolationException e) { String msg = ctx + ": " + e.getMostSpecificCause().getMessage().replaceAll("\n", ""); diff --git a/frontend/src/app/app-module/guides/guide-soc-ai/guide-soc-ai.component.html b/frontend/src/app/app-module/guides/guide-soc-ai/guide-soc-ai.component.html index 7c8c18c08..da11a5256 100644 --- a/frontend/src/app/app-module/guides/guide-soc-ai/guide-soc-ai.component.html +++ b/frontend/src/app/app-module/guides/guide-soc-ai/guide-soc-ai.component.html @@ -155,9 +155,11 @@

SOC AI

Save configuration + diff --git a/frontend/src/app/app-module/guides/guide-soc-ai/guide-soc-ai.component.ts b/frontend/src/app/app-module/guides/guide-soc-ai/guide-soc-ai.component.ts index 389fdcbeb..7bb7d9889 100644 --- a/frontend/src/app/app-module/guides/guide-soc-ai/guide-soc-ai.component.ts +++ b/frontend/src/app/app-module/guides/guide-soc-ai/guide-soc-ai.component.ts @@ -39,6 +39,8 @@ export class GuideSocAiComponent implements OnInit { saving = false; loading = true; + configReady=false + // Form values - what the user sees/edits formValues: {[key: string]: string} = {}; customModelValue = ''; @@ -322,6 +324,7 @@ export class GuideSocAiComponent implements OnInit { }; } this.loading = false; + this.configReady = true; this.cdr.detectChanges(); }, () => { this.loading = false; @@ -439,6 +442,31 @@ export class GuideSocAiComponent implements OnInit { save() { this.saving = true; + + if (!this.groupId || !this.rawConfigs.length) { + this.moduleGroupService.create({ + name: 'socai', + description: 'socai', + moduleId: this.integrationId + }).subscribe( + response => { + this.groupId = response.body.id; + this.rawConfigs = response.body.moduleGroupConfigurations || []; + this.cdr.markForCheck() + this.persistConfig(); + }, + () => { + this.saving = false; + this.cdr.markForCheck(); + this.toast.showError('Error', 'Failed to create configuration group. Please try again.'); + } + ); + return; + } + this.persistConfig(); + } + + private persistConfig() { const changes: UtmModuleGroupConfType[] = []; // Set provider @@ -494,12 +522,16 @@ export class GuideSocAiComponent implements OnInit { // Otherwise: don't touch customHeaders — keep existing value in DB } + this.rawConfigs=changes + this.cdr.markForCheck() + this.moduleGroupConfService.update({ keys: changes, moduleId: this.integrationId }).subscribe( () => { this.saving = false; + this.configReady = true; this.cdr.markForCheck() this.toast.showSuccessBottom('SOC AI configuration saved successfully'); }, @@ -610,4 +642,10 @@ export class GuideSocAiComponent implements OnInit { // Invalid JSON, start empty } } + + + public disableModule(){ + this.groupId=null + this.rawConfigs=[] + } } diff --git a/frontend/src/app/app-module/shared/components/app-module-activate-button/app-module-activate-button.component.ts b/frontend/src/app/app-module/shared/components/app-module-activate-button/app-module-activate-button.component.ts index 7c4fe04f2..458b206f8 100644 --- a/frontend/src/app/app-module/shared/components/app-module-activate-button/app-module-activate-button.component.ts +++ b/frontend/src/app/app-module/shared/components/app-module-activate-button/app-module-activate-button.component.ts @@ -95,11 +95,10 @@ export class AppModuleActivateButtonComponent implements OnInit, OnDestroy { this.moduleRefreshBehavior.$moduleChange.next(true); this.toastService.showSuccessBottom('Module ' + this.moduleDetail.moduleName + ' has been ' + (this.moduleDetail.moduleActive ? 'enabled' : 'disabled') + ' successfully'); + if (!status) { + this.disableModuleClicked.emit(); + } }); - } else { - if (fromOnclick && !status) { - this.disableModuleClicked.emit(); - } } }