Skip to content

Commit

Permalink
fix: Validation of groups
Browse files Browse the repository at this point in the history
  • Loading branch information
wojtekzyla committed Sep 27, 2022
1 parent 23fd776 commit 8ea0bfe
Show file tree
Hide file tree
Showing 13 changed files with 389 additions and 264 deletions.
9 changes: 7 additions & 2 deletions frontend/packages/manager/src/Manager.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ProfileContxtProvider } from "./store/profile-contxt";
import { InventoryContextProvider } from "./store/inventory-contxt";
import { GroupContextProvider } from "./store/group-contxt";
import { ButtonsContextProvider } from "./store/buttons-contx";
import { InventoryDevicesValidationContxtProvider } from "./store/inventory-devices-contxt";

function Uncontrolled() {
return (
Expand All @@ -21,14 +22,18 @@ function Uncontrolled() {
<TabLayout.Panel label="Inventory" panelId="two">
<ButtonsContextProvider>
<InventoryContextProvider>
<InventoryPage />
<InventoryDevicesValidationContxtProvider>
<InventoryPage />
</InventoryDevicesValidationContxtProvider>
</InventoryContextProvider>
</ButtonsContextProvider>
</TabLayout.Panel>
<TabLayout.Panel label="Groups" panelId="three">
<ButtonsContextProvider>
<GroupContextProvider>
<GroupsPage />
<InventoryDevicesValidationContxtProvider>
<GroupsPage />
</InventoryDevicesValidationContxtProvider>
</GroupContextProvider>
</ButtonsContextProvider>
</TabLayout.Panel>
Expand Down
122 changes: 122 additions & 0 deletions frontend/packages/manager/src/components/ValidateInventoryAndGroup.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import React from 'react';

const validateInventoryAndGroup = (validationObj) => {

let errors = {
groupName: [],
address: [],
port: [],
community: [],
secret: [],
securityEngine: [],
walkInterval: [],
profiles: []
};
let isValid = true;
console.log(validationObj);

// Validate group name
if (validationObj.hasOwnProperty("groupName")){
if (validationObj.groupName.length === 0){
errors.groupName.push("Group name is required");
isValid = false;
}else if (!validationObj.groupName.match(/^[a-zA-Z0-9_-]+$/)){
isValid = false;
errors.groupName.push("Group name can consist only of upper and lower english letters, " +
"numbers and two special characters: '-' and '_'. No spaces are allowed.");
}
}

// Validate address
if (validationObj.hasOwnProperty("address")){
if (validationObj.address.length === 0){
let err = ((validationObj.hasOwnProperty("onlyAdress")) ? "Address is required" : "Address or Group is required")
errors.address.push(err);
isValid = false;
}else if (Number.isInteger(Number(validationObj.address.charAt(0))) || validationObj.hasOwnProperty("onlyAdress")){
let doesMatch = validationObj.address.match(/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/);
let octetsValid = true;
if (doesMatch){
let octets = validationObj.address.split(".");
for (const octet of octets){
if (Number(octet) < 0 || Number(octet) > 255) {
octetsValid = false;
break;
}
}
}
if(!doesMatch || !octetsValid){
isValid = false;
errors.address.push("Provided address isn't a valid IPv4 address")
}
}else{
if (!validationObj.address.match(/^[a-zA-Z0-9_-]+$/)){
isValid = false;
errors.address.push("Group name can consist only of upper and lower english letters, " +
"numbers and two special characters: '-' and '_'. No spaces are allowed.");
}
}
}

// Validate port
if (validationObj.hasOwnProperty("port")){
if (!(Number.isInteger(validationObj.port) && validationObj.port > 0 && validationObj.port < 65535)){
isValid = false;
errors.port.push("Port number must be an integer in range 1-65535");
}
}

// Validate community
if (validationObj.hasOwnProperty("community")){
if (validationObj.community.length > 0 && !validationObj.community.match(/^[.a-zA-Z0-9_-]+$/)){
isValid = false;
errors.community.push("Community can consist only of upper and lower english letters, " +
"numbers and three special characters: '.' '-' and '_'. No spaces are allowed.");
}
}

// Validate secret
if (validationObj.hasOwnProperty("version") && validationObj.hasOwnProperty("secret")){
if (validationObj.version === "3" && validationObj.secret.length === 0){
isValid = false;
errors.secret.push("When using SNMP version 3, secret must be specified");
}
if (validationObj.secret.length > 0 && !validationObj.secret.match(/^[.a-zA-Z0-9_-]+$/)){
isValid = false;
errors.secret.push("Secret can consist only of upper and lower english letters, " +
"numbers and three special characters: '.' '-' and '_'. No spaces are allowed.");
}
}

// Validate securityEngine
if (validationObj.hasOwnProperty("securityEngine")){
if (validationObj.securityEngine.length > 0 && !validationObj.securityEngine.match(/^([A-F0-9]{10,64}|[a-f0-9]{10,64})$/)){
isValid = false;
errors.securityEngine.push("If provided, Security Engine can consists only of 10-64 characters in " +
"hexadecimal notation. All letter must be either upper or lowe case.");
}
}

// Validate Walk Interval
if (validationObj.hasOwnProperty("walkInterval")){
if (!(Number.isInteger(validationObj.walkInterval) && validationObj.walkInterval >= 1800)){
isValid = false;
errors.walkInterval.push("Walk Interval number must be an integer greater than or equal 1800");
}
}

// Validate profiles
if (validationObj.hasOwnProperty("profiles") && validationObj.hasOwnProperty("initProfiles")){
console.log(validationObj.initProfiles);
for (const prof of validationObj.profiles){
if (!validationObj.initProfiles.includes(prof)){
isValid = false;
errors.profiles.push(`${prof} is an invalid profile`);
}
}
}

return [isValid, errors];
};

export default validateInventoryAndGroup;
68 changes: 47 additions & 21 deletions frontend/packages/manager/src/components/groups/AddDeviceModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ import Select from '@splunk/react-ui/Select';
import Text from '@splunk/react-ui/Text';
import GroupContext from "../../store/group-contxt";
import axios from "axios";
import validateInventoryAndGroup from "../ValidateInventoryAndGroup";
import InventoryDevicesValidationContxt from "../../store/inventory-devices-contxt";
import { createDOMID } from '@splunk/ui-utils/id';
import P from '@splunk/react-ui/Paragraph';


function AddDeviceModal(){
const GrCtx = useContext(GroupContext);
const ValCtx = useContext(InventoryDevicesValidationContxt);

const handleRequestClose = () => {
ValCtx.resetAllErrors();
GrCtx.resetDevice();
GrCtx.setAddDeviceOpen(false);
}
Expand Down Expand Up @@ -53,42 +59,58 @@ function AddDeviceModal(){
})
};

const handleApply = useCallback(
(e) => {
const handleApply = useCallback((e) => {
console.log("aplying")
const deviceObj = {
address: GrCtx.address,
port: GrCtx.port,
version: GrCtx.version,
community: GrCtx.community,
secret: GrCtx.secret,
securityEngine: GrCtx.securityEngine,
groupId: GrCtx.groupID,
onlyAdress: true,
};
console.log(deviceObj);
const validation = validateInventoryAndGroup(deviceObj)
delete deviceObj.onlyAdress;

if (validation[0]){
// form is valid
let deviceObj = {
address: GrCtx.address,
port: GrCtx.port,
version: GrCtx.version,
community: GrCtx.community,
secret: GrCtx.secret,
security_engine: GrCtx.securityEngine,
group_id: GrCtx.groupID
}

ValCtx.resetAllErrors();
if (GrCtx.isDeviceEdit){
console.log("updating device")
updateDevice(deviceObj, GrCtx.deviceID)
}else{
console.log("posting new device")
postDevice(deviceObj);
}

GrCtx.setEditedGroupID(GrCtx.groupID);
GrCtx.resetDevice();
GrCtx.setAddDeviceOpen(false);
GrCtx.makeGroupsChange();
}else{
// form is invalid
const errors = validation[1];
for (const property in errors) {
if (errors[property].length > 0){
ValCtx.setErrors(property, errors[property]);
}else {
ValCtx.resetErrors(property);
};
};
};
},
[GrCtx.address, GrCtx.port, GrCtx.version, GrCtx.community, GrCtx.secret, GrCtx.securityEngine, GrCtx.isEdit,
GrCtx.deviceID, GrCtx.setAddDeviceOpen, GrCtx.groupID]
);

const validation_group = {
const validationGroup = {
display: "flex",
flexDirection: "column"
};

const validation_message = {
const validationMessage = {
color: "red"
};

Expand All @@ -98,13 +120,15 @@ function AddDeviceModal(){
<Modal.Header title={((GrCtx.isDeviceEdit) ? `Edit device` : `Add new device to group ${GrCtx.groupName}`)} onRequestClose={handleRequestClose} />
<Modal.Body>
<ControlGroup label="IP address/Group">
<div style={validation_group}>
<div style={validationGroup}>
<Text value={GrCtx.address} onChange={handleChangeAddress}/>
{((ValCtx.addressErrors) ? ValCtx.addressErrors.map((el) => <P key={createDOMID()} style={validationMessage}>{el}</P>) : <P/>)}
</div>
</ControlGroup>
<ControlGroup label="Port" >
<div style={validation_group}>
<div style={validationGroup}>
<Number value={GrCtx.port} onChange={handleChangePort}/>
{((ValCtx.portErrors) ? ValCtx.portErrors.map((el) => <P key={createDOMID()} style={validationMessage}>{el}</P>) : <P/>)}
</div>
</ControlGroup>

Expand All @@ -121,21 +145,23 @@ function AddDeviceModal(){
</ControlGroup>

<ControlGroup label="Community">
<div style={validation_group}>
<div style={validationGroup}>
<Text value={GrCtx.community} onChange={handleChangeCommunity}/>
{((ValCtx.communityErrors) ? ValCtx.communityErrors.map((el) => <P key={createDOMID()} style={validationMessage}>{el}</P>) : <P/>)}
</div>
</ControlGroup>


<ControlGroup label="Secret">
<div style={validation_group}>
<div style={validationGroup}>
<Text value={GrCtx.secret} onChange={handleChangeSecret}/>
{((ValCtx.secretErrors) ? ValCtx.secretErrors.map((el) => <P key={createDOMID()} style={validationMessage}>{el}</P>) : <P/>)}
</div>
</ControlGroup>

<ControlGroup label="Security Engine">
<div style={validation_group}>
<div style={validationGroup}>
<Text value={GrCtx.securityEngine} onChange={handleChangeSecurityEngine}/>
{((ValCtx.securityEngineErrors) ? ValCtx.securityEngineErrors.map((el) => <P key={createDOMID()} style={validationMessage}>{el}</P>) : <P/>)}
</div>
</ControlGroup>

Expand Down
55 changes: 43 additions & 12 deletions frontend/packages/manager/src/components/groups/AddGroupModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ import Multiselect from '@splunk/react-ui/Multiselect';
import Text from '@splunk/react-ui/Text';
import GroupContext from "../../store/group-contxt";
import axios from "axios";
import validateInventory from "../inventory/ValidateInventory";
import validateInventoryAndGroup from "../ValidateInventoryAndGroup";
import InventoryDevicesValidationContxt from "../../store/inventory-devices-contxt";
import { createDOMID } from '@splunk/ui-utils/id';

function AddGroupModal() {
const GrCtx = useContext(GroupContext);
const ValCtx = useContext(InventoryDevicesValidationContxt);

const postGroup = (groupObj) => {
axios.post('http://127.0.0.1:5000/groups/add', groupObj)
Expand All @@ -27,35 +30,63 @@ function AddGroupModal() {

const handleRequestClose = () => {
GrCtx.setAddGroupOpen(false);
ValCtx.resetAllErrors();
GrCtx.addGroupModalToggle?.current?.focus(); // Must return focus to the invoking element when the modal closes
};

const handleRequestSubmit = () => {
const groupObj = {
groupName: GrCtx.groupName
};
const validation = validateInventoryAndGroup(groupObj)

let groupObj = {
group_name: GrCtx.groupName
if (validation[0]){
// form is valid
ValCtx.resetAllErrors();
if (GrCtx.isGroupEdit){
updateGroup(groupObj, GrCtx.groupID);
}else {
postGroup(groupObj);
};
if (GrCtx.isGroupEdit){
updateGroup(groupObj, GrCtx.groupID);
}else {
postGroup(groupObj);
};
GrCtx.makeGroupsChange();
GrCtx.setAddGroupOpen(false);
GrCtx.addGroupModalToggle?.current?.focus();
GrCtx.makeGroupsChange();
GrCtx.setAddGroupOpen(false);
GrCtx.addGroupModalToggle?.current?.focus();
}else{
// form is invalid
const errors = validation[1];
for (const property in errors) {
if (errors[property].length > 0){
ValCtx.setErrors(property, errors[property]);
}else {
ValCtx.resetErrors(property);
};
};
}
};

const handleGroupNameChange = useCallback((e, { value: val }) => {
GrCtx.setGroupName(val);
}, [GrCtx.setGroupName]);

const validationGroup = {
display: "flex",
flexDirection: "column"
};

const validationMessage = {
color: "red"
};

return (
<div>
<Modal onRequestClose={handleRequestClose} open={GrCtx.addGroupOpen} style={{ width: '600px' }}>
<Modal.Header title="Add a new group" onRequestClose={handleRequestClose} />
<Modal.Body>
<ControlGroup label="Group Name">
<Text value={GrCtx.groupName} onChange={handleGroupNameChange}/>
<div style={validationGroup}>
<Text value={GrCtx.groupName} onChange={handleGroupNameChange}/>
{((ValCtx.groupNameErrors) ? ValCtx.groupNameErrors.map((el) => <P key={createDOMID()} style={validationMessage}>{el}</P>) : <P/>)}
</div>
</ControlGroup>
</Modal.Body>
<Modal.Footer>
Expand Down
Loading

0 comments on commit 8ea0bfe

Please sign in to comment.