From 6337b75f0e58722d69e8c589d5a98deed0a8fbeb Mon Sep 17 00:00:00 2001 From: Selton Fiuza <40501884+seltonfiuza@users.noreply.github.com> Date: Sat, 18 Sep 2021 14:02:18 -0300 Subject: [PATCH] [TRA-3659] Fix rules (#271) * Fix rules * Not reay, error on running * Empty dissector Rules() * almost working * Finally, fixed * undo changes on agent/pkg/utils/har.go * fix not showing service on rules detail * Update tap/api/api.go Co-authored-by: gadotroee <55343099+gadotroee@users.noreply.github.com> * Update agent/pkg/controllers/entries_controller.go Co-authored-by: gadotroee <55343099+gadotroee@users.noreply.github.com> * Update agent/pkg/controllers/entries_controller.go Co-authored-by: gadotroee <55343099+gadotroee@users.noreply.github.com> * unwrap Data * Fix bug off using more than one latency rule that always get the first. * fix json type, decoding base64 before unmarshal * Run `go mod tidy` on `cli/go.sum` * Fix the linting issues * Remove a `FIXME` comment * Remove a CSS rule * Adapt `ruleNumberText` CSS class to the design language of the UI * Fix an issue in the UI related to `rule.Latency` slipping out * Removed unecessary codes. Co-authored-by: gadotroee <55343099+gadotroee@users.noreply.github.com> Co-authored-by: M. Mert Yildiran --- agent/pkg/api/main.go | 9 ++++ agent/pkg/controllers/entries_controller.go | 15 ++++++- agent/pkg/models/models.go | 45 +++---------------- agent/pkg/rules/{models.go => rulesHTTP.go} | 35 +++++++++------ shared/models.go | 5 +++ tap/api/api.go | 9 ++-- ui/src/components/EntryDetailed.tsx | 3 +- .../EntryDetailed/EntrySections.tsx | 8 ++-- .../components/EntryDetailed/EntryViewer.tsx | 14 +++--- .../EntryListItem/EntryListItem.module.sass | 21 ++++++--- .../EntryListItem/EntryListItem.tsx | 45 ++++++++++--------- 11 files changed, 112 insertions(+), 97 deletions(-) rename agent/pkg/rules/{models.go => rulesHTTP.go} (75%) diff --git a/agent/pkg/api/main.go b/agent/pkg/api/main.go index 3bcf5081a..9c10886b1 100644 --- a/agent/pkg/api/main.go +++ b/agent/pkg/api/main.go @@ -116,6 +116,15 @@ func startReadingChannel(outputItems <-chan *tapApi.OutputChannelItem, extension baseEntry := extension.Dissector.Summarize(mizuEntry) mizuEntry.EstimatedSizeBytes = getEstimatedEntrySizeBytes(mizuEntry) database.CreateEntry(mizuEntry) + if extension.Protocol.Name == "http" { + var pair tapApi.RequestResponsePair + json.Unmarshal([]byte(mizuEntry.Entry), &pair) + harEntry, _ := utils.NewEntry(&pair) + rules, _ := models.RunValidationRulesState(*harEntry, mizuEntry.Service) + baseEntry.Rules = rules + baseEntry.Latency = mizuEntry.ElapsedTime + } + baseEntryBytes, _ := models.CreateBaseEntryWebSocketMessage(baseEntry) BroadcastToBrowserClients(baseEntryBytes) } diff --git a/agent/pkg/controllers/entries_controller.go b/agent/pkg/controllers/entries_controller.go index 25416fff1..035d99fb6 100644 --- a/agent/pkg/controllers/entries_controller.go +++ b/agent/pkg/controllers/entries_controller.go @@ -3,7 +3,6 @@ package controllers import ( "encoding/json" "fmt" - "github.com/google/martian/har" "mizuserver/pkg/database" "mizuserver/pkg/models" "mizuserver/pkg/providers" @@ -13,6 +12,8 @@ import ( "net/http" "time" + "github.com/google/martian/har" + "github.com/gin-gonic/gin" "github.com/romana/rlog" @@ -140,11 +141,23 @@ func GetEntry(c *gin.Context) { extension := extensionsMap[entryData.ProtocolName] protocol, representation, bodySize, _ := extension.Dissector.Represent(&entryData) + + var rules []map[string]interface{} + if entryData.ProtocolName == "http" { + var pair tapApi.RequestResponsePair + json.Unmarshal([]byte(entryData.Entry), &pair) + harEntry, _ := utils.NewEntry(&pair) + _, rulesMatched := models.RunValidationRulesState(*harEntry, entryData.Service) + inrec, _ := json.Marshal(rulesMatched) + json.Unmarshal(inrec, &rules) + } + c.JSON(http.StatusOK, tapApi.MizuEntryWrapper{ Protocol: protocol, Representation: string(representation), BodySize: bodySize, Data: entryData, + Rules: rules, }) } diff --git a/agent/pkg/models/models.go b/agent/pkg/models/models.go index e97069168..fa733b6cb 100644 --- a/agent/pkg/models/models.go +++ b/agent/pkg/models/models.go @@ -2,9 +2,10 @@ package models import ( "encoding/json" + tapApi "github.com/up9inc/mizu/tap/api" + "mizuserver/pkg/rules" - "mizuserver/pkg/utils" "github.com/google/martian/har" "github.com/up9inc/mizu/shared" @@ -15,15 +16,6 @@ func GetEntry(r *tapApi.MizuEntry, v tapApi.DataUnmarshaler) error { return v.UnmarshalData(r) } -// TODO: until we fixed the Rules feature -//func NewApplicableRules(status bool, latency int64, number int) tapApi.ApplicableRules { -// ar := tapApi.ApplicableRules{} -// ar.Status = status -// ar.Latency = latency -// ar.NumberOfRules = number -// return ar -//} - type EntriesFilter struct { Limit int `form:"limit" validate:"required,min=1,max=200"` Operator string `form:"operator" validate:"required,oneof='lt' 'gt'"` @@ -105,33 +97,8 @@ type ExtendedCreator struct { Source *string `json:"_source"` } -type FullEntryWithPolicy struct { - RulesMatched []rules.RulesMatched `json:"rulesMatched,omitempty"` - Entry har.Entry `json:"entry"` - Service string `json:"service"` +func RunValidationRulesState(harEntry har.Entry, service string) (tapApi.ApplicableRules, []rules.RulesMatched) { + resultPolicyToSend := rules.MatchRequestPolicy(harEntry, service) + statusPolicyToSend, latency, numberOfRules := rules.PassedValidationRules(resultPolicyToSend) + return tapApi.ApplicableRules{Status: statusPolicyToSend, Latency: latency, NumberOfRules: numberOfRules}, resultPolicyToSend } - -func (fewp *FullEntryWithPolicy) UnmarshalData(entry *tapApi.MizuEntry) error { - var pair tapApi.RequestResponsePair - if err := json.Unmarshal([]byte(entry.Entry), &pair); err != nil { - return err - } - harEntry, err := utils.NewEntry(&pair) - if err != nil { - return err - } - fewp.Entry = *harEntry - - _, resultPolicyToSend := rules.MatchRequestPolicy(fewp.Entry, entry.Service) - fewp.RulesMatched = resultPolicyToSend - fewp.Service = entry.Service - return nil -} - -// TODO: until we fixed the Rules feature -//func RunValidationRulesState(harEntry har.Entry, service string) tapApi.ApplicableRules { -// numberOfRules, resultPolicyToSend := rules.MatchRequestPolicy(harEntry, service) -// statusPolicyToSend, latency, numberOfRules := rules.PassedValidationRules(resultPolicyToSend, numberOfRules) -// ar := NewApplicableRules(statusPolicyToSend, latency, numberOfRules) -// return ar -//} diff --git a/agent/pkg/rules/models.go b/agent/pkg/rules/rulesHTTP.go similarity index 75% rename from agent/pkg/rules/models.go rename to agent/pkg/rules/rulesHTTP.go index 2107e6447..84215e7fc 100644 --- a/agent/pkg/rules/models.go +++ b/agent/pkg/rules/rulesHTTP.go @@ -1,6 +1,7 @@ package rules import ( + "encoding/base64" "encoding/json" "fmt" "reflect" @@ -41,7 +42,7 @@ func ValidateService(serviceFromRule string, service string) bool { return true } -func MatchRequestPolicy(harEntry har.Entry, service string) (int, []RulesMatched) { +func MatchRequestPolicy(harEntry har.Entry, service string) []RulesMatched { enforcePolicy, _ := shared.DecodeEnforcePolicy(fmt.Sprintf("%s/%s", shared.RulePolicyPath, shared.RulePolicyFileName)) var resultPolicyToSend []RulesMatched for _, rule := range enforcePolicy.Rules { @@ -50,7 +51,8 @@ func MatchRequestPolicy(harEntry har.Entry, service string) (int, []RulesMatched } if rule.Type == "json" { var bodyJsonMap interface{} - if err := json.Unmarshal(harEntry.Response.Content.Text, &bodyJsonMap); err != nil { + contentTextDecoded, _ := base64.StdEncoding.DecodeString(string(harEntry.Response.Content.Text)) + if err := json.Unmarshal(contentTextDecoded, &bodyJsonMap); err != nil { continue } out, err := jsonpath.Read(bodyJsonMap, rule.Key) @@ -63,6 +65,7 @@ func MatchRequestPolicy(harEntry har.Entry, service string) (int, []RulesMatched if err != nil { continue } + fmt.Println(matchValue, rule.Value) } else { val := fmt.Sprint(out) matchValue, err = regexp.MatchString(rule.Value, val) @@ -89,22 +92,28 @@ func MatchRequestPolicy(harEntry har.Entry, service string) (int, []RulesMatched resultPolicyToSend = appendRulesMatched(resultPolicyToSend, true, rule) } } - return len(enforcePolicy.Rules), resultPolicyToSend + return resultPolicyToSend } -func PassedValidationRules(rulesMatched []RulesMatched, numberOfRules int) (bool, int64, int) { - if len(rulesMatched) == 0 { - return false, 0, 0 +func PassedValidationRules(rulesMatched []RulesMatched) (bool, int64, int) { + var numberOfRulesMatched = len(rulesMatched) + var latency int64 = -1 + + if numberOfRulesMatched == 0 { + return false, 0, numberOfRulesMatched } + for _, rule := range rulesMatched { if rule.Matched == false { - return false, -1, len(rulesMatched) + return false, latency, numberOfRulesMatched + } else { + if strings.ToLower(rule.Rule.Type) == "latency" { + if rule.Rule.Latency < latency || latency == -1 { + latency = rule.Rule.Latency + } + } } } - for _, rule := range rulesMatched { - if strings.ToLower(rule.Rule.Type) == "latency" { - return true, rule.Rule.Latency, len(rulesMatched) - } - } - return true, -1, len(rulesMatched) + + return true, latency, numberOfRulesMatched } diff --git a/shared/models.go b/shared/models.go index 9802e3fde..9a02d8043 100644 --- a/shared/models.go +++ b/shared/models.go @@ -99,6 +99,11 @@ type RulePolicy struct { Name string `yaml:"name"` } +type RulesMatched struct { + Matched bool `json:"matched"` + Rule RulePolicy `json:"rule"` +} + func (r *RulePolicy) validateType() bool { permitedTypes := []string{"json", "header", "latency"} _, found := Find(permitedTypes, r.Type) diff --git a/tap/api/api.go b/tap/api/api.go index e9f9c0208..cf89eab80 100644 --- a/tap/api/api.go +++ b/tap/api/api.go @@ -133,10 +133,11 @@ type MizuEntry struct { } type MizuEntryWrapper struct { - Protocol Protocol `json:"protocol"` - Representation string `json:"representation"` - BodySize int64 `json:"bodySize"` - Data MizuEntry `json:"data"` + Protocol Protocol `json:"protocol"` + Representation string `json:"representation"` + BodySize int64 `json:"bodySize"` + Data MizuEntry `json:"data"` + Rules []map[string]interface{} `json:"rulesMatched,omitempty"` } type BaseEntryDetails struct { diff --git a/ui/src/components/EntryDetailed.tsx b/ui/src/components/EntryDetailed.tsx index 56ffd583b..5c7045d6f 100644 --- a/ui/src/components/EntryDetailed.tsx +++ b/ui/src/components/EntryDetailed.tsx @@ -42,7 +42,6 @@ const EntryTitle: React.FC = ({protocol, data, bodySize, elapsedTime}) => {
{response.payload &&
{formatSize(bodySize)}
}
{Math.round(elapsedTime)}ms
-
{'rulesMatched' in data ? data.rulesMatched?.length : '0'} Rules Applied
; }; @@ -72,7 +71,7 @@ export const EntryDetailed: React.FC = ({entryData}) => { /> {entryData.data && } <> - {entryData.data && } + {entryData.data && } }; diff --git a/ui/src/components/EntryDetailed/EntrySections.tsx b/ui/src/components/EntryDetailed/EntrySections.tsx index d4968e046..9b9d9d661 100644 --- a/ui/src/components/EntryDetailed/EntrySections.tsx +++ b/ui/src/components/EntryDetailed/EntrySections.tsx @@ -215,10 +215,10 @@ export const EntryTablePolicySection: React.FC = ({serv <> { rule.Key && - Key:{rule.Key} + Key: {rule.Key} } { - rule.Latency && + rule.Latency !== 0 && Latency: {rule.Latency} } { @@ -231,7 +231,7 @@ export const EntryTablePolicySection: React.FC = ({serv } { rule.Service && - Service: {service} + Service: {rule.Service} } { rule.Type && @@ -251,7 +251,7 @@ export const EntryTablePolicySection: React.FC = ({serv - : + : No rules could be applied to this request. } } diff --git a/ui/src/components/EntryDetailed/EntryViewer.tsx b/ui/src/components/EntryDetailed/EntryViewer.tsx index aa1daae6f..9271a0f47 100644 --- a/ui/src/components/EntryDetailed/EntryViewer.tsx +++ b/ui/src/components/EntryDetailed/EntryViewer.tsx @@ -33,8 +33,7 @@ const SectionsRepresentation: React.FC = ({data, color}) => { return <>{sections}; } -const AutoRepresentation: React.FC = ({representation, color}) => { - const rulesMatched = [] +const AutoRepresentation: React.FC = ({representation, rulesMatched, elapsedTime, color}) => { const TABS = [ { tab: 'request' @@ -68,8 +67,7 @@ const AutoRepresentation: React.FC = ({representation, color}) => { } {currentTab === TABS[2].tab && - {// FIXME: Fix here - } + } } ; @@ -77,11 +75,13 @@ const AutoRepresentation: React.FC = ({representation, color}) => { interface Props { representation: any; - color: string, + rulesMatched: any; + color: string; + elapsedTime: number; } -const EntryViewer: React.FC = ({representation, color}) => { - return +const EntryViewer: React.FC = ({representation, rulesMatched, elapsedTime, color}) => { + return }; export default EntryViewer; diff --git a/ui/src/components/EntryListItem/EntryListItem.module.sass b/ui/src/components/EntryListItem/EntryListItem.module.sass index 0ee428182..ddedd0ae2 100644 --- a/ui/src/components/EntryListItem/EntryListItem.module.sass +++ b/ui/src/components/EntryListItem/EntryListItem.module.sass @@ -19,20 +19,31 @@ .rowSelected border: 1px $blue-color solid -// border-left: 5px $blue-color solid - margin-left: 10px margin-right: 3px .ruleSuccessRow - border: 1px $success-color solid -// border-left: 5px $success-color solid + background: #E8FFF1 + +.ruleSuccessRowSelected + border: 1px #6FCF97 solid + border-left: 5px #6FCF97 solid .ruleFailureRow background: #FFE9EF .ruleFailureRowSelected border: 1px $failure-color solid -// border-left: 5px $failure-color solid + border-left: 5px $failure-color solid + +.ruleNumberText + font-size: 12px; + font-style: italic; + +.ruleNumberTextFailure + color: #DB2156 + +.ruleNumberTextSuccess + color: #219653 .service text-overflow: ellipsis diff --git a/ui/src/components/EntryListItem/EntryListItem.tsx b/ui/src/components/EntryListItem/EntryListItem.tsx index 98ba324a7..81da0a56c 100644 --- a/ui/src/components/EntryListItem/EntryListItem.tsx +++ b/ui/src/components/EntryListItem/EntryListItem.tsx @@ -43,6 +43,7 @@ interface EntryProps { export const EntryItem: React.FC = ({entry, setFocusedEntryId, isSelected, style}) => { const classification = getClassification(entry.statusCode) + const numberOfRules = entry.rules.numberOfRules let ingoingIcon; let outgoingIcon; switch(classification) { @@ -62,47 +63,39 @@ export const EntryItem: React.FC = ({entry, setFocusedEntryId, isSel break; } } - // let additionalRulesProperties = ""; - // let ruleSuccess: boolean; + let additionalRulesProperties = ""; + let ruleSuccess: boolean; let rule = 'latency' in entry.rules if (rule) { if (entry.rules.latency !== -1) { if (entry.rules.latency >= entry.latency) { - // additionalRulesProperties = styles.ruleSuccessRow - // ruleSuccess = true + additionalRulesProperties = styles.ruleSuccessRow + ruleSuccess = true } else { - // additionalRulesProperties = styles.ruleFailureRow - // ruleSuccess = false + additionalRulesProperties = styles.ruleFailureRow + ruleSuccess = false } if (isSelected) { - // additionalRulesProperties += ` ${entry.rules.latency >= entry.latency ? styles.ruleSuccessRowSelected : styles.ruleFailureRowSelected}` + additionalRulesProperties += ` ${entry.rules.latency >= entry.latency ? styles.ruleSuccessRowSelected : styles.ruleFailureRowSelected}` } } else { if (entry.rules.status) { - // additionalRulesProperties = styles.ruleSuccessRow - // ruleSuccess = true + additionalRulesProperties = styles.ruleSuccessRow + ruleSuccess = true } else { - // additionalRulesProperties = styles.ruleFailureRow - // ruleSuccess = false + additionalRulesProperties = styles.ruleFailureRow + ruleSuccess = false } if (isSelected) { - // additionalRulesProperties += ` ${entry.rules.status ? styles.ruleSuccessRowSelected : styles.ruleFailureRowSelected}` + additionalRulesProperties += ` ${entry.rules.status ? styles.ruleSuccessRowSelected : styles.ruleFailureRowSelected}` } } } - let backgroundColor = ""; - if ('latency' in entry.rules) { - if (entry.rules.latency !== -1) { - backgroundColor = entry.rules.latency >= entry.latency ? styles.ruleSuccessRow : styles.ruleFailureRow - } else { - backgroundColor = entry.rules.status ? styles.ruleSuccessRow : styles.ruleFailureRow - } - } return <>
setFocusedEntryId(entry.id)} style={{ border: isSelected ? `1px ${entry.protocol.backgroundColor} solid` : "1px transparent solid", @@ -122,6 +115,13 @@ export const EntryItem: React.FC = ({entry, setFocusedEntryId, isSel {entry.service}
+ { + rule ? +
+ {`Rules (${numberOfRules})`} +
+ : "" + }
{entry.sourcePort} {entry.isOutgoing ? @@ -138,4 +138,5 @@ export const EntryItem: React.FC = ({entry, setFocusedEntryId, isSel
-}; + +}