Skip to content

Commit 2618ad8

Browse files
xingzhang-suselsongsuse
authored andcommitted
NVSHAS-10016: UI for supporting export response rules (as CRD)
1 parent db402eb commit 2618ad8

File tree

11 files changed

+337
-44
lines changed

11 files changed

+337
-44
lines changed

admin/src/main/scala/com/neu/api/policy/PolicyApi.scala

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -53,36 +53,61 @@ class PolicyApi(resourceService: PolicyService) extends BaseApi {
5353
}
5454
}
5555
} ~
56-
path("responsePolicy") {
57-
get {
58-
parameter(Symbol("scope").?) { scope =>
59-
Utils.respondWithWebServerHeaders() {
60-
resourceService.getResponsePolicy(tokenId, scope)
56+
pathPrefix("responsePolicy") {
57+
pathEnd {
58+
get {
59+
parameter(Symbol("scope").?) { scope =>
60+
Utils.respondWithWebServerHeaders() {
61+
resourceService.getResponsePolicy(tokenId, scope)
62+
}
6163
}
62-
}
63-
} ~
64-
post {
65-
entity(as[ResponseRulesWrap]) { responseRulesWrap =>
66-
Utils.respondWithWebServerHeaders() {
67-
resourceService.insertResponseRules(tokenId, responseRulesWrap)
64+
} ~
65+
post {
66+
entity(as[ResponseRulesWrap]) { responseRulesWrap =>
67+
Utils.respondWithWebServerHeaders() {
68+
resourceService.insertResponseRules(tokenId, responseRulesWrap)
69+
}
70+
}
71+
} ~
72+
patch {
73+
entity(as[ResponseRuleConfig]) { responseRuleConfig =>
74+
Utils.respondWithWebServerHeaders() {
75+
resourceService.updateResponsePolicy(tokenId, responseRuleConfig)
76+
}
77+
}
78+
} ~
79+
delete {
80+
parameter(Symbol("scope").?, Symbol("id").?) { (scope, id) =>
81+
Utils.respondWithWebServerHeaders() {
82+
resourceService.deleteResponseRule(
83+
tokenId,
84+
scope,
85+
id
86+
)
87+
}
6888
}
6989
}
7090
} ~
71-
patch {
72-
entity(as[ResponseRuleConfig]) { responseRuleConfig =>
73-
Utils.respondWithWebServerHeaders() {
74-
resourceService.updateResponsePolicy(tokenId, responseRuleConfig)
91+
path("export") {
92+
post {
93+
entity(as[ExportedResponseRuleList]) { exportedResponseRuleList =>
94+
Utils.respondWithWebServerHeaders() {
95+
resourceService.exportResponseRuleConfig(tokenId, exportedResponseRuleList)
96+
}
7597
}
7698
}
7799
} ~
78-
delete {
79-
parameter(Symbol("scope").?, Symbol("id").?) { (scope, id) =>
80-
Utils.respondWithWebServerHeaders() {
81-
resourceService.deleteResponseRule(
82-
tokenId,
83-
scope,
84-
id
85-
)
100+
path("import") {
101+
post {
102+
headerValueByName("X-Transaction-Id") { transactionId =>
103+
Utils.respondWithWebServerHeaders() {
104+
resourceService.importResponseRuleConfig(tokenId, transactionId)
105+
}
106+
} ~
107+
entity(as[String]) { formData =>
108+
Utils.respondWithWebServerHeaders() {
109+
resourceService.importResponseRuleConfigByFormData(tokenId, formData)
110+
}
86111
}
87112
}
88113
}

admin/src/main/scala/com/neu/model/Policy.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,3 +355,8 @@ case class WorkloadV2(
355355
case class WorkloadsWrapV2(
356356
workloads: Array[WorkloadV2]
357357
)
358+
359+
case class ExportedResponseRuleList(
360+
ids: Array[Int],
361+
remote_export_options: Option[RemoteExportOptions] = None
362+
)

admin/src/main/scala/com/neu/model/PolicyJsonProtocol.scala

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,20 +72,26 @@ object PolicyJsonProtocol extends DefaultJsonProtocol with LazyLogging {
7272
given ruleIdsFormat: RootJsonFormat[RuleIds] = jsonFormat1(RuleIds.apply)
7373
given promoteConfigFormat: RootJsonFormat[PromoteConfig] = jsonFormat1(PromoteConfig.apply)
7474

75-
given workloadBriefV2Format: RootJsonFormat[WorkloadBriefV2] = jsonFormat14(
75+
given workloadBriefV2Format: RootJsonFormat[WorkloadBriefV2] = jsonFormat14(
7676
WorkloadBriefV2.apply
7777
)
78-
given workloadSecurityV2Format: RootJsonFormat[WorkloadSecurityV2] = jsonFormat10(
78+
given workloadSecurityV2Format: RootJsonFormat[WorkloadSecurityV2] = jsonFormat10(
7979
WorkloadSecurityV2.apply
8080
)
81-
given workloadRtAttribesV2Format: RootJsonFormat[WorkloadRtAttribesV2] = jsonFormat12(
81+
given workloadRtAttribesV2Format: RootJsonFormat[WorkloadRtAttribesV2] = jsonFormat12(
8282
WorkloadRtAttribesV2.apply
8383
)
84-
given workloadV2ChildFormat: RootJsonFormat[WorkloadV2Child] = jsonFormat12(
84+
given workloadV2ChildFormat: RootJsonFormat[WorkloadV2Child] = jsonFormat12(
8585
WorkloadV2Child.apply
8686
)
87-
given workloadV2Format: RootJsonFormat[WorkloadV2] = jsonFormat13(WorkloadV2.apply)
88-
given workloadsWrapV2Format: RootJsonFormat[WorkloadsWrapV2] = jsonFormat1(WorkloadsWrapV2.apply)
87+
given workloadV2Format: RootJsonFormat[WorkloadV2] = jsonFormat13(WorkloadV2.apply)
88+
given workloadsWrapV2Format: RootJsonFormat[WorkloadsWrapV2] = jsonFormat1(WorkloadsWrapV2.apply)
89+
given remoteExportOptionsFormat: RootJsonFormat[RemoteExportOptions] = jsonFormat3(
90+
RemoteExportOptions.apply
91+
)
92+
given exportedResponseRuleListFmt: RootJsonFormat[ExportedResponseRuleList] = jsonFormat2(
93+
ExportedResponseRuleList.apply
94+
)
8995

9096
def policyToJson(policy: Policy): String = policy.toJson.compactPrint
9197
def policy2ToJson(policy2: Policy2): String = policy2.toJson.compactPrint
@@ -118,6 +124,9 @@ object PolicyJsonProtocol extends DefaultJsonProtocol with LazyLogging {
118124
def jsonToWorkloadsWrapV2(response: String): WorkloadsWrapV2 =
119125
response.parseJson.convertTo[WorkloadsWrapV2]
120126

127+
def exportedResponseRuleToJson(list: ExportedResponseRuleList): String =
128+
list.toJson.compactPrint
129+
121130
def groupToNode(group: Group): Node =
122131
Node(
123132
group.name,

admin/src/main/scala/com/neu/service/policy/PolicyService.scala

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,76 @@ class PolicyService() extends BaseService with DefaultJsonFormats with LazyLoggi
168168
)
169169
}
170170

171+
def exportResponseRuleConfig(
172+
tokenId: String,
173+
exportedResponseRuleList: ExportedResponseRuleList
174+
): Route = {
175+
logger.info("Export sensors")
176+
complete {
177+
RestClient.httpRequestWithHeader(
178+
s"${baseClusterUri(tokenId)}/file/dlp",
179+
POST,
180+
exportedResponseRuleToJson(exportedResponseRuleList),
181+
tokenId
182+
)
183+
}
184+
}
185+
186+
def importResponseRuleConfig(tokenId: String, transactionId: String): Route =
187+
complete {
188+
try {
189+
val cachedBaseUrl = AuthenticationManager.getBaseUrl(tokenId)
190+
val baseUrl = cachedBaseUrl.fold {
191+
baseClusterUri(tokenId)
192+
}(cachedBaseUrl => cachedBaseUrl)
193+
AuthenticationManager.setBaseUrl(tokenId, baseUrl)
194+
logger.info("test baseUrl: {}", baseUrl)
195+
logger.info("Transaction ID(Post): {}", transactionId)
196+
RestClient.httpRequestWithHeader(
197+
s"$baseUrl/file/response/rule/config",
198+
POST,
199+
"",
200+
tokenId,
201+
Some(transactionId)
202+
)
203+
} catch {
204+
case NonFatal(e) =>
205+
RestClient.handleError(
206+
timeOutStatus,
207+
authenticationFailedStatus,
208+
serverErrorStatus,
209+
e
210+
)
211+
}
212+
}
213+
214+
def importResponseRuleConfigByFormData(tokenId: String, formData: String): Route =
215+
complete {
216+
try {
217+
val baseUrl = baseClusterUri(tokenId)
218+
AuthenticationManager.setBaseUrl(tokenId, baseUrl)
219+
logger.info("test baseUrl: {}", baseUrl)
220+
logger.info("No Transaction ID(Post)")
221+
val lines: Array[String] = formData.split("\n")
222+
val contentLines = lines.slice(4, lines.length - 1)
223+
val bodyData = contentLines.mkString("\n")
224+
RestClient.httpRequestWithHeader(
225+
s"$baseUrl/file/response/rule/config",
226+
POST,
227+
bodyData,
228+
tokenId
229+
)
230+
} catch {
231+
case NonFatal(e) =>
232+
RestClient.handleError(
233+
timeOutStatus,
234+
authenticationFailedStatus,
235+
serverErrorStatus,
236+
e
237+
)
238+
}
239+
}
240+
171241
def getPolicy(
172242
tokenId: String,
173243
scope: Option[String],

admin/webapp/websrc/app/common/constants/global.constant.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ export class GlobalConstant {
281281
WAF: 'cfgWafExport.yaml',
282282
COMPLIANCE_PROFILE: 'cfgComplianceProfileExport.yaml',
283283
VUL_PROFILE: 'cfgVulProfileExport.yaml',
284+
RESPONSE_RULES: 'cfgReponseRulesExport.yaml',
284285
};
285286

286287
public static SIGSTORE_ATTRIBUTE = {

admin/webapp/websrc/app/common/constants/path.constant.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ export class PathConstant {
7373
public static SNIFF_URL = 'sniffer'; //graph
7474
public static SNIFF_PCAP_URL = 'sniffer/pcap'; //graph
7575
public static RESPONSE_POLICY_URL = 'responsePolicy'; //policy
76+
public static RESPONSE_RULE_IMPORT_URL = 'responsePolicy/import';
77+
public static RESPONSE_RULE_EXPORT_URL = 'responsePolicy/export';
7678
public static UNQUARANTINE_URL = 'unquarantine'; //policy
7779
public static CONDITION_OPTION_URL = 'conditionOption'; //policy
7880
public static RESPONSE_RULE_URL = 'responseRule'; //policy

admin/webapp/websrc/app/common/services/response-rules.service.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,23 @@ export class ResponseRulesService {
4040
{
4141
headerName: this.translate.instant('responsePolicy.gridHeader.ID'),
4242
field: 'id',
43+
headerCheckboxSelection: params =>
44+
source ===
45+
GlobalConstant.NAV_SOURCE.SELF,
46+
headerCheckboxSelectionFilteredOnly: params =>
47+
params.context.componentParent.source ===
48+
GlobalConstant.NAV_SOURCE.SELF,
49+
cellRenderer: params => {
50+
if (params.value)
51+
return `<span class="${
52+
source === GlobalConstant.NAV_SOURCE.SELF
53+
? 'left-margin-32'
54+
: ''
55+
}">
56+
${params.value}
57+
</span>`;
58+
return false;
59+
},
4360
width: 80,
4461
minWidth: 80,
4562
maxWidth: 80,
@@ -101,13 +118,27 @@ export class ResponseRulesService {
101118
},
102119
];
103120

121+
if (source === GlobalConstant.NAV_SOURCE.SELF) {
122+
columnDefs[0]['checkboxSelection'] = params => {
123+
if (params.data)
124+
return (
125+
params.context.componentParent.source ===
126+
GlobalConstant.NAV_SOURCE.SELF &&
127+
params.data.cfg_type !== GlobalConstant.CFG_TYPE.FED
128+
);
129+
return false;
130+
};
131+
}
132+
104133
let gridOptions = this.utils.createGridOptions(columnDefs, this.$win);
105134
gridOptions.rowClassRules = {
106135
'disabled-row': params => {
107136
if (!params.data) return false;
108137
return !!params.data.disable;
109138
},
110139
};
140+
gridOptions.rowSelection =
141+
source === GlobalConstant.NAV_SOURCE.SELF ? 'multiple' : 'single';
111142
return gridOptions;
112143
}
113144

@@ -402,4 +433,13 @@ export class ResponseRulesService {
402433
.pipe();
403434
return forkJoin([unquarantineReq, removeReq]).pipe();
404435
}
436+
437+
getResponseRuleConfigFileData(payload) {
438+
return GlobalVariable.http
439+
.post(PathConstant.RESPONSE_RULE_EXPORT_URL, payload, {
440+
observe: 'response',
441+
responseType: 'text',
442+
})
443+
.pipe();
444+
}
405445
}

admin/webapp/websrc/app/routes/components/response-rules/response-rules.component.html

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,38 @@ <h1 class="font-weight-light" id="response rules title">
66
{{ 'responsePolicy.head.TITLE' | translate }}
77
</h1>
88
</div>
9-
<app-loading-button
10-
(btnClick)="refresh()"
11-
[disabled]="!!(refreshing$ | async)"
12-
[appearance]="'mat-button'"
13-
[buttonClasses]="'d-flex justify-content-center align-items-center'"
14-
[disabled]="false"
15-
[iconClasses]="'eos-icons icon-18'"
16-
[iconName]="'refresh'"
17-
[id]="'response-rule-refresh-button'"
18-
[loading]="!!(refreshing$ | async)"
19-
[text]="'network.REFRESH' | translate"
20-
[type]="'button'">
21-
</app-loading-button>
9+
<div class="d-flex align-items-center justify-content-end">
10+
<button
11+
mat-button
12+
aria-label="export WAF SENSORS"
13+
*appDisplayControl="'review_rule'"
14+
[disabled]="!selectedRules || selectedRules.length === 0"
15+
(click)="exportResponseRules()">
16+
<em class="eos-icons icon-18">download</em>
17+
{{ 'admissionControl.EXPORT' | translate }}
18+
</button>
19+
<button
20+
mat-button
21+
aria-label="import WAF SENSORS"
22+
(click)="openImportResponseRulesModal()"
23+
*appDisplayControl="'write_dlp_rule'">
24+
<em class="eos-icons icon-18">upload</em>
25+
{{ 'admissionControl.IMPORT' | translate }}
26+
</button>
27+
<app-loading-button
28+
(btnClick)="refresh()"
29+
[disabled]="!!(refreshing$ | async)"
30+
[appearance]="'mat-button'"
31+
[buttonClasses]="'d-flex justify-content-center align-items-center'"
32+
[disabled]="false"
33+
[iconClasses]="'eos-icons icon-18'"
34+
[iconName]="'refresh'"
35+
[id]="'response-rule-refresh-button'"
36+
[loading]="!!(refreshing$ | async)"
37+
[text]="'network.REFRESH' | translate"
38+
[type]="'button'">
39+
</app-loading-button>
40+
</div>
2241
</div>
2342
<div>
2443
<div

0 commit comments

Comments
 (0)