diff --git a/activity/README.md b/activity/README.md index 301c7da..f9cb3b9 100644 --- a/activity/README.md +++ b/activity/README.md @@ -5,3 +5,4 @@ Activities that are very specific to the operation of the Microgateway. * [jwt](jwt) allows for JSON web token based authentication * [ratelimiter](ratelimiter) is a rate limiter implementation * [sqld](sqld) is a SQL injection attack detector +* [obfuscate](obfuscate) is an activity that allows you to obfuscate the required value in the JSON using defined function \ No newline at end of file diff --git a/activity/obfuscatejson/README.md b/activity/obfuscatejson/README.md new file mode 100644 index 0000000..331ac24 --- /dev/null +++ b/activity/obfuscatejson/README.md @@ -0,0 +1,45 @@ + + +# Obfuscate JSON Payload +This activity allows you to obfuscate the required value in the playload of type JSON using defined function. + +Eg . If the payload has a field contianing sensitive information; this activity would obfuscate that field. +```{ + ... + "BookingCreditCard":"41462917261957261", + ... + } + becomes + { + ... + "BookingCreditCard":"**********7261", + ... + } +``` +## Installation + +### Flogo CLI +```bash +flogo install github.com/microgateway/activity/obfuscatejson +``` + +## Configuration + +### Settings: +| Name | Type | Description +|:--- | :--- | :--- +| operation | string | The operation to perform (Allowed values are setLastFour) - **REQUIRED** +| fields | array | The fields of json to obfuscate - **REQUIRED** + +### Supoorted Operations +| Name | Description +|:--- | :--- +| setLastFour | This operation adds "*" in place of characters of the field except last four. + +### Input: +| Name | Type | Description +|:--- | :--- | :--- +| payload | string | The message to obfuscate \ No newline at end of file diff --git a/activity/obfuscatejson/activity.go b/activity/obfuscatejson/activity.go new file mode 100644 index 0000000..a8c11f0 --- /dev/null +++ b/activity/obfuscatejson/activity.go @@ -0,0 +1,107 @@ +package obfusactejson + +import ( + "bytes" + "strings" + + "github.com/project-flogo/core/activity" + "github.com/project-flogo/core/data/metadata" +) + +func init() { + _ = activity.Register(&Activity{}, New) //activity.Register(&Activity{}, New) to create instances using factory method 'New' +} + +// Function which defines how to obfuscate the value +type obfuscateFunc func(a string) string + +var activityMd = activity.ToMetadata(&Settings{}, &Input{}, &Output{}) + +//New optional factory method, should be used if one activity instance per configuration is desired +func New(ctx activity.InitContext) (activity.Activity, error) { + + s := &Settings{} + err := metadata.MapToStruct(ctx.Settings(), s, true) + if err != nil { + return nil, err + } + + var op obfuscateFunc + + // Set the operation. + if s.Operation == "setLastFour" { + op = setLastFour + } + + act := &Activity{settings: s, operation: op} + + return act, nil +} + +type Activity struct { + settings *Settings + operation obfuscateFunc +} + +func (a *Activity) Metadata() *activity.Metadata { + return activityMd +} + +func (a *Activity) Eval(ctx activity.Context) (done bool, err error) { + + input := &Input{} + err = ctx.GetInputObject(input) + if err != nil { + return true, err + } + payload := input.Payload + + // Iterate over the keys for which the obfuscate function should apply. + for _, val := range a.settings.Fields { + payload = obfuscate(a.operation, val.(string), payload) + } + + ctx.SetOutput("result", payload) + + return true, nil +} + +// Onfuscate takes in the obfuscate function, key and the payload +// and returns the string where the value of the key is obfuscated. +func obfuscate(op obfuscateFunc, key, payload string) string { + // Get the index where the key ends. + // Eg "key":"13445". Should return 6. + keyEndIndex := strings.Index(payload, key) + len(key) + 3 + + //Get the index where the value corresponding to that key ends. + //Eg. with the above eg it should return 12 + valEndIndex := keyEndIndex + strings.Index(payload[keyEndIndex+1:], "\"") + + //Get the value corresponding to the key. + keyVal := payload[keyEndIndex:valEndIndex] + + // Apply obfuscate function. + val := op(keyVal) + + // Stich the result + result := payload[:keyEndIndex] + val + payload[valEndIndex:] + + return result +} + +func setLastFour(val string) string { + + var buffer bytes.Buffer + + for key, v := range val { + if key < len(val)-3 { + buffer.WriteString("*") + } else { + buffer.WriteString(string(v)) + } + + } + + return buffer.String() + +} diff --git a/activity/obfuscatejson/activity_test.go b/activity/obfuscatejson/activity_test.go new file mode 100644 index 0000000..31162c9 --- /dev/null +++ b/activity/obfuscatejson/activity_test.go @@ -0,0 +1,43 @@ +package obfusactejson + +import ( + "testing" + + "github.com/project-flogo/core/activity" + "github.com/project-flogo/core/support/test" + "github.com/stretchr/testify/assert" +) + +func TestRegister(t *testing.T) { + + ref := activity.GetRef(&Activity{}) + act := activity.Get(ref) + + assert.NotNil(t, act) +} + +var payload string + +func TestEval(t *testing.T) { + settings := &Settings{Operation: "setLastFour", Fields: []interface{}{"LoyaltyRewardsNumber", "BookingCreditCard"}} + + iCtx := test.NewActivityInitContext(settings, nil) + act, err := New(iCtx) + assert.Nil(t, err) + + payload = `{"application/json":{"flightTrack":{"flightId":271143235,"carrier":{"fs":"EK","name":"Emirates","phoneNumber":"1-800-777-3999","active":true},"CustomerProfile":{"FirstName":"Arden","LastName":"Kaur","LoyaltyRewardsNumber":"EK2340983419","BookingCreditCard":"41462917261957261"},"flightNumber":"202","tailNumber":"N774AN","callsign":"EK202","departureAirport":{"fs":"JFK","iata":"JFK","icao":"KJFK","faa":"JFK","name":"John F. Kennedy International Airport","street1":"JFK Airport","street2":"","city":"New York","cityCode":"NYC","stateCode":"NY","postalCode":"11430","countryCode":"US","countryName":"United States","regionName":"North America","timeZoneRegionName":"America/New_York","weatherZone":"NYZ076","localTime":"2020-08-09T14:58:44.106","utcOffsetHours":-4,"latitude":40.642335,"longitude":-73.78817,"elevationFeet":13,"classification":1,"active":true},"arrivalAirport":{"fs":"EK","name":"Dubai International Airport","city":"Dubai","utcOffsetHours":1,"latitude":51.469603,"longitude":-0.453566,"elevationFeet":80,"classification":1,"active":true},"departureDate":{"dateLocal":"2020-08-08T18:10:00.000","dateUtc":"2020-08-08T22:10:00.000Z"},"equipment":"777","delayMinutes":1,"bearing":119.04182593265193,"heading":89.9998044218202,"positions":[{"lon":-0.4657000005245209,"lat":51.47380065917969,"speedMph":154,"altitudeFt":360,"source":"ADS-B","date":"2020-08-09T05:13:13.000Z"},{"lon":-0.46619999408721924,"lat":51.47380065917969,"speedMph":154,"altitudeFt":360,"source":"ADS-B","date":"2020-08-09T05:12:52.000Z"},{"lon":-0.46650001406669617,"lat":51.47380065917969,"speedMph":154,"altitudeFt":360,"source":"ADS-B","date":"2020-08-09T05:12:33.000Z"},{"lon":-0.4668999910354614,"lat":51.47380065917969,"speedMph":154,"altitudeFt":360,"source":"ADS-B","date":"2020-08-09T05:12:23.000Z"}]}}}` + + tc := test.NewActivityContext(act.Metadata()) + input := &Input{Payload: payload} + err = tc.SetInputObject(input) + assert.Nil(t, err) + + done, err := act.Eval(tc) + assert.True(t, done) + assert.Nil(t, err) + + val := tc.GetOutput("result") + assert.NotNil(t, val) + + assert.Contains(t, val.(string), "*************7261", "It obfuscated the digits of BookingCreditCard") +} diff --git a/activity/obfuscatejson/descriptor.json b/activity/obfuscatejson/descriptor.json new file mode 100644 index 0000000..6df3b49 --- /dev/null +++ b/activity/obfuscatejson/descriptor.json @@ -0,0 +1,33 @@ +{ + "name": "obfuscatejson-activity", + "type": "flogo:activity", + "version": "0.0.1", + "title": "ObfuscateJSON Activity", + "description": "Obfuscate JSON Activity llows you to obfuscate the required value in the playload of type JSON using defined function.", + "settings": [ + { + "name": "operation", + "type": "string", + "required": true, + "allowed": ["setLastFour"] + }, + { + "name": "fields", + "type": "array", + "required": true + } + ], + "input": [ + { + "name": "payload", + "type": "string", + "required": true + } + ], + "output": [ + { + "name": "result", + "type": "any" + } + ] + } \ No newline at end of file diff --git a/activity/obfuscatejson/metadata.go b/activity/obfuscatejson/metadata.go new file mode 100644 index 0000000..43f7ab7 --- /dev/null +++ b/activity/obfuscatejson/metadata.go @@ -0,0 +1,39 @@ +package obfusactejson + +import "github.com/project-flogo/core/data/coerce" + +type Settings struct { + Operation string `md:"operation,required"` + Fields []interface{} `md:"fields,required"` +} + +type Input struct { + Payload string `md:"payload"` +} + +func (r *Input) FromMap(values map[string]interface{}) error { + payload, _ := coerce.ToString(values["payload"]) + r.Payload = payload + return nil +} + +func (r *Input) ToMap() map[string]interface{} { + return map[string]interface{}{ + "payload": r.Payload, + } +} + +type Output struct { + Result interface{} `md:"result"` +} + +func (o *Output) FromMap(values map[string]interface{}) error { + o.Result, _ = values["result"] + return nil +} + +func (o *Output) ToMap() map[string]interface{} { + return map[string]interface{}{ + "result": o.Result, + } +}