feat(security): add security policy management and IP intelligence operations to the ops UI
This commit is contained in:
+236
-3
@@ -54,6 +54,7 @@ export interface INetworkState {
|
||||
topIPs: Array<{ ip: string; count: number }>;
|
||||
topIPsByBandwidth: Array<{ ip: string; count: number; bwIn: number; bwOut: number }>;
|
||||
throughputByIP: Array<{ ip: string; in: number; out: number }>;
|
||||
ipIntelligence: interfaces.data.IIpIntelligenceRecord[];
|
||||
domainActivity: interfaces.data.IDomainActivity[];
|
||||
throughputHistory: Array<{ timestamp: number; in: number; out: number }>;
|
||||
requestsPerSecond: number;
|
||||
@@ -66,6 +67,16 @@ export interface INetworkState {
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
export interface ISecurityPolicyState {
|
||||
rules: interfaces.data.ISecurityBlockRule[];
|
||||
ipIntelligence: interfaces.data.IIpIntelligenceRecord[];
|
||||
compiledPolicy: interfaces.data.ISecurityCompiledPolicy | null;
|
||||
auditEvents: interfaces.data.ISecurityPolicyAuditEvent[];
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
lastUpdated: number;
|
||||
}
|
||||
|
||||
export interface ICertificateState {
|
||||
certificates: interfaces.requests.ICertificateInfo[];
|
||||
summary: { total: number; valid: number; expiring: number; expired: number; failed: number; unknown: number };
|
||||
@@ -164,6 +175,7 @@ export const networkStatePart = await appState.getStatePart<INetworkState>(
|
||||
topIPs: [],
|
||||
topIPsByBandwidth: [],
|
||||
throughputByIP: [],
|
||||
ipIntelligence: [],
|
||||
domainActivity: [],
|
||||
throughputHistory: [],
|
||||
requestsPerSecond: 0,
|
||||
@@ -178,6 +190,20 @@ export const networkStatePart = await appState.getStatePart<INetworkState>(
|
||||
'soft'
|
||||
);
|
||||
|
||||
export const securityPolicyStatePart = await appState.getStatePart<ISecurityPolicyState>(
|
||||
'securityPolicy',
|
||||
{
|
||||
rules: [],
|
||||
ipIntelligence: [],
|
||||
compiledPolicy: null,
|
||||
auditEvents: [],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
lastUpdated: 0,
|
||||
},
|
||||
'soft',
|
||||
);
|
||||
|
||||
export const emailOpsStatePart = await appState.getStatePart<IEmailOpsState>(
|
||||
'emailOps',
|
||||
{
|
||||
@@ -517,9 +543,18 @@ export const fetchNetworkStatsAction = networkStatePart.createAction(async (stat
|
||||
interfaces.requests.IReq_GetNetworkStats
|
||||
>('/typedrequest', 'getNetworkStats');
|
||||
|
||||
const networkStatsResponse = await networkStatsRequest.fire({
|
||||
identity: context.identity,
|
||||
});
|
||||
const ipIntelligenceRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||
interfaces.requests.IReq_ListIpIntelligence
|
||||
>('/typedrequest', 'listIpIntelligence');
|
||||
|
||||
const [networkStatsResponse, ipIntelligenceResponse] = await Promise.all([
|
||||
networkStatsRequest.fire({
|
||||
identity: context.identity,
|
||||
}),
|
||||
ipIntelligenceRequest.fire({
|
||||
identity: context.identity,
|
||||
}),
|
||||
]);
|
||||
|
||||
// Use the connections data for the connection list
|
||||
// and network stats for throughput and IP analytics
|
||||
@@ -561,6 +596,7 @@ export const fetchNetworkStatsAction = networkStatePart.createAction(async (stat
|
||||
topIPs: networkStatsResponse.topIPs || [],
|
||||
topIPsByBandwidth: networkStatsResponse.topIPsByBandwidth || [],
|
||||
throughputByIP: networkStatsResponse.throughputByIP || [],
|
||||
ipIntelligence: ipIntelligenceResponse.records || [],
|
||||
domainActivity: networkStatsResponse.domainActivity || [],
|
||||
throughputHistory: networkStatsResponse.throughputHistory || [],
|
||||
requestsPerSecond: networkStatsResponse.requestsPerSecond || 0,
|
||||
@@ -582,6 +618,182 @@ export const fetchNetworkStatsAction = networkStatePart.createAction(async (stat
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Security Policy Actions
|
||||
// ============================================================================
|
||||
|
||||
export const fetchSecurityPolicyAction = securityPolicyStatePart.createAction(
|
||||
async (statePartArg): Promise<ISecurityPolicyState> => {
|
||||
const context = getActionContext();
|
||||
const currentState = statePartArg.getState()!;
|
||||
if (!context.identity) return currentState;
|
||||
|
||||
try {
|
||||
const rulesRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||
interfaces.requests.IReq_ListSecurityBlockRules
|
||||
>('/typedrequest', 'listSecurityBlockRules');
|
||||
const intelligenceRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||
interfaces.requests.IReq_ListIpIntelligence
|
||||
>('/typedrequest', 'listIpIntelligence');
|
||||
const compiledPolicyRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||
interfaces.requests.IReq_GetCompiledSecurityPolicy
|
||||
>('/typedrequest', 'getCompiledSecurityPolicy');
|
||||
const auditRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||
interfaces.requests.IReq_ListSecurityPolicyAudit
|
||||
>('/typedrequest', 'listSecurityPolicyAudit');
|
||||
|
||||
const [rulesResponse, intelligenceResponse, compiledPolicyResponse, auditResponse] = await Promise.all([
|
||||
rulesRequest.fire({ identity: context.identity }),
|
||||
intelligenceRequest.fire({ identity: context.identity }),
|
||||
compiledPolicyRequest.fire({ identity: context.identity }),
|
||||
auditRequest.fire({ identity: context.identity, limit: 100 }),
|
||||
]);
|
||||
|
||||
return {
|
||||
rules: rulesResponse.rules || [],
|
||||
ipIntelligence: intelligenceResponse.records || [],
|
||||
compiledPolicy: compiledPolicyResponse.policy,
|
||||
auditEvents: auditResponse.events || [],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
lastUpdated: Date.now(),
|
||||
};
|
||||
} catch (error: unknown) {
|
||||
return {
|
||||
...currentState,
|
||||
isLoading: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to fetch security policy',
|
||||
};
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export const createSecurityBlockRuleAction = securityPolicyStatePart.createAction<{
|
||||
type: interfaces.data.TSecurityBlockRuleType;
|
||||
value: string;
|
||||
matchMode?: interfaces.data.TSecurityBlockRuleMatchMode;
|
||||
reason?: string;
|
||||
enabled?: boolean;
|
||||
}>(async (statePartArg, dataArg, actionContext): Promise<ISecurityPolicyState> => {
|
||||
const context = getActionContext();
|
||||
const currentState = statePartArg.getState()!;
|
||||
if (!context.identity) return currentState;
|
||||
|
||||
try {
|
||||
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||
interfaces.requests.IReq_CreateSecurityBlockRule
|
||||
>('/typedrequest', 'createSecurityBlockRule');
|
||||
|
||||
const response = await request.fire({
|
||||
identity: context.identity,
|
||||
type: dataArg.type,
|
||||
value: dataArg.value,
|
||||
matchMode: dataArg.matchMode,
|
||||
reason: dataArg.reason,
|
||||
enabled: dataArg.enabled,
|
||||
});
|
||||
|
||||
if (!response.success) {
|
||||
return { ...currentState, error: response.message || 'Failed to create security block rule' };
|
||||
}
|
||||
|
||||
return await actionContext!.dispatch(fetchSecurityPolicyAction, null);
|
||||
} catch (error: unknown) {
|
||||
return {
|
||||
...currentState,
|
||||
error: error instanceof Error ? error.message : 'Failed to create security block rule',
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
export const updateSecurityBlockRuleAction = securityPolicyStatePart.createAction<{
|
||||
id: string;
|
||||
value?: string;
|
||||
matchMode?: interfaces.data.TSecurityBlockRuleMatchMode;
|
||||
reason?: string;
|
||||
enabled?: boolean;
|
||||
}>(async (statePartArg, dataArg, actionContext): Promise<ISecurityPolicyState> => {
|
||||
const context = getActionContext();
|
||||
const currentState = statePartArg.getState()!;
|
||||
if (!context.identity) return currentState;
|
||||
|
||||
try {
|
||||
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||
interfaces.requests.IReq_UpdateSecurityBlockRule
|
||||
>('/typedrequest', 'updateSecurityBlockRule');
|
||||
|
||||
const response = await request.fire({
|
||||
identity: context.identity,
|
||||
id: dataArg.id,
|
||||
value: dataArg.value,
|
||||
matchMode: dataArg.matchMode,
|
||||
reason: dataArg.reason,
|
||||
enabled: dataArg.enabled,
|
||||
});
|
||||
|
||||
if (!response.success) {
|
||||
return { ...currentState, error: response.message || 'Failed to update security block rule' };
|
||||
}
|
||||
|
||||
return await actionContext!.dispatch(fetchSecurityPolicyAction, null);
|
||||
} catch (error: unknown) {
|
||||
return {
|
||||
...currentState,
|
||||
error: error instanceof Error ? error.message : 'Failed to update security block rule',
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
export const deleteSecurityBlockRuleAction = securityPolicyStatePart.createAction<string>(
|
||||
async (statePartArg, ruleId, actionContext): Promise<ISecurityPolicyState> => {
|
||||
const context = getActionContext();
|
||||
const currentState = statePartArg.getState()!;
|
||||
if (!context.identity) return currentState;
|
||||
|
||||
try {
|
||||
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||
interfaces.requests.IReq_DeleteSecurityBlockRule
|
||||
>('/typedrequest', 'deleteSecurityBlockRule');
|
||||
|
||||
const response = await request.fire({ identity: context.identity, id: ruleId });
|
||||
if (!response.success) {
|
||||
return { ...currentState, error: response.message || 'Failed to delete security block rule' };
|
||||
}
|
||||
|
||||
return await actionContext!.dispatch(fetchSecurityPolicyAction, null);
|
||||
} catch (error: unknown) {
|
||||
return {
|
||||
...currentState,
|
||||
error: error instanceof Error ? error.message : 'Failed to delete security block rule',
|
||||
};
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export const refreshIpIntelligenceAction = securityPolicyStatePart.createAction<string>(
|
||||
async (statePartArg, ipAddress, actionContext): Promise<ISecurityPolicyState> => {
|
||||
const context = getActionContext();
|
||||
const currentState = statePartArg.getState()!;
|
||||
if (!context.identity) return currentState;
|
||||
|
||||
try {
|
||||
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||
interfaces.requests.IReq_RefreshIpIntelligence
|
||||
>('/typedrequest', 'refreshIpIntelligence');
|
||||
const response = await request.fire({ identity: context.identity, ipAddress });
|
||||
if (!response.success) {
|
||||
return { ...currentState, error: response.message || 'Failed to refresh IP intelligence' };
|
||||
}
|
||||
return await actionContext!.dispatch(fetchSecurityPolicyAction, null);
|
||||
} catch (error: unknown) {
|
||||
return {
|
||||
...currentState,
|
||||
error: error instanceof Error ? error.message : 'Failed to refresh IP intelligence',
|
||||
};
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// ============================================================================
|
||||
// Email Operations Actions
|
||||
// ============================================================================
|
||||
@@ -2665,6 +2877,27 @@ async function dispatchCombinedRefreshActionInner() {
|
||||
isLoading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
try {
|
||||
const intelligenceRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||
interfaces.requests.IReq_ListIpIntelligence
|
||||
>('/typedrequest', 'listIpIntelligence');
|
||||
const intelligenceResponse = await intelligenceRequest.fire({ identity: context.identity });
|
||||
networkStatePart.setState({
|
||||
...networkStatePart.getState()!,
|
||||
ipIntelligence: intelligenceResponse.records || [],
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('IP intelligence refresh failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentView === 'security') {
|
||||
try {
|
||||
await securityPolicyStatePart.dispatchAction(fetchSecurityPolicyAction, null);
|
||||
} catch (error) {
|
||||
console.error('Security policy refresh failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh certificate data if on Domains > Certificates subview
|
||||
|
||||
Reference in New Issue
Block a user