diff --git a/configmanager/server.js b/configmanager/server.js index 712bb14..f3cd72b 100644 --- a/configmanager/server.js +++ b/configmanager/server.js @@ -281,7 +281,8 @@ app.get('/throttlelist', (req, res) => { }) }) // Start the server -app.listen(3000, () => { +app.listen(3000, async () => { + console.log('Config manager started'); try { if (!fs.existsSync('/data/cad-default.json')) fs.cpSync('/app/cad-default.json', '/data/cad-default.json'); if (!fs.existsSync('/data/config-default.json')) fs.cpSync('/app/config-default.json', '/data/config-default.json'); @@ -295,5 +296,15 @@ app.listen(3000, () => { } catch(e) { console.error(`Could not create blacklist files: ${e}`); } - console.log('Config manager started'); + loop = 0 + while (loop < 5) { + try { + await fetch("http://controlpanel-api:8050/configmanager/sync"); + break; + } catch(e) { + loop++; + await new Promise(resolve => setTimeout(resolve, 5000)); // Wait for 5 seconds before retrying + } + } + if (loop == 5) console.log('Cannot connect to api') }); diff --git a/controlpanel/api/routes/configmanager.js b/controlpanel/api/routes/configmanager.js index 60db387..eb452e7 100644 --- a/controlpanel/api/routes/configmanager.js +++ b/controlpanel/api/routes/configmanager.js @@ -50,5 +50,15 @@ router.put('/config/:namespace/:application', async (req, res) => { } }) +router.get('/sync', async (req, res) => { + try { + const result = await configmanagerService.sendDataToConfigmanager(); + return res.status(result.code).send(result); + } catch(e) { + console.error(e); + return res.status(500).send("Server error"); + } +}) + module.exports = router; \ No newline at end of file diff --git a/controlpanel/api/server.js b/controlpanel/api/server.js index 89ecbd3..4136a1c 100644 --- a/controlpanel/api/server.js +++ b/controlpanel/api/server.js @@ -3,6 +3,7 @@ const cors = require('cors'); require('dotenv').config({ path: __dirname + '/.env' }); const { CONFIGMANAGER_URL } = require('./util/variables'); const { CONTROLPANEL_URL } = require('./util/variables'); +const { sendDataToConfigmanager } = require('./services/configmanager'); const configmanager = require('./routes/configmanager'); const decoys = require('./routes/decoys'); @@ -39,6 +40,7 @@ app.listen(8050, async () => { // CONFIGMANAGER URL await fetch(CONFIGMANAGER_URL); console.log("Successfully connected to configmanager !") + setInterval(async () => await sendDataToConfigmanager(), 60 * 60 * 1000) } catch(e) { console.error("Configmanager is not up, please (re)start configmanager"); } diff --git a/controlpanel/api/services/configmanager.js b/controlpanel/api/services/configmanager.js index c870345..f600d23 100644 --- a/controlpanel/api/services/configmanager.js +++ b/controlpanel/api/services/configmanager.js @@ -1,6 +1,9 @@ const { validateDecoyFilter } = require('../util/decoy-validator'); const { validateConfig } = require('../util/config-validator'); const { CONFIGMANAGER_URL } = require('../util/variables'); +const ProtectedApp = require('../models/ProtectedApp') +const Config = require('../models/Config-data') +const Decoy = require('../models/Decoy-data') const axios = require('axios'); @@ -31,12 +34,13 @@ module.exports = { * Update decoys list in configmanager * @param {Object} decoys New list of decoys */ - updateDecoysList: async (namespace, application, decoys) => { + updateDecoysList: async (namespace, application, decoys) => { try { if (!namespace || !application) { namespace = 'unknown'; application = 'unknown'; } + if (!decoys.length) return { type: 'warning', code: 200, message: "Decoys list is empty or full of inactive decoy, cannot sync" }; for (const decoy of decoys) { if (validateDecoyFilter(decoy).length) return { type: 'error', code: 422, message: "There are errors in one of the decoys, cannot send to configmanager" } } @@ -103,4 +107,17 @@ module.exports = { throw e; } }, + + sendDataToConfigmanager: async () => { + try { + const protectedApps = await ProtectedApp.findAll({ include: [{ model: Decoy, as: 'decoys', attributes: ['decoy', 'state'] }, { model: Config, as: 'configs', attributes: ['config'] }] }) + for (const pa of protectedApps) { + module.exports.updateDecoysList(pa.namespace, pa.application, pa.decoys.map(decoyData => decoyData.state == 'active' && decoyData.decoy)) + module.exports.updateConfig(pa.namespace, pa.application, pa.configs); + } + return { type: 'success', message: "Successful operation", code: 200 }; + } catch(e) { + return { type: 'error', message: "Server error", data: e, code: 500 }; + } + } } \ No newline at end of file diff --git a/controlpanel/cad/src/app/pages/config/config.component.html b/controlpanel/cad/src/app/pages/config/config.component.html index c99e8b8..b239f9f 100644 --- a/controlpanel/cad/src/app/pages/config/config.component.html +++ b/controlpanel/cad/src/app/pages/config/config.component.html @@ -116,6 +116,7 @@

Config 🔧

{{ config | json }}
\ No newline at end of file diff --git a/controlpanel/cad/src/app/pages/config/config.component.ts b/controlpanel/cad/src/app/pages/config/config.component.ts index be2dda6..7408941 100644 --- a/controlpanel/cad/src/app/pages/config/config.component.ts +++ b/controlpanel/cad/src/app/pages/config/config.component.ts @@ -12,6 +12,7 @@ import { ConfigService } from '../../services/config.service'; import { GlobalStateService } from '../../services/global-state.service'; import { ToastrService } from 'ngx-toastr'; import { isEmptyObject } from '../../utils'; +import { ConfigmanagerApiService } from '../../services/api/configmanager-api.service'; @Component({ selector: 'app-config', @@ -61,7 +62,7 @@ onLeaveInfo() { } //#endregion - constructor(private configService: ConfigService, private globalState: GlobalStateService, private toastr: ToastrService) { + constructor(private configService: ConfigService, private globalState: GlobalStateService, private toastr: ToastrService, private configmanagerApi: ConfigmanagerApiService) { this.configForm = new FormGroup({ sessionIn: new FormControl(''), sessionKey: new FormControl(''), @@ -276,4 +277,10 @@ onLeaveInfo() { if (apiResponse.type == 'error') this.toastr.error(apiResponse.message, "Error when saving global config"); else this.toastr.success(apiResponse.message, 'Successfully updated global config'); } + async sync() { + const saveResponse = await this.configmanagerApi.updateConfigmanagerConfig(this.globalState.selectedApp.namespace, this.globalState.selectedApp.application, this.config) + if (saveResponse.type == 'error') this.toastr.error(saveResponse.message, "Error Synchronizing"); + else if (saveResponse.type == 'warning') this.toastr.warning(saveResponse.message, "Warning"); + else this.toastr.success("Successfully synchronized with configmanager", "Synchronized"); + } } diff --git a/controlpanel/cad/src/app/pages/list-decoy/list-decoy.component.html b/controlpanel/cad/src/app/pages/list-decoy/list-decoy.component.html index 685671a..05f791b 100644 --- a/controlpanel/cad/src/app/pages/list-decoy/list-decoy.component.html +++ b/controlpanel/cad/src/app/pages/list-decoy/list-decoy.component.html @@ -64,6 +64,6 @@

Decoys list 🗒️

- +
\ No newline at end of file diff --git a/controlpanel/cad/src/app/pages/list-decoy/list-decoy.component.ts b/controlpanel/cad/src/app/pages/list-decoy/list-decoy.component.ts index 1cecc1a..d42bacb 100644 --- a/controlpanel/cad/src/app/pages/list-decoy/list-decoy.component.ts +++ b/controlpanel/cad/src/app/pages/list-decoy/list-decoy.component.ts @@ -10,6 +10,7 @@ import { isProtectedAppEmpty } from '../../models/protected-app'; import { RouterLink } from '@angular/router'; import { UUID } from '../../models/types'; import { Subscription } from 'rxjs'; +import { ConfigmanagerApiService } from '../../services/api/configmanager-api.service'; @Component({ selector: 'app-list-decoy', @@ -22,7 +23,7 @@ export class ListDecoyComponent implements OnInit, OnDestroy { decoys: DecoyData[] = []; globalStateSubscription?: Subscription; - constructor(private decoyService: DecoyService, private toastr: ToastrService, private globalState: GlobalStateService) { } + constructor(private decoyService: DecoyService, private toastr: ToastrService, private globalState: GlobalStateService, private configmanagerApi: ConfigmanagerApiService) { } async ngOnInit() { this.globalStateSubscription = this.globalState.selectedApp$.subscribe(data => { @@ -68,10 +69,11 @@ export class ListDecoyComponent implements OnInit, OnDestroy { } } - // async save() { - // if (!this.decoys.length) return; - // const saveResponse = await this.decoyService.updateDecoysState(this.decoys); - // if (saveResponse.type == 'error') this.toastr.error(saveResponse.message, "Error saving"); - // else this.toastr.success("Successfully saved decoys states", "Saved"); - // } + async save() { + if (!this.decoys.length) return; + const saveResponse = await this.configmanagerApi.updateConfigmanagerDecoys(this.globalState.selectedApp.namespace, this.globalState.selectedApp.application, this.decoys.filter(decoyData => decoyData.state == 'active').map(decoyData => decoyData.decoy)) + if (saveResponse.type == 'error') this.toastr.error(saveResponse.message, "Error Synchronizing"); + else if (saveResponse.type == 'warning') this.toastr.warning(saveResponse.message, "Warning"); + else this.toastr.success("Successfully synchronized with configmanager", "Synchronized"); + } } diff --git a/controlpanel/cad/src/app/services/api/configmanager-api.service.ts b/controlpanel/cad/src/app/services/api/configmanager-api.service.ts new file mode 100644 index 0000000..a222439 --- /dev/null +++ b/controlpanel/cad/src/app/services/api/configmanager-api.service.ts @@ -0,0 +1,41 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { lastValueFrom } from 'rxjs'; +import { ApiResponse } from '../../models/api-response'; +import { GlobalStateService } from '../global-state.service'; +import { Decoy } from '../../models/decoy'; +import { Config } from '../../models/config'; + +@Injectable({ + providedIn: 'root' +}) +export class ConfigmanagerApiService { + + constructor(private http: HttpClient, private globalState: GlobalStateService) { } + + async updateConfigmanagerDecoys(namespace: string, application: string, decoys: Decoy[]) { + try { + return await lastValueFrom(this.http.put(`${this.globalState.API_URL}/configmanager/decoys/${namespace}/${application}`, decoys)); + } catch (e) { + console.error(e); + return { message: 'Cannot synchronize decoys with configmanager', type: 'error' }; + } + } + async updateConfigmanagerConfig(namespace: string, application: string, config: Config) { + try { + return await lastValueFrom(this.http.put(`${this.globalState.API_URL}/configmanager/config/${namespace}/${application}`, config)); + } catch (e) { + console.error(e); + return { message: 'Cannot synchronize config with configmanager', type: 'error' }; + } + } + async getConfigmanagerDecoys(namespace: string, application: string) { + try { + return await lastValueFrom(this.http.get(`${this.globalState.API_URL}/configmanager/decoys/${namespace}/${application}`)); + } catch (e) { + console.error(e); + return { message: 'Cannot synchronize config with configmanager', type: 'error' }; + } + } + +}