Skip to content

Commit

Permalink
Merge pull request #455 from Security-Onion-Solutions/jertel/clone
Browse files Browse the repository at this point in the history
support duplicate settings
  • Loading branch information
jertel authored Apr 30, 2024
2 parents b6695dc + f89a420 commit a199d8b
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 0 deletions.
12 changes: 12 additions & 0 deletions html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4928,6 +4928,18 @@ <h4 v-if="!$root.loading">{{ i18n.settingsCustomized }} {{ settingsCustomized }}
<v-subtitle v-text="i18n.settingReadOnly" />
</div>
<v-container fluid>
<v-row v-if="selected.duplicates && !selected.readonly">
<v-col>
<v-btn id="config-toggle-duplicate-button" @click="toggleDuplicate(selected)" v-text="i18n.settingDuplicate" data-aid="config_item_show_duplicate"/>
<div v-if="showDuplicate" class="mt-2 mb-8 pl-8">
<div class="mb-4">{{ i18n.settingDuplicateHelp }}</div>
<v-form id="duplicate-id-form" v-model="duplicateIdValid">
<v-text-field id="duplicate-id-input" :rules="duplicate_id_rules" :label="i18n.settingDuplicateName" v-model="duplicateId" data-aid="config_item_duplicate_name_input" />
<v-btn id="config-create-duplicate-button" :disabled="!duplicateIdValid" @click="duplicate(selected)" v-text="i18n.settingDuplicateCreate" data-aid="config_item_create_duplicate"/>
</v-form>
</div>
</v-col>
</v-row>
<v-row v-if="!selected.readonly && selected.defaultAvailable">
<v-col>
<v-btn id="config-toggle-default-button" @click="showDefault = !showDefault" v-text="showDefault ? i18n.settingHideDefault : i18n.settingShowDefault" data-aid="config_item_show_default"/>
Expand Down
6 changes: 6 additions & 0 deletions html/js/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,12 @@ const i18n = {
settingDefault: 'Default Value',
settingDeleted: 'Setting deleted/reset successfully. Changes typically apply within 15 minutes.',
settingDeleteError: 'Setting could not be deleted.',
settingDuplicate: 'Duplicate',
settingDuplicateCreate: 'Create Setting',
settingDuplicateInvalid: 'The chosen duplicate ID is already taken. Change the ID to a unique value and try again.',
settingDuplicateName: 'Name of new setting',
settingDuplicateNameInvalid: 'Must be between 3 and 50 characters in length, and must only contain letters, underscores, and digits',
settingDuplicateHelp: 'This config setting can be duplicated if additional or explicitly-named settings are needed. Clicking the Duplicate button will result in a new sibling setting with the given name. Setting names must be unique. Duplicated settings cannot be removed or renamed via the SOC user interface.',
settingHelp: 'View documentation or related information for this setting',
settingSelect: 'Select a setting from the tree view on the left or the quick links on the right.',
settingGlobal: 'Current Grid Value',
Expand Down
35 changes: 35 additions & 0 deletions html/js/routes/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ routes.push({ path: '/config', name: 'config', component: {
key: "",
value: "",
},
duplicate_id_rules: [
value => !!value || this.$root.i18n.required,
value => (!!value && /^[a-zA-Z0-9_]{3,50}$/.test(value)) || this.$root.i18n.settingDuplicateNameInvalid,
],

selectedNode: null,
cancelDialog: false,
Expand All @@ -31,6 +35,9 @@ routes.push({ path: '/config', name: 'config', component: {
settingsAvailable: 0,
showDefault: false,
nextStopId: null,
showDuplicate: false,
duplicateId: null,
duplicateIdValid: false,
}},
mounted() {
this.processRouteParameters();
Expand Down Expand Up @@ -167,6 +174,7 @@ routes.push({ path: '/config', name: 'config', component: {
helpLink: setting.helpLink,
advanced: setting.advanced,
syntax: setting.syntax,
duplicates: setting.duplicates,
};
this.merge(created, setting);
return created;
Expand Down Expand Up @@ -473,5 +481,32 @@ routes.push({ path: '/config', name: 'config', component: {
});
this.availableNodes = eligible.map(n => { return { text: n.name + " (" + n.role + ")", value: n.id } });
},
toggleDuplicate(setting) {
this.duplicateId = this.suggestDuplicateName(setting);
this.showDuplicate = !this.showDuplicate;
},
suggestDuplicateName(setting) {
return setting.name + "_dup";
},
duplicate(setting) {
const new_name = this.duplicateId;
var new_id = setting.id.substring(0, setting.id.lastIndexOf(setting.name));
new_id += new_name;
this.$root.startLoading();
const found = this.settings.find(s => s.id == new_id);
this.$root.stopLoading();
if (found) {
this.$root.showWarning(this.i18n.settingDuplicateInvalid);
return
}
var new_setting = structuredClone(setting);
new_setting.id = new_id;
new_setting.name = new_name;
this.settings.push(new_setting);
this.settings.sort((a,b) => { if (a.id > b.id) return 1; else if (a.id < b.id) return -1; else return 0 });
this.refreshTree();
this.active = [new_id]
this.showDuplicate = false;
},
}
}});
41 changes: 41 additions & 0 deletions html/js/routes/config.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ test('loadData', async () => {
"default": null,
"defaultAvailable": false,
"description": "Nearby",
"duplicates": undefined,
"file": undefined,
"global": false,
"helpLink": undefined,
Expand All @@ -73,6 +74,7 @@ test('loadData', async () => {
"default": undefined,
"defaultAvailable": undefined,
"description": "NADA",
"duplicates": undefined,
"file": undefined,
"global": undefined,
"helpLink": undefined,
Expand All @@ -94,6 +96,7 @@ test('loadData', async () => {
"default": undefined,
"defaultAvailable": undefined,
"description": "Cocoa",
"duplicates": undefined,
"file": undefined,
"global": undefined,
"helpLink": undefined,
Expand Down Expand Up @@ -122,6 +125,7 @@ test('loadData', async () => {
"default": null,
"defaultAvailable": false,
"description": "Nearby",
"duplicates": undefined,
"file": undefined,
"global": false,
"helpLink": undefined,
Expand All @@ -143,6 +147,7 @@ test('loadData', async () => {
"default": undefined,
"defaultAvailable": undefined,
"description": "Cocoa",
"duplicates": undefined,
"file": undefined,
"global": undefined,
"helpLink": undefined,
Expand Down Expand Up @@ -172,6 +177,7 @@ test('loadData', async () => {
"default": undefined,
"defaultAvailable": undefined,
"description": "NADA",
"duplicates": undefined,
"file": undefined,
"global": undefined,
"helpLink": undefined,
Expand Down Expand Up @@ -516,4 +522,39 @@ test('addToNode_Malformed', () => {
comp.addToNode({name: 'test'}, {}, ['parent'], {name: 'test'});
};
expect(closure).toThrow("Setting name 'test' conflicts with another similarly named setting");
});

test('toggleDuplicate', () => {
const setting = {
id: "a.b.c",
name: "c",
duplicates: true,
}
expect(comp.showDuplicate).toBe(false)
comp.toggleDuplicate(setting)
expect(comp.duplicateId).toBe("c_dup");
expect(comp.showDuplicate).toBe(true)
});

test('duplicate', () => {
const setting = {
id: "a.b.c",
name: "c",
duplicates: true,
}
const setting2 = {
id: "a.b.c",
name: "c",
duplicates: true,
}
global.structuredClone = jest.fn().mockReturnValueOnce(setting2);
comp.settings = [setting];
comp.showDuplicate = true;
comp.duplicateId = "foo"
expect(comp.settings.length).toBe(1);
comp.duplicate(setting);
expect(comp.showDuplicate).toBe(false);
expect(comp.settings.length).toBe(2);
expect(comp.settings[1].id).toBe("a.b.foo");
expect(comp.settings[1].name).toBe("foo");
});
1 change: 1 addition & 0 deletions model/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type Setting struct {
HelpLink string `json:"helpLink"`
Syntax string `json:"syntax"`
ForcedType string `json:"forcedType"`
Duplicates bool `json:"duplicates"`
}

func NewSetting(Id string) *Setting {
Expand Down
2 changes: 2 additions & 0 deletions server/modules/salt/saltstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,8 @@ func (store *Saltstore) updateSettingWithAnnotation(setting *model.Setting, anno
setting.Value = setting.Default
}
}
case "duplicates":
setting.Duplicates = value.(bool)
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions server/modules/salt/saltstore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,7 @@ func TestUpdateSettingWithAnnotation(tester *testing.T) {
annotations["regexFailureMessage"] = "My Failure Message"
annotations["helpLink"] = "My help link"
annotations["syntax"] = "yaml"
annotations["duplicates"] = true

assert.False(tester, setting.Multiline)
salt.updateSettingWithAnnotation(setting, annotations)
Expand All @@ -1078,6 +1079,7 @@ func TestUpdateSettingWithAnnotation(tester *testing.T) {
assert.Equal(tester, "some default", setting.Default)
assert.Equal(tester, "some local", setting.Value)
assert.Equal(tester, "yaml", setting.Syntax)
assert.True(tester, setting.Duplicates)
}

func TestManageUser_AddUser(tester *testing.T) {
Expand Down

0 comments on commit a199d8b

Please sign in to comment.